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/service/main/workflow/cmd:all-srcs",
"//app/service/main/workflow/conf:all-srcs",
"//app/service/main/workflow/dao:all-srcs",
"//app/service/main/workflow/http:all-srcs",
"//app/service/main/workflow/model:all-srcs",
"//app/service/main/workflow/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,98 @@
#### 工单流对外接口
#### Version 1.2.13
> 1. workflow_business表不使用uk查询
#### Version 1.2.12
> 1. 举报异步更新第一个用户举报tag
#### Version 1.2.11
> 1. 去掉查询稿件db
#### Version 1.2.10
> 1. 增加移交众裁set接口
#### Version 1.2.9
> 1. sobot dao 初始化
#### Version 1.2.8
> 1. v1上报接口写rid
#### Version 1.2.7
> 1. 提交举报aid字段覆盖db workflow_business.oid 字段
#### Version 1.2.6
> 1. 增加错误码
#### Version 1.2.5
> 1. 修改tag返回结果
#### Version 1.2.4
> 1. 修改verify验证
#### Version 1.2.3
> 1. 增加删除工单接口
#### Version 1.2.2
> 1. fix ut
#### Version 1.2.1
> 1. 修改lasttime插入
#### Version 1.2.0
> 1. 修复 extra 信息读写
#### Version 1.1.9
> 1. 修改获取tag路由
#### Version 1.1.8
> 1. 修改business插入逻辑
#### Version 1.1.7
> 1. 修改tag获取方式
#### Version 1.1.6
> 1. 增加错误日志信息
#### Version 1.1.5
> 1. workflow v3 和 稿件临时方案
#### Version 1.1.4
> 1.replace identify to verify
#### Version 1.1.3
> 1.net/http/parse 改成 bm/binding
#### Version 1.1.2
> 1.迁移到bm框架
##### Version 1.1.1
> 1.修复审核状态
##### Version 1.1.0
> 1.增加事务开始错误处理
##### Version 1.1.0
> 1.稿件申诉接口大调整
##### Version 1.0.4
> 1.增加小黑屋业务的支持
> 2.以及更多骚操作...
##### Version 1.0.3
> 1.增加点评业务的支持
> 2.支持储存业务字段
##### Version 1.0.2
> 1.增加 appeal 相关接口
> 2.好像没有了~
##### Version 1.0.1
> 1.集成智齿工单系统
> 2.引入 gorm
##### Version 1.0.0
> 1.集成稿件投诉/申诉
##### Version 0.0.1
> 1.工单流项目初版

View File

@@ -0,0 +1,13 @@
### workflow-service
# Owner
haoguanwei
zhapuyu
zhoushuguang
# Author
zhoushuguang
# Reviewer
zhapuyu
zhoushuguang

View File

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

View File

@@ -0,0 +1,10 @@
#### workflow-service
##### 项目简介
> 1.申诉投诉工单流对外接口
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common

View File

@@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["workflow-service.toml"],
importpath = "go-common/app/service/main/workflow/cmd",
tags = ["automanaged"],
deps = [
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/http:go_default_library",
"//app/service/main/workflow/service:go_default_library",
"//library/log:go_default_library",
"//library/net/trace: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,50 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/service/main/workflow/conf"
"go-common/app/service/main/workflow/http"
"go-common/app/service/main/workflow/service"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.Log)
defer trace.Close()
defer log.Close()
log.Info("workflow-service start")
// init server
svr := service.New(conf.Conf)
// init http
http.Init(conf.Conf, svr)
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("workflow-service get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
svr.Close()
time.Sleep(1 * time.Second)
log.Info("workflow-service exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,97 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
pid = "/tmp/workflow-service.pid"
dir = "./"
perf = "127.0.0.1:7220"
family = "workflow-service"
[log]
dir = "/data/log/workflow-service/"
#[log.agent]
#family = "workflow-service"
#addr = "172.18.20.17:8520"
#chan = 10240
[tracer]
proto = "udp"
addr = "172.16.33.46:5140"
tag = "platform/workflow-service"
[bm]
addr = "0.0.0.0:7221"
timeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
[host]
serviceURI = "http://service.bilibili.com"
managerURI = "http://uat-manager.bilibili.co"
[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 = 5
idle = 5
idleTimeout = "4h"
[httpClient]
[httpClient.sobot]
key = "fLmW4cJFn7mL7pBo"
secret = "a9e79f60e30b42a0bf621f615d96ec25"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 1000
[httpClient.sobot.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient.read]
key = "b8f239ca38a53308"
secret = "5460ef72fe13c10dfb53442b9111427e"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 128
[httpClient.read.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient.write]
key = "b8f239ca38a53308"
secret = "5460ef72fe13c10dfb53442b9111427e"
dial = "80ms"
timeout = "700ms"
keepAlive = "60s"
timer = 128
[httpClient.write.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[archiveRPC]
timeout = "2s"
[elastic]
host = "http://uat-manager.bilibili.co"
[elastic.HTTPClient]
key = "3c4e41f926e51656"
secret = "26a2095b60c24154521d24ae62b885bb"
dial = "1s"
timeout = "2s"
keepAlive = "60s"
timer = 1000
[elastic.HTTPClient.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100

View File

@@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/service/main/workflow/conf",
tags = ["automanaged"],
deps = [
"//library/conf:go_default_library",
"//library/database/elastic:go_default_library",
"//library/database/orm:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/rpc/warden: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,114 @@
package conf
import (
"errors"
"flag"
"go-common/library/conf"
"go-common/library/database/elastic"
"go-common/library/database/orm"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/rpc/warden"
"github.com/BurntSushi/toml"
)
var (
confPath string
// Conf .
Conf = &Config{}
client *conf.Client
)
// Config struct
type Config struct {
// base
// log
Log *log.Config
// http
BM *bm.ServerConfig
// verify
Verify *verify.Config
// db
ORM *ORM
// host
Host *Host
// http client test
HTTPClient *HTTPClient
// archive rpc
ArchiveRPC *warden.ClientConfig
// es
Elastic *elastic.Config
}
// ORM struct
type ORM struct {
Write *orm.Config
}
// Host struct
type Host struct {
ServiceURI string
ManagerURI string
}
// HTTPClient struct
type HTTPClient struct {
Sobot *bm.ClientConfig
Read *bm.ClientConfig
Write *bm.ClientConfig
Audit *bm.ClientConfig
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init .
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,61 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"callback_test.go",
"dao_test.go",
"es_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"callback.go",
"dao.go",
"es.go",
],
importpath = "go-common/app/service/main/workflow/dao",
tags = ["automanaged"],
deps = [
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/model:go_default_library",
"//library/database/elastic:go_default_library",
"//library/database/orm:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/workflow/dao/sobot:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,21 @@
package dao
import (
"context"
"errors"
"go-common/app/service/main/workflow/model"
"go-common/library/log"
)
// Callback callback message
func (d *Dao) Callback(c context.Context, chall *model.Challenge, businessID int8) (err error) {
if URL, ok := d.callbackMap[businessID]; ok {
if err = d.callback.Post(context.Background(), URL, "", nil, &chall); err != nil {
log.Error("d.CallbackSetting(%s) error(%v)", chall, err)
return
}
return
}
return errors.New("Callback cannot find businessID in callbackMap")
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"context"
"testing"
"go-common/app/service/main/workflow/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoCallback(t *testing.T) {
var (
c = context.Background()
chall = &model.Challenge{}
businessID = int8(6)
)
convey.Convey("Callback", t, func(ctx convey.C) {
err := d.Callback(c, chall, businessID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,81 @@
package dao
import (
"context"
"go-common/app/service/main/workflow/conf"
"go-common/app/service/main/workflow/model"
"go-common/library/database/elastic"
"go-common/library/database/orm"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"github.com/jinzhu/gorm"
)
const (
_mngTagURL = "/x/admin/manager/internal/tag/list"
_mngControlURL = "/x/admin/manager/internal/control/list"
)
// Dao tag dao
type Dao struct {
c *conf.Config
// db *sql.DB
DB *gorm.DB
callback *bm.Client
callbackMap map[int8]string
ReadClient *bm.Client
MngTagURL string
MngControlURL string
es *elastic.Elastic
}
// New init dao
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
DB: orm.NewMySQL(c.ORM.Write),
callback: bm.NewClient(c.HTTPClient.Write),
callbackMap: make(map[int8]string),
ReadClient: bm.NewClient(c.HTTPClient.Read),
MngTagURL: c.Host.ManagerURI + _mngTagURL,
MngControlURL: c.Host.ManagerURI + _mngControlURL,
es: elastic.NewElastic(c.Elastic),
}
d.initORM()
d.initCallback()
return
}
func (d *Dao) initORM() {
d.DB.LogMode(true)
}
func (d *Dao) initCallback() {
callbacks := []model.Callback{}
if err := d.DB.Where("state =?", model.Enabled).Find(&callbacks).Error; err != nil {
log.Error("d.CallbackSetting() error(%v)", err)
panic(err)
}
for _, callback := range callbacks {
d.callbackMap[callback.Business] = callback.URL
}
}
// Close close dao.
func (d *Dao) Close() {
if d.DB != nil {
d.DB.Close()
}
}
// Ping ping cpdb
func (d *Dao) Ping(c context.Context) (err error) {
if d.DB != nil {
err = d.DB.DB().PingContext(c)
}
return
}

View File

@@ -0,0 +1,51 @@
package dao
import (
"context"
"flag"
"os"
"testing"
"go-common/app/service/main/workflow/conf"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.manager.workflow-service")
flag.Set("conf_token", "410a3a978eeda5cf44b66f193708c283")
flag.Set("tree_id", "6791")
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-service.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
d.callbackMap = map[int8]string{6: "http://uat-manager.bilibili.co/api/v4/archive/ticket/callback"}
os.Exit(m.Run())
}
func TestPing(t *testing.T) {
var (
c = context.Background()
)
convey.Convey("Ping", t, func(ctx convey.C) {
err := d.Ping(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,20 @@
package dao
import (
"context"
"go-common/library/log"
)
// UpdateUserTag 更新举报对象的第一个用户tag
func (d *Dao) UpdateUserTag(c context.Context, gid int64, userTid int32) (err error) {
us := d.es.NewUpdate("workflow_group_common").Insert()
data := map[string]int64{"id": gid, "first_user_tid": int64(userTid)}
us.AddData("workflow_group_common", data)
if err = us.Do(c); err != nil {
log.Error("failed upsert elastic error(%v)", err)
return
}
log.Info("gid(%d) has first user complain tid(%d) success upsert", gid, userTid)
return
}

View File

@@ -0,0 +1,24 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoUpdateUserTag(t *testing.T) {
convey.Convey("UpdateUserTag", t, func(ctx convey.C) {
var (
c = context.Background()
gid = int64(1)
userTid = int32(1)
)
ctx.Convey("When everything gose positive", func(ctx convey.C) {
err := d.UpdateUserTag(c, gid, userTid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"sobot_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/model/sobot:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"sobot.go",
],
importpath = "go-common/app/service/main/workflow/dao/sobot",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/model/sobot: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,30 @@
package sobot
import (
"go-common/app/service/main/workflow/conf"
bm "go-common/library/net/http/blademaster"
)
// Dao it
type Dao struct {
c *conf.Config
ticketInfoURL string
ticketAddURL string
ticketModifyURL string
replyAddURL string
// sobot httpclient
httpSobot *bm.Client
}
// New Dao
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
httpSobot: bm.NewClient(c.HTTPClient.Sobot),
ticketInfoURL: c.Host.ServiceURI + _sobotTicketInfoURL,
ticketAddURL: c.Host.ServiceURI + _sobotAddTicketURL,
ticketModifyURL: c.Host.ServiceURI + _sobotTicketModifyURL,
replyAddURL: c.Host.ServiceURI + _sobotAddReplyURL,
}
return
}

View File

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

View File

@@ -0,0 +1,160 @@
package sobot
import (
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"go-common/library/log"
"net/http"
"net/url"
"strconv"
"strings"
"go-common/app/service/main/workflow/model/sobot"
)
const (
_sobotTicketModifyURL = "/ws/updateStatusBilibili/4"
_sobotAddTicketURL = "/ws/addCustomerTicketBilibili/4"
_sobotAddReplyURL = "/ws/addCustomerReplyInfoBilibili/4"
_sobotTicketInfoURL = "/ws/queryTicketReplyByCustomerListBilibili/4"
)
// SobotTicketInfo get ticket into
func (d *Dao) SobotTicketInfo(c context.Context, ticketID int32) (res json.RawMessage, err error) {
var (
req *http.Request
)
params := url.Values{}
params.Set("companyId", d.c.HTTPClient.Sobot.Secret)
params.Set("ticketId", strconv.Itoa(int(ticketID)))
sign := md5.Sum([]byte(fmt.Sprintf("%s%s%d", d.c.HTTPClient.Sobot.Secret, d.c.HTTPClient.Sobot.Key, ticketID)))
params.Set("sobotKey", hex.EncodeToString(sign[:]))
if req, err = http.NewRequest("GET", d.ticketInfoURL+"?"+params.Encode(), nil); err != nil {
log.Error("http.NewRequest(GET,%s) error(%v)", d.ticketInfoURL, err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err = d.httpSobot.Do(c, req, &res); err != nil {
log.Error("d.httpSobot.Do() error(%v)", err)
return
}
return
}
// SobotAddTicket add ticket to sobot
func (d *Dao) SobotAddTicket(c context.Context, tp *sobot.TicketParam) (err error) {
var (
req *http.Request
res struct {
RetCode string `json:"retCode"`
Item string `json:"item"`
}
)
params := url.Values{}
params.Set("fileStr", tp.FileStr)
params.Set("companyId", d.c.HTTPClient.Sobot.Secret)
params.Set("customerName", tp.CustomerName)
params.Set("customerQq", tp.CustomerQQ)
params.Set("customerNick", tp.CustomerNick)
params.Set("customerEmail", tp.CustomerEmail)
params.Set("customerPhone", tp.CustomerPhone)
params.Set("customerSource", strconv.Itoa(int(tp.CustomerSource)))
params.Set("ticketId", strconv.Itoa(int(tp.TicketID)))
params.Set("ticketTitle", tp.TicketTitle)
params.Set("ticketContent", tp.TicketContent)
params.Set("ticketLevel", strconv.Itoa(int(tp.TicketLevel)))
params.Set("ticketStatus", strconv.Itoa(int(tp.TicketStatus)))
params.Set("ticketFrom", strconv.Itoa(int(sobot.TicketFrom)))
sign := md5.Sum([]byte(fmt.Sprintf("%s%s%d", d.c.HTTPClient.Sobot.Secret, d.c.HTTPClient.Sobot.Key, tp.TicketID)))
params.Set("sobotKey", hex.EncodeToString(sign[:]))
if req, err = http.NewRequest("POST", d.ticketAddURL, strings.NewReader(params.Encode())); err != nil {
log.Error("http.NewRequest(POST,%s) error(%v)", d.ticketAddURL, err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err = d.httpSobot.Do(c, req, &res); err != nil {
log.Error("d.httpSobot.Do() error(%v)", err)
return
}
if res.RetCode != sobot.EcodeOK {
log.Error("d.httpSobot.Do() url(%s) ecode(%s)", d.ticketAddURL, res.RetCode)
err = errors.New(res.RetCode)
return
}
return
}
// SobotAddReply add reply to sobot
func (d *Dao) SobotAddReply(c context.Context, rp *sobot.ReplyParam) (err error) {
var (
req *http.Request
res struct {
RetCode string `json:"retCode"`
Item string `json:"item"`
}
)
params := url.Values{}
params.Set("companyId", d.c.HTTPClient.Sobot.Secret)
params.Set("customerEmail", rp.CustomerEmail)
params.Set("replyContent", rp.ReplyContent)
params.Set("ticketId", strconv.Itoa(int(rp.TicketID)))
params.Set("replyType", strconv.Itoa(int(rp.ReplyType)))
params.Set("startType", strconv.Itoa(int(rp.StartType)))
sign := md5.Sum([]byte(fmt.Sprintf("%s%s%d", d.c.HTTPClient.Sobot.Secret, d.c.HTTPClient.Sobot.Key, rp.TicketID)))
params.Set("sobotKey", hex.EncodeToString(sign[:]))
if req, err = http.NewRequest("POST", d.replyAddURL, strings.NewReader(params.Encode())); err != nil {
log.Error("http.NewRequest(POST,%s) error(%v)", d.replyAddURL, err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err = d.httpSobot.Do(c, req, &res); err != nil {
log.Error("d.httpSobot.Do() error(%v)", err)
return
}
if res.RetCode != sobot.EcodeOK {
log.Error("d.httpSobot.Do() url(%s) ecode(%s)", d.replyAddURL, res.RetCode)
err = errors.New(res.RetCode)
return
}
return
}
// SobotTicketModify modify ticket
func (d *Dao) SobotTicketModify(c context.Context, tp *sobot.TicketParam) (err error) {
// http
var (
req *http.Request
res struct {
RetCode string `json:"retCode"`
Item string `json:"item"`
}
)
params := url.Values{}
params.Set("companyId", d.c.HTTPClient.Sobot.Secret)
params.Set("customerEmail", tp.CustomerEmail)
params.Set("ticketId", strconv.Itoa(int(tp.TicketID)))
params.Set("ticketFrom", strconv.Itoa(int(sobot.TicketFrom)))
params.Set("ticketStatus", strconv.Itoa(int(tp.TicketStatus)))
params.Set("startType", strconv.Itoa(int(tp.StartType)))
sign := md5.Sum([]byte(fmt.Sprintf("%s%s%d", d.c.HTTPClient.Sobot.Secret, d.c.HTTPClient.Sobot.Key, tp.TicketID)))
params.Set("sobotKey", hex.EncodeToString(sign[:]))
if req, err = http.NewRequest("POST", d.ticketModifyURL, strings.NewReader(params.Encode())); err != nil {
log.Error("http.NewRequest(POST,%s) error(%v)", d.ticketModifyURL, err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err = d.httpSobot.Do(c, req, &res); err != nil {
log.Error("d.httpSobot.Do() error(%v)", err)
return
}
if res.RetCode != sobot.EcodeOK {
log.Error("d.httpSobot.Do() url(%s) ecode(%s)", d.ticketModifyURL, res.RetCode)
err = errors.New(res.RetCode)
return
}
return
}

View File

@@ -0,0 +1,78 @@
package sobot
import (
"context"
"testing"
"go-common/app/service/main/workflow/model/sobot"
"github.com/smartystreets/goconvey/convey"
)
func TestSobotSobotTicketInfo(t *testing.T) {
var (
c = context.Background()
ticketID = int32(0)
)
convey.Convey("SobotTicketInfo", t, func(ctx convey.C) {
res, err := d.SobotTicketInfo(c, ticketID)
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 TestSobotSobotAddTicket(t *testing.T) {
var (
c = context.Background()
tp = &sobot.TicketParam{
TicketTitle: "我是202工单",
TicketID: 202,
TicketContent: "233333333",
CustomerEmail: "1107691251@qq.com",
}
)
convey.Convey("SobotAddTicket", t, func(ctx convey.C) {
err := d.SobotAddTicket(c, tp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err.Error(), convey.ShouldEqual, "2000101")
})
})
}
func TestSobotSobotAddReply(t *testing.T) {
var (
c = context.Background()
rp = &sobot.ReplyParam{
TicketID: 202,
ReplyContent: "reply_test",
CustomerEmail: "1107691251@qq.com",
StartType: 1,
ReplyType: 1,
}
)
convey.Convey("SobotAddReply", t, func(ctx convey.C) {
err := d.SobotAddReply(c, rp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestSobotSobotTicketModify(t *testing.T) {
var (
c = context.Background()
tp = &sobot.TicketParam{
TicketID: 202,
CustomerEmail: "1107691251@qq.com",
StartType: 1,
}
)
convey.Convey("SobotTicketModify", t, func(ctx convey.C) {
err := d.SobotTicketModify(c, tp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}

View File

@@ -0,0 +1,47 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"business.go",
"challenge.go",
"group.go",
"http.go",
"sign.go",
"sobot.go",
"tag.go",
],
importpath = "go-common/app/service/main/workflow/http",
tags = ["automanaged"],
deps = [
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/model:go_default_library",
"//app/service/main/workflow/model/account:go_default_library",
"//app/service/main/workflow/model/sobot:go_default_library",
"//app/service/main/workflow/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/http/blademaster/middleware/verify: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,34 @@
package http
import (
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// UpBusinessExtra update business extra
func upBusinessExtra(c *bm.Context) {
ap := new(struct {
Cid int32 `form:"cid" validate:"required"`
Mid int64 `form:"mid" validate:"required"`
Business int8 `form:"business" validate:"required"`
Key string `form:"key" validate:"required"`
Val string `form:"val" validate:"required"`
})
if err := c.BindWith(ap, binding.FormPost); err != nil {
return
}
c.JSON(nil, wkfSvc.UpBusinessExtraV2(c, ap.Cid, ap.Mid, ap.Business, ap.Key, ap.Val))
}
// BusinessExtra get business extra
func businessExtra(c *bm.Context) {
ap := new(struct {
Cid int32 `form:"cid" validate:"required"`
Mid int64 `form:"mid" validate:"required"`
Business int8 `form:"business" validate:"required"`
})
if err := c.Bind(ap); err != nil {
return
}
c.JSON(wkfSvc.BusinessExtraV2(c, ap.Cid, ap.Mid, ap.Business))
}

View File

@@ -0,0 +1,194 @@
package http
import (
"strconv"
"strings"
"go-common/app/service/main/workflow/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
// AddChallenge add challenge
func addChallenge(c *bm.Context) {
ap := new(model.ChallengeParam)
if err := c.BindWith(ap, binding.FormPost); err != nil {
return
}
if ap.AttachmentsStr != "" {
ap.Attachments = strings.Split(ap.AttachmentsStr, ",")
}
if wkfSvc.TagMap(ap.Business, ap.Tid) == nil {
c.JSON(nil, ecode.RequestErr)
return
}
for _, ctrl := range wkfSvc.TagMap(ap.Business, ap.Tid).Controls {
if ctrlValue := c.Request.PostForm.Get(ctrl.Name); ctrlValue != "" {
ap.MetaData += ctrl.Name + ": " + ctrlValue + "\n"
} else if ctrl.Required {
log.Error("http addChallenge() control parms error ctrl.Name(%s) is required! ap(%+v)", ctrl.Name, ap)
c.JSON(nil, ecode.RequestErr)
return
} else {
log.Info("http addChallenge() control parms missing ctrl.Name(%s) but not required ap(%+v)", ctrl.Name, ap)
continue
}
}
if !ap.CheckAdd() {
log.Error("s.AddChallenge() params(%+v) error", ap)
c.JSON(nil, ecode.RequestErr)
return
}
challengeNo, err := wkfSvc.AddChallenge(c, ap)
if err != nil {
c.JSON(nil, err)
return
}
data := map[string]int64{
"challengeNo": challengeNo,
}
c.JSON(data, nil)
}
// ListChallenge get challenge list
func listChallenge(c *bm.Context) {
ap := new(model.ChallengeParam)
if err := c.Bind(ap); err != nil {
return
}
if !ap.CheckList() {
log.Error("s.Challenges() params(%+v) error", ap)
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(wkfSvc.Challenges(c, ap))
}
// ReplyAddChallenge add reply to challenge
func replyAddChallenge(c *bm.Context) {
rp := new(struct {
Cid int32 `form:"cid" validate:"required"`
Event int8 `form:"event" validate:"required"`
Content string `form:"content" validate:"required"`
Attachments string `form:"attachments"`
})
if err := c.BindWith(rp, binding.FormPost); err != nil {
return
}
_, err := wkfSvc.AddEvent(c, rp.Cid, rp.Content, rp.Attachments, rp.Event)
c.JSON(nil, err)
}
// ChallengeInfo get challenge info
func challengeInfo(c *bm.Context) {
ap := new(model.ChallengeParam)
if err := c.Bind(ap); err != nil {
return
}
if !ap.CheckInfo() {
log.Error("s.Challenge() params(%+v) error", ap)
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(wkfSvc.Challenge(c, ap))
}
// upChallengeState update challenge business state
func upChallengeState(c *bm.Context) {
var role int8
ap := new(struct {
ID int32 `form:"id" validate:"required"`
Mid int64 `form:"mid" validate:"required"`
Business int8 `form:"business" validate:"required"`
BusinessState int8 `form:"business_state"`
})
roleStr := c.Request.PostForm.Get("role")
if roleStr == "" {
role = model.CustomerServiceRole
} else {
result, err := strconv.ParseUint(roleStr, 10, 8)
if err != nil {
c.JSON(nil, ecode.RequestErr)
c.Abort()
return
}
role = int8(result)
}
if err := c.BindWith(ap, binding.FormPost); err != nil {
return
}
c.JSON(nil, wkfSvc.UpChallengeState(c, ap.ID, ap.Mid, ap.Business, role, ap.BusinessState))
}
// CloseChallenge make challenge business state closed
func closeChallenge(c *bm.Context) {
ap := new(struct {
Cid int32 `form:"cid" validate:"required"`
Business int8 `form:"business" validate:"required"`
Role int8 `form:"role" validate:"required"`
BusinessState int8 `form:"business_state"`
Note string `form:"note" validate:"required"`
})
if err := c.Bind(ap); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, wkfSvc.CloseChallenge(c, ap.Cid, ap.Business, ap.Role, ap.BusinessState, ap.Note))
}
// untreatedChallenge get untreated challenge
func untreatedChallenge(c *bm.Context) {
ap := new(struct {
Oid int64 `form:"oid" validate:"required"`
Role int8 `form:"role" validate:"required"`
})
if err := c.Bind(ap); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(wkfSvc.UntreatedChallenge(c, ap.Oid, ap.Role))
}
// addChallenge3 add challange v3
func addChallenge3(c *bm.Context) {
cp3 := &model.ChallengeParam3{}
if err := c.Bind(cp3); err != nil {
return
}
challengeNo, err := wkfSvc.AddChallenge3(c, cp3)
if err != nil {
c.JSON(nil, err)
return
}
data := map[string]int64{
"challengeNo": challengeNo,
}
c.JSON(data, nil)
}
// listChallenge3 .
func listChallenge3(c *bm.Context) {
cp3 := &model.ChallengeParam3{}
if err := c.Bind(cp3); err != nil {
return
}
c.JSON(wkfSvc.Challenges3(c, cp3))
}
// groupState3 .
func groupState3(c *bm.Context) {
cp3 := &model.ChallengeParam3{}
if err := c.Bind(cp3); err != nil {
return
}
state, err := wkfSvc.GroupState3(c, cp3)
if err != nil {
c.JSON(nil, err)
return
}
c.JSON(map[string]interface{}{
"state": state,
}, err)
}

View File

@@ -0,0 +1,24 @@
package http
import (
"go-common/app/service/main/workflow/model"
bm "go-common/library/net/http/blademaster"
)
// deleteGroup delete group .
func deleteGroup(c *bm.Context) {
p := &model.DeleteGroupParams{}
if err := c.Bind(p); err != nil {
return
}
c.JSON(nil, wkfSvc.DeleteGroup(c, p))
}
// pubRefereeGroup delete group .
func pubRefereeGroup(c *bm.Context) {
p := &model.PublicRefereeGroupParams{}
if err := c.Bind(p); err != nil {
return
}
c.JSON(nil, wkfSvc.PublicRefereeGroup(c, p))
}

View File

@@ -0,0 +1,88 @@
package http
import (
"net/http"
"go-common/app/service/main/workflow/conf"
"go-common/app/service/main/workflow/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
verifySrv *verify.Verify
wkfSvc *service.Service
)
// Init http server
func Init(c *conf.Config, svc *service.Service) {
wkfSvc = svc
verifySrv = verify.New(c.Verify)
engine := bm.DefaultServer(c.BM)
innerRouter(engine)
if err := engine.Start(); err != nil {
log.Error("engine.Start() error(%v)", err)
panic(err)
}
}
// innerRouter
func innerRouter(e *bm.Engine) {
e.Ping(ping)
wkfG := e.Group("/x/internal/workflow")
{
wkfG.POST("/add", verifySrv.Verify, addChallenge)
wkfG.GET("/close", verifySrv.Verify, closeChallenge)
wkfG.GET("/info", verifySrv.Verify, challengeInfo)
wkfG.GET("/list", verifySrv.Verify, listChallenge)
wkfG.POST("/update/state", verifySrv.Verify, upChallengeState)
wkfG.GET("/extra/info", verifySrv.Verify, businessExtra)
wkfG.POST("/extra/up", verifySrv.Verify, upBusinessExtra)
wkfG.POST("/reply/add", verifySrv.Verify, replyAddChallenge)
wkfG.GET("/untreated", verifySrv.Verify, untreatedChallenge)
appealG := wkfG.Group("/appeal")
{
appealG.POST("/add", verifySrv.Verify, addChallenge)
appealG.GET("/close", verifySrv.Verify, closeChallenge)
appealG.GET("/info", verifySrv.Verify, challengeInfo)
appealG.GET("/list", verifySrv.Verify, listChallenge)
appealG.POST("/state", verifySrv.Verify, upChallengeState)
appealG.GET("/extra/info", verifySrv.Verify, businessExtra)
appealG.POST("/extra/up", verifySrv.Verify, upBusinessExtra)
appealG.POST("/reply/add", verifySrv.Verify, replyAddChallenge)
}
appealG3 := wkfG.Group("/appeal/v3")
{
appealG3.POST("/add", verifySrv.Verify, addChallenge3)
appealG3.GET("/list", verifySrv.Verify, listChallenge3)
appealG3.GET("/state", verifySrv.Verify, groupState3)
appealG3.POST("/delete", verifySrv.Verify, deleteGroup)
appealG3.POST("/public/referee", verifySrv.Verify, pubRefereeGroup)
}
tagG3 := wkfG.Group("/tag/v3", verifySrv.Verify)
{
tagG3.GET("/list", tagList3)
}
tagG := wkfG.Group("/tag", verifySrv.Verify)
{
tagG.GET("/list", tagList)
}
sobotG := wkfG.Group("/sobot")
{
sobotG.GET("/user", sobotSign(sobotFetchUser))
sobotG.GET("/ticket/info", verifySrv.Verify, sobotInfoTicket)
sobotG.POST("/ticket/add", verifySrv.Verify, sobotAddTicket)
sobotG.POST("/ticket/modify", verifySrv.Verify, sobotModifyTicket)
sobotG.POST("/reply/add", verifySrv.Verify, sobotAddReply)
}
}
}
// ping check server ok.
func ping(c *bm.Context) {
if err := wkfSvc.Ping(c); err != nil {
log.Error("workflow-service ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}

View File

@@ -0,0 +1,52 @@
package http
import (
"crypto/md5"
"encoding/hex"
"strings"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
const (
_sobotAppKey = "bcef69bb71499209"
_sobotAppSecret = "ace486f144f1467eefdce1fe5dfc7b14"
_sobotAPI = "https://sso-api.bilibili.co/x/internal/workflow/sobot/user"
)
func sobotSign(handler func(*bm.Context)) func(*bm.Context) {
return func(c *bm.Context) {
req := c.Request
query := req.Form
if query.Get("ts") == "" {
log.Error("ts is empty")
c.JSON(nil, ecode.RequestErr)
return
}
sign := query.Get("sign")
query.Del("sign")
sappkey := query.Get("appkey")
if sappkey != _sobotAppKey {
log.Error("appkey not matched")
c.JSON(nil, ecode.RequestErr)
return
}
query.Set("appsecret", _sobotAppSecret)
tmp := query.Encode()
if strings.IndexByte(tmp, '+') > -1 {
tmp = strings.Replace(tmp, "+", "%20", -1)
}
mh := md5.Sum([]byte(_sobotAPI + "?" + strings.ToLower(tmp) + _sobotAppSecret))
if hex.EncodeToString(mh[:]) != sign {
mh1 := md5.Sum([]byte(_sobotAPI + "?" + tmp + _sobotAppSecret))
if hex.EncodeToString(mh1[:]) != sign {
log.Error("Get sign: %s, expect %x", sign, mh1)
c.JSON(nil, ecode.SignCheckErr)
return
}
}
handler(c)
}
}

View File

@@ -0,0 +1,106 @@
package http
import (
"encoding/json"
"go-common/app/service/main/workflow/model/account"
"go-common/app/service/main/workflow/model/sobot"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func sobotFetchUser(c *bm.Context) {
data := []byte(`
{
"mid": 1,
"uname": "biliuser",
"tel": "132****1234",
"email": "biliuser@qq.com",
"status": 0,
"formal": 0,
"moral": 70,
"level": 3,
"exp": "4000",
"coin": 300.12,
"bcoin": 10.12,
"medal": "青铜殿堂",
"up": {
"relation": {
"following": 1,
"whisper": 1,
"black": 0,
"follower": 1
},
"archive": 5,
"identify": 1,
"office": "bilibili认证",
"shell": 10.12,
"bank_card": "6227123412341234123"
},
"extra": {
"arc_pubed": 312,
"arc_not_pubed": 34,
"arc_is_pubing": 175
}
}
`)
user := &account.User{}
user.Extra = make(map[string]interface{})
if err := json.Unmarshal(data, user); err != nil {
c.JSON(nil, err)
return
}
c.JSON(user, nil)
}
func sobotInfoTicket(c *bm.Context) {
tp := new(struct {
TicketID int32 `form:"ticket_id" validate:"required"`
})
if err := c.Bind(tp); err != nil {
return
}
c.JSON(wkfSvc.SobotTicketInfo(c, tp.TicketID))
}
func sobotAddTicket(c *bm.Context) {
tp := new(sobot.TicketParam)
if err := c.BindWith(tp, binding.FormPost); err != nil {
return
}
c.JSON(nil, wkfSvc.SobotTicketAdd(c, tp))
}
func sobotModifyTicket(c *bm.Context) {
tp := new(sobot.TicketParam)
if err := c.BindWith(tp, binding.FormPost); err != nil {
return
}
c.JSON(nil, wkfSvc.SobotTicketModify(c, tp))
}
func sobotAddReply(c *bm.Context) {
rp := new(sobot.ReplyParam)
if err := c.BindWith(rp, binding.FormPost); err != nil {
return
}
c.JSON(nil, wkfSvc.SobotReplyAdd(c, rp))
}
// func sobotCallback(c *bm.Context) {
// req := c.Request
// bs, err := ioutil.ReadAll(req.Body)
// if err != nil {
// log.Error("ioutil.ReadAll() error(%v)", err)
// c.JSON(nil, ecode.RequestErr)
// return
// }
// req.Body.Close()
// var jsbody map[string]interface{}
// if err := json.Unmarshal(bs, &jsbody); err != nil {
// c.JSON(nil, ecode.RequestErr)
// return
// }
// log.Info("sobotCallback(%s)", string(bs))
// c.JSON(jsbody, nil)
// }

View File

@@ -0,0 +1,28 @@
package http
import (
bm "go-common/library/net/http/blademaster"
)
// tagList get enabled tag list by business
func tagList(c *bm.Context) {
tp := new(struct {
Business int8 `form:"business" validate:"required"`
})
if err := c.Bind(tp); err != nil {
return
}
c.JSON(wkfSvc.TagSlice(tp.Business), nil)
}
// tagList3 .
func tagList3(c *bm.Context) {
tp := new(struct {
BID int64 `form:"bid" validate:"required"`
RID int64 `form:"rid"`
})
if err := c.Bind(tp); err != nil {
return
}
c.JSON(wkfSvc.Tag3(tp.BID, tp.RID), nil)
}

View File

@@ -0,0 +1,53 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["challenge_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = ["//vendor/github.com/smartystreets/goconvey/convey:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"archive.go",
"attachment.go",
"business.go",
"callback.go",
"challenge.go",
"control.go",
"event.go",
"group.go",
"log.go",
"tag.go",
],
importpath = "go-common/app/service/main/workflow/model",
tags = ["automanaged"],
deps = ["//library/time:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/service/main/workflow/model/account:all-srcs",
"//app/service/main/workflow/model/sobot:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["user.go"],
importpath = "go-common/app/service/main/workflow/model/account",
tags = ["automanaged"],
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,21 @@
package account
// User for sobot userinfo
type User struct {
Mid int64 `json:"mid"`
UName string `json:"uname"`
Tel string `json:"tel"`
EMail string `json:"email"`
Status int32 `json:"status"`
Formal int32 `json:"formal"`
Moral int32 `json:"moral"`
Level int32 `json:"level"`
Exp string `json:"exp"`
Coin float64 `json:"coin"`
BCoin float64 `json:"bcoin"`
Medal string `json:"medal"`
Up map[string]interface{} `json:"up"`
// extra field for further buseinss
Extra map[string]interface{} `json:"extra"`
}

View File

@@ -0,0 +1,15 @@
package model
// Archive .
type Archive struct {
ID int64 `json:"id" gorm:"column:id"`
MID int64 `json:"mid" gorm:"column:mid"`
TypeID int32 `json:"typeid" gorm:"column:typeid"`
Title string `json:"title" gorm:"column:title"`
Content string `json:"content" gorm:"column:content"`
}
// TableName .
func (a *Archive) TableName() string {
return "archive"
}

View File

@@ -0,0 +1,13 @@
package model
// Attachment struct
type Attachment struct {
ID int32 `gorm:"column:id" json:"id"`
Cid int32 `gorm:"column:cid" json:"cid"`
Path string `gorm:"column:path" json:"path"`
}
// TableName by Attachment
func (*Attachment) TableName() string {
return "workflow_attachment"
}

View File

@@ -0,0 +1,35 @@
package model
const (
// BusinessArchiveComplain 稿件投诉
BusinessArchiveComplain = int8(1)
// BusinessArchiveAppeal 稿件申诉
BusinessArchiveAppeal = int8(2)
// BusinessBlackedAppeal 小黑屋申诉
BusinessBlackedAppeal = int8(5)
// BusinessAudit 稿件审核
BusinessAudit = int8(6)
// Disbaled 禁用
Disbaled = int8(0)
// Enabled 启用
Enabled = int8(1)
)
// Business struct
type Business struct {
ID int32 `gorm:"column:id" json:"id"`
Cid int32 `gorm:"column:cid" json:"cid"`
Oid int64 `gorm:"column:oid" json:"oid"`
Business int8 `gorm:"column:business" json:"business"`
Typeid int16 `gorm:"column:typeid" json:"business_typeid"`
Mid int64 `gorm:"column:mid" json:"business_mid"`
Title string `gorm:"column:title" json:"business_title"`
Content string `gorm:"column:content" json:"business_content"`
Extra string `gorm:"column:extra" json:"business_extra"`
}
// TableName by business
func (*Business) TableName() string {
return "workflow_business"
}

View File

@@ -0,0 +1,14 @@
package model
// Callback struct
type Callback struct {
ID int32 `gorm:"column:id"`
URL string `gorm:"column:url"`
Business int8 `gorm:"column:business"`
State int8 `gorm:"column:state"`
}
// TableName by Callback
func (*Callback) TableName() string {
return "workflow_callback"
}

View File

@@ -0,0 +1,216 @@
package model
import (
"errors"
"go-common/library/time"
)
const (
// AuditRole 审核
AuditRole = int8(0)
// CustomerServiceRole 客服
CustomerServiceRole = int8(1)
// StateUntreated .
StateUntreated = int8(0)
// StatePassed .
StatePassed = int8(1)
// StateReject .
StateReject = int8(2)
// StateClose .
StateClose = int8(3)
// DispatchStateAuditMask 1111
DispatchStateAuditMask = int32(0xf)
// DispatchStateCustomerServiceMask 11110000
DispatchStateCustomerServiceMask = int32(0xf0)
// QueueState 队列中审核状态
QueueState = 15
// QueueBusinessState 队列中客服状态
QueueBusinessState = 15
// QueueStateBefore 默认审核状态
QueueStateBefore = 0
// QueueBusinessStateBefore 默认客服状态
QueueBusinessStateBefore = 1
)
// Challenge struct
type Challenge struct {
ID int32 `gorm:"column:id" json:"id"`
Tid int32 `gorm:"column:tid" json:"tid"`
Gid int32 `gorm:"column:gid" json:"gid"`
Oid int64 `gorm:"column:oid" json:"oid"`
Mid int64 `gorm:"column:mid" json:"mid"`
Eid int64 `gorm:"column:eid" json:"eid"`
State int8 `gorm:"-" json:"state"`
Business int8 `gorm:"column:business" json:"business"`
BusinessState int8 `gorm:"-" json:"business_state"`
Assignee int32 `gorm:"column:assignee_adminid" json:"assignee_adminid"`
Adminid int32 `gorm:"column:adminid" json:"adminid"`
MetaData string `gorm:"column:metadata" json:"metadata"`
Desc string `gorm:"column:description" json:"description"`
Attachments []*Attachment `gorm:"-" json:"attachments"`
Events []*Event `gorm:"-" json:"events"`
Ctime time.Time `gorm:"ctime" json:"ctime"`
Mtime time.Time `gorm:"mtime" json:"mtime"`
DispatchState uint32 `gorm:"dispatch_state"`
BusinessInfo Business `gorm:"-" json:"business_info"`
}
// ChallengeParam appeal param
type ChallengeParam struct {
ID int32 `form:"id"`
Tid int32 `form:"tid"`
Oid int64 `form:"oid"`
Mid int64 `form:"mid"`
Desc string `form:"description"`
AdminID int32 `form:"admin_id"`
AssigneeID int32 `form:"assignee_id"`
AttachmentsStr string `form:"attachments"`
Attachments []string `form:"attachments[]"`
Business int8 `form:"business"`
BusinessState int8 `form:"business_state"`
MetaData string `form:"metadata"`
BusinessTypeid int32 `form:"business_typeid"`
BusinessTitle string `form:"business_title"`
BusinessContent string `form:"business_content"`
BusinessMid int64 `form:"business_mid"`
BusinessExtra string `form:"business_extra"`
Role uint8 `form:"role"`
}
// TableName by Challenge
func (*Challenge) TableName() string {
return "workflow_chall"
}
// SetDispatchState set DispatchState
func SetDispatchState(dispatchState int32, role, state int8) (result int32, err error) {
switch role {
case AuditRole:
result = dispatchState&(^DispatchStateAuditMask) + int32(state)
case CustomerServiceRole:
result = dispatchState&(^DispatchStateCustomerServiceMask) + (int32(state) << 4)
default:
err = errors.New("changeDispatchState Unknown Role")
}
return result, err
}
// DispatchState get DispatchState
func DispatchState(dispatchState int32, role int8) (result int32, err error) {
switch role {
case AuditRole:
result = dispatchState & DispatchStateAuditMask
case CustomerServiceRole:
result = (dispatchState & DispatchStateCustomerServiceMask) >> 4
default:
err = errors.New("changeDispatchState Unknown Role")
}
return result, err
}
// SetState update state of a role
// ex. oldState=0x3a4b5c6d, state=15, role=1 then result is 0x3a4b5cfd
func (c *Challenge) SetState(state uint32, role uint8) {
oldState := c.DispatchState
mod := uint32(^(0xf << (4 * role)))
oldState = oldState & mod // all bit keep unchanged and bits you want update change to 0
c.DispatchState = oldState + state<<(4*role)
}
// GetState return state of a role from dispatchState field
// ex. dispatchState=0x3a4b5c6d, role=1 then result is 0x6
func (c *Challenge) GetState(role uint8) (result int8) {
dispatchState := c.DispatchState
mod := uint32(0xf << (4 * role))
dispatchState &= mod
result = int8(dispatchState >> (4 * role))
return
}
// FromState set State and BusinessState field from DispatchState field
func (c *Challenge) FromState() {
c.State = c.GetState(uint8(0))
if c.State == QueueState {
c.State = QueueStateBefore
}
c.BusinessState = c.GetState(uint8(1))
if c.BusinessState == QueueBusinessState {
c.BusinessState = QueueBusinessStateBefore
}
}
// CheckAdd check add challenge by params
func (ap *ChallengeParam) CheckAdd() bool {
return !(ap.Oid == 0 || ap.Mid == 0 || ap.Business == 0 || ap.Tid == 0 || ap.Desc == "")
}
// CheckList check get list challenge by params
func (ap *ChallengeParam) CheckList() bool {
return !(ap.Mid == 0 || ap.Business == 0)
}
// CheckInfo check get challenge info by params
func (ap *ChallengeParam) CheckInfo() bool {
return !(ap.ID == 0 || ap.Mid == 0 || ap.Business == 0)
}
// CheckBusiness check challenge business field by params
func (ap *ChallengeParam) CheckBusiness() bool {
return !(ap.BusinessTypeid == 0 && ap.BusinessMid == 0 && ap.BusinessTitle == "" && ap.BusinessContent == "" && ap.BusinessExtra == "")
}
// ChallengeParam3 .
type ChallengeParam3 struct {
Business int8 `form:"business" validate:"required"`
Fid int64 `form:"fid"`
Rid int64 `form:"rid"`
Eid int64 `form:"eid"`
Score int64 `form:"score"`
Tid int32 `form:"tid"`
Oid int64 `form:"oid" validate:"required"`
Aid int64 `form:"aid"`
Mid int64 `form:"mid"`
Desc string `form:"description"`
AdminID int32 `form:"admin_id"`
AssigneeID int32 `form:"assignee_id"`
Attachments []string `form:"attachments,split"`
BusinessState int8 `form:"business_state"`
MetaData string `form:"metadata"`
BusinessTypeid int16 `form:"business_typeid"`
BusinessTitle string `form:"business_title"`
BusinessContent string `form:"business_content"`
BusinessMid int64 `form:"business_mid"`
BusinessExtra string `form:"business_extra"`
Role uint8 `form:"role"`
}
// CheckBusiness .
func (cp3 *ChallengeParam3) CheckBusiness() bool {
return !(cp3.BusinessTypeid == 0 && cp3.BusinessMid == 0 && cp3.BusinessTitle == "" && cp3.BusinessContent == "" && cp3.BusinessExtra == "")
}
// Challenge3 .
type Challenge3 struct {
ID int64 `json:"id" gorm:"column:id"`
Gid int64 `json:"gid" gorm:"column:gid"`
Mid int64 `json:"mid" gorm:"column:mid"`
Tid int64 `json:"tid" gorm:"column:tid"`
Eid int64 `json:"eid" gorm:"column:eid"`
Oid int64 `json:"oid" gorm:"column:oid"`
Business int64 `json:"business" gorm:"column:business"`
Desc string `json:"description" gorm:"column:description"`
MetaData string `json:"metadata" gorm:"column:metadata"`
DispatchState int64 `json:"dispatch_state" gorm:"column:dispatch_state"`
Ctime time.Time `json:"ctime" gorm:"column:ctime"`
Mtime time.Time `json:"mtime" gorm:"column:mtime"`
}
// TableName .
func (*Challenge3) TableName() string {
return "workflow_chall"
}

View File

@@ -0,0 +1,27 @@
package model
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDispatchState(t *testing.T) {
convey.Convey("UpState", t, func() {
dispatchState := int32(0x3a4b5c6d)
role := int8(1)
result, _ := DispatchState(dispatchState, role)
convey.So(result, convey.ShouldEqual, uint32(0x6))
})
}
func TestSetDispatchState(t *testing.T) {
convey.Convey("UpState", t, func() {
dispatchState := int32(0x3a4b5c6d)
state := int8(0x1)
role := int8(1)
result, err := SetDispatchState(dispatchState, role, state)
convey.ShouldBeNil(err)
convey.So(result, convey.ShouldEqual, uint32(0x3a4b5c1d))
})
}

View File

@@ -0,0 +1,58 @@
package model
import (
"time"
)
const (
// ControlTypeInput 文本类型控件
ControlTypeInput = "input"
// ControlTypeTextarea 多行文本类型控件
ControlTypeTextarea = "textarea"
// ControlTypeLink 链接类型控件
ControlTypeLink = "link"
// ControlTypeSelector 选择类型控件
ControlTypeSelector = "selector"
// ControlTypeFile 文件类型控件
ControlTypeFile = "file"
// ControlPageSize .
ControlPageSize = int(1000)
)
// Control will describe how the tag be acted
type Control struct {
Cid int32 `gorm:"-" json:"-"`
Tid int32 `gorm:"column:tid" json:"tid"`
Weight int32 `gorm:"-" json:"-"`
Name string `gorm:"column:name" json:"name"`
Title string `gorm:"column:title" json:"title"`
Component string `gorm:"column:component" json:"component"`
Placeholder string `gorm:"column:placeholder" json:"placeholder"`
Required bool `gorm:"column:required" json:"required"`
CTime time.Time `gorm:"-" json:"-"`
MTime time.Time `gorm:"-" json:"-"`
}
// TableName by control
func (*Control) TableName() string {
return "workflow_tag_control"
}
// Control3 .
type Control3 struct {
TID int64 `json:"tid"`
BID int64 `json:"bid"`
Name string `json:"name"`
Title string `json:"title"`
Component string `json:"component"`
Placeholder string `json:"placeholder"`
Required int64 `json:"required"`
}
// ResponseControl3 .
type ResponseControl3 struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int32 `json:"ttl"`
Data []*Control3 `json:"data"`
}

View File

@@ -0,0 +1,33 @@
package model
import (
xtime "go-common/library/time"
)
const (
// EventTypeAdminReply 管理员回复
EventTypeAdminReply = int8(1)
// EventTypeAdminNote 管理员备注
EventTypeAdminNote = int8(2)
// EventTypeUserReply 用户回复
EventTypeUserReply = int8(3)
// EventTypeSystemReply 系统回复
EventTypeSystemReply = int(4)
)
// Event struct
type Event struct {
ID int32 `gorm:"column:id" json:"id"`
Cid int32 `gorm:"column:cid" json:"cid"`
Event int8 `gorm:"column:event" json:"event"`
Adminid int32 `gorm:"column:adminid" json:"adminid"`
Content string `gorm:"column:content" json:"content"`
Attachments string `gorm:"column:attachments" json:"attachments"`
Ctime xtime.Time `gorm:"column:ctime" json:"ctime"`
Mtime xtime.Time `gorm:"column:mtime" json:"mtime"`
}
// TableName by event
func (*Event) TableName() string {
return "workflow_event"
}

View File

@@ -0,0 +1,76 @@
package model
import (
"time"
)
const (
// StateTypePending 处理中
StateTypePending = int8(0)
// StateTypeYes 有效
StateTypeYes = int(1)
// StateTypeNo 无效
StateTypeNo = int(2)
// StateDelete 删除
StateDelete = int(9)
// StatePublicReferee 移交众裁
StatePublicReferee = int(10)
)
// Group appeal group
type Group struct {
ID int32 `gorm:"column:id" json:"id"`
Oid int64 `gorm:"column:oid" json:"oid"`
State int8 `gorm:"column:state" json:"state"`
Business int8 `gorm:"column:business" json:"business"`
Tid int32 `gorm:"column:tid" json:"tid"`
Count int32 `gorm:"column:count" json:"count"`
Handling int32 `gorm:"column:handling" json:"handling"`
Note string `gorm:"column:note" json:"note"`
CTime time.Time `gorm:"column:ctime" json:"ctime"`
MTime time.Time `gorm:"column:mtime" json:"mtime"`
Lasttime time.Time `gorm:"column:lasttime" json:"lasttime"`
}
// TableName by Group
func (*Group) TableName() string {
return "workflow_group"
}
// Group3 .
type Group3 struct {
ID int64 `gorm:"column:id" json:"id"`
Oid int64 `gorm:"column:oid" json:"oid"`
State int64 `gorm:"column:state" json:"state"`
Business int64 `gorm:"column:business" json:"business"`
Fid int64 `gorm:"column:fid" json:"fid"`
Rid int64 `gorm:"column:rid" json:"rid"`
Eid int64 `gorm:"column:eid" json:"eid"`
Score int64 `gorm:"column:score" json:"score"`
Tid int64 `gorm:"column:tid" json:"tid"`
Count int64 `gorm:"column:count" json:"count"`
Handling int64 `gorm:"column:handling" json:"handling"`
Note string `gorm:"column:note" json:"note"`
CTime time.Time `gorm:"column:ctime" json:"ctime"`
MTime time.Time `gorm:"column:mtime" json:"mtime"`
Lasttime time.Time `gorm:"column:lasttime" json:"lasttime"`
}
// TableName .
func (g3 *Group3) TableName() string {
return "workflow_group"
}
// DeleteGroupParams .
type DeleteGroupParams struct {
Business int64 `json:"business" form:"business" validate:"required"`
OID int64 `json:"oid" form:"oid" validate:"required"`
EID int64 `json:"eid" form:"eid"`
}
// PublicRefereeGroupParams .
type PublicRefereeGroupParams struct {
Business int8 `json:"business" form:"business" validate:"required"`
Oid string `json:"oid" form:"oid" validate:"required"`
Eid int64 `json:"eid" form:"eid"`
}

View File

@@ -0,0 +1,24 @@
package model
import (
"time"
)
// Log is the universal tag model, contains any type of tags
// The Business field and the Round field will from any business definition
type Log struct {
AdminID int32 `gorm:"column:adminid"`
Oid int64 `gorm:"column:oid"`
Business int8 `gorm:"column:business"`
Target int32 `gorm:"column:target"`
Module int8 `gorm:"column:module"`
Remark string `gorm:"column:remark"`
Note string `gorm:"column:note"`
CTime time.Time `gorm:"column:ctime"`
MTime time.Time `gorm:"column:mtime"`
}
// TableName Tag tablename
func (*Log) TableName() string {
return "workflow_log"
}

View File

@@ -0,0 +1,31 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"check.go",
"sobot.go",
],
importpath = "go-common/app/service/main/workflow/model/sobot",
tags = ["automanaged"],
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,25 @@
package sobot
// Check the TicketParam
func (tp *TicketParam) Check() bool {
if tp.TicketTitle == "" || tp.TicketID == 0 || tp.TicketContent == "" || tp.CustomerEmail == "" {
return false
}
return true
}
// CheckModify the TicketParam modiy
func (tp *TicketParam) CheckModify() bool {
if tp.TicketID == 0 || tp.CustomerEmail == "" {
return false
}
return true
}
// Check the ReplyParam
func (rp *ReplyParam) Check() bool {
if rp.CustomerEmail == "" || rp.ReplyContent == "" || rp.TicketID == 0 {
return false
}
return true
}

View File

@@ -0,0 +1,95 @@
package sobot
const (
// TicketFrom .
TicketFrom = int8(12)
// EcodeOK .
EcodeOK = "000000"
// ReplyTypePublic 评论可见状态
ReplyTypePublic = int8(0)
// ReplyTypeCSOnly .
ReplyTypeCSOnly = int8(1)
// CustomerSourcePC 客户来源
CustomerSourcePC = int8(0)
// CustomerSourceWX .
CustomerSourceWX = int8(1)
// CustomerSourceAPP .
CustomerSourceAPP = int8(2)
// CustomerSourceWB .
CustomerSourceWB = int8(3)
// CustomerSourceWAP .
CustomerSourceWAP = int8(4)
// TicketLevelLow 工单等级 .
TicketLevelLow = int8(0)
// TicketLevelMedium .
TicketLevelMedium = int8(1)
// TicketLevelHigh .
TicketLevelHigh = int8(2)
// TicketLevelurgency .
TicketLevelurgency = int8(3)
// TicketStatusPending 工单状态
TicketStatusPending = int8(0)
// TicketStatusHandling .
TicketStatusHandling = int8(1)
// TicketStatusReplying .
TicketStatusReplying = int8(2)
// TicketStatusSolved .
TicketStatusSolved = int8(3)
// TicketStatusClosed .
TicketStatusClosed = int8(99)
// TicketStatusDeleted .
TicketStatusDeleted = int8(98)
)
// Ticket struct
type Ticket struct {
TicketID string `json:"ticket_id"`
Content string `json:"ticket_content"`
Level int8 `json:"ticket_level"`
State int8 `json:"ticket_status"`
Title string `json:"ticket_title"`
FileStr string `json:"file_str"`
CTime int64 `json:"ctime"`
}
// Reply struct
type Reply struct {
Face string `json:"face_img"`
FileStr string `json:"file_str"`
Content string `json:"reply_content"`
ReplyType int8 `json:"reply_type"`
ShowName string `json:"show_name"`
StartType int8 `json:"start_type"`
CTime int64 `json:"reply_time"`
}
// ReplyParam reply param
type ReplyParam struct {
TicketID int32 `form:"ticket_id" validate:"required"`
ReplyContent string `form:"reply_content" validate:"required"`
CustomerEmail string `form:"customer_email" validate:"required"`
StartType int8 `form:"start_type"`
ReplyType int8 `form:"reply_type"`
}
// TicketParam ticket param
type TicketParam struct {
CustomerName string `form:"customer_name"`
CustomerQQ string `form:"customer_qq"`
CustomerNick string `form:"customer_nick"`
CustomerEmail string `form:"customer_email" validate:"required"`
CustomerSource int8 `form:"customer_source"`
CustomerPhone string `form:"customer_phone"`
TicketID int32 `form:"ticket_id" validate:"required"`
TicketTitle string `form:"ticket_title"`
TicketContent string `form:"ticket_content"`
TicketLevel int8 `form:"ticket_level"`
TicketStatus int8 `form:"ticket_status"`
TicketFrom int8 `form:"ticket_from"`
StartType int8 `form:"start_type"`
FileStr string `form:"file_str"`
}

View File

@@ -0,0 +1,68 @@
package model
import (
"time"
)
// Tag is the universal tag model, contains any type of tags
// The Business field and the Round field will from any business definition
type Tag struct {
Tid int32 `gorm:"column:id" json:"tid"`
Name string `gorm:"column:name" json:"name"`
Business int8 `gorm:"column:business" json:"business"`
Weight int16 `gorm:"column:weight" json:"weight"`
Round int8 `gorm:"column:round" json:"round"`
State int8 `gorm:"column:state" json:"state"`
Remark string `gorm:"column:remark" json:"remark"`
Controls []*Control `gorm:"-" json:"controls"`
CTime time.Time `gorm:"column:ctime" json:"ctime"`
MTime time.Time `gorm:"column:mtime" json:"mtime"`
}
// TableName Tag tablename
func (*Tag) TableName() string {
return "workflow_tag"
}
// TagsCache tag cache
type TagsCache struct {
TagMap map[int8]map[int32]*Tag
TagSlice map[int8][]*Tag
TagMap3 map[int64]map[int64][]*Tag3
TagMap3Tid map[int64]map[int64]*Tag3
}
// ResponseTag3 .
type ResponseTag3 struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int32 `json:"ttl"`
Data *Data
}
// Data .
type Data struct {
Data []*Tag3 `json:"data"`
Pager *Pager `json:"page"`
}
// Tag3 .
type Tag3 struct {
BID int64 `json:"bid"`
TagID int64 `json:"tag_id"`
RID int64 `json:"rid"`
Name string `json:"name"`
Weight int64 `json:"weight"`
State int64 `json:"state"`
Desc string `json:"description"`
Controls []*Control3 `json:"controls"`
Ctime int64 `json:"ctime"`
Mtime int64 `json:"mtime"`
}
// Pager .
type Pager struct {
Num int64 `json:"num"`
Size int64 `json:"size"`
Total int64 `json:"total"`
}

View File

@@ -0,0 +1,68 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"business_test.go",
"challenge_test.go",
"service_test.go",
"tags_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"business.go",
"challenge.go",
"event.go",
"group.go",
"service.go",
"sobot.go",
"tags.go",
],
importpath = "go-common/app/service/main/workflow/service",
tags = ["automanaged"],
deps = [
"//app/service/main/archive/api:go_default_library",
"//app/service/main/workflow/conf:go_default_library",
"//app/service/main/workflow/dao:go_default_library",
"//app/service/main/workflow/dao/sobot:go_default_library",
"//app/service/main/workflow/model:go_default_library",
"//app/service/main/workflow/model/sobot:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/sync/pipeline/fanout:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/pkg/errors: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,106 @@
package service
import (
"context"
"encoding/json"
"go-common/app/service/main/workflow/model"
"go-common/library/log"
)
// UpBusinessExtra update business extra by cid && mid && business
// Deprecated
func (s *Service) UpBusinessExtra(c context.Context, cid int32, mid int64, business int8, key, value string) (err error) {
var (
bs []byte
bsns = &model.Business{}
extra = make(map[string]string)
)
if err = s.dao.DB.Where("cid=? and mid=? and business=?", cid, mid, business).Find(bsns).Error; err != nil {
log.Error("d.DB.WhereBusiness(%d,%d,%d) error(%v)", cid, mid, business, err)
return
}
if bsns.Extra != "" {
json.Unmarshal([]byte(bsns.Extra), &extra)
}
extra[key] = value
if bs, err = json.Marshal(extra); err != nil {
log.Error("json.Marshal(%+v)", extra)
return
}
if err = s.dao.DB.Model(&model.Business{}).Where("cid=? and mid=? and business=?", cid, mid, business).Update("extra", string(bs)).Error; err != nil {
log.Error("s.dao.UpBusinessExtra(%d,%d,%d,%s) error(%v)", cid, mid, business, string(bs), err)
return
}
return
}
// UpBusinessExtraV2 .
func (s *Service) UpBusinessExtraV2(c context.Context, cid int32, mid int64, business int8, key, value string) (err error) {
var (
bs []byte
bsns = &model.Business{}
chall = &model.Challenge{}
extra = make(map[string]string)
)
if err = s.dao.DB.Where("id=?", cid).Find(chall).Error; err != nil {
log.Error("failed to find challenge cid(%d) error(%v)", cid, err)
return
}
if err = s.dao.DB.Table("workflow_business").Where("business=? and oid=?", chall.Business, chall.Oid).Last(bsns).Error; err != nil {
log.Error("failed to find last business object business(%d) oid(%d) error(%v)", chall.Business, chall.Oid, err)
return
}
if bsns.Extra != "" {
json.Unmarshal([]byte(bsns.Extra), &extra)
}
extra[key] = value
if bs, err = json.Marshal(extra); err != nil {
log.Error("json.Marshal(%+v)", extra)
return
}
if err = s.dao.DB.Model(&model.Business{}).Where("id=?", bsns.ID).Update("extra", string(bs)).Error; err != nil {
log.Error("s.dao.UpBusinessExtra(%d,%s) error(%v)", bsns.ID, string(bs), err)
return
}
return
}
// BusinessExtra get business extra field by cid
// Deprecated
func (s *Service) BusinessExtra(c context.Context, cid int32, mid int64, business int8) (extra json.RawMessage, err error) {
bsns := &model.Business{}
if err = s.dao.DB.Where("cid=? and mid=? and business=?", cid, mid, business).Find(bsns).Error; err != nil {
log.Error("d.DB.WhereBusiness(%d,%d,%d) error(%v)", cid, mid, business, err)
return
}
if bsns.Extra == "" {
bsns.Extra = "{}"
}
extra = json.RawMessage(bsns.Extra)
return
}
// BusinessExtraV2 get business extra field by cid
func (s *Service) BusinessExtraV2(c context.Context, cid int32, mid int64, business int8) (extra json.RawMessage, err error) {
var (
bsns = &model.Business{}
chall = &model.Challenge{}
)
if err = s.dao.DB.Where("id=?", cid).Find(chall).Error; err != nil {
log.Error("failed find challenge cid(%d) error(%v)", cid, err)
return
}
if err = s.dao.DB.Table("workflow_business").Where("business=? and oid=?", chall.Business, chall.Oid).Last(bsns).Error; err != nil {
log.Error("failed to find last business object business(%d) oid(%d) error(%v)", chall.Business, chall.Oid, err)
return
}
if bsns.Extra == "" {
bsns.Extra = "{}"
}
extra = json.RawMessage(bsns.Extra)
return
}

View File

@@ -0,0 +1,22 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestUpBusinessExtra(t *testing.T) {
convey.Convey("Test UpBusinessExtra", t, func() {
err := s.UpBusinessExtra(context.Background(), 1, 1, 1, "", "")
convey.ShouldBeNil(err)
})
}
func TestBusinessExtra(t *testing.T) {
convey.Convey("Test TestBusinessExtra", t, func() {
_, err := s.BusinessExtra(context.Background(), 1, 1, 1)
convey.ShouldBeNil(err)
})
}

View File

@@ -0,0 +1,380 @@
package service
import (
"context"
"database/sql"
"errors"
"fmt"
"strings"
"time"
arc "go-common/app/service/main/archive/api"
"go-common/app/service/main/workflow/model"
"go-common/library/log"
"github.com/jinzhu/gorm"
)
const (
_inGroupSQL = "INSERT INTO workflow_group(oid,business,rid,tid,lasttime,count,handling) VALUE(?,?,?,?,?,1,1) ON DUPLICATE KEY UPDATE lasttime=?,state=?,count=count+1,handling=handling+1"
_inGroupSQL3 = "INSERT INTO workflow_group(oid,business,fid,rid,eid,score,tid,lasttime,count,handling) VALUES(?,?,?,?,?,?,?,?,1,1) ON DUPLICATE KEY UPDATE lasttime=?,state=?,count=count+1,handling=handling+1"
//_inBusiness = "INSERT INTO workflow_business(oid,typeid,business,title,content,mid,extra,cid,gid) VALUES(?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE typeid=values(typeid),title=values(title),content=values(content),extra=values(extra)"
_inBusiness3 = "INSERT INTO workflow_business(oid,typeid,business,title,content,mid,extra,cid,gid) VALUES(?,?,?,?,?,?,?,?,?)"
_upBusiness3 = "UPDATE workflow_business SET typeid=?, title=?, content=?, extra=? WHERE gid = ?"
)
// AddChallenge add challenge
func (s *Service) AddChallenge(c context.Context, ap *model.ChallengeParam) (row int64, err error) {
var (
lasttime = time.Now()
apl model.Group
chall *model.Challenge
attach *model.Attachment
)
tx := s.dao.DB.Begin()
if tx.Error != nil {
return
}
if tx.Error != nil {
log.Error("s.dao.AddGroup(%d,%d,%d,%v) error(%v)", ap.Oid, ap.Business, ap.Tid, lasttime, err)
return
}
defer func() {
if err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("s.dao.AddGroup(%v,%v) rollback error(%v)", ap, lasttime, err1)
}
}
}()
var rid int64
if tag, ok := s.tagsCache.TagMap3Tid[int64(ap.Business)][int64(ap.Tid)]; !ok {
log.Error("not found tag bid(%d) tid(%d)", ap.Business, ap.Tid)
} else {
rid = tag.RID
}
if err = tx.Exec(_inGroupSQL, ap.Oid, ap.Business, rid, ap.Tid, lasttime, lasttime, model.StateTypePending).Error; err != nil {
log.Error("tx.Raw(%d,%d,%d,%v) error(%v)", ap.Oid, ap.Business, ap.Tid, lasttime, err)
return
}
if err = tx.Where("oid=? AND business=?", ap.Oid, ap.Business).Find(&apl).Error; err != nil {
log.Error("tx.Where(%d,%d).Find() error(%v)", ap.Oid, ap.Business, err)
return
}
chall = &model.Challenge{
Tid: ap.Tid,
Gid: apl.ID,
Oid: ap.Oid,
Mid: ap.Mid,
Desc: ap.Desc,
MetaData: ap.MetaData,
Business: ap.Business,
BusinessState: ap.BusinessState,
Adminid: ap.AdminID,
Assignee: ap.AssigneeID,
}
chall.SetState(uint32(0), ap.Role)
if err = tx.Create(chall).Error; err != nil {
log.Error("tx.Create(%+v) error(%v)", chall, err)
return
}
row = int64(chall.ID)
for _, path := range ap.Attachments {
attach = &model.Attachment{Cid: chall.ID, Path: path}
if err = tx.Create(&attach).Error; err != nil {
log.Error("tx.Create(%+v) error(%v)", attach, err)
return
}
}
// 刷新稿件信息
if ap.Business == 1 {
in := &arc.ArcRequest{
Aid: ap.Oid,
}
var res *arc.ArcReply
if res, err = s.arcClient.Arc(c, in); err != nil {
log.Error("s.arcClient.Arc(%d) error(%v)", ap.Oid, err)
return
}
ap.BusinessTypeid = res.Arc.TypeID
ap.BusinessMid = res.Arc.Author.Mid
ap.BusinessTitle = res.Arc.Title
ap.BusinessContent = res.Arc.Desc
}
if ap.CheckBusiness() {
bus := &model.Business{}
if err = tx.Table("workflow_business").Where("gid = ?", apl.ID).Find(bus).Error; err != nil {
if err != gorm.ErrRecordNotFound {
log.Error("s.AddChallenge tx.Where error(%v)", err)
return
}
if err = tx.Exec(_inBusiness3, ap.Oid, ap.BusinessTypeid, ap.Business, ap.BusinessTitle, ap.BusinessContent, ap.BusinessMid, ap.BusinessExtra, chall.ID, apl.ID).Error; err != nil {
log.Error("tx.Exec(%s) params(oid=%d,typeid=%d,business=%d,title=%s,content=%s,mid=%d,extra=%s,cid=%d,gid=%d) error(%v)", _inBusiness3, ap.Oid, ap.BusinessTypeid, ap.Business, ap.BusinessTitle, ap.BusinessContent, ap.BusinessMid, ap.BusinessExtra, chall.ID, apl.ID, err)
return
}
} else {
if err = tx.Exec(_upBusiness3, ap.BusinessTypeid, ap.BusinessTitle, ap.BusinessContent, ap.BusinessExtra, apl.ID).Error; err != nil {
log.Error("tx.Exec(%s) params(typeid=%d,title=%s,content=%s,extra=%s) error(%v)", _upBusiness3, ap.BusinessTypeid, ap.BusinessTitle, ap.BusinessContent, ap.BusinessExtra, err)
}
}
}
if err = tx.Commit().Error; err != nil {
log.Error("tx.Commit() err(%v)", err)
return
}
// upsert
if apl.Count <= 1 {
s.cache.Do(context.Background(), func(c context.Context) {
if err = s.dao.UpdateUserTag(c, int64(apl.ID), ap.Tid); err != nil {
log.Error("s.dao.UpdateUserTag gid:%d error:%v", apl.ID, err)
}
})
}
return
}
// UpChallengeState update challenge business state by mid && id && business
func (s *Service) UpChallengeState(c context.Context, id int32, mid int64, business, role, state int8) (err error) {
if role != model.AuditRole && role != model.CustomerServiceRole {
err = errors.New("UntreatedChallenge Unknown Role")
log.Error("s.dao.UpChallengeState(%d,%d,%d,%d,%d) error(%v)", id, mid, business, role, state, err)
return
}
chall := &model.Challenge{}
if err = s.dao.DB.Where("id=? and mid=? and business=?", id, mid, business).Find(chall).Error; err != nil {
log.Error("s.dao.UpChallengeState(%d,%d,%d,%d,%d) error(%v)", id, mid, business, role, state, err)
return
}
chall.SetState(uint32(state), uint8(role))
if err = s.dao.DB.Model(&model.Challenge{}).Where("id=? and mid=? and business=?", id, mid, business).Update("dispatch_state", chall.DispatchState).Error; err != nil {
log.Error("s.dao.DB.UpState(%d,%d,%d,%d,%d) error(%v)", id, mid, business, role, state, err)
return
}
return
}
// CloseChallenge set challenge business state closed by challenge id
func (s *Service) CloseChallenge(c context.Context, id int32, business, role, businessState int8, note string) (err error) {
if role != model.AuditRole && role != model.CustomerServiceRole {
err = errors.New("UntreatedChallenge Unknown Role")
log.Error("s.dao.CloseChallenge(%d,%d) error(%v)", id, role, err)
return
}
chall := &model.Challenge{}
if err = s.dao.DB.Where("id=? and business=?", id, business).Find(chall).Error; err != nil {
log.Error("s.dao.Challenge(%d,%d) error(%v)", id, business, err)
return
}
chall.SetState(uint32(model.StateClose), uint8(role))
if err = s.dao.DB.Model(&model.Challenge{}).Where("id=? and business=?", id, business).Update("dispatch_state", chall.DispatchState).Error; err != nil {
log.Error("s.dao.UpChallengeState(%d) error(%v)", id, err)
return
}
changeLog := &model.Log{AdminID: chall.Adminid, Oid: chall.Oid, Business: chall.Business, Target: chall.ID, Module: 1, Remark: "close challenge", Note: note}
if err = s.dao.DB.Create(changeLog).Error; err != nil {
log.Error("s.dao.CloseChallengeStateLog(%d) error(%v)", id, err)
}
return
}
// Challenge get challenge info
func (s *Service) Challenge(c context.Context, ap *model.ChallengeParam) (chall *model.Challenge, err error) {
chall = &model.Challenge{}
if err = s.dao.DB.Where("id=? and mid=? and business=?", ap.ID, ap.Mid, ap.Business).Find(chall).Error; err != nil {
log.Error("s.dao.Challenge(%d,%d,%d) error(%v)", ap.ID, ap.Mid, ap.Business, err)
return
}
// read new state field of challenge
chall.FromState()
if err = s.dao.DB.Where("cid=?", ap.ID).Order("id desc").Find(&chall.Attachments).Error; err != nil {
log.Error("s.dao.Attachments(%d) error(%v)", ap.ID, err)
return
}
if err = s.dao.DB.Where("cid=?", ap.ID).Order("id asc").Find(&chall.Events).Error; err != nil {
log.Error("s.dao.Events(%d) error(%v)", ap.ID, err)
return
}
return
}
// Challenges get challenge list
func (s *Service) Challenges(c context.Context, ap *model.ChallengeParam) (challs []*model.Challenge, err error) {
if err = s.dao.DB.Where("mid=? and business=?", ap.Mid, ap.Business).Order("id DESC").Find(&challs).Error; err != nil {
log.Error("s.dao.Challenges(%d,%d) error(%v)", ap.Mid, ap.Business, err)
return
}
// read new state field of challenge
for cid := range challs {
challs[cid].FromState()
}
return
}
// UntreatedChallenge get untreated chanllenges by oid
func (s *Service) UntreatedChallenge(c context.Context, oid int64, role int8) (challs []*model.Challenge, err error) {
if role != model.AuditRole && role != model.CustomerServiceRole {
err = errors.New("UntreatedChallenge Unknown Role")
log.Error("s.dao.UntreatedChallenge(%d,%d) error(%v)", oid, role, err)
return
}
allChalls := []*model.Challenge{}
if err = s.dao.DB.Where("oid=?", oid).Order("id DESC").Find(&allChalls).Error; err != nil {
log.Error("s.dao.UntreatedChallenge(%d,%d) error(%v)", oid, role, err)
return
}
for _, challenge := range allChalls {
if value := challenge.GetState(uint8(role)); value == 0 {
challs = append(challs, challenge)
}
}
for _, chall := range challs {
if err = s.dao.DB.Where("cid=?", chall.ID).Find(&chall.BusinessInfo).Error; err != nil {
log.Error("s.dao.UntreatedChallenge(%d,%d) GetBusiness(%d) error(%v)", oid, role, chall.ID, err)
continue
}
}
err = nil
return
}
// Callback callback audit event to manage
func (s *Service) Callback(c context.Context, chall *model.Challenge, businessID int8) (err error) {
switch chall.Business {
case model.BusinessAudit:
err = s.dao.Callback(c, chall, businessID)
default:
}
return
}
// CallbackByID callback audit event to manage
func (s *Service) CallbackByID(c context.Context, challengeID int32, businessID int8) (err error) {
chall := &model.Challenge{}
if err = s.dao.DB.Where("id=? ", challengeID).Find(chall).Error; err != nil {
log.Error("s.CallbackByID(%d) error(%v)", challengeID, err)
return
}
return s.Callback(c, chall, businessID)
}
// Challenges3 .
func (s *Service) Challenges3(c context.Context, cp3 *model.ChallengeParam3) (res []*model.Challenge3, err error) {
if err = s.dao.DB.Where("business=? AND oid=? AND mid=?", cp3.Business, cp3.Oid, cp3.Mid).Order("id DESC").Find(&res).Error; err != nil {
log.Error("s.Challenges3 error(%v)", err)
}
return
}
// AddChallenge3 .
func (s *Service) AddChallenge3(c context.Context, cp3 *model.ChallengeParam3) (row int64, err error) {
var (
lasttime = time.Now()
group = &model.Group3{}
challenge *model.Challenge
)
tx := s.dao.DB.Begin()
if tx.Error != nil {
log.Error("s.dao.DB.Begin error(%v)", err)
return
}
defer func() {
if err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("s.AddChallenge3 tx.Rollback error(%v)", err)
}
}
}()
if err = tx.Exec(_inGroupSQL3, cp3.Oid, cp3.Business, cp3.Fid, cp3.Rid, cp3.Eid, cp3.Score, cp3.Tid, lasttime, lasttime, model.StateTypePending).Error; err != nil {
log.Error("s.AddChallenge3 tx.Exec error(%v)", err)
return
}
if err = tx.Where("business=? AND oid=? AND eid=?", cp3.Business, cp3.Oid, cp3.Eid).Find(&group).Error; err != nil {
log.Error("s.AddChallenge3 tx.Where error(%v)", err)
return
}
challenge = &model.Challenge{
Tid: cp3.Tid,
Gid: int32(group.ID),
Oid: cp3.Oid,
Mid: cp3.Mid,
Eid: cp3.Eid,
Desc: cp3.Desc,
MetaData: cp3.MetaData,
Business: cp3.Business,
BusinessState: cp3.BusinessState,
Adminid: cp3.AdminID,
Assignee: cp3.AssigneeID,
}
challenge.SetState(uint32(0), cp3.Role)
if err = tx.Create(challenge).Error; err != nil {
log.Error("s.AddChallenge3 tx.Create error(%v)", err)
return
}
row = int64(challenge.ID)
if len(cp3.Attachments) > 0 {
values := []string{}
valueArgs := []interface{}{}
for _, a := range cp3.Attachments {
values = append(values, "(?,?)")
valueArgs = append(valueArgs, challenge.ID, a)
}
stmt := fmt.Sprintf("INSERT INTO workflow_attachment(cid,path) VALUES %s", strings.Join(values, ","))
if err = tx.Exec(stmt, valueArgs...).Error; err != nil {
return
}
}
if cp3.CheckBusiness() {
// todo workflow_business.oid save cp3.aid if exist
var aid = cp3.Oid
if cp3.Aid > 0 {
aid = cp3.Aid
}
// todo insert or update business record
bus := &model.Business{}
if err = tx.Table("workflow_business").Where("gid = ?", group.ID).Find(bus).Error; err != nil {
if err != gorm.ErrRecordNotFound {
log.Error("s.AddChallenge3 tx.Where error(%v)", err)
return
}
// insert business table
if err = tx.Exec(_inBusiness3, aid, cp3.BusinessTypeid, cp3.Business, cp3.BusinessTitle, cp3.BusinessContent, cp3.BusinessMid, cp3.BusinessExtra, challenge.ID, group.ID).Error; err != nil {
log.Error("tx.Exec(%s) params(oid=%d,typeid=%d,business=%d,title=%s,content=%s,mid=%d,extra=%s,cid=%d,gid=%d) error(%v)", _inBusiness3, cp3.Oid, cp3.BusinessTypeid, cp3.Business, cp3.BusinessTitle, cp3.BusinessContent, cp3.BusinessMid, cp3.BusinessExtra, challenge.ID, group.ID, err)
return
}
} else {
// update business table
if err = tx.Exec(_upBusiness3, cp3.BusinessTypeid, cp3.BusinessTitle, cp3.BusinessContent, cp3.BusinessExtra, group.ID).Error; err != nil {
log.Error("tx.Exec(%s) params(typeid=%d,title=%s,content=%s,extra=%s) error(%v)", _upBusiness3, cp3.BusinessTypeid, cp3.BusinessTitle, cp3.BusinessContent, cp3.BusinessExtra, err)
}
}
}
if err = tx.Commit().Error; err != nil {
log.Error("s.AddChallenge3 tx.Commit error(%v)", err)
}
// upsert
if group.Count <= 1 {
s.cache.Do(context.Background(), func(c context.Context) {
if err = s.dao.UpdateUserTag(c, group.ID, cp3.Tid); err != nil {
log.Error("s.dao.UpdateUserTag gid:%d error:%v", group.ID, err)
}
})
}
return
}
// GroupState3 .
func (s *Service) GroupState3(c context.Context, cp3 *model.ChallengeParam3) (state int, err error) {
err = s.dao.DB.Table("workflow_group").Select("state").Where("business=? AND oid=? AND eid=?", cp3.Business, cp3.Oid, cp3.Eid).Row().Scan(&state)
if err == sql.ErrNoRows {
err = nil
state = -1
}
return
}

View File

@@ -0,0 +1,24 @@
package service
import (
"context"
"testing"
"go-common/app/service/main/workflow/model"
"github.com/smartystreets/goconvey/convey"
)
func TestAddChallenge(t *testing.T) {
convey.Convey("Test AddChallenge", t, func() {
_, err := s.AddChallenge(context.Background(), &model.ChallengeParam{})
convey.ShouldBeNil(err)
})
}
func TestCancelChallenge(t *testing.T) {
convey.Convey("Test CancelChallenge", t, func() {
err := s.CloseChallenge(context.Background(), 1, 1, 1, 1, "abc")
convey.ShouldBeNil(err)
})
}

View File

@@ -0,0 +1,19 @@
package service
import (
"context"
"go-common/app/service/main/workflow/model"
"go-common/library/log"
)
// AddEvent add event
func (s *Service) AddEvent(c context.Context, cid int32, content, attachments string, event int8) (row int32, err error) {
et := &model.Event{Cid: cid, Event: event, Content: content, Attachments: attachments}
if err = s.dao.DB.Create(et).Error; err != nil {
log.Error("s.workflow.AddEvent(%+v) error(%v)", et, err)
return
}
row = et.ID
return
}

View File

@@ -0,0 +1,93 @@
package service
import (
"context"
"go-common/app/service/main/workflow/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
)
// DeleteGroup .
func (s *Service) DeleteGroup(c context.Context, p *model.DeleteGroupParams) (err error) {
tx := s.dao.DB.Begin()
if tx.Error != nil {
log.Error("s.DeleteGroup error(%v)", tx.Error)
return
}
defer func() {
if err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("service tx rollback error(%v)", err1)
}
}
}()
var gid int64
if err = tx.Table("workflow_group").Select("id").Where("business=? AND oid=? AND eid=?", p.Business, p.OID, p.EID).Row().Scan(&gid); err != nil {
log.Error("service select gid err business(%d) oid(%d) eid(%d) error(%v)", p.Business, p.OID, p.EID, err)
if err == sql.ErrNoRows {
err = ecode.WkfGroupNotFound
}
return
}
if err = tx.Table("workflow_group").Where("id=?", gid).Update("state", model.StateDelete).Error; err != nil {
log.Error("service delete group state error(%v)", err)
return
}
challs := []*model.Challenge3{}
if err = tx.Table("workflow_chall").Where("gid=?", gid).Find(&challs).Error; err != nil {
log.Error("service find chall by gid(%d) error(%v)", gid, err)
return
}
cids := []int64{}
for _, c := range challs {
cids = append(cids, c.ID)
}
if err = tx.Table("workflow_chall").Where("id IN (?)", cids).Update("dispatch_state", model.StateDelete).Error; err != nil {
log.Error("service update chall state ids(%v) error(%v)", cids, err)
return
}
if err = tx.Commit().Error; err != nil {
log.Error("service delete group commit error(%v)", err)
}
return
}
// PublicRefereeGroup .
func (s *Service) PublicRefereeGroup(c context.Context, prgp *model.PublicRefereeGroupParams) (err error) {
tx := s.dao.DB.Begin()
if tx.Error != nil {
log.Error("s.PublicRefereeGroup error(%v)", tx.Error)
return
}
defer func() {
if err != nil {
if err1 := tx.Rollback(); err1 != nil {
log.Error("service tx rollback error(%v)", err1)
}
}
}()
var gid int64
if err = tx.Table("workflow_group").Select("id").Where("business=? AND oid=? AND eid=?", prgp.Business, prgp.Oid, prgp.Eid).Row().Scan(&gid); err != nil {
log.Error("service select gid err business(%d) oid(%d) eid(%d) error(%v)", prgp.Business, prgp.Oid, prgp.Eid, err)
if err == sql.ErrNoRows {
err = ecode.WkfGroupNotFound
}
return
}
if err = tx.Table("workflow_group").Where("id=?", gid).Update("state", model.StatePublicReferee).Error; err != nil {
log.Error("service public refree group state error(%v)", err)
return
}
if err = tx.Table("workflow_chall").Where("gid=?", gid).Update("dispatch_state", model.StatePublicReferee).Error; err != nil {
log.Error("service update chall state by gid(%d) error(%v)", gid, err)
return
}
if err = tx.Commit().Error; err != nil {
log.Error("service delete group commit error(%v)", err)
}
return
}

View File

@@ -0,0 +1,73 @@
package service
import (
"context"
"time"
archive "go-common/app/service/main/archive/api"
"go-common/app/service/main/workflow/conf"
"go-common/app/service/main/workflow/dao"
"go-common/app/service/main/workflow/dao/sobot"
"go-common/app/service/main/workflow/model"
"go-common/library/log"
"go-common/library/sync/pipeline/fanout"
"github.com/pkg/errors"
)
// Service is service.
type Service struct {
c *conf.Config
// dao
dao *dao.Dao
sobot *sobot.Dao
// tags cache
tagsCache *model.TagsCache
arcClient archive.ArchiveClient //archive rpc client
cache *fanout.Fanout
}
// New is videoup-admin service implementation.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
sobot: sobot.New(c),
tagsCache: &model.TagsCache{
TagMap: make(map[int8]map[int32]*model.Tag),
TagSlice: make(map[int8][]*model.Tag),
TagMap3: make(map[int64]map[int64][]*model.Tag3),
},
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)),
}
var err error
if s.arcClient, err = archive.NewClient(c.ArchiveRPC); err != nil {
panic(errors.Wrap(err, "archive.NewClient failed"))
}
// load cache
go s.cacheproc()
return
}
// cacheproc goroutine
func (s *Service) cacheproc() {
for {
s.loadTags()
s.loadTags3()
time.Sleep(5 * time.Minute)
}
}
// Close consumer close.
func (s *Service) Close() {
s.dao.Close()
}
// Ping check server ok.
func (s *Service) Ping(c context.Context) (err error) {
if err = s.dao.Ping(c); err != nil {
log.Error("Ping() err(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,30 @@
package service
import (
"context"
"flag"
"testing"
"go-common/app/service/main/workflow/conf"
"github.com/smartystreets/goconvey/convey"
)
var s *Service
func init() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
s = New(conf.Conf)
}
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,61 @@
package service
import (
"context"
"encoding/json"
"go-common/app/service/main/workflow/model/sobot"
"go-common/library/ecode"
"go-common/library/log"
)
// SobotTicketInfo get ticket info
func (s *Service) SobotTicketInfo(c context.Context, ticketID int32) (res json.RawMessage, err error) {
if res, err = s.sobot.SobotTicketInfo(c, ticketID); err != nil {
log.Error("s.sobot.SobotAddTicket(%d) error(%v)", ticketID, err)
return
}
return
}
// SobotTicketAdd add ticket
func (s *Service) SobotTicketAdd(c context.Context, tp *sobot.TicketParam) (err error) {
if !tp.Check() {
log.Error("s.SobotTicketAdd() params(%+v) error", tp)
err = ecode.RequestErr
return
}
if err = s.sobot.SobotAddTicket(c, tp); err != nil {
log.Error("s.sobot.SobotAddTicket(%+v) error(%v)", tp, err)
return
}
return
}
// SobotTicketModify modify ticket status
func (s *Service) SobotTicketModify(c context.Context, tp *sobot.TicketParam) (err error) {
if !tp.CheckModify() {
log.Error("s.SobotTicketModify() params(%+v) error", tp)
err = ecode.RequestErr
return
}
if err = s.sobot.SobotTicketModify(c, tp); err != nil {
log.Error("s.sobot.SobotTicketModify(%+v) error(%v)", tp, err)
return
}
return
}
// SobotReplyAdd add reply to sobot
func (s *Service) SobotReplyAdd(c context.Context, rp *sobot.ReplyParam) (err error) {
if !rp.Check() {
log.Error("s.SobotReplyAdd() params(%+v) error", rp)
err = ecode.RequestErr
return
}
if err = s.sobot.SobotAddReply(c, rp); err != nil {
log.Error("s.sobot.SobotAddReply(%+v) error(%v)", rp, err)
return
}
return
}

View File

@@ -0,0 +1,154 @@
package service
import (
"context"
"net/url"
"strconv"
"go-common/app/service/main/workflow/model"
"go-common/library/ecode"
"go-common/library/log"
)
// TagSlice get Tagslice
func (s *Service) TagSlice(business int8) []*model.Tag {
return s.tagsCache.TagSlice[business]
}
// TagMap get TagMap
func (s *Service) TagMap(business int8, tid int32) *model.Tag {
return s.tagsCache.TagMap[business][tid]
}
// tags get all tag list
func (s *Service) tags(c context.Context) (tagsCache *model.TagsCache, err error) {
var (
tags []*model.Tag
tagsMap = make(map[int8]map[int32]*model.Tag)
tagSlice = make(map[int8][]*model.Tag)
)
if err = s.dao.DB.Where("state=?", 1).Order("weight desc").Find(&tags).Error; err != nil {
log.Error("d.DB.Where().Find() error(%v)", err)
return
}
for _, v := range tags {
if tagsMap[v.Business] == nil {
tagsMap[v.Business] = make(map[int32]*model.Tag)
}
tagsMap[v.Business][v.Tid] = v
tagSlice[v.Business] = append(tagSlice[v.Business], v)
}
tagsCache = &model.TagsCache{}
tagsCache.TagMap = tagsMap
tagsCache.TagSlice = tagSlice
return
}
// tags loading...
func (s *Service) loadTags() {
var (
tagsCache *model.TagsCache
err error
)
if tagsCache, err = s.tags(context.TODO()); err != nil {
log.Error("s.tag.Tags() error(%v)", err)
return
}
for business, tags := range tagsCache.TagSlice {
var tagTidList []int32
var controls []*model.Control
for _, tag := range tags {
tagTidList = append(tagTidList, tag.Tid)
}
if err = s.dao.DB.Where("tid in (?)", tagTidList).Order("weight").Find(&controls).Error; err != nil {
log.Error("s.tag.Controls(%d) error(%v)", tagTidList, err)
return
}
for _, control := range controls {
tagsCache.TagMap[business][control.Tid].Controls = append(tagsCache.TagMap[business][control.Tid].Controls, control)
}
}
s.tagsCache = tagsCache
}
// Tag3 .
func (s *Service) Tag3(bid, rid int64) (res []*model.Tag3) {
var (
ok = false
bidRes = make(map[int64][]*model.Tag3)
)
res = []*model.Tag3{}
if bidRes, ok = s.tagsCache.TagMap3[bid]; !ok {
log.Error("nothing to get tag list by bid(%d)", bid)
return
}
if rid != 0 {
if res, ok = bidRes[rid]; !ok {
log.Error("nothing to get tag list by bid(%d) rid(%d)", bid, rid)
}
return
}
for _, br := range bidRes {
res = append(res, br...)
}
return
}
// tags3 .
func (s *Service) tags3(c context.Context) (res []*model.Tag3, err error) {
res = []*model.Tag3{}
tmpTag3 := model.ResponseTag3{}
tagUV := url.Values{}
tagUV.Set("ps", strconv.Itoa(model.ControlPageSize))
if err = s.dao.ReadClient.Get(c, s.dao.MngTagURL, "", tagUV, &tmpTag3); err != nil {
log.Error("s.dao.ReadClient.Get error(%v)", err)
return
}
if tmpTag3.Code != ecode.OK.Code() {
err = ecode.Int(tmpTag3.Code)
log.Error("get tag info error(%v)", err)
return
}
tmpControl3 := model.ResponseControl3{}
controlUV := url.Values{}
if err = s.dao.ReadClient.Get(c, s.dao.MngControlURL, "", controlUV, &tmpControl3); err != nil {
log.Error("get control list error(%v)", err)
return
}
if tmpControl3.Code != ecode.OK.Code() {
err = ecode.Int(tmpControl3.Code)
log.Error("get control list error(%v)", err)
return
}
res = tmpTag3.Data.Data
controls := tmpControl3.Data
for _, r := range res {
for _, c := range controls {
if r.BID == c.BID && r.TagID == c.TID {
r.Controls = append(r.Controls, c)
}
}
}
return
}
// loadTags3 .
func (s *Service) loadTags3() {
s.tagsCache.TagMap3 = map[int64]map[int64][]*model.Tag3{}
s.tagsCache.TagMap3Tid = map[int64]map[int64]*model.Tag3{}
data, err := s.tags3(context.Background())
if err != nil {
log.Error("s.tags3 error(%v)", err)
return
}
for _, d := range data {
if _, ok := s.tagsCache.TagMap3[d.BID]; !ok {
s.tagsCache.TagMap3[d.BID] = make(map[int64][]*model.Tag3)
}
if _, ok := s.tagsCache.TagMap3Tid[d.BID]; !ok {
s.tagsCache.TagMap3Tid[d.BID] = make(map[int64]*model.Tag3)
}
s.tagsCache.TagMap3[d.BID][d.RID] = append(s.tagsCache.TagMap3[d.BID][d.RID], d)
s.tagsCache.TagMap3Tid[d.BID][d.TagID] = d
}
}

View File

@@ -0,0 +1,43 @@
package service
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestTagSlice(t *testing.T) {
convey.Convey("Test TagSlice", t, func() {
_ = s.TagSlice(int8(0))
convey.ShouldBeNil(nil)
})
}
func TestTagMap(t *testing.T) {
convey.Convey("Test TagMap", t, func() {
_ = s.TagMap(int8(0), int32(0))
convey.ShouldBeNil(nil)
})
}
func TestTags(t *testing.T) {
convey.Convey("Test Tags", t, func() {
_, err := s.tags(context.Background())
convey.ShouldBeNil(err)
})
}
func TestTag3(t *testing.T) {
convey.Convey("Test Tag3", t, func() {
_ = s.Tag3(int64(0), int64(0))
convey.ShouldBeNil(nil)
})
}
func TestTags3(t *testing.T) {
convey.Convey("Test Tags3", t, func() {
_, err := s.tags3(context.Background())
convey.ShouldBeNil(err)
})
}