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

View File

@@ -0,0 +1,173 @@
#### 工单流审核后台接口
##### Version 1.6.6
> 1.account服务切到grpc
##### Version 1.6.5
> 1.account服务切回到gorpc
##### Version 1.6.4
> 1.group列表支持是否优先匹配关键字
> 2.支持搜索举报对象的第一个用户举报tag id
##### Version 1.6.3
> 1.接入配置中心sdk paladin
##### Version 1.6.2
> 1.v1举报接口下线
##### Version 1.6.1
> 1.评论举报增加火鸟来源, 站内信推送过滤
##### Version 1.6.0
> 1.长短评接入v2
##### Version 1.5.2
> 1.封禁发送站内信
##### Version 1.5.1
> 1.优化用户tag统计
##### Version 1.5.0
> 1.稿件投诉迁移v3
##### Version 1.4.5
> 1.站内信title长度截断
##### Version 1.4.4
> 1.v3/group/set 操作校验 rid, 有效操作同步写rid
##### Version 1.4.3
> 1.评论业务站内信内容优化
##### Version 1.4.2
> 1.增加封禁类型(恶意冒充他人)
##### Version 1.4.1
> 1.封禁操作上报小黑屋
##### Version 1.4.0
> 1.对接评论举报
##### Version 1.3.7
> 1.修复manager_v4请求修改申诉状态
##### Version 1.3.6
> 1.修复站内信推送内容过长
##### Version 1.3.5
> 1.修复es排序参数
##### Version 1.3.4
> 1.工单列表增加rid二次验证
##### Version 1.3.3
> 2.优化日志上报
##### Version 1.3.2
> 1.字幕举报对接
##### Version 1.3.1
> 1.修复oid溢出问题
> 2.tid参数允许0值
##### Version 1.3.0
> 1.评论举报对接
> 2.model代码优化
> 3.ctx取admin_name上报行为日志
> 4.不再依赖旧的es appid
> 5.新的es appid迁移databus
##### Version 1.2.9
> 1.去掉操作日志写db
##### Version 1.2.8
> 1.工单列表meta_data字段通用化
##### Version 1.2.7
> 1.account-service 切换rpc请求
##### Version 1.2.6
> 1.全局通过 gid 查询 business object
> 2.操作日志查询行为日志
> 3.修复反馈详情列表
> 4.优化操作日志查询
> 5.移除本地tag依赖,迁移到manager-admin
##### Version 1.2.5
> 1.修复errgroup内存泄露
> 2.优化es查询工单详情
> 3.修复工单列表日志显示
> 4.修复log,tag列表
> 5.es client 配置化
> 6.请求列表后访问db check工单状态并过滤
##### Version 1.2.4
> 1.修复logproc
##### Version 1.2.3
> 1.优化调用账号api的错误日志
##### Version 1.2.2
> 1.修复参数错误
##### Version 1.2.1
> 1.优化日志上报
##### Version 1.2.0
> 1.显示用户 special tag, 粉丝数
> 2.支持分词查询回复内容, 搜索受理人 username
> 3.修复点评展示的 typename
> 4.消息推送记录行为日志
##### Version 1.1.1
> 1.不依赖 php
> 2.修复api权限验证
> 3.修复列表翻页和排序
> 4.老api下线
> 5.log 接入行为日志
> 6.修复点评展示的 typename
##### Version 1.1.0
> 1.工作台 v1.1 基础功能
> 2.稿件申诉回复进入工作台
> 3.引入 redis
> 4.规范路由
> 5.部分api不再依赖php
> 6.规范api返回格式
> 7.修复延时导致回复状态错误
> 8.修复工作台分页展示
> 9.前端与 manager 解耦,直接访问 workflow
> 10.workflow tag默认状态为开启
##### Version 1.0.1
> 1.读取新的状态字段
> 2.复合的状态字段不返回到前端
> 3.修复对group操作的状态双写
> 4.移除双写操作,只使用复合的状态字段
> 5.优化group操作操作group时异步对challenge操作
> 6.修复审核操作日志丢失
##### Version 1.0.0
> 1.增加工作台功能
> 2.修复工作台工单展示
> 3.接入memcache
> 4.处理工单同时更新group表
> 5.整合修复项目路由
> 6.修复状态双写
##### Version 0.2.1
> 1.增加小黑屋申诉业务
##### Version 0.2.0
> 1.投诉通用化
> 2.给出申诉的反馈处理时间
##### Version 0.1.0
> 1.添加申诉反馈单的功能
##### Version 0.0.2
> 1.添加申诉执法单功能
##### Version 0.0.1
> 1.工单流项目初版

View File

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

View File

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

View File

@@ -0,0 +1,93 @@
#### workflow-admin
##### 项目简介
> 1.申诉投诉工单流后台接口
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common
#### 初始化 Tag
```SQL
INSERT INTO workflow_tag (id, name, business, weight, round, state, remark) VALUES
(1, '有其他问题', 1, 0, 2, 1, '为帮助审核人员更快处理,请补充问题类型和出现位置等详细信息'),
(2, '违法违禁', 1, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
(3, '色情', 1, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
(4, '低俗', 1, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
(5, '赌博诈骗', 1, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
(6, '血腥暴力', 1, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
(7, '人身攻击', 1, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
(8, '与站内其他视频撞车', 1, 0, 1, 1, '为帮助审核人员更快处理,请补充重复稿件 av 号'),
(9, '引战', 1, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置'),
(10, '不能参加充电计划', 1, 0, 2, 1, '为帮助审核人员更快处理, 请补充问题类型和出现位置等详细信息');
INSERT INTO workflow_tag_control (tid, title, name, weight, component, placeholder, required) VALUES
(1, '描述', '描述', 100, 'textarea', '', 1),
(2, '描述', '描述', 100, 'textarea', '', 1),
(3, '描述', '描述', 100, 'textarea', '', 1),
(4, '描述', '描述', 100, 'textarea', '', 1),
(5, '描述', '描述', 100, 'textarea', '', 1),
(6, '描述', '描述', 100, 'textarea', '', 1),
(7, '描述', '描述', 100, 'textarea', '', 1),
(8, 'av 号', 'av 号', 100, 'input', '', 1),
(8, '描述', '描述', 100, 'textarea', '', 0),
(9, '描述', '描述', 100, 'textarea', '', 1),
(10, '描述', '描述', 100, 'textarea', '', 1);
INSERT INTO workflow_tag (name, business, weight, round, state, remark) VALUES
('违法违禁', 3, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('色情', 3, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('低俗', 3, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('赌博诈骗', 3, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('人身攻击', 3, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('内容抄袭', 3, 0, 2, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('侵犯隐私', 3, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('垃圾广告', 3, 0, 2, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('引战', 3, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置'),
('恶意剧透', 3, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置'),
('作品不相关', 3, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置'),
('其他问题', 3, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置');
INSERT INTO workflow_tag (name, business, weight, round, state, remark) VALUES
('违法违禁', 4, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('色情', 4, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('低俗', 4, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('赌博诈骗', 4, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('人身攻击', 4, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('内容抄袭', 4, 0, 2, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('侵犯隐私', 4, 0, 1, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('垃圾广告', 4, 0, 2, 1, '为帮助审核人员更快处理,请补充违规内容出现位置'),
('引战', 4, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置'),
('恶意剧透', 4, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置'),
('作品不相关', 4, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置'),
('其他问题', 4, 0, 2, 1, '为帮助审核人员更快处理,请补充引战的话题和出现位置');
INSERT INTO workflow_tag (name, business, weight, round, state) VALUES
('评论', 5, 0, 1, 1),
('弹幕', 5, 0, 2, 1),
('私信', 5, 0, 3, 1),
('标签', 5, 0, 4, 1),
('个人资料', 5, 0, 5, 1),
('投稿', 5, 0, 6, 1),
('音频', 5, 0, 7, 1),
('专栏', 5, 0, 8, 1),
('空间头图', 5, 0, 9, 1);
```
#### DB Migration
##### 增加申诉权限点
```SQL
-- 这里是 bilibili_manager.auth_item
UPDATE auth_item set name='投诉处理列表',description='投诉处理列表',data='WF_ARCHIVE_COMPLAIN_LIST' WHERE name='稿件反馈列表';
UPDATE auth_item set name='投诉处理一审',description='投诉处理一审',data='WF_ARCHIVE_COMPLAIN_ROUND_1' WHERE name='稿件反馈一审';
UPDATE auth_item set name='投诉处理回查',description='投诉处理回查',data='WF_ARCHIVE_COMPLAIN_ROUND_2' WHERE name='稿件反馈回查';
INSERT auth_item (name,description,data) VALUES
('申诉处理列表', '申诉处理列表', 'WF_ARCHIVE_APPEAL_LIST'),
('申诉处理一审', '申诉处理一审', 'WF_ARCHIVE_APPEAL_ROUND_1'),
('申诉处理回查', '申诉处理回查', 'WF_ARCHIVE_APPEAL_ROUND_2');
```

View File

@@ -0,0 +1,43 @@
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-admin-develop.toml"],
importpath = "go-common/app/admin/main/workflow/cmd",
tags = ["automanaged"],
deps = [
"//app/admin/main/workflow/http:go_default_library",
"//app/admin/main/workflow/service:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus/report: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,51 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/admin/main/workflow/http"
"go-common/app/admin/main/workflow/service"
"go-common/library/conf/paladin"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/queue/databus/report"
)
func main() {
flag.Parse()
if err := paladin.Init(); err != nil {
panic(err)
}
// init log
log.Init(nil)
defer log.Close()
log.Info("workflow-admin start")
svc := service.New()
http.Init(svc)
// report
report.InitManager(nil)
// ecode init
ecode.Init(nil)
// 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-admin get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("workflow-admin exit")
time.Sleep(1 * time.Second)
svc.Close()
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,223 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
pid = "/tmp/workflow-admin.pid"
dir = "./"
family = "workflow-admin"
chanSize = 1024
[log]
dir = "/data/log/workflow-admin"
[multiHTTP]
[multiHTTP.inner]
addrs = ["0.0.0.0:7211"]
[multiHTTP.local]
addrs = ["0.0.0.0:7212"]
[bm]
addr = "0.0.0.0:7211"
maxListen = 1000
timeout = "10s"
[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 = 10
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 = 10
idleTimeout = "4h"
[db]
[db.workflow]
name = "172.22.16.3:3459"
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 = 10
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "1s"
[db.workflow.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[db.workflowread]
name = "172.22.16.3:3459"
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 = 10
queryTimeout = "1s"
execTimeout = "1s"
tranTimeout = "1s"
[db.workflowread.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify]
[identify.host]
auth = "http://uat-passport.bilibili.co"
secret = "http://uat-open.bilibili.co"
[identify.HTTPClient]
key = "6aa4286456d16b97"
secret = "351cf022e1ae8296109c3c524faafcc8"
dial = "100ms"
timeout = "200ms"
keepAlive = "60s"
[identify.httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[host]
searchURL = "http://uat-bili-search.bilibili.co"
#searchURL = "http://127.0.0.1:18080"
reviewURL = "https://uat-bangumi-mng.bilibili.co"
platformURL = "http://uat-api.bilibili.co"
messageURL = "http://uat-message.bilibili.co"
unameURL = "http://uat-manager.bilibili.co/x/admin/manager/users/unames"
uidURL = "http://uat-manager.bilibili.co/x/admin/manager/users/uids"
pgcURL = "http://uat-bangumi-mng.bilibili.co/media/internal/review/views"
archiveURL = "http://uat-archive.api.bilibili.co/videoup/views"
accountURL = "http://uat-api.bilibili.co/x/internal/v3/account/profile/stat"
archivetypesURL = "http://uat-archive.api.bilibili.co/videoup/types"
upergroupURL = "http://uat-api.bilibili.co/x/internal/uper/special/get"
relationURL = "http://uat-api.bilibili.co/x/internal/relation/stats"
businessListURL = "http://uat-manager.bilibili.co/x/admin/manager/internal/business/list"
userListURL = "http://uat-manager.bilibili.co/x/admin/manager/business/user/list"
tagListURL = "http://uat-manager.bilibili.co/x/admin/manager/internal/tag/list"
channelInfoURL = "http://uat-api.bilibili.co/x/internal/channel/resource/checkback"
businessRoleURL = "http://uat-manager.bilibili.co/x/admin/manager/internal/business/role"
blockURL = "http://uat-api.bilibili.co/x/internal/block/batch/block"
creditBlockedURL = "http://uat-api.bilibili.co/x/internal/credit/blocked/info/add"
blockInfoURL = "http://uat-api.bilibili.co/x/internal/block/info"
blockNumURL = "http://uat-api.bilibili.co/x/internal/credit/blocked/user/num"
blockCaseAddURL = "http://uat-api.bilibili.co/x/internal/credit/blocked/case/add"
[httpClient]
[httpClient.read]
key = "6aa4286456d16b97"
secret = "351cf022e1ae8296109c3c524faafcc8"
dial = "50ms"
timeout = "3s"
keepAlive = "60s"
timer = 16
[httpClient.read.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[httpClient.write]
key = "6aa4286456d16b97"
secret = "351cf022e1ae8296109c3c524faafcc8"
dial = "50ms"
timeout = "3s"
keepAlive = "60s"
timer = 16
[httpClient.write.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth]
managerHost = "http://uat-manager.bilibili.co"
dashboardHost = "http://dashboard-mng.bilibili.co"
dashboardCaller = "manager-go"
[auth.DsHTTPClient]
key = "manager-go"
secret = "949bbb2dd3178252638c2407578bc7ad"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.DsHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.MaHTTPClient]
key = "f6433799dbd88751"
secret = "36f8ddb1806207fe07013ab6a77a3935"
dial = "1s"
timeout = "1s"
keepAlive = "60s"
[auth.MaHTTPClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[auth.session]
sessionIDLength = 32
cookieLifeTime = 1800
cookieName = "mng-go"
domain = ".bilibili.co"
[auth.session.Memcache]
name = "go-business/auth"
proto = "tcp"
addr = "172.18.33.61:11232"
active = 10
idle = 10
dialTimeout = "5s"
readTimeout = "5s"
writeTimeout = "5s"
idleTimeout = "80s"
[memcache]
proto = "tcp"
addr = "172.18.33.60:11236"
idle = 10
active = 10
dialTimeout = "2s"
readTimeout = "2s"
writeTimeout = "2s"
idleTimeout = "7h"
expireCount = "10s"
expireOnline = "0s"
[redis]
proto = "tcp"
addr = "172.18.33.61:6889"
idle = 10
active = 10
dialTimeout = "500ms"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
expire = "0s"
[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
[accountRPC]
timeout = "2s"
[archiveRPC]
timeout = "2s"

View File

@@ -0,0 +1,95 @@
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",
"callback_test.go",
"challenge_test.go",
"dao_test.go",
"event_test.go",
"extra_test.go",
"group_test.go",
"memcache_test.go",
"message_test.go",
"redis_test.go",
"search2_test.go",
"tag_test.go",
],
embed = [":go_default_library"],
tags = ["automanaged"],
deps = [
"//app/admin/main/workflow/model:go_default_library",
"//app/admin/main/workflow/model/param:go_default_library",
"//app/admin/main/workflow/model/search:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/ecode:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"business.go",
"callback.go",
"challenge.go",
"dao.go",
"event.go",
"extra.go",
"group.go",
"memcache.go",
"message.go",
"redis.go",
"search2.go",
"tag.go",
],
importpath = "go-common/app/admin/main/workflow/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/workflow/model:go_default_library",
"//app/admin/main/workflow/model/manager:go_default_library",
"//app/admin/main/workflow/model/param:go_default_library",
"//app/admin/main/workflow/model/search:go_default_library",
"//app/interface/main/credit/model:go_default_library",
"//app/job/main/member/model/block:go_default_library",
"//app/service/main/account/api:go_default_library",
"//app/service/main/archive/api:go_default_library",
"//app/service/main/member/model/block:go_default_library",
"//library/cache/memcache:go_default_library",
"//library/cache/redis:go_default_library",
"//library/conf/paladin: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/net/rpc/warden:go_default_library",
"//library/sync/errgroup:go_default_library",
"//library/xstr: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,303 @@
package dao
import (
"context"
"encoding/json"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/manager"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
const _businessRoleURI = "http://manager.bilibili.co/x/admin/manager/internal/business/role"
var metas map[int8]*model.Meta
func init() {
metas = make(map[int8]*model.Meta)
data := `[
{
"business": 1,
"name": "稿件投诉",
"item_type": "group",
"rounds": [
{
"id": 1,
"name": "一审"
},
{
"id": 2,
"name": "回查"
}
]
},
{
"business": 2,
"name": "稿件申诉",
"item_type": "challenge",
"rounds": [
{
"id": 1,
"name": "一审"
},
{
"id": 2,
"name": "回查"
},
{
"id": 3,
"name": "三查"
},
{
"id": 11,
"name": "客服"
}
]
},
{
"business": 3,
"name": "短点评投诉",
"item_type": "group",
"rounds": [
{
"id": 1,
"name": "一审"
},
{
"id": 2,
"name": "回查"
}
]
},
{
"business": 4,
"name": "长点评投诉",
"item_type": "group",
"rounds": [
{
"id": 1,
"name": "一审"
},
{
"id": 2,
"name": "回查"
}
]
},
{
"business": 5,
"name": "小黑屋",
"item_type": "challenge",
"rounds": [
{
"id": 1,
"name": "评论"
},
{
"id": 2,
"name": "弹幕"
},
{
"id": 3,
"name": "私信"
},
{
"id": 4,
"name": "标签"
},
{
"id": 5,
"name": "个人资料"
},
{
"id": 6,
"name": "投稿"
},
{
"id": 7,
"name": "音频"
},
{
"id": 8,
"name": "专栏"
},
{
"id": 9,
"name": "空间头图"
}
]
},
{
"business": 6,
"name": "稿件审核",
"item_type": "challenge",
"rounds": [
{
"id": 1,
"name": "一审"
}
]
},
{
"business": 7,
"name": "任务质检",
"item_type": "challenge",
"rounds": [
{
"id": 1,
"name": "一审"
}
]
},
{
"business": 8,
"name": "频道举报",
"item_type": "group",
"rounds": [
{
"id": 1,
"name": "一审"
}
]
}
]`
ml := make([]*model.Meta, 0)
err := json.Unmarshal([]byte(data), &ml)
if err != nil {
panic(err)
}
for _, m := range ml {
metas[m.Business] = m
}
}
// BatchLastBusRecIDs will retrive the last business record ids by serveral conditions
func (d *Dao) BatchLastBusRecIDs(c context.Context, oids []int64, business int8) (bids []int64, err error) {
bids = make([]int64, 0, len(oids))
if len(oids) <= 0 {
return
}
rows, err := d.ReadORM.Table("workflow_business").Select("max(id)").
Where("oid IN (?) AND business=?", oids, business).
Group("oid,business").Rows()
if err != nil {
err = errors.Wrapf(err, "Query(%v, %d)", oids, business)
return
}
defer rows.Close()
for rows.Next() {
var bid int64
if err = rows.Scan(&bid); err != nil {
err = errors.WithStack(err)
return
}
bids = append(bids, bid)
}
return
}
// BusinessRecs will retrive the business record by ids
func (d *Dao) BusinessRecs(c context.Context, bids []int64) (bs map[int32]*model.Business, err error) {
bs = make(map[int32]*model.Business, len(bids))
if len(bids) <= 0 {
return
}
blist := make([]*model.Business, 0, len(bids))
err = d.ReadORM.Table("workflow_business").Where("id IN (?)", bids).Find(&blist).Error
if err != nil {
err = errors.Wrapf(err, "Query(%v)", bids)
return
}
for _, b := range blist {
bs[b.Bid] = b
}
return
}
// LastBusRec will retrive last business record by business oid
func (d *Dao) LastBusRec(c context.Context, business int8, oid int64) (bs *model.Business, err error) {
bs = new(model.Business)
err = d.ReadORM.Table("workflow_business").Where("oid=? AND business=?", oid, business).Last(bs).Error
if err != nil || bs.Bid == 0 {
err = errors.Wrapf(err, "Query(%d, %d)", business, oid)
bs = nil
return
}
return
}
// BatchBusRecByCids will retrive businesses by cids
func (d *Dao) BatchBusRecByCids(c context.Context, cids []int64) (cidToBus map[int64]*model.Business, err error) {
cidToBus = make(map[int64]*model.Business)
if len(cids) <= 0 {
return
}
blist := make([]*model.Business, 0, len(cids))
err = d.ReadORM.Table("workflow_business").Where("cid IN (?)", cids).Find(&blist).Error
if err != nil {
err = errors.Wrapf(err, "Query(%v)", cids)
return
}
for _, b := range blist {
cidToBus[b.Cid] = b
}
return
}
// BusObjectByGids will retrive businesses by gids
func (d *Dao) BusObjectByGids(c context.Context, gids []int64) (gidToBus map[int64]*model.Business, err error) {
gidToBus = make(map[int64]*model.Business, len(gids))
if len(gids) <= 0 {
return
}
blist := make([]*model.Business, 0, len(gids))
if err = d.ReadORM.Table("workflow_business").Where("gid IN (?)", gids).Find(&blist).Error; err != nil {
err = errors.Wrapf(err, "Query(%v)", gids)
return
}
for _, b := range blist {
gidToBus[b.Gid] = b
}
return
}
// AllMetas will retrive business meta infomation from pre-configured
func (d *Dao) AllMetas(c context.Context) map[int8]*model.Meta {
return metas
}
// LoadRole .
func (d *Dao) LoadRole(c context.Context) (role map[int8]map[int8]string, err error) {
var (
resp *manager.RoleResponse
ok bool
uri = _businessRoleURI
)
role = make(map[int8]map[int8]string)
if err = d.httpRead.Get(c, uri, "", nil, &resp); err != nil {
log.Error("failed call %s error(%v)", uri, err)
return
}
if resp.Code != ecode.OK.Code() {
err = ecode.Int(resp.Code)
log.Error("call %s error response code(%d) message(%s)", uri, resp.Code, resp.Message)
return
}
for _, r := range resp.Data {
if _, ok = role[r.Bid]; !ok {
role[r.Bid] = make(map[int8]string)
}
role[r.Bid][r.Rid] = r.Name
}
return
}

View File

@@ -0,0 +1,128 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoinit(t *testing.T) {
convey.Convey("init", t, func(ctx convey.C) {
ctx.Convey("When everything goes positive", func(ctx convey.C) {
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestDaoBatchLastBusRecIDs(t *testing.T) {
convey.Convey("BatchLastBusRecIDs", t, func(ctx convey.C) {
var (
c = context.Background()
oids = []int64{1}
business = int8(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
bids, err := d.BatchLastBusRecIDs(c, oids, business)
ctx.Convey("Then err should be nil.bids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(bids, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBusinessRecs(t *testing.T) {
convey.Convey("BusinessRecs", t, func(ctx convey.C) {
var (
c = context.Background()
bids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
bs, err := d.BusinessRecs(c, bids)
ctx.Convey("Then err should be nil.bs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(bs, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoLastBusRec(t *testing.T) {
convey.Convey("LastBusRec", t, func(ctx convey.C) {
var (
c = context.Background()
business = int8(1)
oid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
bs, err := d.LastBusRec(c, business, oid)
ctx.Convey("Then err should be nil.bs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(bs, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBatchBusRecByCids(t *testing.T) {
convey.Convey("BatchBusRecByCids", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
cidToBus, err := d.BatchBusRecByCids(c, cids)
ctx.Convey("Then err should be nil.cidToBus should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(cidToBus, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBusObjectByGids(t *testing.T) {
convey.Convey("BusObjectByGids", t, func(ctx convey.C) {
var (
c = context.Background()
gids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
gidToBus, err := d.BusObjectByGids(c, gids)
ctx.Convey("Then err should be nil.gidToBus should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(gidToBus, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAllMetas(t *testing.T) {
convey.Convey("AllMetas", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.AllMetas(c)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoLoadRole(t *testing.T) {
convey.Convey("LoadRole", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
role, err := d.LoadRole(c)
ctx.Convey("Then err should be nil.role should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(role, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,85 @@
package dao
import (
"bytes"
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
// AllCallbacks return all callbacks in database
func (d *Dao) AllCallbacks(c context.Context) (cbs map[int32]*model.Callback, err error) {
cbs = make(map[int32]*model.Callback)
cblist := make([]*model.Callback, 0)
err = d.ReadORM.Table("workflow_callback").Find(&cblist).Error
if err != nil {
err = errors.WithStack(err)
return
}
for _, cb := range cblist {
cbs[cb.CbID] = cb
}
return
}
// SendCallback send callback to pre configured server
func (d *Dao) SendCallback(c context.Context, cb *model.Callback, payload *model.Payload) (err error) {
var (
req *http.Request
pdata []byte
)
if pdata, err = json.Marshal(payload); err != nil {
return
}
// TODO:(zhoujiahui): with sign?
uv := url.Values{}
ts := strconv.FormatInt(time.Now().Unix(), 10)
uv.Set("ts", ts)
uv.Set("appkey", d.writeConf.Key)
sign := sign(uv, d.writeConf.Key, d.writeConf.Secret, true)
if req, err = http.NewRequest(http.MethodPost, cb.URL+"?ts="+ts+"&appkey="+d.writeConf.Key+"&sign="+sign, bytes.NewReader(pdata)); err != nil {
return
}
req.Header.Set("Content-Type", "application/json; charset=utf-8")
res := &model.CommonResponse{}
if err = d.httpWrite.Do(c, req, &res); err != nil {
log.Error("d.httpWrite.Do(%+v) error(%v)", req, err)
return
}
if res.Code != ecode.OK.Code() {
log.Error("callback occur code error url(%s) body(%s) error code(%v)", req.URL, string(pdata), ecode.Int(res.Code))
return
}
log.Info("send callback ok, req(%+v) body(%s) callback(%+v) ", req, string(pdata), cb)
return
}
// sign is used to sign form params by given condition.
func sign(params url.Values, appkey string, secret string, lower bool) (hexdigest string) {
data := params.Encode()
if strings.IndexByte(data, '+') > -1 {
data = strings.Replace(data, "+", "%20", -1)
}
if lower {
data = strings.ToLower(data)
}
digest := md5.Sum([]byte(data + secret))
hexdigest = hex.EncodeToString(digest[:])
return
}

View File

@@ -0,0 +1,61 @@
package dao
import (
"context"
"net/url"
"testing"
"go-common/app/admin/main/workflow/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoAllCallbacks(t *testing.T) {
convey.Convey("AllCallbacks", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
cbs, err := d.AllCallbacks(c)
ctx.Convey("Then err should be nil.cbs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(cbs, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSendCallback(t *testing.T) {
convey.Convey("SendCallback", t, func(ctx convey.C) {
var (
c = context.Background()
cb = &model.Callback{
URL: "http://uat-manager.bilibili.co/x/admin/reply/internal/callback/del",
}
payload = &model.Payload{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SendCallback(c, cb, payload)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaosign(t *testing.T) {
convey.Convey("sign", t, func(ctx convey.C) {
var (
params url.Values
appkey = ""
secret = ""
lower bool
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
hexdigest := sign(params, appkey, secret, lower)
ctx.Convey("Then hexdigest should not be nil.", func(ctx convey.C) {
ctx.So(hexdigest, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,326 @@
package dao
import (
"context"
"database/sql"
"encoding/json"
"time"
"go-common/app/admin/main/workflow/model"
"github.com/jinzhu/gorm"
"github.com/pkg/errors"
)
// Chall will retrive challenge by cid
func (d *Dao) Chall(c context.Context, cid int64) (chall *model.Chall, err error) {
chall = &model.Chall{}
if db := d.ReadORM.Table("workflow_chall").Where("id=?", cid).First(chall); db.Error != nil {
err = db.Error
if db.RecordNotFound() {
chall = nil
err = nil
} else {
err = errors.Wrapf(err, "chall(%d)", cid)
}
}
return
}
// Challs will select challenges by ids
func (d *Dao) Challs(c context.Context, cids []int64) (challs map[int64]*model.Chall, err error) {
challs = make(map[int64]*model.Chall, len(cids))
if len(cids) <= 0 {
return
}
chlist := make([]*model.Chall, 0)
err = d.ReadORM.Table("workflow_chall").Where("id IN (?)", cids).Find(&chlist).Error
if err != nil {
err = errors.WithStack(err)
return
}
for _, c := range chlist {
challs[c.Cid] = c
}
return
}
// StateChalls will select a set of groups by challenge ids and state
func (d *Dao) StateChalls(c context.Context, cids []int64, state int8) (challs map[int64]*model.Chall, err error) {
challs = make(map[int64]*model.Chall, len(cids))
challSlice := make([]*model.Chall, 0, len(cids))
if len(cids) <= 0 {
return
}
if err = d.ORM.Table("workflow_chall").Where("id IN (?)", cids).Find(&challSlice).Error; err != nil {
err = errors.WithStack(err)
return
}
for _, chall := range challSlice {
chall.FromState()
if chall.State != state {
continue
}
challs[chall.Cid] = chall
}
return
}
// LastChallIDsByGids will select last chall ids by given gids
func (d *Dao) LastChallIDsByGids(c context.Context, gids []int64) (cids []int64, err error) {
if len(gids) <= 0 {
return
}
var rows *sql.Rows
if rows, err = d.ReadORM.Table("workflow_chall").Select("max(id)").Where("gid IN (?)", gids).Group("gid").Rows(); err != nil {
return
}
defer rows.Close()
for rows.Next() {
var maxID int64
if err = rows.Scan(&maxID); err != nil {
return
}
cids = append(cids, maxID)
}
return
}
// TxUpChall will update state of a challenge
// Deprecated
func (d *Dao) TxUpChall(tx *gorm.DB, chall *model.Chall) (rows int64, err error) {
// write old field
chall.FromState()
db := tx.Table("workflow_chall").Where("id=?", chall.Cid).
Update("dispatch_state", chall.DispatchState)
if db.Error != nil {
err = errors.WithStack(db.Error)
return
}
rows = db.RowsAffected
return
}
// TxBatchUpChallByIDs will update state of challenges by cids
func (d *Dao) TxBatchUpChallByIDs(tx *gorm.DB, cids []int64, state int8) (err error) {
challSlice := make([]*model.Chall, 0, len(cids))
if len(cids) <= 0 {
return
}
if err = tx.Table("workflow_chall").Where("id IN (?)", cids).Find(&challSlice).Error; err != nil {
err = errors.WithStack(err)
return
}
for _, chall := range challSlice {
chall.SetState(uint32(state), uint8(0))
if err = tx.Table("workflow_chall").Where("id=?", chall.Cid).
Update("dispatch_state", chall.DispatchState).Error; err != nil {
err = errors.WithStack(err)
return
}
}
return
}
// AttPathsByCids will select a set of attachments paths by challenge ids
func (d *Dao) AttPathsByCids(c context.Context, cids []int64) (paths map[int64][]string, err error) {
var pathSlice []*struct {
Cid int64
Path string
}
paths = make(map[int64][]string, len(cids))
if len(cids) <= 0 {
return
}
if err = d.ReadORM.Table("workflow_attachment").Where("cid IN (?)", cids).Select("cid,path").Find(&pathSlice).Error; err != nil {
return
}
for _, cp := range pathSlice {
if _, ok := paths[cp.Cid]; !ok {
paths[cp.Cid] = make([]string, 0, 1)
}
paths[cp.Cid] = append(paths[cp.Cid], cp.Path)
}
return
}
// AttPathsByCid will select a set of attachments paths by challenge id
// Deprecated
func (d *Dao) AttPathsByCid(c context.Context, cid int64) (paths []string, err error) {
paths = make([]string, 0)
rows, err := d.ReadORM.Table("workflow_attachment").Select("cid,path").Where("cid=?", cid).Rows()
if err != nil {
err = errors.Wrapf(err, "cid(%d)", cid)
return
}
defer rows.Close()
for rows.Next() {
var cp struct {
cid int32
path string
}
if err = rows.Scan(&cp.cid, &cp.path); err != nil {
err = errors.WithStack(err)
return
}
paths = append(paths, cp.path)
}
return
}
// UpChallBusState will update specified business_state by conditions
// Deprecated
func (d *Dao) UpChallBusState(c context.Context, cid int64, busState int8, assigneeAdminid int64) (err error) {
var chall *model.Chall
if chall, err = d.Chall(c, cid); err != nil {
return
}
if chall == nil {
err = errors.Wrapf(err, "can not find challenge cid(%d)", cid)
return
}
// double write to new field
chall.SetState(uint32(busState), uint8(1))
if err = d.ORM.Table("workflow_chall").Where("id=?", cid).
Update("dispatch_state", chall.DispatchState).
Update("assignee_adminid", assigneeAdminid).
Error; err != nil {
err = errors.WithStack(err)
}
return
}
// BatchUpChallBusState will update specified business_state by conditions
func (d *Dao) BatchUpChallBusState(c context.Context, cids []int64, busState int8, assigneeAdminid int64) (err error) {
var challs map[int64]*model.Chall
challs, err = d.Challs(c, cids)
if err != nil {
return
}
for cid := range challs {
challs[cid].SetState(uint32(busState), uint8(1))
if err = d.ORM.Table("workflow_chall").Where("id=?", cid).
Update("dispatch_state", challs[cid].DispatchState).
Update("assignee_adminid", assigneeAdminid).Error; err != nil {
err = errors.WithStack(err)
return
}
}
return
}
// TxChallsByBusStates select cids by business and oid
func (d *Dao) TxChallsByBusStates(tx *gorm.DB, business int8, oid int64, busStates []int8) (cids []int64, err error) {
cids = make([]int64, 0)
rows, err := tx.Table("workflow_chall").Where("business=? AND oid=? ",
business, oid).Select("id,dispatch_state").Rows()
if err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
c := &model.Chall{}
if err = rows.Scan(&c.Cid, &c.DispatchState); err != nil {
return
}
for _, busState := range busStates {
c.FromState()
if c.BusinessState == busState {
cids = append(cids, int64(c.Cid))
}
}
}
return
}
// TxUpChallsBusStateByIDs will update specified business_state by conditions
func (d *Dao) TxUpChallsBusStateByIDs(tx *gorm.DB, cids []int64, busState int8, assigneeAdminid int64) (err error) {
challSlice := make([]*model.Chall, 0)
if err = tx.Table("workflow_chall").Where("id IN (?)", cids).
Select("id,gid,mid,dispatch_state").Find(&challSlice).Error; err != nil {
err = errors.WithStack(err)
return
}
for _, chall := range challSlice {
chall.SetState(uint32(busState), uint8(1))
if err = tx.Table("workflow_chall").Where("id=?", chall.Cid).
Update("dispatch_state", chall.DispatchState).Update("assignee_adminid", assigneeAdminid).Error; err != nil {
err = errors.WithStack(err)
return
}
}
return
}
// TxUpChallExtraV2 will update Extra data by business oid
func (d *Dao) TxUpChallExtraV2(tx *gorm.DB, business int8, oid, adminid int64, extra map[string]interface{}) (rows int64, err error) {
exData, err := json.Marshal(extra)
if err != nil {
err = errors.Wrapf(err, "business(%d) oid(%d), extra(%s)", business, oid, extra)
return
}
if err = tx.Table("workflow_business").Where("business=? AND oid=?", business, oid).Update("extra", exData).Error; err != nil {
err = errors.Wrapf(err, "business(%d), oid(%d), extra(%s)", business, oid, exData)
}
return
}
// UpExtraV3 will update Extra data by gids
func (d *Dao) UpExtraV3(gids []int64, adminid int64, extra string) error {
return d.ORM.Table("workflow_business").Where("gid IN (?)", gids).Update("extra", extra).Error
}
// TxUpChallTag will update tid by cid
func (d *Dao) TxUpChallTag(tx *gorm.DB, cid int64, tid int64) (err error) {
if err = tx.Table("workflow_chall").Where("id=?", cid).Update("tid", tid).Error; err != nil {
err = errors.Wrapf(err, "cid(%d), tid(%d)", cid, tid)
}
return
}
// BatchUpChallByIDs update dispatch_state field of cids
func (d *Dao) BatchUpChallByIDs(cids []int64, dispatchState uint32, adminid int64) (err error) {
if len(cids) <= 0 {
return
}
if err = d.ORM.Table("workflow_chall").Where("id IN (?)", cids).
Update("dispatch_state", dispatchState).Update("adminid", adminid).Error; err != nil {
err = errors.WithStack(err)
}
return
}
// BatchResetAssigneeAdminID reset assignee_adminid by cids
func (d *Dao) BatchResetAssigneeAdminID(cids []int64) (err error) {
if len(cids) <= 0 {
return
}
if err = d.ORM.Table("workflow_chall").Where("id IN (?)", cids).
Update("assignee_adminid", 0).Error; err != nil {
err = errors.WithStack(err)
}
return
}
// TxUpChallAssignee update assignee_adminid and dispatch_time when admin start a mission
func (d *Dao) TxUpChallAssignee(tx *gorm.DB, cids []int64) error {
return tx.Table("workflow_chall").Where("id IN (?)", cids).
Update("dispatch_time", time.Now().Format("2006-01-02 15:04:05")).Error
}

View File

@@ -0,0 +1,353 @@
package dao
import (
"context"
"testing"
"go-common/app/admin/main/workflow/model"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoChall(t *testing.T) {
convey.Convey("Chall", t, func(ctx convey.C) {
var (
c = context.Background()
cid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
chall, err := d.Chall(c, cid)
ctx.Convey("Then err should be nil.chall should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(chall, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoChalls(t *testing.T) {
convey.Convey("Challs", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
challs, err := d.Challs(c, cids)
ctx.Convey("Then err should be nil.challs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(challs, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoStateChalls(t *testing.T) {
convey.Convey("StateChalls", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{1}
state = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
challs, err := d.StateChalls(c, cids, state)
ctx.Convey("Then err should be nil.challs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(challs, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoLastChallIDsByGids(t *testing.T) {
convey.Convey("LastChallIDsByGids", t, func(ctx convey.C) {
var (
c = context.Background()
gids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
cids, err := d.LastChallIDsByGids(c, gids)
ctx.Convey("Then err should be nil.cids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(cids, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxUpChall(t *testing.T) {
convey.Convey("TxUpChall", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
chall = &model.Chall{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.TxUpChall(tx, chall)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxBatchUpChallByIDs(t *testing.T) {
convey.Convey("TxBatchUpChallByIDs", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
cids = []int64{1}
state = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxBatchUpChallByIDs(tx, cids, state)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoAttPathsByCids(t *testing.T) {
convey.Convey("AttPathsByCids", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
paths, err := d.AttPathsByCids(c, cids)
ctx.Convey("Then err should be nil.paths should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(paths, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAttPathsByCid(t *testing.T) {
convey.Convey("AttPathsByCid", t, func(ctx convey.C) {
var (
c = context.Background()
cid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
paths, err := d.AttPathsByCid(c, cid)
ctx.Convey("Then err should be nil.paths should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(paths, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpChallBusState(t *testing.T) {
convey.Convey("UpChallBusState", t, func(ctx convey.C) {
var (
c = context.Background()
cid = int64(1)
busState = int8(1)
assigneeAdminid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.UpChallBusState(c, cid, busState, assigneeAdminid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoBatchUpChallBusState(t *testing.T) {
convey.Convey("BatchUpChallBusState", t, func(ctx convey.C) {
var (
c = context.Background()
cids = []int64{1}
busState = int8(1)
assigneeAdminid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.BatchUpChallBusState(c, cids, busState, assigneeAdminid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxChallsByBusStates(t *testing.T) {
convey.Convey("TxChallsByBusStates", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
business = int8(1)
oid = int64(1)
busStates = []int8{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
cids, err := d.TxChallsByBusStates(tx, business, oid, busStates)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.cids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
ctx.So(cids, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxUpChallsBusStateByIDs(t *testing.T) {
convey.Convey("TxUpChallsBusStateByIDs", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
cids = []int64{1}
busState = int8(1)
assigneeAdminid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxUpChallsBusStateByIDs(tx, cids, busState, assigneeAdminid)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxUpChallExtraV2(t *testing.T) {
convey.Convey("TxUpChallExtraV2", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
business = int8(1)
oid = int64(1)
adminid = int64(1)
extra map[string]interface{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
rows, err := d.TxUpChallExtraV2(tx, business, oid, adminid, extra)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoUpExtraV3(t *testing.T) {
convey.Convey("UpExtraV3", t, func(ctx convey.C) {
var (
gids = []int64{1}
adminid = int64(1)
extra = "test extra"
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.UpExtraV3(gids, adminid, extra)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxUpChallTag(t *testing.T) {
convey.Convey("TxUpChallTag", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
cid = int64(1)
tid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxUpChallTag(tx, cid, tid)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoBatchUpChallByIDs(t *testing.T) {
convey.Convey("BatchUpChallByIDs", t, func(ctx convey.C) {
var (
cids = []int64{1}
dispatchState = uint32(1)
adminid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.BatchUpChallByIDs(cids, dispatchState, adminid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoBatchResetAssigneeAdminID(t *testing.T) {
convey.Convey("BatchResetAssigneeAdminID", t, func(ctx convey.C) {
var (
cids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.BatchResetAssigneeAdminID(cids)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxUpChallAssignee(t *testing.T) {
convey.Convey("TxUpChallAssignee", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
cids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxUpChallAssignee(tx, cids)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,139 @@
package dao
import (
"context"
account "go-common/app/service/main/account/api"
archive "go-common/app/service/main/archive/api"
"go-common/library/cache/memcache"
"go-common/library/cache/redis"
"go-common/library/conf/paladin"
"go-common/library/database/elastic"
"go-common/library/database/orm"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/rpc/warden"
"github.com/jinzhu/gorm"
)
// Dao is the appeal database access object
type Dao struct {
ReadORM *gorm.DB
ORM *gorm.DB
// search
httpRead *bm.Client
httpWrite *bm.Client
// memcache
mc *memcache.Pool
// redis
redis *redis.Pool
// es
es *elastic.Elastic
// account-service rpc
accRPC account.AccountClient
// archive-service rpc
arcRPC archive.ArchiveClient
c *paladin.Map // application.toml can reload
writeConf *bm.ClientConfig
}
// New will create a new appeal Dao instance
func New() (d *Dao) {
var (
db struct {
ReadORM *orm.Config
ORM *orm.Config
}
http struct {
HTTPClientRead *bm.ClientConfig
HTTPClient *bm.ClientConfig
Elastic *elastic.Config
}
grpc struct {
Account *warden.ClientConfig
Archive *warden.ClientConfig
}
mc struct {
Workflow *memcache.Config
}
rds struct {
Workflow *redis.Config
}
ac = new(paladin.TOML)
)
checkErr(paladin.Watch("application.toml", ac))
checkErr(paladin.Get("mysql.toml").UnmarshalTOML(&db))
checkErr(paladin.Get("http.toml").UnmarshalTOML(&http))
checkErr(paladin.Get("memcache.toml").UnmarshalTOML(&mc))
checkErr(paladin.Get("redis.toml").UnmarshalTOML(&rds))
checkErr(paladin.Get("grpc.toml").UnmarshalTOML(&grpc))
d = &Dao{
c: ac,
ReadORM: orm.NewMySQL(db.ReadORM),
ORM: orm.NewMySQL(db.ORM),
httpRead: bm.NewClient(http.HTTPClientRead),
httpWrite: bm.NewClient(http.HTTPClient),
// memcache
mc: memcache.NewPool(mc.Workflow),
// redis
redis: redis.NewPool(rds.Workflow),
// es
//es: elastic.NewElastic(nil),
es: elastic.NewElastic(http.Elastic),
writeConf: http.HTTPClient,
}
// account-service rpc
var err error
if d.accRPC, err = account.NewClient(grpc.Account); err != nil {
panic(err)
}
// archive-service rpc
if d.arcRPC, err = archive.NewClient(grpc.Archive); err != nil {
panic(err)
}
d.initORM()
return
}
func (d *Dao) initORM() {
d.ORM.LogMode(true)
d.ReadORM.LogMode(true)
}
// Close close dao.
func (d *Dao) Close() {
if d.ORM != nil {
d.ORM.Close()
}
if d.ReadORM != nil {
d.ReadORM.Close()
}
}
// Ping ping cpdb
func (d *Dao) Ping(c context.Context) (err error) {
if err = d.ORM.DB().PingContext(c); err != nil {
return
}
if err = d.ReadORM.DB().PingContext(c); err != nil {
return
}
if err = d.pingMC(c); err != nil {
return
}
return d.pingRedis(c)
}
func checkErr(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -0,0 +1,52 @@
package dao
import (
"context"
"flag"
"os"
"testing"
"go-common/library/conf/paladin"
"github.com/smartystreets/goconvey/convey"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") == "uat" {
flag.Set("app_id", "main.manager.workflow-admin")
flag.Set("conf_token", "daebcaa3b1886d74e1b8f0361b34b04c")
flag.Set("tree_id", "6812")
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("region", "sh")
flag.Set("zone", "sh001")
flag.Set("app_id", "main.manager.workflow-admin")
flag.Set("deploy.env", "dev")
flag.Set("conf_token", "1de17252107b89394b5e72e07bfbc8de")
flag.Set("conf_path", "/tmp")
flag.Set("conf_version", "docker-1")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("tree_id", "6812")
}
flag.Parse()
if err := paladin.Init(); err != nil {
panic(err)
}
d = New()
os.Exit(m.Run())
}
func TestPing(t *testing.T) {
convey.Convey("Ping", t, func() {
d.Ping(context.TODO())
})
}

View File

@@ -0,0 +1,80 @@
package dao
import (
"context"
"go-common/app/admin/main/workflow/model"
"github.com/pkg/errors"
)
// EventsByCid will select events by cid
func (d *Dao) EventsByCid(c context.Context, cid int64) (events map[int64]*model.Event, err error) {
events = make(map[int64]*model.Event)
elist := make([]*model.Event, 0)
if err = d.ReadORM.Table("workflow_event").Where("cid=?", cid).Find(&elist).Error; err != nil {
err = errors.WithStack(err)
return
}
for _, e := range elist {
e.FixAttachments()
events[e.Eid] = e
}
return
}
// EventsByIDs will select events by eids
func (d *Dao) EventsByIDs(c context.Context, eids []int64) (events map[int64]*model.Event, err error) {
if len(eids) == 0 {
return
}
events = make(map[int64]*model.Event, len(eids))
elist := make([]*model.Event, 0)
if err = d.ReadORM.Table("workflow_event").Where("id IN (?)", eids).Find(&elist).Error; err != nil {
err = errors.WithStack(err)
return
}
for _, e := range elist {
e.FixAttachments()
events[e.Eid] = e
}
return
}
// LastEventByCid will retrive last event by cid
func (d *Dao) LastEventByCid(c context.Context, cid int64) (event *model.Event, err error) {
event = new(model.Event)
err = d.ReadORM.Table("workflow_event").Where("cid=?", cid).Order("id").Last(&event).Error
if err != nil {
err = errors.WithStack(err)
return
}
return
}
// BatchLastEventIDs will retrive the last event ids by serveral conditions
func (d *Dao) BatchLastEventIDs(c context.Context, cids []int64) (eids []int64, err error) {
eids = make([]int64, 0, len(cids))
if len(cids) <= 0 {
return
}
rows, err := d.ReadORM.Table("workflow_event").Select("max(id)").Where("cid IN (?)", cids).Group("cid").Rows()
if err != nil {
err = errors.WithStack(err)
return
}
defer rows.Close()
for rows.Next() {
var eid int64
if err = rows.Scan(&eid); err != nil {
err = errors.WithStack(err)
return
}
eids = append(eids, eid)
}
return
}

View File

@@ -0,0 +1,44 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoEventsByCid(t *testing.T) {
var c = context.TODO()
convey.Convey("events by cid", t, func() {
events, err := d.EventsByCid(c, 1)
convey.So(err, convey.ShouldBeNil)
convey.So(events, convey.ShouldNotBeNil)
})
}
func TestDaoEventsByIDs(t *testing.T) {
var c = context.TODO()
convey.Convey("events by multi event ids", t, func() {
events, err := d.EventsByIDs(c, []int64{1})
convey.So(err, convey.ShouldBeNil)
convey.So(events, convey.ShouldNotBeNil)
})
}
func TestDaoLastEventByCid(t *testing.T) {
var c = context.TODO()
convey.Convey("last event by eids", t, func() {
events, err := d.EventsByIDs(c, []int64{1})
convey.So(err, convey.ShouldBeNil)
convey.So(events, convey.ShouldNotBeNil)
})
}
func TestDaoBatchLastEventIDs(t *testing.T) {
var c = context.TODO()
convey.Convey("batch last event by multi cids", t, func() {
events, err := d.BatchLastEventIDs(c, []int64{1})
convey.So(err, convey.ShouldBeNil)
convey.So(events, convey.ShouldNotBeNil)
})
}

View File

@@ -0,0 +1,369 @@
package dao
import (
"context"
"fmt"
"net/url"
"strconv"
"sync"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
credit "go-common/app/interface/main/credit/model"
"go-common/app/job/main/member/model/block"
acc "go-common/app/service/main/account/api"
arc "go-common/app/service/main/archive/api"
member "go-common/app/service/main/member/model/block"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/sync/errgroup"
"go-common/library/xstr"
"github.com/pkg/errors"
)
const (
_upergroupURI = "http://api.bilibili.co/x/internal/uper/special/get"
_tagListURI = "http://manager.bilibili.co/x/admin/manager/internal/tag/list"
_blockURI = "http://api.bilibili.co/x/internal/block/batch/block"
_creditBlockedURI = "http://api.bilibili.co/x/internal/credit/blocked/info/add"
_blockInfoURI = "http://api.bilibili.co/x/internal/block/info"
_blockNumURI = "http://api.bilibili.co/x/internal/credit/blocked/user/num"
_blockCaseAddURI = "http://api.bilibili.co/x/internal/credit/blocked/case/add"
)
// BatchUperSpecial .
// http://info.bilibili.co/pages/viewpage.action?pageId=8479274
func (d *Dao) BatchUperSpecial(c context.Context, mids []int64) (UperTagMap map[int64][]*model.SpecialTag, err error) {
uri := _upergroupURI
uperSpecialResp := new(model.UperSpecial)
UperTagMap = make(map[int64][]*model.SpecialTag)
uv := url.Values{}
uv.Set("group_id", "0")
uv.Set("mids", xstr.JoinInts(mids))
uv.Set("pn", "1")
uv.Set("ps", "1000")
if err = d.httpRead.Get(c, uri, "", uv, uperSpecialResp); err != nil {
err = errors.Wrap(err, fmt.Sprintf("search uper special tag failed mids(%v)", uv.Get("mids")))
return
}
if uperSpecialResp.Code != ecode.OK.Code() {
log.Error("call %s result error code(%d), message(%s)", uri, uperSpecialResp.Code, uperSpecialResp.Message)
err = ecode.Int(uperSpecialResp.Code)
return
}
for _, special := range uperSpecialResp.Data.Items {
UperTagMap[special.MID] = append(UperTagMap[special.MID], special)
}
return
}
// ArchiveRPC .
func (d *Dao) ArchiveRPC(c context.Context, oids []int64) (archives map[int64]*model.Archive, err error) {
if len(oids) == 0 {
return
}
archives = make(map[int64]*model.Archive, len(oids))
var res *arc.ArcsReply
arg := &arc.ArcsRequest{
Aids: oids,
}
if res, err = d.arcRPC.Arcs(c, arg); err != nil {
log.Error("d.arcRPC.Archives3(%+v) error(%v)", arg, err)
return
}
for oid, arc := range res.Arcs {
tmplArc := &model.Archive{
Author: arc.Author.Name,
State: arc.State,
Mid: arc.Author.Mid,
TypeID: arc.TypeID,
Type: arc.TypeName,
Title: arc.Title,
}
archives[oid] = tmplArc
}
return
}
// TagList .
// http://info.bilibili.co/pages/viewpage.action?pageId=9831467
// tags map[bid][tagid]
func (d *Dao) TagList(c context.Context) (tags map[int8]map[int64]*model.TagMeta, err error) {
uri := _tagListURI
uv := url.Values{}
uv.Set("ps", "1000")
uv.Set("pn", "1")
result := new(model.TagListResult)
tags = make(map[int8]map[int64]*model.TagMeta)
if err = d.httpRead.Get(c, uri, "", uv, result); err != nil {
return
}
if result.Code != ecode.OK.Code() {
log.Error("tag list failed: %s?%s, error code(%d), message(%s)", uri, uv.Encode(), result.Code, result.Message)
err = ecode.Int(result.Code)
return
}
for _, tm := range result.Data.Tags {
if _, ok := tags[tm.Bid]; !ok {
tags[tm.Bid] = make(map[int64]*model.TagMeta)
}
tags[tm.Bid][tm.TagID] = tm
}
return
}
// CommonExtraInfo return common external info
func (d *Dao) CommonExtraInfo(c context.Context, bid int8, uri string, ids, oids, eids []int64) (data map[string]interface{}, err error) {
log.Info("start call common extra info bid(%d) gids(%v) oids(%v) eids(%v)", bid, ids, oids, eids)
data = make(map[string]interface{})
uv := url.Values{}
uv.Set("bid", strconv.FormatInt(int64(bid), 10))
uv.Set("ids", xstr.JoinInts(ids))
uv.Set("oids", xstr.JoinInts(oids))
uv.Set("eids", xstr.JoinInts(eids))
res := &model.CommonExtraDataResponse{}
if err = d.httpRead.Get(c, uri, "", uv, &res); err != nil {
return data, err
}
if res.Code != ecode.OK.Code() {
log.Error("get extra info failed: url(%s), code(%d), message(%s), bid(%d)", uri+"?"+uv.Encode(), res.Code, res.Message, bid)
err = ecode.Int(res.Code)
return
}
log.Info("success extra info (%+v) req url(%s)", res, uri+"?"+uv.Encode())
data = res.Data
return
}
// AccountInfoRPC .
func (d *Dao) AccountInfoRPC(c context.Context, mids []int64) (authors map[int64]*model.Account) {
g := &errgroup.Group{}
mutex := sync.RWMutex{}
authors = make(map[int64]*model.Account)
if len(mids) == 0 {
return
}
// distinct mid
mMap := make(map[int64]bool)
dMids := make([]int64, 0)
for _, m := range mids {
if _, ok := mMap[m]; !ok {
mMap[m] = false
dMids = append(dMids, m)
continue
}
}
for _, mid := range dMids {
gMid := mid
g.Go(func() (err error) {
var res *acc.ProfileStatReply
start := time.Now()
arg := &acc.MidReq{Mid: gMid}
if res, err = d.accRPC.ProfileWithStat3(c, arg); err != nil {
log.Error("d.accRPC.ProfileWithStat3(%v) error(%v)", arg, err)
err = nil
return
}
mutex.Lock()
acc := &model.Account{
Mid: res.Profile.Mid,
Name: res.Profile.Name,
Rank: res.Profile.Rank,
Follower: res.Follower,
Official: &model.Official{Role: res.Profile.Official.Role},
}
authors[res.Profile.Mid] = acc
log.Info("mid(%d) data(%+v) wrap success", res.Profile.Mid, acc)
mutex.Unlock()
log.Info("account rpc request gmid(%d) time %s", gMid, time.Since(start).String())
return
})
}
g.Wait()
return
}
// AddMoral 扣节操
func (d *Dao) AddMoral(c context.Context, mids []int64, gssp *param.GroupStateSetParam) (err error) {
var errFlag bool
for _, mid := range mids {
arg := &acc.MoralReq{
Mid: mid,
Moral: float64(gssp.DecreaseMoral),
Oper: gssp.AdminName,
Reason: gssp.Reason,
Remark: "workflow",
}
if _, err = d.accRPC.AddMoral3(c, arg); err != nil {
log.Error("failed decrease moral arg(%+v) error(%v)", arg, err)
errFlag = true
}
}
if !errFlag {
log.Info("add moral success mids(%v) param(%+v)", mids, gssp)
}
return
}
// AddBlock 发起账号封禁
// http://info.bilibili.co/pages/viewpage.action?pageId=7559616
func (d *Dao) AddBlock(c context.Context, mids []int64, gssp *param.GroupStateSetParam) (err error) {
uri := _blockURI
uv := url.Values{}
uv.Set("mids", xstr.JoinInts(mids))
source := strconv.Itoa(int(member.BlockSourceBlackHouse))
uv.Set("source", source) //来源: 后台相关
if gssp.Business == model.CommentComplain {
area := strconv.Itoa(int(member.BlockAreaReply))
uv.Set("area", area) //违规业务
}
if gssp.BlockDay > 0 { //限时封禁
action := strconv.Itoa(int(block.BlockActionLimit))
uv.Set("action", action)
uv.Set("duration", strconv.FormatInt(gssp.BlockDay*86400, 10))
} else { //永久封禁
action := strconv.Itoa(int(block.BlockActionForever))
uv.Set("action", action)
}
uv.Set("start_time", strconv.FormatInt(time.Now().Unix(), 10))
uv.Set("op_id", strconv.FormatInt(gssp.AdminID, 10))
uv.Set("operator", gssp.AdminName)
reason := model.BlockReason[gssp.BlockReason]
uv.Set("reason", reason)
uv.Set("notify", "1")
resp := &model.CommonResponse{}
if err = d.httpWrite.Post(c, uri, "", uv, resp); err != nil {
log.Error("add block url(%s) param(%s) error(%v)", uri, uv.Encode(), err)
return
}
if resp.Code != ecode.OK.Code() {
err = ecode.Int(resp.Code)
log.Error("call add block url(%s) param(%s) error res code(%d)", uri, uv.Encode(), resp.Code)
return
}
log.Info("add block success mids(%v) param(%+v)", mids, gssp)
return
}
// AddCreditBlockInfo 上报封禁信息到小黑屋
// http://info.bilibili.co/pages/viewpage.action?pageId=5417571
func (d *Dao) AddCreditBlockInfo(c context.Context, bus map[int64]*model.Business, gssp *param.GroupStateSetParam) (err error) {
// 请求小黑屋
uri := _creditBlockedURI
for _, b := range bus {
uv := url.Values{}
uv.Set("mid", strconv.FormatInt(b.Mid, 10))
uv.Set("oper_id", strconv.FormatInt(gssp.AdminID, 10))
uv.Set("origin_content", b.Title)
if gssp.Business == model.CommentComplain {
uv.Set("origin_type", strconv.Itoa(int(member.BlockAreaReply))) //违规业务
}
if gssp.BlockDay > 0 { //限时封禁
uv.Set("blocked_days", strconv.FormatInt(gssp.BlockDay, 10))
uv.Set("blocked_forever", strconv.Itoa(int(credit.NotInBlockedForever)))
uv.Set("punish_type", strconv.Itoa(int(credit.PunishTypeBlock)))
} else { //永久封禁
uv.Set("blocked_forever", strconv.Itoa(int(credit.InBlockedForever)))
uv.Set("punish_type", strconv.Itoa(int(credit.PunishTypeForever)))
}
uv.Set("punish_time", strconv.FormatInt(time.Now().Unix(), 10))
uv.Set("reason_type", strconv.Itoa(int(gssp.BlockReason)))
uv.Set("operator_name", gssp.AdminName)
resp := &model.CommonResponse{}
if err = d.httpWrite.Post(c, uri, "", uv, resp); err != nil {
log.Error("add credit block info url(%s) param(%s) error(%v)", uri, uv.Encode(), err)
continue
}
if resp.Code != ecode.OK.Code() {
err = ecode.Int(resp.Code)
log.Error("call add credit block info url(%s) param(%s) error res code(%d)", uri, uv.Encode(), resp.Code)
continue
}
log.Info("add credit block info success mid(%v) param(%+v)", b.Mid, gssp)
}
return
}
// AddCreditCase 请求风纪委众裁
func (d *Dao) AddCreditCase(c context.Context, uv url.Values) (err error) {
uri := _blockCaseAddURI
var res model.CommonResponse
if err = d.httpWrite.Post(c, uri, "", uv, &res); err != nil {
log.Error("d.httpWrite.Post(%s) body(%s) error(%v)", uri, uv.Encode(), err)
err = ecode.WkfSetPublicRefereeFailed
return
}
if res.Code != ecode.OK.Code() {
log.Error("call url(%s) body(%s) error code(%d)", uri, uv.Encode(), res.Code)
err = ecode.WkfSetPublicRefereeFailed
return
}
log.Info("call block add case success url(%s) body(%s)", uri, uv.Encode())
return
}
// BlockNum 查询封禁次数
func (d *Dao) BlockNum(c context.Context, mid int64) (sum int64, err error) {
uri := _blockNumURI
uv := url.Values{}
uv.Set("mid", strconv.FormatInt(mid, 10))
var numResp model.BlockNumResp
if err = d.httpRead.Get(c, uri, "", uv, &numResp); err != nil {
log.Error("d.httpRead.Get() error(%v) url(%s?%s)", err, uri, uv.Encode())
err = ecode.WkfGetBlockInfoFailed
return
}
if numResp.Code != ecode.OK.Code() {
log.Error("call url(%s?%s) response code (%d) error", uri, uv.Encode(), numResp.Code)
err = ecode.WkfGetBlockInfoFailed
return
}
sum = numResp.Data.BlockedSum
log.Info("url(%s) mid(%d) block num (%d)", uri, mid, sum)
return
}
// BlockInfo 查询封禁信息
func (d *Dao) BlockInfo(c context.Context, mid int64) (resp model.BlockInfoResp, err error) {
uri := _blockInfoURI
uv := url.Values{}
uv.Set("mid", strconv.FormatInt(mid, 10))
if err = d.httpRead.Get(c, uri, "", uv, &resp); err != nil {
log.Error("d.httpRead.Get() error(%v) url(%s?%s)", err, uri, uv.Encode())
err = ecode.WkfGetBlockInfoFailed
return
}
if resp.Code != ecode.OK.Code() {
log.Error("call url(%s?%s) response code (%d) error", uri, uv.Encode(), resp.Code)
err = ecode.WkfGetBlockInfoFailed
return
}
log.Info("url(%s) mid(%d) block info resp(%+v)", uri, mid, resp)
return
}
// SourceInfo 返回业务来源
func (d *Dao) SourceInfo(c context.Context, uri string) (data map[string]interface{}, err error) {
log.Info("start call SourceInfo uri(%s)", uri)
res := &model.SourceQueryResponse{}
if err = d.httpRead.Get(c, uri, "", nil, &res); err != nil {
return
}
if res.Code != ecode.OK.Code() {
log.Error("get SourceInfo failed: url(%s), code(%d), message(%s)", uri, res.Code, res.Message)
err = ecode.Int(res.Code)
return
}
log.Info("success SourceInfo (%+v)", res.Data)
return res.Data, nil
}

View File

@@ -0,0 +1,190 @@
package dao
import (
"context"
"net/url"
"testing"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/ecode"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoBatchUperSpecial(t *testing.T) {
convey.Convey("BatchUperSpecial", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{27515256}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
UperTagMap, err := d.BatchUperSpecial(c, mids)
ctx.Convey("Then err should be nil.UperTagMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(UperTagMap, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoArchiveRPC(t *testing.T) {
convey.Convey("ArchiveRPC", t, func(ctx convey.C) {
var (
c = context.Background()
oids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
archives, err := d.ArchiveRPC(c, oids)
ctx.Convey("Then err should be nil.archives should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(archives, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTagList(t *testing.T) {
convey.Convey("TagList", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
tags, err := d.TagList(c)
ctx.Convey("Then err should be nil.tags should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(tags, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoCommonExtraInfo(t *testing.T) {
convey.Convey("CommonExtraInfo", t, func(ctx convey.C) {
var (
c = context.Background()
bid = int8(13)
uri = "http://uat-manager.bilibili.co/x/admin/reply/internal/reply"
ids = []int64{1}
oids = []int64{1}
eids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
data, err := d.CommonExtraInfo(c, bid, uri, ids, oids, eids)
ctx.Convey("Then err should be nil.data should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(data, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAccountInfoRPC(t *testing.T) {
convey.Convey("AccountInfoRPC", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
authors := d.AccountInfoRPC(c, mids)
ctx.Convey("Then authors should not be nil.", func(ctx convey.C) {
ctx.So(authors, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddMoral(t *testing.T) {
convey.Convey("AddMoral", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{1}
gssp = &param.GroupStateSetParam{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddMoral(c, mids, gssp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err.Error(), convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddBlock(t *testing.T) {
convey.Convey("AddBlock", t, func(ctx convey.C) {
var (
c = context.Background()
mids = []int64{1}
gssp = &param.GroupStateSetParam{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddBlock(c, mids, gssp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddCreditBlockInfo(t *testing.T) {
convey.Convey("AddCreditBlockInfo", t, func(ctx convey.C) {
var (
c = context.Background()
bus map[int64]*model.Business
gssp = &param.GroupStateSetParam{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCreditBlockInfo(c, bus, gssp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoAddCreditCase(t *testing.T) {
convey.Convey("AddCreditCase", t, func(ctx convey.C) {
var (
c = context.Background()
uv url.Values
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddCreditCase(c, uv)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldEqual, ecode.Int(56504))
})
})
})
}
func TestDaoBlockNum(t *testing.T) {
convey.Convey("BlockNum", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
sum, err := d.BlockNum(c, mid)
ctx.Convey("Then err should be nil.sum should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(sum, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoBlockInfo(t *testing.T) {
convey.Convey("BlockInfo", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
resp, err := d.BlockInfo(c, mid)
ctx.Convey("Then err should be nil.resp should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(resp, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,155 @@
package dao
import (
"context"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"github.com/jinzhu/gorm"
"github.com/pkg/errors"
)
// GroupByID will select a group by group id
func (d *Dao) GroupByID(c context.Context, gid int64) (g *model.Group, err error) {
g = &model.Group{}
if db := d.ReadORM.Table("workflow_group").Where("id=?", gid).First(g); db.Error != nil {
err = db.Error
if db.RecordNotFound() {
g = nil
err = nil
} else {
err = errors.Wrapf(err, "group(%d)", gid)
}
}
return
}
// GroupByOid will select a group by oid and business
func (d *Dao) GroupByOid(c context.Context, oid int64, business int8) (g *model.Group, err error) {
g = &model.Group{}
err = d.ReadORM.Table("workflow_group").Where("oid=? AND business=?", oid, business).Find(g).Error
return
}
// TxGroupsByOidsStates will select a set of groups by oids, business and states
func (d *Dao) TxGroupsByOidsStates(tx *gorm.DB, oids []int64, business, state int8) (groups map[int64]*model.Group, err error) {
var groupSlice []*model.Group
groups = make(map[int64]*model.Group, len(oids))
if len(oids) <= 0 {
return
}
if err = tx.Table("workflow_group").Where("oid IN (?) AND business=?", oids, business).Find(&groupSlice).Error; err != nil {
return
}
for _, g := range groupSlice {
groups[g.ID] = g
}
return
}
// Groups will select a set of groups by group ids
func (d *Dao) Groups(c context.Context, gids []int64) (groups map[int64]*model.Group, err error) {
var groupSlice []*model.Group
groups = make(map[int64]*model.Group, len(gids))
if len(gids) <= 0 {
return
}
if err = d.ReadORM.Table("workflow_group").Where("id IN (?)", gids).Find(&groupSlice).Error; err != nil {
return
}
for _, g := range groupSlice {
groups[g.ID] = g
}
return
}
// TxGroups .
func (d *Dao) TxGroups(tx *gorm.DB, gids []int64) (groups map[int64]*model.Group, err error) {
var groupSlice []*model.Group
groups = make(map[int64]*model.Group, len(gids))
if len(gids) <= 0 {
return
}
if err = tx.Set("gorm:query_option", "FOR UPDATE").Table("workflow_group").Where("id IN (?)", gids).Find(&groupSlice).Error; err != nil {
return
}
for _, g := range groupSlice {
groups[g.ID] = g
}
return
}
// TxUpGroup will update a group
func (d *Dao) TxUpGroup(tx *gorm.DB, oid int64, business int8, tid int64, note string, rid int8) (err error) {
err = tx.Table("workflow_group").Where("oid=? AND business=?", oid, business).UpdateColumn(map[string]interface{}{
"tid": tid,
"note": note,
"rid": rid,
}).Error
return
}
// UpGroupRole update tid note rid of groups
func (d *Dao) UpGroupRole(c context.Context, grsp *param.GroupRoleSetParam) (err error) {
err = d.ORM.Table("workflow_group").Where("id IN (?)", grsp.GID).UpdateColumn(map[string]interface{}{
"tid": grsp.TID,
"note": grsp.Note,
"rid": grsp.RID,
}).Error
return
}
// TxUpGroupState will update a group state
func (d *Dao) TxUpGroupState(tx *gorm.DB, gid int64, state int8) (err error) {
err = tx.Table("workflow_group").Where("id=? AND state=0", gid).Update("state", state).Error
return
}
// TxUpGroupHandling will use gorm update a group handling stat field
func (d *Dao) TxUpGroupHandling(tx *gorm.DB, gid int64, handling int32) (err error) {
err = tx.Table("workflow_group").Where("id=?", gid).Update("handling", handling).Error
return
}
// TxBatchUpGroupHandling will update a set of groups handling stat field
func (d *Dao) TxBatchUpGroupHandling(tx *gorm.DB, gids []int64, handling int32) (err error) {
if len(gids) <= 0 {
return
}
return tx.Table("workflow_group").Where("id IN (?)", gids).Update("handling", handling).Error
}
// TxBatchUpGroupState will update a group state
func (d *Dao) TxBatchUpGroupState(tx *gorm.DB, gids []int64, state int8) (err error) {
if len(gids) <= 0 {
return
}
return tx.Table("workflow_group").Where("id IN (?) AND state=0", gids).Update("state", state).Error
}
// TxSetGroupStateTid set group state,tid,rid
func (d *Dao) TxSetGroupStateTid(tx *gorm.DB, gids []int64, state, rid int8, tid int64) (err error) {
if len(gids) <= 0 {
return
}
return tx.Table("workflow_group").Where("id IN (?)", gids).UpdateColumn(map[string]interface{}{
"state": state,
"tid": tid,
"rid": rid,
}).Error
}
// TxSimpleSetGroupState only set group state
func (d *Dao) TxSimpleSetGroupState(tx *gorm.DB, gids []int64, state int8) (err error) {
if len(gids) <= 0 {
return
}
return tx.Table("workflow_group").Where("id IN (?)", gids).Update("state", state).Error
}

View File

@@ -0,0 +1,290 @@
package dao
import (
"context"
"testing"
"go-common/app/admin/main/workflow/model/param"
"github.com/jinzhu/gorm"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoGroupByID(t *testing.T) {
convey.Convey("GroupByID", t, func(ctx convey.C) {
var (
c = context.Background()
gid = int64(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
g, err := d.GroupByID(c, gid)
ctx.Convey("Then err should be nil.g should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(g, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoGroupByOid(t *testing.T) {
convey.Convey("GroupByOid", t, func(ctx convey.C) {
var (
c = context.Background()
oid = int64(1)
business = int8(1)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
g, err := d.GroupByOid(c, oid, business)
ctx.Convey("Then err should be nil.g should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(g, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxGroupsByOidsStates(t *testing.T) {
convey.Convey("TxGroupsByOidsStates", t, func(ctx convey.C) {
var (
tx = &gorm.DB{}
oids = []int64{}
business = int8(0)
state = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
groups, err := d.TxGroupsByOidsStates(tx, oids, business, state)
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)
})
})
})
}
func TestDaoGroups(t *testing.T) {
convey.Convey("Groups", t, func(ctx convey.C) {
var (
c = context.Background()
gids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
groups, err := d.Groups(c, gids)
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)
})
})
})
}
func TestDaoTxGroups(t *testing.T) {
convey.Convey("TxGroups", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
gids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
groups, err := d.TxGroups(tx, gids)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.groups should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
ctx.So(groups, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxUpGroup(t *testing.T) {
convey.Convey("TxUpGroup", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
oid = int64(1)
business = int8(1)
tid = int64(0)
note = "test note"
rid = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxUpGroup(tx, oid, business, tid, note, rid)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoUpGroupRole(t *testing.T) {
convey.Convey("UpGroupRole", t, func(ctx convey.C) {
var (
c = context.Background()
grsp = &param.GroupRoleSetParam{
GID: []int64{1},
AdminID: 1,
AdminName: "anonymous",
BID: 1,
RID: 1,
TID: 1,
Note: "test note",
}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.UpGroupRole(c, grsp)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxUpGroupState(t *testing.T) {
convey.Convey("TxUpGroupState", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
gid = int64(1)
state = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxUpGroupState(tx, gid, state)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxUpGroupHandling(t *testing.T) {
convey.Convey("TxUpGroupHandling", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
gid = int64(1)
handling = int32(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxUpGroupHandling(tx, gid, handling)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxBatchUpGroupHandling(t *testing.T) {
convey.Convey("TxBatchUpGroupHandling", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
gids = []int64{1, 2}
handling = int32(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxBatchUpGroupHandling(tx, gids, handling)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxBatchUpGroupState(t *testing.T) {
convey.Convey("TxBatchUpGroupState", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
gids = []int64{1, 2}
state = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxBatchUpGroupState(tx, gids, state)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxSetGroupStateTid(t *testing.T) {
convey.Convey("TxSetGroupStateTid", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
gids = []int64{1, 2}
state = int8(0)
tid = int64(0)
rid = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxSetGroupStateTid(tx, gids, state, rid, tid)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}
func TestDaoTxSimpleSetGroupState(t *testing.T) {
convey.Convey("TxSimpleSetGroupState", t, func(ctx convey.C) {
var (
tx = d.ORM.Begin()
gids = []int64{1, 2}
state = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.TxSimpleSetGroupState(tx, gids, state)
err1 := tx.Commit().Error
defer func() {
if err != nil {
tx.Rollback()
}
}()
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(err1, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,70 @@
package dao
import (
"context"
"encoding/json"
"go-common/library/conf/paladin"
"time"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/cache/memcache"
"go-common/library/log"
)
const (
_prefixChallPendingCount = "wkf_cpc_uid_%d"
)
// pingMC ping memcache.
func (d *Dao) pingMC(c context.Context) (err error) {
conn := d.mc.Get(c)
//if err = conn.Store("set", "ping", []byte{1}, 0, d.mcExpire, 0); err != nil {
if err = conn.Set(&memcache.Item{Key: "ping", Value: []byte{1}, Expiration: 0}); err != nil {
log.Error("conn.Store(set, ping, 1) error(%v)", err)
}
conn.Close()
return
}
// ChallCountCache read pending chall count by uid from memcache
func (d *Dao) ChallCountCache(c context.Context, uid int64) (challCount *search.ChallCount, err error) {
var (
conn memcache.Conn
item *memcache.Item
key string
)
conn = d.mc.Get(c)
defer conn.Close()
key = d.keyChallCount(uid)
if item, err = conn.Get(key); err != nil {
if err == memcache.ErrNotFound {
err = nil
return
}
return
}
err = json.Unmarshal(item.Value, &challCount)
return
}
// UpChallCountCache will write chall count to cache
func (d *Dao) UpChallCountCache(c context.Context, challCount *search.ChallCount, uid int64) (err error) {
var (
conn memcache.Conn
item *memcache.Item
jsonChallCount []byte
)
jsonChallCount, err = json.Marshal(challCount)
if err != nil {
return
}
conn = d.mc.Get(c)
defer conn.Close()
item = &memcache.Item{
Key: d.keyChallCount(uid),
Value: jsonChallCount,
Expiration: int32(paladin.Duration(d.c.Get("expireCount"), time.Duration(10*time.Second)) / time.Second),
}
err = conn.Set(item)
return
}

View File

@@ -0,0 +1,55 @@
package dao
import (
"context"
"testing"
"go-common/app/admin/main/workflow/model/search"
"github.com/smartystreets/goconvey/convey"
)
func TestDaopingMC(t *testing.T) {
convey.Convey("pingMC", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.pingMC(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoChallCountCache(t *testing.T) {
convey.Convey("ChallCountCache", t, func(ctx convey.C) {
var (
c = context.Background()
uid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.ChallCountCache(c, uid)
ctx.Convey("Then err should be nil.challCount should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoUpChallCountCache(t *testing.T) {
convey.Convey("UpChallCountCache", t, func(ctx convey.C) {
var (
c = context.Background()
challCount = &search.ChallCount{}
uid = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.UpChallCountCache(c, challCount, uid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}

View File

@@ -0,0 +1,27 @@
package dao
import (
"context"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/ecode"
)
const _userNotifyURI = "http://message.bilibili.co/api/notify/send.user.notify.do"
// SendMessage send message to upper.
func (d *Dao) SendMessage(c context.Context, msg *param.MessageParam) (err error) {
uri := _userNotifyURI
params := msg.Query()
var res struct {
Code int `json:"code"`
}
if err = d.httpWrite.Post(c, uri, "", params, &res); err != nil {
return
}
if res.Code != 0 {
err = ecode.Int(res.Code)
return
}
return
}

View File

@@ -0,0 +1,34 @@
package dao
import (
"context"
"testing"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoSendMessage(t *testing.T) {
convey.Convey("SendMessage", t, func(ctx convey.C) {
var (
c = context.Background()
msg = &param.MessageParam{
Type: "json",
Source: 1,
DataType: 4,
MC: model.WkfNotifyMC,
Title: "test title",
Context: "test context",
MidList: []int64{1},
}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.SendMessage(c, msg)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err.Error(), convey.ShouldEqual, "-6")
})
})
})
}

View File

@@ -0,0 +1,130 @@
package dao
import (
"context"
"fmt"
"go-common/app/admin/main/workflow/model"
"go-common/library/cache/redis"
"go-common/library/log"
)
const (
_listKeyFormat = "wf_%d_%d"
_fbRound = 11
_auditFlowDealType = 0
_feedbackFlowDealType = 1
_adminOnlineHash = "wkf_online_hash"
_prefixAdminOnlineField = "wkf_online_uid_%d"
)
func (d *Dao) pingRedis(c context.Context) (err error) {
conn := d.redis.Get(c)
_, err = conn.Do("SET", "PING", "PONG")
conn.Close()
return
}
// redis list key wf_business_dealtype like wf_2_1
// dealtype=0 audit dealtype=1 feedback
// RedisRPOPCids returns cids from a list
func (d *Dao) RedisRPOPCids(c context.Context, business int8, round int64, num int8) (cids []int64, err error) {
var (
key string
conn = d.redis.Get(c)
flow int
cid int64
chall *model.Chall
)
defer conn.Close()
cids = make([]int64, 0)
if round == _fbRound {
flow = _feedbackFlowDealType
} else {
flow = _auditFlowDealType
}
key = fmt.Sprintf(_listKeyFormat, business, flow)
for {
exist := false
if exist, err = redis.Bool(conn.Do("EXISTS", key)); err != nil {
log.Error("redis.Bool key(%s) err(%v)", key, err)
return
}
if !exist {
log.Warn("key(%s) not exist", key)
return
}
if cid, err = redis.Int64(conn.Do("RPOP", key)); err != nil {
log.Error("conn.Do(RPOP,%s) error(%v)", key, err)
return
}
// judge if business_state is queue state
if chall, err = d.Chall(c, cid); err != nil {
return
}
chall.FromState()
if chall.BusinessState == model.QueueState {
cids = append(cids, cid)
}
if len(cids) >= int(num) {
break
}
}
return
}
// IsOnline judge if admin is online
func (d *Dao) IsOnline(c context.Context, assigneeAdminID int64) (online bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := _adminOnlineHash
field := d.fieldOnlineList(assigneeAdminID)
return redis.Bool(conn.Do("HEXISTS", key, field))
}
// AddOnline checkin if start subscribe mission in platform, set key
func (d *Dao) AddOnline(c context.Context, assigneeAdminID int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := _adminOnlineHash
field := d.fieldOnlineList(assigneeAdminID)
_, err = conn.Do("HSET", key, field, assigneeAdminID)
return
}
// DelOnline checkout if exit subscribe mission in platform, delete key
func (d *Dao) DelOnline(c context.Context, assigneeAdminID int64) (err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := _adminOnlineHash
field := d.fieldOnlineList(assigneeAdminID)
_, err = conn.Do("HDEL", key, field)
return
}
// ListOnline list online admin
func (d *Dao) ListOnline(c context.Context) (ids []int64, err error) {
conn := d.redis.Get(c)
defer conn.Close()
key := _adminOnlineHash
return redis.Int64s(conn.Do("HVALS", key))
}
// LogInOutTime show last online or offline time
func (d *Dao) LogInOutTime(c context.Context, uids []int64) {
}
// uid field in key wkf_online_hash
func (d *Dao) fieldOnlineList(assigneeAdminID int64) string {
return fmt.Sprintf(_prefixAdminOnlineField, assigneeAdminID)
}
func (d *Dao) keyChallCount(assigneeAdminID int64) string {
return fmt.Sprintf(_prefixChallPendingCount, assigneeAdminID)
}

View File

@@ -0,0 +1,142 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaopingRedis(t *testing.T) {
convey.Convey("pingRedis", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.pingRedis(c)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoRedisRPOPCids(t *testing.T) {
convey.Convey("RedisRPOPCids", t, func(ctx convey.C) {
var (
c = context.Background()
business = int8(0)
round = int64(0)
num = int8(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
cids, err := d.RedisRPOPCids(c, business, round, num)
ctx.Convey("Then err should be nil.cids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(cids, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoIsOnline(t *testing.T) {
convey.Convey("IsOnline", t, func(ctx convey.C) {
var (
c = context.Background()
assigneeAdminID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
online, err := d.IsOnline(c, assigneeAdminID)
ctx.Convey("Then err should be nil.online should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(online, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddOnline(t *testing.T) {
convey.Convey("AddOnline", t, func(ctx convey.C) {
var (
c = context.Background()
assigneeAdminID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.AddOnline(c, assigneeAdminID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoDelOnline(t *testing.T) {
convey.Convey("DelOnline", t, func(ctx convey.C) {
var (
c = context.Background()
assigneeAdminID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
err := d.DelOnline(c, assigneeAdminID)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoListOnline(t *testing.T) {
convey.Convey("ListOnline", t, func(ctx convey.C) {
var (
c = context.Background()
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
_, err := d.ListOnline(c)
ctx.Convey("Then err should be nil.ids should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
})
}
func TestDaoLogInOutTime(t *testing.T) {
convey.Convey("LogInOutTime", t, func(ctx convey.C) {
var (
c = context.Background()
uids = []int64{}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
d.LogInOutTime(c, uids)
ctx.Convey("No return values", func(ctx convey.C) {
})
})
})
}
func TestDaofieldOnlineList(t *testing.T) {
convey.Convey("fieldOnlineList", t, func(ctx convey.C) {
var (
assigneeAdminID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.fieldOnlineList(assigneeAdminID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}
func TestDaokeyChallCount(t *testing.T) {
convey.Convey("keyChallCount", t, func(ctx convey.C) {
var (
assigneeAdminID = int64(0)
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
p1 := d.keyChallCount(assigneeAdminID)
ctx.Convey("Then p1 should not be nil.", func(ctx convey.C) {
ctx.So(p1, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,233 @@
package dao
import (
"context"
"net/url"
"strconv"
"time"
"go-common/app/admin/main/workflow/model/manager"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/database/elastic"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_unameURI = "http://manager.bilibili.co/x/admin/manager/users/unames"
_srhAuditLogURI = "http://bili-search.bilibili.co/x/admin/search/log"
)
// SearchGroup will search group by given conditions
func (d *Dao) SearchGroup(c context.Context, cond *search.GroupSearchCommonCond) (resp *search.GroupSearchCommonResp, err error) {
start := time.Now()
var r *elastic.Request
defer func() {
log.Info("SearchGroup params(%s) group search ts %s err_or(%v)", r.Params(), time.Since(start).String(), err)
}()
r = d.es.NewRequest(search.GroupSrhComID).Index(search.GroupSrhComID).Fields(cond.Fields...).
WhereEq("business", cond.Business).WhereIn("round", cond.Rounds).WhereIn("tid", cond.Tids).
WhereIn("state", cond.States).WhereIn("mid", cond.Mids).WhereIn("oid", cond.Oids).WhereIn("typeid", cond.TypeIDs).
WhereIn("fid", cond.FID).WhereIn("rid", cond.RID).WhereIn("eid", cond.EID).
WhereIn("report_mid", cond.ReportMID).WhereIn("first_user_tid", cond.FirstUserTid).
Order(cond.Order, cond.Sort).
Pn(int(cond.PN)).Ps(int(cond.PS))
// 是否关键字匹配优先
if cond.KWPriority == true {
r.OrderScoreFirst(true)
} else {
r.OrderScoreFirst(false)
}
if len(cond.KWFields) > 0 && len(cond.KWFields) == len(cond.KW) {
r.WhereLike(cond.KWFields, cond.KW, true, elastic.LikeLevelMiddle)
}
r.WhereRange("ctime", cond.CTimeFrom, cond.CTimeTo, elastic.RangeScopeLcRc)
if err = r.Scan(c, &resp); err != nil {
log.Error("r.Scan(%+v) error(%v) params(%s)", resp, err, r.Params())
}
return
}
// SearchGroupMultiPage .
func (d *Dao) SearchGroupMultiPage(c context.Context, cond *search.GroupSearchCommonCond) (result []*search.GroupSearchCommonData, err error) {
var resp *search.GroupSearchCommonResp
cond.PS = 1000
cond.PN = 1
result = make([]*search.GroupSearchCommonData, 0, len(cond.IDs))
for {
if resp, err = d.SearchGroup(c, cond); err != nil {
return
}
result = append(result, resp.Result...)
if len(resp.Result) < resp.Page.Size {
break
}
cond.PN++
}
return
}
// SearchChallenge will search challenge by given conditions
func (d *Dao) SearchChallenge(c context.Context, cond *search.ChallSearchCommonCond) (resp *search.ChallSearchCommonResp, err error) {
start := time.Now()
var r *elastic.Request
defer func() {
log.Info("SearchChallenge params(%s) challenge search ts %s err_or(%v)", r.Params(), time.Since(start).String(), err)
}()
r = d.es.NewRequest(search.ChallSrhComID).Fields(cond.Fields...).
WhereIn("id", cond.IDs).WhereIn("round", cond.Rounds).WhereIn("tid", cond.Tids).WhereIn("state", cond.States).
WhereIn("business_state", cond.BusinessStates).WhereIn("mid", cond.Mids).WhereIn("oid", cond.Oids).WhereIn("typeid", cond.TypeIDs).
WhereIn("gid", cond.Gids).WhereIn("assignee_adminid", cond.AssigneeAdminIDs).WhereIn("adminid", cond.AdminIDs)
if cond.Business > 0 {
r.WhereEq("business", cond.Business)
}
if len(cond.KWFields) > 0 && len(cond.KWFields) == len(cond.KW) {
r.WhereLike(cond.KWFields, cond.KW, true, elastic.LikeLevelLow)
}
if cond.Order == "" {
cond.Order = "id"
}
if cond.Sort == "" {
cond.Sort = "desc"
}
r.Order(cond.Order, cond.Sort)
r.WhereRange("ctime", cond.CTimeFrom, cond.CTimeTo, elastic.RangeScopeLcRc)
if len(cond.Distinct) > 0 {
for _, g := range cond.Distinct {
r.GroupBy("distinct", g, nil)
}
}
r.Index(search.ChallSrhComID)
r.Pn(1)
r.Ps(50)
if cond.PN != 0 {
r.Pn(int(cond.PN))
}
if cond.PS != 0 {
r.Ps(int(cond.PS))
}
if err = r.Scan(c, &resp); err != nil {
log.Error("r.Scan(%+v) error(%v) params(%s)", resp, err, r.Params())
}
return
}
// SearchChallengeMultiPage .
func (d *Dao) SearchChallengeMultiPage(c context.Context, cond *search.ChallSearchCommonCond) (result []*search.ChallSearchCommonData, err error) {
var resp *search.ChallSearchCommonResp
cond.PS = 1000
cond.PN = 1
result = make([]*search.ChallSearchCommonData, 0, len(cond.IDs))
for {
if resp, err = d.SearchChallenge(c, cond); err != nil {
return
}
result = append(result, resp.Result...)
if len(resp.Result) < resp.Page.Size {
break
}
cond.PN++
// return if result too long
if cond.PN > 10 {
log.Warn("cond(%+v) result is too long to degrade", cond)
return
}
}
return
}
// BatchUNameByUID will search unames by uids
func (d *Dao) BatchUNameByUID(c context.Context, uids []int64) (UNames map[int64]string, err error) {
//todo: local cache uname
uri := _unameURI
uv := url.Values{}
UNames = make(map[int64]string)
if len(uids) == 0 {
return
}
uv.Set("uids", xstr.JoinInts(uids))
unameSchRes := new(manager.UNameSearchResult)
if err = d.httpRead.Get(c, uri, "", uv, unameSchRes); err != nil {
return
}
if unameSchRes.Code != ecode.OK.Code() {
log.Error("search uname failed: %s?%s, error code(%d)", uri, uv.Get("uids"), unameSchRes.Code)
err = ecode.Int(unameSchRes.Code)
return
}
UNames = unameSchRes.Data
return
}
// SearchAuditLogGroup search archive audit log from log platform
func (d *Dao) SearchAuditLogGroup(c context.Context, cond *search.AuditLogGroupSearchCond) (auditLogSchRes *search.AuditLogSearchResult, err error) {
uri := _srhAuditLogURI
uv := cond.Query()
auditLogSchRes = new(search.AuditLogSearchResult)
if err = d.httpRead.Get(c, uri, "", uv, auditLogSchRes); err != nil {
log.Error("call search audit log %s error(%v)", uri, err)
return
}
if auditLogSchRes.Code != ecode.OK.Code() {
log.Error("call search audit log %s result error code(%d), message(%s)", uri, auditLogSchRes.Code, auditLogSchRes.Message)
err = ecode.Int(auditLogSchRes.Code)
}
return
}
// SearchAuditReportLog .
func (d *Dao) SearchAuditReportLog(c context.Context, cond *search.AuditReportSearchCond) (resp *search.AuditLogSearchCommonResult, err error) {
if len(cond.Fields) == 0 {
return
}
r := d.es.NewRequest(search.LogAuditAction).Fields(cond.Fields...).WhereIn("uid", cond.UID).WhereIn("oid", cond.Oid).
WhereEq("business", cond.Business).WhereIn("type", cond.Type).Order(cond.Order, cond.Sort).Pn(1).Ps(1000)
indexPrefix := search.LogAuditAction + "_" + strconv.Itoa(cond.Business)
if cond.IndexTimeType != "" {
switch cond.IndexTimeType {
case "year":
r.IndexByTime(indexPrefix, elastic.IndexTypeYear, cond.IndexTimeFrom, cond.IndexTimeEnd)
case "month":
r.IndexByTime(indexPrefix, elastic.IndexTypeMonth, cond.IndexTimeFrom, cond.IndexTimeEnd)
case "week":
r.IndexByTime(indexPrefix, elastic.IndexTypeWeek, cond.IndexTimeFrom, cond.IndexTimeEnd)
case "day":
r.IndexByTime(indexPrefix, elastic.IndexTypeDay, cond.IndexTimeFrom, cond.IndexTimeEnd)
default:
r.Index(indexPrefix + "_all")
}
} else {
r.Index(indexPrefix + "_all")
}
r.WhereIn("int_0", cond.Int0).WhereIn("int_1", cond.Int1).WhereIn("int_2", cond.Int2)
if cond.Str0 != "" {
r.WhereEq("str_0", cond.Str0)
}
if cond.Str1 != "" {
r.WhereEq("str_1", cond.Str1)
}
if cond.Str2 != "" {
r.WhereEq("str_2", cond.Str2)
}
if cond.Group != "" {
r.GroupBy(elastic.EnhancedModeGroupBy, cond.Group, []map[string]string{{"ctime": "desc"}})
}
if cond.Distinct != "" {
r.GroupBy(elastic.EnhancedModeDistinct, cond.Distinct, []map[string]string{{"ctime": "desc"}})
}
if err = r.Scan(c, &resp); err != nil {
log.Error("r.Scan(%+v) error(%v) params(%s)", resp, err, r.Params())
}
log.Info("SearchAuditReportLog end param(%v) err(%v)", r.Params(), err)
return
}

View File

@@ -0,0 +1,108 @@
package dao
import (
"context"
"fmt"
"math"
"strconv"
"testing"
"time"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/search"
"github.com/smartystreets/goconvey/convey"
)
func TestSearchAuditLogGroup(t *testing.T) {
convey.Convey("SearchAuditLogGroup", t, func() {
cond := &search.AuditLogGroupSearchCond{
Group: []string{"oid"},
Businesses: []int64{3},
Order: "ctime",
PS: 1000,
PN: 1,
Sort: "desc",
//Oids: []int64{24648472, 24694284, 24706061, 24583668, 24703363},
Oids: []int64{10110151},
}
result, err := d.SearchAuditLogGroup(context.TODO(), cond)
convey.So(err, convey.ShouldBeNil)
convey.So(result, convey.ShouldNotBeEmpty)
fmt.Println(len(result.Data.Result))
fmt.Println(result.Data.Result)
})
}
func TestSearchChallengeMultiPage(t *testing.T) {
convey.Convey("SearchChallengeMultiPage", t, func() {
cond := &search.ChallSearchCommonCond{
Fields: []string{"id", "gid", "mid", "state", "ctime"},
//IDs: []int64{1869, 1872},
IDs: []int64{1967, 1966, 1960},
}
result, err := d.SearchChallengeMultiPage(context.TODO(), cond)
convey.So(err, convey.ShouldBeNil)
fmt.Println("len", len(result))
for _, c := range result {
tc := &model.TinyChall{
Cid: c.ID,
Gid: c.Gid,
Mid: c.Mid,
}
var t time.Time
if t, err = time.Parse("2006-01-02 15:04:05", c.CTime); err != nil {
fmt.Printf("time.Parse() error(%v)\n", err)
}
if err = tc.CTime.Scan(t); err != nil {
fmt.Printf("xtime.Scan() error(%v)\n", err)
}
if str, ok := c.State.(string); ok {
st, _ := strconv.Atoi(str)
tc.State = int8(st)
}
if f, ok := c.State.(float64); ok {
tc.State = int8(math.Floor(f))
}
fmt.Printf("%+v\n", tc)
}
})
}
func TestSearchWorkflowReportLog(t *testing.T) {
convey.Convey("SearchWkf", t, func() {
cond := &search.AuditReportSearchCond{
AppID: search.LogAuditAction,
Fields: []string{"uname", "extra_data"},
Business: 11,
Order: "ctime",
Sort: "desc",
UID: []int64{6},
}
result, err := d.SearchAuditReportLog(context.TODO(), cond)
convey.So(err, convey.ShouldBeNil)
convey.So(result, convey.ShouldNotBeEmpty)
fmt.Println(len(result.Result))
fmt.Printf("%+v\n", result.Result[0])
})
}
func TestSearchArchiveReportLog(t *testing.T) {
convey.Convey("SearchArc", t, func() {
cond := &search.AuditReportSearchCond{
AppID: search.LogAuditAction,
Fields: []string{"oid", "ctime"},
IndexTimeType: "month",
IndexTimeFrom: time.Now().AddDate(0, -6, 0),
IndexTimeEnd: time.Now(),
Business: 3,
Order: "ctime",
Sort: "desc",
Oid: []int64{1},
Distinct: "oid",
}
result, err := d.SearchAuditReportLog(context.TODO(), cond)
convey.So(err, convey.ShouldBeNil)
convey.So(result, convey.ShouldNotBeEmpty)
})
}

View File

@@ -0,0 +1,64 @@
package dao
import (
"context"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/log"
)
// ChallTagsCount will retrive challenge tags count from group ids and group by tag id
// group id to challenge id to tag count
func (d *Dao) ChallTagsCount(c context.Context, gids []int64) (counts map[int64]map[int64]int64, err error) {
counts = make(map[int64]map[int64]int64, len(gids))
if len(gids) <= 0 {
return
}
rows, err := d.ReadORM.Table("workflow_chall").Where("gid IN (?)", gids).Select("gid,tid,count(tid)").
Group("gid,tid").Rows()
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
var gtc struct {
Gid int64
Tid int64
Count int64
}
if err = rows.Scan(&gtc.Gid, &gtc.Tid, &gtc.Count); err != nil {
return
}
if _, ok := counts[gtc.Gid]; !ok {
counts[gtc.Gid] = make(map[int64]int64)
}
counts[gtc.Gid][gtc.Tid] = gtc.Count
}
return
}
// ChallTagsCountV3 .
func (d *Dao) ChallTagsCountV3(c context.Context, gids []int64) (counts map[int64]map[int64]int64, err error) {
var result []*search.ChallSearchCommonData
counts = make(map[int64]map[int64]int64, len(gids))
cond := &search.ChallSearchCommonCond{
Fields: []string{"gid", "tid"},
Gids: gids,
States: []int64{int64(model.Pending)},
}
if result, err = d.SearchChallengeMultiPage(c, cond); err != nil {
log.Error("s.dao.SearchChallengeMultiPage(%+v) error(%v)", cond, err)
return
}
for _, r := range result {
if _, ok := counts[r.Gid]; !ok {
counts[r.Gid] = make(map[int64]int64)
}
counts[r.Gid][r.Tid]++
}
return
}

View File

@@ -0,0 +1,40 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoChallTagsCount(t *testing.T) {
convey.Convey("ChallTagsCount", t, func(ctx convey.C) {
var (
c = context.Background()
gids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
counts, err := d.ChallTagsCount(c, gids)
ctx.Convey("Then err should be nil.counts should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(counts, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoChallTagsCountV3(t *testing.T) {
convey.Convey("ChallTagsCountV3", t, func(ctx convey.C) {
var (
c = context.Background()
gids = []int64{1}
)
ctx.Convey("When everything goes positive", func(ctx convey.C) {
counts, err := d.ChallTagsCountV3(c, gids)
ctx.Convey("Then err should be nil.counts should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(counts, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,53 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"activity.go",
"business.go",
"callback.go",
"challenge.go",
"event.go",
"group.go",
"http.go",
"permission.go",
"reply.go",
"util.go",
],
importpath = "go-common/app/admin/main/workflow/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/workflow/model:go_default_library",
"//app/admin/main/workflow/model/param:go_default_library",
"//app/admin/main/workflow/model/search:go_default_library",
"//app/admin/main/workflow/service:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/binding:go_default_library",
"//library/net/http/blademaster/middleware/permit:go_default_library",
"//library/net/http/blademaster/render:go_default_library",
"//library/xstr: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,17 @@
package http
import (
bm "go-common/library/net/http/blademaster"
)
func listChallActivity(ctx *bm.Context) {
v := &struct {
Business int8 `form:"business" validate:"required,gt=0"`
Cid int64 `form:"cid" validate:"required,gt=0"`
}{}
if err := ctx.Bind(v); err != nil {
return
}
ctx.JSON(wkfSvc.ActivityList(ctx, v.Business, v.Cid))
}

View File

@@ -0,0 +1,100 @@
package http
import (
"net/http"
"net/url"
"strings"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
"go-common/library/net/http/blademaster/render"
)
func busMetaList(ctx *bm.Context) {
req := ctx.Request
itemType := req.Form.Get("item_type")
ctx.JSON(wkfSvc.ListMeta(ctx, itemType))
}
func listBusAttr(ctx *bm.Context) {
ctx.JSON(wkfSvc.ListBusAttr(ctx))
}
func listBusAttrV3(ctx *bm.Context) {
ctx.JSON(wkfSvc.ListBusAttrV3(ctx))
}
func addOrUpdateBusAttr(ctx *bm.Context) {
abap := &param.AddBusAttrParam{}
if err := ctx.BindWith(abap, binding.FormPost); err != nil {
return
}
ctx.JSON(nil, wkfSvc.AddOrUpdateBusAttr(ctx, abap))
}
func setSwitch(ctx *bm.Context) {
bs := new(param.BusAttrButtonSwitch)
if err := ctx.BindWith(bs, binding.FormPost); err != nil {
return
}
ctx.JSON(nil, wkfSvc.SetSwitch(ctx, bs))
}
func setShortCut(ctx *bm.Context) {
sc := new(param.BusAttrButtonShortCut)
if err := ctx.BindWith(sc, binding.FormPost); err != nil {
return
}
if len(sc.ShortCut) != 1 { // only support char
ctx.Render(http.StatusOK, render.JSON{
Code: ecode.RequestErr.Code(),
Message: "short cut only length 1",
Data: nil,
})
ctx.Abort()
return
}
sc.ShortCut = strings.ToUpper(sc.ShortCut)
ctx.JSON(nil, wkfSvc.SetShortCut(ctx, sc))
}
func setExtAPI(ctx *bm.Context) {
ea := new(param.BusAttrExtAPI)
if err := ctx.BindWith(ea, binding.FormPost); err != nil {
return
}
if ea.ExternalAPI != "" {
if _, err := url.Parse(ea.ExternalAPI); err != nil {
ctx.Render(http.StatusOK, render.JSON{
Code: ecode.RequestErr.Code(),
Message: err.Error(),
Data: nil,
})
ctx.Abort()
return
}
}
ctx.JSON(nil, wkfSvc.SetExtAPI(ctx, ea))
}
func mngTag(ctx *bm.Context) {
ctx.JSON(wkfSvc.ManagerTag(ctx))
}
func userBlockInfo(ctx *bm.Context) {
bi := new(param.BlockInfo)
if err := ctx.Bind(bi); err != nil {
return
}
ctx.JSON(wkfSvc.UserBlockInfo(ctx, bi))
}
func srcList(ctx *bm.Context) {
src := new(param.Source)
if err := ctx.Bind(src); err != nil {
return
}
ctx.JSON(wkfSvc.SourceList(ctx, src))
}

View File

@@ -0,0 +1,35 @@
package http
import (
"go-common/app/admin/main/workflow/model/param"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func listCallback(ctx *bm.Context) {
ctx.JSON(wkfSvc.ListCallback(ctx))
}
func addOrUpCallback(ctx *bm.Context) {
cbp := &param.AddCallbackParam{}
if err := ctx.BindWith(cbp, binding.JSON); err != nil {
return
}
if cbp.State > 0 {
cbp.State = 1
}
cbID, err := wkfSvc.AddOrUpCallback(ctx, cbp)
if err != nil {
log.Error("wkfSvc.AddUpCallback(%+v) error(%v)", cbp, err)
ctx.JSON(nil, ecode.RequestErr)
return
}
ctx.JSON(map[string]int32{
"callbackNo": cbID,
}, nil)
}

View File

@@ -0,0 +1,784 @@
package http
import (
"strconv"
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
"go-common/library/net/http/blademaster/middleware/permit"
)
func challList(ctx *bm.Context) {
params := ctx.Request.Form
cidStr := params.Get("cid")
gidStr := params.Get("gid")
midStr := params.Get("mid")
tidStr := params.Get("tid")
roundsStr := params.Get("rounds")
statesStr := params.Get("states")
ctimeFrom := params.Get("ctime_from")
ctimeTo := params.Get("ctime_to")
order := params.Get("order")
sort := params.Get("sort_order")
pageStr := params.Get("pn")
pagesizeStr := params.Get("ps")
cc := &search.ChallSearchCommonCond{}
numsmap := []*intsParam{
{value: cidStr, p: &cc.IDs},
{value: gidStr, p: &cc.Gids},
{value: midStr, p: &cc.Mids},
{value: tidStr, p: &cc.Tids},
{value: statesStr, p: &cc.States},
{value: roundsStr, p: &cc.Rounds},
}
var pn, ps int64
nummap := []*intParam{
{value: pageStr, p: &pn},
{value: pagesizeStr, p: &ps},
}
if err := dealNumsmap(numsmap); err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
if err := dealNummap(nummap); err != nil {
ctx.JSON(nil, ecode.RequestErr)
return
}
cc.Order = order
if cc.Order == "" {
cc.Order = "id"
}
if cc.Order == "last_time" {
cc.Order = "ctime"
}
cc.Sort = adjustOrder("challenge", sort)
if cc.Sort != "asc" {
cc.Sort = "desc"
}
if ctimeFrom != "" {
cc.CTimeFrom = ctimeFrom
}
if ctimeTo != "" {
cc.CTimeTo = ctimeTo
}
cc.FormatState()
cc.Fields = []string{"id"}
cc.PN, _ = strconv.Atoi(pageStr)
cc.PS, _ = strconv.Atoi(pagesizeStr)
ctx.JSON(wkfSvc.ChallList(ctx, cc))
}
func challListCommon(ctx *bm.Context) {
var (
IPers interface{}
ok bool
pers []string
err error
)
v := new(param.ChallengeListCommonParam)
if err = ctx.Bind(v); err != nil {
return
}
cc := &search.ChallSearchCommonCond{}
cc.Business = v.Business
cc.IDs = v.IDs
cc.Oids = v.Oids
cc.Mids = v.Mids
cc.ObjectMids = v.ObjectMids
cc.Gids = v.Gids
cc.TypeIDs = v.TypeIDs
cc.Tids = v.Tids
cc.Rounds = v.Rounds
cc.AssigneeAdminIDs = v.AssigneeAdminIDs
cc.AssigneeAdminNames = v.AssigneeAdminNames
cc.AdminIDs = v.AdminIDs
cc.CTimeTo = v.CTimeTo
cc.CTimeFrom = v.CTimeFrom
cc.Order = v.Order
cc.Sort = v.Sort
cc.States = v.States
cc.BusinessStates = v.BusinessStates
cc.PN = v.PN
cc.PS = v.PS
cc.CTimeFrom = v.CTimeFrom
cc.CTimeTo = v.CTimeTo
cc.FormatState()
if v.Title != "" {
cc.KWFields = append(cc.KWFields, "title")
cc.KW = append(cc.KW, v.Title)
}
if v.Content != "" {
cc.KWFields = append(cc.KWFields, "content")
cc.KW = append(cc.KW, v.Content)
}
if IPers, ok = ctx.Get(permit.CtxPermissions); ok {
pers = IPers.([]string)
}
if ok = isPermitChallList(pers, cc); !ok {
ctx.JSON(nil, ecode.AccessDenied)
ctx.Abort()
return
}
cc.Fields = []string{"id", "gid"}
ctx.JSON(wkfSvc.ChallListCommon(ctx, cc))
}
func challListV3(ctx *bm.Context) {
v := new(param.ChallengeListV3Param)
if err := ctx.Bind(v); err != nil {
return
}
cc := &search.ChallSearchCommonCond{}
cc.Fields = []string{"id", "oid", "mid"}
cc.Business = v.Business
cc.IDs = v.IDs
cc.Oids = v.Oids
cc.Mids = v.Mids
cc.Gids = v.Gids
cc.TypeIDs = v.TypeIDs
cc.Tids = v.Tids
cc.Rounds = v.Roles
cc.AssigneeAdminIDs = v.AssigneeAdminIDs
cc.AssigneeAdminNames = v.AssigneeAdminNames
cc.AdminIDs = v.AdminIDs
cc.CTimeTo = v.CTimeTo
cc.CTimeFrom = v.CTimeFrom
cc.Order = v.Order
cc.Sort = v.Sort
cc.States = v.States
cc.BusinessStates = v.BusinessStates
cc.PN = v.PN
cc.PS = v.PS
cc.CTimeFrom = v.CTimeFrom
cc.CTimeTo = v.CTimeTo
cc.KW = v.KW
cc.KWFields = v.KWField
cc.FormatState()
ctx.JSON(wkfSvc.ChallListV3(ctx, cc))
}
func challDetail(ctx *bm.Context) {
v := &struct {
Cid int64 `form:"cid" validate:"required,gt=0"`
}{}
if err := ctx.Bind(v); err != nil {
return
}
ctx.JSON(wkfSvc.ChallDetail(ctx, v.Cid))
}
func upChallBusState(ctx *bm.Context) {
reqUpFields := &struct {
Cid int64 `form:"cid" json:"cid"`
AssigneeAdminid int64 `json:"adminid"`
BusState int8 `form:"business_state" json:"business_state" validate:"min=0,max=14"`
}{}
if err := ctx.BindWith(reqUpFields, binding.FormPost); err != nil {
return
}
adminID, adminName := adminInfo(ctx)
ctx.JSON(nil, wkfSvc.UpChallBusState(ctx, reqUpFields.Cid, adminID, adminName, reqUpFields.BusState))
}
func batchUpChallBusState(ctx *bm.Context) {
var (
err error
reqUpFields = new(struct {
Cids []int64 `form:"cids,split" json:"cids" validate:"required,gt=0"`
AssigneeAdminid int64 `json:"adminid"`
BusState int8 `form:"business_state" json:"business_state" validate:"min=0,max=14"`
})
)
if err = ctx.BindWith(reqUpFields, binding.FormPost); err != nil {
return
}
adminID, adminName := adminInfo(ctx)
ctx.JSON(nil, wkfSvc.BatchUpChallBusState(ctx, reqUpFields.Cids, adminID, adminName, reqUpFields.BusState))
}
func upChallBusStateV3(ctx *bm.Context) {
bcbsp := new(param.BatchChallBusStateParam)
if err := ctx.BindWith(bcbsp, binding.FormPost); err != nil {
return
}
bcbsp.AssigneeAdminID, bcbsp.AssigneeAdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.SetChallBusState(ctx, bcbsp))
}
func upBusChallsBusState(ctx *bm.Context) {
var (
err error
reqUpFields struct {
Business int8 `form:"business" json:"business" validate:"required,min=1"`
Oid int64 `form:"oid" json:"oid" validate:"required,min=1"`
AssigneeAdminid int64 `form:"adminid" json:"adminid" validate:"required,min=1"`
State int8 `form:"business_state" json:"business_state" validate:"min=0,max=14"`
PreStates []int8 `form:"pre_business_states" json:"pre_business_states" validate:"dive,gt=-1"` // business_state修改前的状态
Extra map[string]interface{} `form:"extra" json:"extra"`
}
)
if err = ctx.BindWith(&reqUpFields, binding.JSON); err != nil {
log.Error("/business/busState/update bind failed error(%v)", err)
return
}
if len(reqUpFields.PreStates) <= 0 {
reqUpFields.PreStates = append(reqUpFields.PreStates, int8(0))
}
upCids, err := wkfSvc.UpBusChallsBusState(ctx, reqUpFields.Business, reqUpFields.State, reqUpFields.PreStates, reqUpFields.Oid, reqUpFields.AssigneeAdminid, reqUpFields.Extra)
log.Info("call upBusChallsBusState param(%v) upcids(%v)", reqUpFields, upCids)
ctx.JSON(map[string]interface{}{"cids": upCids}, err)
}
func setChallResult(ctx *bm.Context) {
crp := &param.ChallResParam{}
if err := ctx.BindWith(crp, binding.FormPost); err != nil {
return
}
crp.AdminID, crp.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.SetChallResult(ctx, crp))
}
func batchSetChallResult(ctx *bm.Context) {
bcrp := &param.BatchChallResParam{}
if err := ctx.BindWith(bcrp, binding.FormPost); err != nil {
return
}
bcrp.AdminID, bcrp.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.BatchSetChallResult(ctx, bcrp))
}
func setChallStateV3(ctx *bm.Context) {
bcrp := &param.BatchChallResParam{}
if err := ctx.BindWith(bcrp, binding.FormPost); err != nil {
return
}
bcrp.AdminID, bcrp.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.BatchSetChallResult(ctx, bcrp))
}
func rstChallResult(ctx *bm.Context) {
crp := new(param.ChallRstParam)
if err := ctx.BindWith(crp, binding.FormPost); err != nil {
return
}
crp.AdminID, crp.AdminName = adminInfo(ctx)
//force to pending
crp.State = model.Pending
ctx.JSON(nil, wkfSvc.RstChallResult(ctx, crp))
}
func rstChallResultV3(ctx *bm.Context) {
crp := new(param.ChallRstParam)
if err := ctx.BindWith(crp, binding.FormPost); err != nil {
return
}
crp.AdminID, crp.AdminName = adminInfo(ctx)
// TODO(zhoujiahui): force to pending now
crp.State = model.Pending
ctx.JSON(nil, wkfSvc.RstChallResult(ctx, crp))
}
func upChallExtra(ctx *bm.Context) {
cep := &param.ChallExtraParam{}
if err := ctx.BindWith(cep, binding.JSON); err != nil {
return
}
cep.AdminID, cep.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.UpChallExtraV2(ctx, cep))
}
func upChallExtraV3(ctx *bm.Context) {
cep3 := &param.ChallExtraParamV3{}
if err := ctx.BindWith(cep3, binding.Form); err != nil {
return
}
cep3.AdminID, cep3.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.UpChallExtraV3(ctx, cep3))
}
func batchUpChallExtra(ctx *bm.Context) {
bcep := new(param.BatchChallExtraParam)
if err := ctx.BindWith(bcep, binding.JSON); err != nil {
return
}
bcep.AdminID, bcep.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.BatchUpChallExtraV2(ctx, bcep))
}
func listChallBusiness(ctx *bm.Context) {
v := new(struct {
Cids []int64 `form:"cids,split" validate:"required,gt=0"`
})
if err := ctx.Bind(v); err != nil {
return
}
ctx.JSON(wkfSvc.BusinessList(ctx, v.Cids))
}
func upChall(ctx *bm.Context) {
cup := new(param.ChallUpParam)
if err := ctx.BindWith(cup, binding.FormPost); err != nil {
return
}
cup.AdminID, cup.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.UpChall(ctx, cup))
}
func upChallV3(ctx *bm.Context) {
cup := new(param.ChallUpParam)
if err := ctx.BindWith(cup, binding.FormPost); err != nil {
return
}
cup.AdminID, cup.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.UpChall(ctx, cup))
}
func platformChallCount(ctx *bm.Context) {
var (
assigneeAdminID int64
ok bool
IUid interface{}
IPers interface{}
permissionMap map[int8]int64
)
if IUid, ok = ctx.Get("uid"); ok {
assigneeAdminID = IUid.(int64)
}
if IPers, ok = ctx.Get(permit.CtxPermissions); ok {
permissionMap = parsePermission(IPers.([]string))
}
ctx.JSON(wkfSvc.PlatformChallCount(ctx, assigneeAdminID, permissionMap))
}
func platformChallListPending(ctx *bm.Context) {
var (
err error
pclp *param.ChallListParam
assigneeAdminID int64
ok bool
IPers interface{}
IUid interface{}
permissionMap map[int8]int64
)
pclp = new(param.ChallListParam)
if err = ctx.Bind(pclp); err != nil {
return
}
if len(pclp.Businesses) != len(pclp.AssignNum) {
ctx.JSON("business and AssignNum length not equal", ecode.RequestErr)
return
}
if pclp.PS == 0 {
pclp.PS = 10
}
if IUid, ok = ctx.Get("uid"); ok {
assigneeAdminID = IUid.(int64)
}
if IPers, ok = ctx.Get(permit.CtxPermissions); ok {
permissionMap = parsePermission(IPers.([]string))
}
ctx.JSON(wkfSvc.PlatformChallListPending(ctx, assigneeAdminID, permissionMap, pclp))
}
func platformHandlingChalllist(ctx *bm.Context) {
var (
err error
assigneeAdminID int64
ok bool
permissionMap map[int8]int64
IUid interface{}
IPers interface{}
)
chdlp := new(param.ChallHandlingDoneListParam)
if err = ctx.Bind(chdlp); err != nil {
return
}
if IUid, ok = ctx.Get("uid"); ok {
assigneeAdminID = IUid.(int64)
}
if IPers, ok = ctx.Get(permit.CtxPermissions); ok {
permissionMap = parsePermission(IPers.([]string))
}
ctx.JSON(wkfSvc.PlatformChallListHandlingDone(ctx, chdlp, permissionMap, assigneeAdminID, model.PlatformStateHandling))
}
func platformDoneChallList(ctx *bm.Context) {
var (
err error
assigneeAdminID int64
ok bool
permissionMap map[int8]int64
IUid interface{}
IPers interface{}
)
chdlp := new(param.ChallHandlingDoneListParam)
if err = ctx.Bind(chdlp); err != nil {
return
}
if IUid, ok = ctx.Get("uid"); ok {
assigneeAdminID = IUid.(int64)
}
if IPers, ok = ctx.Get(permit.CtxPermissions); ok {
permissionMap = parsePermission(IPers.([]string))
}
ctx.JSON(wkfSvc.PlatformChallListHandlingDone(ctx, chdlp, permissionMap, assigneeAdminID, model.PlatformStateDone))
}
func platformCreatedChallList(ctx *bm.Context) {
var (
err error
cclp *param.ChallCreatedListParam
adminID int64
IUid interface{}
ok bool
)
cclp = new(param.ChallCreatedListParam)
if err = ctx.Bind(cclp); err != nil {
return
}
if cclp.PS == 0 {
cclp.PS = 10
}
if IUid, ok = ctx.Get("uid"); ok {
adminID = IUid.(int64)
}
cond := new(search.ChallSearchCommonCond)
cond.Fields = []string{"id", "gid"}
cond.Business = cclp.Businesses
cond.AdminIDs = []int64{adminID}
cond.Order = cclp.Order
cond.Sort = cclp.Sort
cond.PS = cclp.PS
cond.PN = cclp.PN
ctx.JSON(wkfSvc.PlatformChallListCreated(ctx, cond))
}
func platformRelease(ctx *bm.Context) {
var (
exist bool
IUid interface{}
IPers interface{}
permissionMap map[int8]int64
assigneeAdminID int64
)
if IUid, exist = ctx.Get("uid"); !exist {
ctx.JSON(nil, ecode.UserNotExist)
return
}
assigneeAdminID = IUid.(int64)
if IPers, exist = ctx.Get(permit.CtxPermissions); !exist {
ctx.JSON(nil, ecode.MethodNoPermission)
return
}
permissionMap = parsePermission(IPers.([]string))
ctx.JSON(nil, wkfSvc.PlatformRelease(ctx, permissionMap, assigneeAdminID))
}
func platformCheckIn(ctx *bm.Context) {
var (
exist bool
IUid interface{}
assigneeAdminID int64
)
if IUid, exist = ctx.Get("uid"); !exist {
ctx.JSON(nil, ecode.UserNotExist)
return
}
assigneeAdminID = IUid.(int64)
ctx.JSON(nil, wkfSvc.PlatformCheckIn(ctx, assigneeAdminID))
}
func isPermitChallList(pers []string, cond *search.ChallSearchCommonCond) (ok bool) {
if cond.Business == 0 {
return
}
var (
business int8
round int64
)
if len(cond.Rounds) != 0 {
round = cond.Rounds[0]
}
business = cond.Business
switch business {
case 2: //稿件申诉
switch round {
case 0:
for _, per := range pers {
if per == ArchiveAppealRound1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
if per == ArchiveAppealRound2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
if per == ArchiveAppealRound3 {
cond.Rounds = append(cond.Rounds, 3)
ok = true
}
}
case 1:
for _, per := range pers {
if per == ArchiveAppealRound1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
}
case 2:
for _, per := range pers {
if per == ArchiveAppealRound2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 3:
for _, per := range pers {
if per == ArchiveAppealRound3 {
cond.Rounds = append(cond.Rounds, 3)
ok = true
}
}
}
case 3: // 短点评投诉
switch round {
case 0:
for _, per := range pers {
if per == ReviewShortComplainRoun1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
if per == ReviewShortComplainRoun2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 1:
for _, per := range pers {
if per == ReviewShortComplainRoun1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
}
case 2:
for _, per := range pers {
if per == ReviewShortComplainRoun2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
}
case 4: // 长点评投诉
switch round {
case 0:
for _, per := range pers {
if per == ReviewLongComplainRoun1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
if per == ReviewLongComplainRoun2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 1:
for _, per := range pers {
if per == ReviewLongComplainRoun1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
}
case 2:
for _, per := range pers {
if per == ReviewLongComplainRoun2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
}
case 5: // 小黑屋申诉
switch round {
case 0:
for _, per := range pers {
if per == CreditAppealRoun1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
if per == CreditAppealRoun2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
if per == CreditAppealRoun3 {
cond.Rounds = append(cond.Rounds, 3)
ok = true
}
if per == CreditAppealRoun4 {
cond.Rounds = append(cond.Rounds, 4)
ok = true
}
if per == CreditAppealRoun5 {
cond.Rounds = append(cond.Rounds, 5)
ok = true
}
if per == CreditAppealRoun6 {
cond.Rounds = append(cond.Rounds, 6)
ok = true
}
if per == CreditAppealRoun7 {
cond.Rounds = append(cond.Rounds, 7)
ok = true
}
if per == CreditAppealRoun8 {
cond.Rounds = append(cond.Rounds, 8)
ok = true
}
}
case 1:
for _, per := range pers {
if per == CreditAppealRoun1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
}
case 2:
for _, per := range pers {
if per == CreditAppealRoun2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 3:
for _, per := range pers {
if per == CreditAppealRoun3 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 4:
for _, per := range pers {
if per == CreditAppealRoun4 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 5:
for _, per := range pers {
if per == CreditAppealRoun5 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 6:
for _, per := range pers {
if per == CreditAppealRoun6 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 7:
for _, per := range pers {
if per == CreditAppealRoun7 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 8:
for _, per := range pers {
if per == CreditAppealRoun8 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
}
case 6: // 稿件审核
switch round {
case 0:
for _, per := range pers {
if per == ArchiveAuditRound1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
}
case 1:
for _, per := range pers {
if per == ArchiveAuditRound1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
}
}
case 9: //频道举报
switch round {
case 0:
for _, per := range pers {
if per == ChannelComplainRound1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
if per == ChannelComplainRound1 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
case 1:
for _, per := range pers {
if per == ChannelComplainRound1 {
cond.Rounds = append(cond.Rounds, 1)
ok = true
}
}
case 2:
for _, per := range pers {
if per == ChannelComplainRound2 {
cond.Rounds = append(cond.Rounds, 2)
ok = true
}
}
}
}
return
}

View File

@@ -0,0 +1,68 @@
package http
import (
"strconv"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func addEvent(ctx *bm.Context) {
ep := &param.EventParam{}
if err := ctx.BindWith(ep, binding.JSON); err != nil {
return
}
if !ep.ValidComponent() {
ctx.JSON(nil, ecode.RequestErr)
return
}
eid, err := wkfSvc.AddEvent(ctx, ep)
if err != nil {
log.Error("wkfSvc.AddEvent(%v) error(%v)", ep, err)
ctx.JSON(nil, ecode.RequestErr)
return
}
ctx.JSON(map[string]int64{
"eventNo": eid,
}, nil)
}
func batchAddEvent(ctx *bm.Context) {
bep := &param.BatchEventParam{}
if err := ctx.BindWith(bep, binding.JSON); err != nil {
return
}
if !bep.ValidComponent() {
ctx.JSON(nil, ecode.RequestErr)
return
}
eids, err := wkfSvc.BatchAddEvent(ctx, bep)
if err != nil {
log.Error("wkfSvc.BatchAddEvent(%v) error(%v)", bep, err)
ctx.JSON(nil, ecode.RequestErr)
return
}
ctx.JSON(map[string][]int64{
"eventNo": eids,
}, nil)
}
func eventList(ctx *bm.Context) {
params := ctx.Request.Form
cidStr := params.Get("cid")
// check params
cid, err := strconv.ParseInt(cidStr, 10, 32)
if err != nil {
log.Error("strconv.ParseInt(%s) error(%v)", cidStr, err)
ctx.JSON(nil, ecode.RequestErr)
return
}
ctx.JSON(wkfSvc.ListEvent(ctx, cid))
}

View File

@@ -0,0 +1,107 @@
package http
import (
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/app/admin/main/workflow/model/search"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func setGroupStateV3(ctx *bm.Context) {
gssp := &param.GroupStateSetParam{}
if err := ctx.BindWith(gssp, binding.FormPost); err != nil {
return
}
gssp.AdminID, gssp.AdminName = adminInfo(ctx)
// check ban account operate 账号封禁不支持批处理
if len(gssp.ID) > 1 && gssp.BlockDay != 0 {
ctx.JSON(nil, ecode.WkfBanNotSupportBatchOperate)
return
}
ctx.JSON(nil, wkfSvc.SetGroupState(ctx, gssp))
}
func groupListV3(ctx *bm.Context) {
v := new(param.GroupListParamV3)
if err := ctx.Bind(v); err != nil {
return
}
gscc := &search.GroupSearchCommonCond{
Fields: []string{"id", "oid", "typeid", "mid", "eid", "report_mid", "title", "first_user_tid"},
Business: v.Business,
Oids: v.Oid,
Mids: v.Mid,
States: v.State,
TypeIDs: v.TypeID,
Rounds: v.Round,
RID: v.Rid,
FID: v.Fid,
EID: v.Eid,
Tids: v.Tid,
FirstUserTid: v.FirstUserTid,
Order: v.Order,
Sort: v.Sort,
PN: v.PN,
PS: v.PS,
KWPriority: v.KWPriority,
KW: v.KW,
KWFields: v.KWField,
CTimeFrom: v.CTimeFrom,
CTimeTo: v.CTimeTo,
ReportMID: v.ReportMid,
}
ctx.JSON(wkfSvc.GroupListV3(ctx, gscc))
}
func setGroupRole(ctx *bm.Context) {
grsp := &param.GroupRoleSetParam{}
if err := ctx.BindWith(grsp, binding.FormPost); err != nil {
return
}
grsp.AdminID, grsp.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.UpGroupRole(ctx, grsp))
}
func upGroupExtra(ctx *bm.Context) {
uep := &param.UpExtraParam{}
if err := ctx.BindWith(uep, binding.Form); err != nil {
return
}
uep.AdminID, uep.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.UpGroupExtra(ctx, uep))
}
func setPublicReferee(ctx *bm.Context) {
gspr := &param.GroupStatePublicReferee{}
if err := ctx.BindWith(gspr, binding.FormPost); err != nil {
return
}
// if bid support public judge
if gspr.Business != model.CommentComplain {
ctx.JSON(nil, ecode.WkfBidNotSupportPublicReferee)
}
gspr.AdminID, gspr.AdminName = adminInfo(ctx)
ctx.JSON(nil, wkfSvc.SetPublicReferee(ctx, gspr))
}
func countPendingGroup(ctx *bm.Context) {
gpp := &param.GroupPendingParam{}
if err := ctx.Bind(gpp); err != nil {
return
}
gscc := &search.GroupSearchCommonCond{
Fields: []string{"id"},
Business: gpp.Business,
RID: gpp.Rid,
States: []int8{model.Pending},
PS: 1,
PN: 1,
Order: "id",
Sort: "desc",
}
ctx.JSON(wkfSvc.GroupPendingCount(ctx, gscc))
}

View File

@@ -0,0 +1,167 @@
package http
import (
"go-common/app/admin/main/workflow/service"
"go-common/library/conf/paladin"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/permit"
)
var (
authSvc *permit.Permit
wkfSvc *service.Service
)
// Init http server
func Init(s *service.Service) {
var (
hc struct {
BM *bm.ServerConfig
Permit *permit.Config
}
)
if err := paladin.Get("http.toml").UnmarshalTOML(&hc); err != nil {
panic(err)
}
// init service
iniService(hc.Permit, s)
// init internal router
engine := bm.DefaultServer(hc.BM)
//global timeout
setupInnerEngine(engine)
// init internal server
if err := engine.Start(); err != nil {
log.Error("engine.Start error(%v)", err)
panic(err)
}
}
func iniService(auth *permit.Config, s *service.Service) {
authSvc = permit.New(auth)
wkfSvc = s
}
// innerRouter
func setupInnerEngine(e *bm.Engine) {
// monitor ping
e.Ping(ping)
workflow := e.Group("/x/admin/workflow")
// platform
platform := workflow.Group("/platform", authSvc.Permit(""))
{
platform.GET("/count", platformChallCount)
platform.GET("/list/pending", platformChallListPending)
platform.GET("/list/handling", platformHandlingChalllist)
platform.GET("/list/done", platformDoneChallList)
platform.GET("/list/created", platformCreatedChallList)
platform.GET("/release", platformRelease)
platform.GET("/checkin", platformCheckIn)
}
// challenge
challenge := workflow.Group("/challenge")
{
challenge.GET("/list", challList)
challenge.GET("/list2", authSvc.Permit(""), challListCommon)
challenge.GET("/detail", challDetail)
challenge.GET("/activity/list", listChallActivity)
challenge.GET("/business/list", listChallBusiness) // Deprecated
challenge.POST("/update", authSvc.Permit(""), upChall)
challenge.POST("/reset", authSvc.Permit(""), rstChallResult)
challenge.POST("/state/set", authSvc.Permit(""), setChallResult) // new api logic
challenge.POST("/state/batch/set", authSvc.Permit(""), batchSetChallResult) //new api logic
challenge.POST("/batch/result/set", authSvc.Permit(""), batchSetChallResult) // todo: deprecated
challenge.POST("/extra/update", authSvc.Permit(""), upChallExtra)
challenge.POST("/extra/batch/update", authSvc.Permit(""), batchUpChallExtra)
challenge.POST("/business/state/set", authSvc.Permit(""), upChallBusState)
challenge.POST("/business/state/batch/set", authSvc.Permit(""), batchUpChallBusState)
// manager-v4 used
challenge.POST("/business/busState/update", upBusChallsBusState) //Deprecated
challenge.POST("/reset/business/state/batch/set", upBusChallsBusState)
// challenge event
event := challenge.Group("/event")
event.POST("/add", addEvent)
event.POST("/batch/add", batchAddEvent)
event.GET("/list", eventList)
// call reply/add sync add event and set business_state
reply := challenge.Group("/reply")
reply.POST("/add", authSvc.Permit(""), addReply)
reply.POST("/batch/add", authSvc.Permit(""), batchAddReply)
}
// business
business := workflow.Group("/business")
{
business.GET("/meta/list", busMetaList)
// callback
callback := business.Group("/callback")
{
callback.GET("/list", listCallback)
callback.POST("/add", authSvc.Permit(""), addOrUpCallback)
callback.POST("/external/api/set", setExtAPI)
}
// attr
busAttr := business.Group("/attr")
{
busAttr.GET("/list", listBusAttr)
busAttr.POST("/add", addOrUpdateBusAttr)
}
}
// todo: workflow_v3
v3 := workflow.Group("/v3")
challenge3 := v3.Group("/challenge", authSvc.Permit(""))
{
challenge3.GET("/list", challListV3)
challenge3.POST("/update", upChallV3)
challenge3.POST("/reset", rstChallResultV3)
challenge3.POST("/state/set", setChallStateV3)
challenge3.POST("/business/state/set", upChallBusStateV3)
challenge3.POST("/extra/update", upChallExtraV3)
}
group3 := v3.Group("/group", authSvc.Permit(""))
{
group3.GET("/list", groupListV3)
group3.GET("/pending/count", countPendingGroup)
group3.POST("/role/set", setGroupRole) //角色流转
group3.POST("/state/set", setGroupStateV3) //工单状态变更/处罚
group3.POST("/extra/update", upGroupExtra)
group3.POST("/public/referee/set", setPublicReferee) //移交众裁
}
bus3 := v3.Group("/business")
{
attr3 := bus3.Group("/attr")
{
attr3.GET("/list", listBusAttrV3)
attr3.POST("/button/switch", setSwitch)
attr3.POST("/button/shortcut/set", setShortCut)
}
mng := bus3.Group("/manager")
{
mng.GET("/tag", mngTag)
}
source := bus3.Group("/source")
{
source.GET("/list", srcList)
}
}
v3.GET("/user/block/info", userBlockInfo) //单个用户封禁次数/封禁状态
}
// ping check server ok.
func ping(ctx *bm.Context) {
if err := wkfSvc.Ping(ctx); err != nil {
ctx.Error = err
ctx.AbortWithStatus(503)
}
}

View File

@@ -0,0 +1,34 @@
package http
// permission point
const (
ArchiveComplainRound1 = "WF_ARCHIVE_COMPLAIN_ROUND_1"
ArchiveComplainRound2 = "WF_ARCHIVE_COMPLAIN_ROUND_2"
ArchiveAppealRound1 = "WF_ARCHIVE_APPEAL_ROUND_1"
ArchiveAppealRound2 = "WF_ARCHIVE_APPEAL_ROUND_2"
ArchiveAppealRound3 = "WF_ARCHIVE_APPEAL_ROUND_3"
ArchiveAppealRepeat = "WF_BUSINESS_2_ROUND_11"
ReviewShortComplainRoun1 = "WF_BUSINESS_3_ROUND_1"
ReviewShortComplainRoun2 = "WF_BUSINESS_3_ROUND_2"
ReviewLongComplainRoun1 = "WF_BUSINESS_4_ROUND_1"
ReviewLongComplainRoun2 = "WF_BUSINESS_4_ROUND_2"
CreditAppealRoun1 = "WF_BUSINESS_5_ROUND_1"
CreditAppealRoun2 = "WF_BUSINESS_5_ROUND_2"
CreditAppealRoun3 = "WF_BUSINESS_5_ROUND_3"
CreditAppealRoun4 = "WF_BUSINESS_5_ROUND_4"
CreditAppealRoun5 = "WF_BUSINESS_5_ROUND_5"
CreditAppealRoun6 = "WF_BUSINESS_5_ROUND_6"
CreditAppealRoun7 = "WF_BUSINESS_5_ROUND_7"
CreditAppealRoun8 = "WF_BUSINESS_5_ROUND_8"
ArchiveAuditRound1 = "WF_BUSINESS_6_ROUND_1"
ArchiveVTRound1 = "WF_BUSINESS_7_ROUND_1"
ChannelComplainRound1 = "WF_BUSINESS_8_ROUND_1"
ChannelComplainRound2 = "WF_BUSINESS_8_ROUND_2"
)

View File

@@ -0,0 +1,71 @@
package http
import (
"go-common/app/admin/main/workflow/model"
"go-common/app/admin/main/workflow/model/param"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/binding"
)
func addReply(ctx *bm.Context) {
var (
eid int64
err error
)
e := &param.EventParam{}
if err = ctx.BindWith(e, binding.FormPost); err != nil {
return
}
e.AdminID, e.AdminName = adminInfo(ctx)
if eid, err = wkfSvc.AddEvent(ctx, e); err != nil {
ctx.JSON(nil, err)
return
}
// 管理员回复同步修改 business_state
if e.Event == 1 {
if err = wkfSvc.UpChallBusState(ctx, e.Cid, e.AdminID, e.AdminName, model.FeedbackReplyNotRead); err != nil {
ctx.JSON(nil, err)
return
}
}
ctx.JSON(map[string]int64{
"eventNo": eid,
}, nil)
}
func batchAddReply(ctx *bm.Context) {
var (
eids []int64
err error
)
bep := &param.BatchEventParam{}
if err = ctx.BindWith(bep, binding.FormPost); err != nil {
return
}
bep.AdminID, bep.AdminName = adminInfo(ctx)
eids, err = wkfSvc.BatchAddEvent(ctx, bep)
if err != nil {
log.Error("wkfSvc.BatchAddEvent(%v) error(%v)", bep, err)
ctx.JSON(nil, ecode.RequestErr)
return
}
// 管理员回复同步修改 business_state
if bep.Event == 1 {
if err = wkfSvc.BatchUpChallBusState(ctx, bep.Cids, bep.AdminID, bep.AdminName, model.FeedbackReplyNotRead); err != nil {
ctx.JSON(nil, err)
return
}
}
ctx.JSON(map[string][]int64{
"eventNo": eids,
}, nil)
}

View File

@@ -0,0 +1,119 @@
package http
import (
"strconv"
"strings"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/xstr"
)
type intsParam struct {
value string
p *[]int64
}
type intParam struct {
value string
p *int64
}
func dealNumsmap(intsparams []*intsParam) error {
for _, isp := range intsparams {
if isp.value != "" {
ids, err := xstr.SplitInts(isp.value)
if err != nil {
log.Error("strconv.ParseInt(%s) error(%v)", isp.value, err)
return err
}
*isp.p = ids
}
}
return nil
}
func dealNummap(intparams []*intParam) error {
for _, ip := range intparams {
if ip.value != "" {
id, err := strconv.ParseInt(ip.value, 10, 64)
if err != nil {
log.Error("strconv.ParseInt(%s) error(%v)", ip.value, err)
return err
}
*ip.p = id
}
}
return nil
}
// adjustOrder will convert order field from request to search service
func adjustOrder(subject string, order string) string {
SubOrderFields := map[string]map[string]string{
"group": {
"last_time": "lasttime",
},
"tag": {
"count": "tag_all_num",
"handling": "tag_todo_num",
},
"challenge": {},
"log": {
"ctime": "opt_ctime",
},
}
orderFields, ok := SubOrderFields[subject]
if !ok {
return order
}
field, ok := orderFields[order]
if !ok {
return order
}
return field
}
// read permissions of an admin
// only use workflow permissions in platform
// like WF_BUSINESS_2_ROUND_11
// round > 10 means feedback flow
// one admin only has one flow permission in a business
func parsePermission(permissions []string) (permissionMap map[int8]int64) {
//permissionMap map[business]round
//var regex = `^WF_BUSINESS_[1-9]\d*_ROUND_[1-9]\d*`
permissionMap = make(map[int8]int64)
for _, str := range permissions {
splitStr := strings.Split(str, "_")
if len(splitStr) == 5 && splitStr[0] == "WF" && splitStr[1] == "BUSINESS" && splitStr[3] == "ROUND" {
business, err := strconv.ParseInt(splitStr[2], 10, 32)
if err != nil {
continue
}
round, err := strconv.ParseInt(splitStr[4], 10, 32)
if err != nil {
continue
}
permissionMap[int8(business)] = round
//todo: support round??
}
}
// todo: use cache?
return
}
func adminInfo(ctx *bm.Context) (adminID int64, adminName string) {
if IUid, ok := ctx.Get("uid"); ok {
adminID = IUid.(int64)
}
if IUName, ok := ctx.Get("username"); ok {
adminName = IUName.(string)
}
return
}

View File

@@ -0,0 +1,65 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/admin/main/workflow/model/manager:all-srcs",
"//app/admin/main/workflow/model/param:all-srcs",
"//app/admin/main/workflow/model/search:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
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 = [
"activity.go",
"block.go",
"business.go",
"business_attr.go",
"callback.go",
"challenge.go",
"common.go",
"event.go",
"extra.go",
"group.go",
"message.go",
"platform_meta.go",
"reply.go",
"state.go",
"tag.go",
"wlog.go",
],
importpath = "go-common/app/admin/main/workflow/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/workflow/model/param:go_default_library",
"//app/job/main/credit/model:go_default_library",
"//library/time:go_default_library",
],
)

View File

@@ -0,0 +1,7 @@
package model
// Activities is the model for all challenge activities
type Activities struct {
Logs []*WLog `json:"logs"`
Events []*Event `json:"events"`
}

View File

@@ -0,0 +1,39 @@
package model
import credit "go-common/app/job/main/credit/model"
// BlockReason 封禁理由
var BlockReason = map[int8]string{
credit.ReasonOtherType: "其他",
credit.ReasonBrushScreen: "刷屏",
credit.ReasonGrabFloor: "抢楼",
credit.ReasonGamblingFraud: "发布赌博诈骗信息",
credit.ReasonProhibited: "发布违禁相关信息",
credit.ReasonGarbageAds: "发布垃圾广告信息",
credit.ReasonPersonalAttacks: "发布人身攻击言论",
credit.ReasonViolatePrivacy: "发布侵犯他人隐私信息",
credit.ReasonLeadBattle: "发布引战言论",
credit.ReasonSpoiler: "发布剧透信息",
credit.ReasonAddUnrelatedTags: "恶意添加无关标签",
credit.ReasonDelOtherTags: "恶意删除他人标签",
credit.ReasonPornographic: "发布色情信息",
credit.ReasonVulgar: "发布低俗信息",
credit.ReasonBloodyViolence: "发布暴力血腥信息",
credit.ReasonAnimusVideoUp: "涉及恶意投稿行为",
credit.ReasonIllegalWebsite: "发布非法网站信息",
credit.ReasonSpreadErrinfo: "发布传播不实信息",
credit.ReasonAbettingEncouragement: "发布怂恿教唆信息",
credit.ReasonAnimusBrushScreen: "恶意刷屏",
credit.ReasonAccountViolation: "账号违规",
credit.ReasonMaliciousPlagiarism: "恶意抄袭",
credit.ReasonPosingAsHomemade: "冒充自制原创",
credit.ReasonPostTeenBadContent: "发布青少年不良内容",
credit.ReasonDestroyCyberSecurity: "破坏网络安全",
credit.ReasonPostingMisleadingInfo: "发布虚假误导信息",
credit.ReasonCounterfeitOfficialAuth: "仿冒官方认证账号",
credit.ReasonPublishInappropriateContent: "发布不适宜内容",
credit.ReasonViolationOperatingRules: "违反运营规则",
credit.ReasonIllegalCreateTopic: "恶意创建话题",
credit.ReasonIllegalDrawLottery: "发布违规抽奖",
credit.ReasonIllegalFakeMan: "恶意冒充他人",
}

View File

@@ -0,0 +1,88 @@
package model
import xtime "go-common/library/time"
const (
// 业务bid 对应manager项目子业务
// ArchiveComplain 稿件投诉
ArchiveComplain = 1
// ArchiveAppeal 稿件申诉
ArchiveAppeal = 2
// ReviewShortComplain 短点评投诉
ReviewShortComplain = 3
// ReviewLongComplain 长点评投诉
ReviewLongComplain = 4
// CreditAppeal 小黑屋申诉
CreditAppeal = 5
// ArchiveAudit 稿件审核
ArchiveAudit = 6
// ArchiveVT 任务质检
ArchiveVT = 7
// ChannelComplain 频道举报
ChannelComplain = 9
// CommentComplain 评论举报
CommentComplain = 13
// SubtitleComplain 字幕举报
SubtitleComplain = 14
)
// Business will record any business properties
type Business struct {
Bid int32 `json:"-" gorm:"column:id"`
Gid int64 `json:"gid" gorm:"column:gid"`
Cid int64 `json:"cid" gorm:"column:cid"`
Oid int64 `json:"oid" gorm:"column:oid"`
Business int8 `json:"business" gorm:"column:business"`
TypeID int32 `json:"typeid" gorm:"column:typeid"`
Title string `json:"title" gorm:"column:title"`
Content string `json:"content" gorm:"column:content"`
Mid int64 `json:"mid" gorm:"column:mid"`
Extra string `json:"extra" gorm:"column:extra"`
CTime xtime.Time `json:"-" gorm:"column:ctime"`
MTime xtime.Time `json:"-" gorm:"column:mtime"`
}
// Meta is the model to store business metadata
type Meta struct {
Business int8 `json:"business"`
Name string `json:"name"`
ItemType string `json:"item_type"`
Rounds []*Round `json:"rounds"`
Attr *BusinessAttr `json:"attr"`
}
// MetaSlice is used to support sort Metas
type MetaSlice []*Meta
func (ms MetaSlice) Len() int {
return len(ms)
}
func (ms MetaSlice) Swap(i, j int) {
ms[i], ms[j] = ms[j], ms[i]
}
func (ms MetaSlice) Less(i, j int) bool {
return ms[i].Business < ms[j].Business
}
// Round is the model to describe how many business rounds are
type Round struct {
ID int8 `json:"id"`
Name string `json:"name"`
}
// RoundSlice is used to support sort Rounds
type RoundSlice []*Round
func (rs RoundSlice) Len() int {
return len(rs)
}
func (rs RoundSlice) Swap(i, j int) {
rs[i], rs[j] = rs[j], rs[i]
}
func (rs RoundSlice) Less(i, j int) bool {
return rs[i].ID < rs[j].ID
}

View File

@@ -0,0 +1,34 @@
package model
import xtime "go-common/library/time"
// BusinessAttr will record business attributes
type BusinessAttr struct {
ID int64 `json:"id" gorm:"column:id"`
BID int64 `json:"bid" gorm:"column:bid"`
BusinessName string `json:"business_name" gorm:"business_name"`
Name string `json:"name" gorm:"column:name"`
DealType int8 `json:"deal_type" gorm:"column:deal_type"`
ExpireTime int64 `json:"expire_time" gorm:"column:expire_time"`
AssignType int8 `json:"assign_type" gorm:"column:assign_type"`
AssignMax int8 `json:"assign_max" gorm:"column:assign_max"`
GroupType int8 `json:"group_type" gorm:"column:group_type"`
Button uint8 `json:"-" gorm:"button"`
ButtonKey string `json:"-" gorm:"button_key"`
CTime xtime.Time `json:"ctime" gorm:"column:ctime"`
MTime xtime.Time `json:"mtime" gorm:"column:mtime"`
Buttons []*Button `json:"button" gorm:"-"`
}
// TableName is used to identify chall table name in gorm
func (BusinessAttr) TableName() string {
return "workflow_business_attr"
}
// Button .
type Button struct {
Index int `json:"index"`
Name string `json:"name"`
State bool `json:"state"`
Key string `json:"key"`
}

View File

@@ -0,0 +1,58 @@
package model
import (
xtime "go-common/library/time"
)
// consts for callback
const (
GroupSetResult = "group.SetResult"
BatchGroupSetResult = "group.BatchSetResult"
ChallSetResult = "chall.SetResult"
BatchChallSetResult = "chall.BatchSetResult"
GroupSetState = "group.SetState"
GroupSetPublicReferee = "group.SetPublicReferee"
CallbackDisable = 0
CallbackEnable = 1
)
// Slice is the slice model for callback
type CallbackSlice []*Callback
// Callback is the workflow callback model
type Callback struct {
CbID int32 `json:"cbid" gorm:"column:id"`
URL string `json:"url" gorm:"column:url"`
Business int8 `json:"business" gorm:"column:business"`
IsSobot bool `json:"is_sobot" gorm:"column:is_sobot"`
State int8 `json:"state" gorm:"column:state"`
ExternalAPI string `json:"external_api" gorm:"column:external_api"`
SourceAPI string `json:"source_api" gorm:"column:source_api"`
CTime xtime.Time `json:"ctime" gorm:"column:ctime"`
MTime xtime.Time `json:"mtime" gorm:"column:mtime"`
}
// Actor for callback payload
type Actor struct {
AdminID int64 `json:"admin_id"`
AdminName string `json:"admin_name"`
}
// Payload is the payload model for callback
type Payload struct {
Bid int `json:"bid"`
Verb string `json:"verb"`
Actor Actor `json:"actor"`
CTime xtime.Time `json:"ctime"`
Object interface{} `json:"object"` //处理请求参数
Target interface{} `json:"target"` //被修改的工单或工单详情
Targets []interface{} `json:"targets"` //所有被修改的工单或工单详情
Influence interface{} `json:"influence"` //业务自定义 Deprecated
Extra interface{} `json:"extra"` //业务自定义
}
// TableName is used to identify table name for gorm
func (Callback) TableName() string {
return "workflow_callback"
}

View File

@@ -0,0 +1,150 @@
package model
import (
"net/url"
xtime "go-common/library/time"
)
// Chall is the row view for every challenge
type Chall struct {
Cid int64 `json:"cid" gorm:"column:id"`
Gid int64 `json:"gid" gorm:"column:gid"`
Oid int64 `json:"oid" gorm:"column:oid"`
OidStr string `json:"oid_str" gorm:"-"`
Business int8 `json:"business" gorm:"column:business"`
Mid int64 `json:"mid" gorm:"column:mid"`
MName string `json:"m_name" gorm:"-"`
Tid int64 `json:"tid" gorm:"column:tid"`
State int8 `json:"state"`
BusinessState int8 `json:"business_state"`
DispatchState uint32 `json:"-" gorm:"column:dispatch_state"`
DispatchTime xtime.Time `json:"dispatch_time" gorm:"column:dispatch_time"`
Description string `json:"description" gorm:"column:description"`
Metadata string `json:"metadata" gorm:"column:metadata"`
CTime xtime.Time `json:"ctime" gorm:"column:ctime"`
MTime xtime.Time `json:"mtime" gorm:"column:mtime"`
BusinessObject *Business `json:"business_object,omitempty" gorm:"-"`
AssigneeAdminID int64 `json:"assignee_adminid" gorm:"column:assignee_adminid"`
AdminID int64 `json:"adminid" gorm:"column:adminid"`
AssigneeAdminName string `json:"assignee_admin_name" gorm:"-"`
AdminName string `json:"admin_name" gorm:"-"`
TotalStates string `json:"total_states" gorm:"-"`
// tag related fields
Tag string `json:"tag" gorm:"-"`
Round int8 `json:"round" gorm:"-"`
// log related
LastLog string `json:"last_log" gorm:"-"`
// event related
LastEvent *Event `json:"last_event" gorm:"-"`
// Attachments
Attachments []string `json:"attachments" gorm:"-"`
// linked group object
Group *Group `json:"group,omitempty" gorm:"-"`
Meta interface{} `json:"meta" gorm:"-"`
AuditLog interface{} `json:"audit_log" gorm:"-"`
Producer *Account `json:"producer" gorm:"-"` //举报人
// business table
Title string `json:"title,omitempty" gorm:"-"`
TypeID int64 `json:"type_id,omitempty" gorm:"-"`
}
// TinyChall is the tiny row view for every challenge
type TinyChall struct {
Cid int64 `json:"cid" gorm:"column:id"`
Gid int64 `json:"gid" gorm:"column:gid"`
Mid int64 `json:"mid" gorm:"column:mid"`
CTime xtime.Time `json:"ctime" gorm:"column:ctime"`
State int8 `json:"state" gorm:"-"`
Title string `json:"title" gorm:"-"`
}
// ChallTagSlice is the slice to ChallTag
type ChallTagSlice []*ChallTag
// ChallTag is the model to retrive user submitted tags in group view
type ChallTag struct {
ID int64 `json:"id"`
Tag string `json:"tag"`
Round int8 `json:"round"`
Count int64 `json:"count"`
Percent int32 `json:"percent"`
}
// TableName is used to identify chall table name in gorm
func (Chall) TableName() string {
return "workflow_chall"
}
func (c ChallTagSlice) Len() int {
return len(c)
}
func (c ChallTagSlice) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
func (c ChallTagSlice) Less(i, j int) bool {
return c[i].Percent < c[j].Percent
}
// FixAttachments will fix attachments url as user friendly
// ignore https case
// FIXME: this should be removed after attachment url is be normed
func (c *Chall) FixAttachments() {
if c.Attachments == nil {
return
}
fixed := make([]string, 0, len(c.Attachments))
for _, a := range c.Attachments {
u, err := url.Parse(a)
if err != nil {
continue
}
u.Scheme = "http"
fixed = append(fixed, u.String())
}
c.Attachments = fixed
}
// SetState update state of a role
// ex. oldState=0x3a4b5c6d, state=15, role=1 then result is 0x3a4b5cfd
func (c *Chall) 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 *Chall) 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 *Chall) FromState() {
c.State = c.getState(uint8(0))
c.BusinessState = c.getState(uint8(1))
}
// FormatState transform state in queue
func (c *Chall) FormatState() {
if c.State == QueueState {
c.State = QueueStateBefore
}
if c.BusinessState == QueueBusinessState {
c.BusinessState = QueueBusinessStateBefore
}
}

View File

@@ -0,0 +1,44 @@
package model
import (
"testing"
"github.com/smartystreets/goconvey/convey"
)
var c *Chall
func init() {
c = new(Chall)
}
func TestSetState(t *testing.T) {
convey.Convey("SetState", t, func() {
c.DispatchState = uint32(0x3a4b5c6d)
state := uint32(0xf)
role := uint8(1)
c.SetState(state, role)
convey.So(c.DispatchState, convey.ShouldEqual, uint32(0x3a4b5cfd))
})
}
func TestGetState(t *testing.T) {
convey.Convey("GetState", t, func() {
c.DispatchState = uint32(0x3a4b5c6d)
role := uint8(2)
result := c.getState(role)
convey.So(result, convey.ShouldEqual, uint32(0xc))
})
}
func TestFormatState(t *testing.T) {
convey.Convey("FormatState", t, func() {
c.FormatState()
})
}
func TestFromState(t *testing.T) {
convey.Convey("FromState", t, func() {
c.FromState()
})
}

View File

@@ -0,0 +1,27 @@
package model
// Page common page struct for list api
type Page struct {
Num int `json:"num"`
Size int `json:"size"`
Total int `json:"total"`
}
// CommonResponse .
type CommonResponse struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int `json:"ttl"`
}
// CommonExtraDataResponse .
type CommonExtraDataResponse struct {
*CommonResponse
Data map[string]interface{} `json:"data"` //map[gid]interface{}
}
// SourceQueryResponse
type SourceQueryResponse struct {
*CommonResponse
Data map[string]interface{} `json:"data"`
}

View File

@@ -0,0 +1,62 @@
package model
import (
"net/url"
"strings"
xtime "go-common/library/time"
)
// consts for workflow event
const (
// EventAdminReply 管理员回复
EventAdminReply = 1
// EventAdminNote 管理员回复并记录
EventAdminNote = 2
// EventUserReply 用户回复
EventUserReply = 3
// EventSystem 系统回复
EventSystem = 4
)
// EventSlice is a Event slice struct
type EventSlice []*Event
// 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"
}
// FixAttachments will fix attachments url as user friendly
// ignore https case
// FIXME: this should be removed after attachment url is be normed
func (e *Event) FixAttachments() {
if len(e.Attachments) <= 0 {
return
}
sep := ";"
atts := strings.Split(e.Attachments, sep)
fixed := make([]string, 0, len(atts))
for _, a := range atts {
u, err := url.Parse(a)
if err != nil {
continue
}
u.Scheme = "http"
fixed = append(fixed, u.String())
}
e.Attachments = strings.Join(fixed, sep)
}

View File

@@ -0,0 +1,242 @@
package model
import (
"net/url"
xtime "go-common/library/time"
)
// ChannelInfoResult .
type ChannelInfoResult struct {
*CommonResponse
Data map[int64]*ArchiveChannelData
}
// ArchiveChannelData .
type ArchiveChannelData struct {
Tids []int64 `json:"tids"`
ChannelMap map[int64]*Channel `json:"channels"`
Archive *Archive `json:"-"`
}
// Channel .
type Channel struct {
//Tid int64 `json:"tid"`
TName string `json:"tname"`
//HitRule string `json:"hit_rule"`
//HitNames []string `json:"hit_names"`
HitRules []string `json:"hit_rules"`
Archive *Archive `json:"archive"`
}
// Review review struct
// http://info.bilibili.co/pages/viewpage.action?pageId=5417343
type Review struct {
ReviewID int64 `json:"review_id"`
Type int8 `json:"type"`
Mid int64 `json:"mid"`
MediaName string `json:"media_name"`
Title string `json:"title"`
Content string `json:"content"`
Status int8 `json:"status"`
IsDelete int8 `json:"is_delete"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
TypeName string `json:"type_name"`
Author string `json:"author"`
Composer *Account `json:"-"`
}
// MultiReviewInfoResult response of multi review info api
type MultiReviewInfoResult struct {
Code int `json:"code"`
Result map[int64]*Review `json:"result"`
Message string `json:"message"`
TTL int64 `json:"ttl"`
}
// SpecialTag .
type SpecialTag struct {
MID int64 `json:"mid"`
GroupTag string `json:"group_tag"`
BGColor string `json:"bg_color"`
FontColor string `json:"font_color"`
GroupID int `json:"group_id"`
}
// UperSpecial .
// http://info.bilibili.co/pages/viewpage.action?pageId=8479274
type UperSpecial struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int64 `json:"ttl"`
Data struct {
Page Page `json:"page"`
Items []*SpecialTag `json:"items"`
} `json:"data"`
}
// Account account info struct
// http://info.bilibili.co/pages/viewpage.action?pageId=8465956#V3HTTP接口-用户全量信息含计数ProfileStat
// ignore unused field
type Account struct {
Mid int64 `json:"mid"`
Name string `json:"name"`
Rank int32 `json:"rank"`
Follower int64 `json:"follower"`
Official *Official `json:"official"`
SpecialTag []*SpecialTag `json:"special_tag"`
}
// Official .
type Official struct {
Role int8 `json:"role"`
}
// Relation .
// http://info.bilibili.co/pages/viewpage.action?pageId=8742464
type Relation struct {
Follower int `json:"follower"`
}
// AccountInfosResult response of account info api
type AccountInfosResult struct {
Code int `json:"code"`
Data *Account `json:"data"`
Message string `json:"message"`
TTL int64 `json:"ttl"`
}
// ArchiveComplainGroup is the model for archvie appeal group view
type ArchiveComplainGroup struct {
Group
Archive *Archive `json:"archive"`
ChallengeTags ChallTagSlice `json:"challenge_tags"`
}
// ArchiveAppealGroup is the model for archvie appeal group view
type ArchiveAppealGroup struct {
ArchiveComplainGroup
// Appeal Related
Cid int64 `json:"cid"`
AppealReason string `json:"appeal_reason"`
AppealAttachments []string `json:"appeal_attachments"`
}
// ArchiveListPage is the model for archive list result
type ArchiveListPage struct {
Items interface{} `json:"items"`
Page *Page `json:"page"`
}
// Archive is archive info struct
// http://info.bilibili.co/pages/viewpage.action?pageId=3686646
type Archive struct {
Author string `json:"author"`
State int32 `json:"state"`
Mid int64 `json:"mid"`
TypeID int32 `json:"tid"`
Type string `json:"type"`
Title string `json:"title"`
RejectReason string `json:"reject_reason"`
Composer *Account `json:"-"`
OPName string `json:"op_name"`
OPContent string `json:"op_content"`
OPRemark string `json:"op_remark"`
}
// ArchiveResult is response of archive info api
type ArchiveResult struct {
*Archive `json:"archive"`
}
// MultiArchiveInfoResult is response of multi archive info api
type MultiArchiveInfoResult struct {
Code int `json:"code"`
Data map[int64]*ArchiveResult `json:"data"`
Message string `json:"message"`
TTL int64 `json:"ttl"`
}
// ArchiveType archive type struct
type ArchiveType struct {
ID int64 `json:"id"`
PID int64 `json:"pid"`
Name string `json:"name"`
Description string `json:"description"`
}
// ArchiveTypeResult response of archive type api
type ArchiveTypeResult struct {
Code int `json:"code"`
Data map[int32]*ArchiveType `json:"data"`
Message string `json:"message"`
TTL int64 `json:"ttl"`
}
// FixAttachments will fix attachments url as user friendly
// ignore https case
// FIXME: this should be removed after attachment url is be normed
func (ag *ArchiveAppealGroup) FixAttachments() {
if ag.AppealAttachments == nil {
return
}
fixed := make([]string, 0, len(ag.AppealAttachments))
for _, a := range ag.AppealAttachments {
u, err := url.Parse(a)
if err != nil {
continue
}
u.Scheme = "http"
fixed = append(fixed, u.String())
}
ag.AppealAttachments = fixed
}
// CreditMeta .
type CreditMeta struct {
*Business
Producer *Account `json:"producer"`
}
// BlockInfoResp .
type BlockInfoResp struct {
*CommonResponse
Data *struct {
Mid int64 `json:"mid"`
Status int64 `json:"status"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
BlockedSum int64 `json:"blocked_sum"`
} `json:"data"`
}
// BlockNumResp .
type BlockNumResp struct {
*CommonResponse
Data *struct {
BlockedSum int64 `json:"blockedSum"`
} `json:"data"`
}
// BlockCaseAdd .
// http://info.bilibili.co/pages/viewpage.action?pageId=5417571
type BlockCaseAdd struct {
Aid int64 `json:"aid"`
Mid int64 `json:"mid"`
Operator string `json:"operator"`
OperID int64 `json:"oper_id"`
OriginType int64 `json:"origin_type"`
OriginContent string `json:"origin_content"`
OriginTitle string `json:"origin_title"`
OriginURL string `json:"origin_url"`
ReasonType int64 `json:"reason_type"`
Oid int64 `json:"oid"`
RpID int64 `json:"rp_id"`
TagID int64 `json:"tag_id"`
Type int64 `json:"type"`
Page int64 `json:"page"`
BusinessTime int64 `json:"business_time"`
}

View File

@@ -0,0 +1,69 @@
package model
import (
xtime "go-common/library/time"
)
// Group model is the group view for several challanges
type Group struct {
ID int64 `json:"id" gorm:"column:id"`
Oid int64 `json:"oid" gorm:"column:oid"`
OidStr string `json:"oid_str" gorm:"-"`
Business int8 `json:"business" gorm:"column:business"`
Fid int64 `json:"fid" gorm:"column:fid"`
Rid int8 `json:"rid" gorm:"column:rid"`
Eid int64 `json:"eid" gorm:"eid"`
EidStr string `json:"eid_str" gorm:"-"`
State int8 `json:"state" gorm:"column:state"`
Tid int64 `json:"tid" gorm:"column:tid"`
FirstUserTid int64 `json:"first_user_tid" gorm:"-"`
Note string `json:"note" gorm:"column:note"`
Score int64 `json:"score" gorm:"column:score"`
// Stat fields
// this is a workround solution for calcuating appeals
Count int32 `json:"count" gorm:"column:count"`
Handling int32 `json:"handling" gorm:"column:handling"`
CTime xtime.Time `json:"ctime" gorm:"column:ctime"`
MTime xtime.Time `json:"mtime" gorm:"column:mtime"`
LastTime xtime.Time `json:"last_time" gorm:"column:lasttime"`
LastLog string `json:"last_log" gorm:"-"`
BusinessObject *Business `json:"business_object,omitempty" gorm:"-"`
// Tags related to Group
Tag string `json:"tag" gorm:"-"`
Round int8 `json:"round" gorm:"-"`
ChallengeTags ChallTagSlice `json:"challenge_tags" gorm:"-"`
Meta interface{} `json:"meta" gorm:"-"`
MetaData interface{} `json:"meta_data" gorm:""`
TypeID int64 `json:"type_id" gorm:"-"`
LastProducer *Account `json:"last_producer" gorm:"-"`
Defendant *Account `json:"defendant" gorm:"-"`
}
// GroupListPage is the model for group list result
type GroupListPage struct {
Items []*Group `json:"items"`
Page *Page `json:"page"`
}
// GroupPendingCount .
type GroupPendingCount struct {
Total int `json:"total"`
}
// GroupMeta .
type GroupMeta struct {
Archive *Archive `json:"archive"`
Object *Business `json:"object"`
External interface{} `json:"external"`
}
// TableName is used to identify group table name in gorm
func (Group) TableName() string {
return "workflow_group"
}

View File

@@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"admin.go",
"business.go",
],
importpath = "go-common/app/admin/main/workflow/model/manager",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//app/admin/main/workflow/model: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,9 @@
package manager
// UNameSearchResult .
type UNameSearchResult struct {
Code int `json:"code"`
Data map[int64]string `json:"data"`
Message string `json:"message"`
TTL int32 `json:"ttl"`
}

View File

@@ -0,0 +1,43 @@
package manager
import "go-common/app/admin/main/workflow/model"
// Meta business meta
// http://info.bilibili.co/pages/viewpage.action?pageId=9846887
type Meta struct {
ID int `json:"id"`
PID int `json:"pid"`
Name string `json:"name"`
Flow int `json:"flow"`
FlowState int `json:"flow_state"`
State int `json:"state"`
FlowChild []*Flow `json:"flowchild"`
}
// Flow is child flow meta
type Flow struct {
FlowState int `json:"flow_state"`
Child []*Meta `json:"child"`
}
// ListResponse .
type ListResponse struct {
*model.CommonResponse
Data []*Meta `json:"data"`
}
// Role .
type Role struct {
ID int `json:"id"`
Bid int8 `json:"bid"`
Rid int8 `json:"rid"`
Name string `json:"name"`
Type int `json:"type"`
State int `json:"state"`
}
// RoleResponse .
type RoleResponse struct {
*model.CommonResponse
Data []*Role
}

View File

@@ -0,0 +1,40 @@
package model
import (
"fmt"
"go-common/app/admin/main/workflow/model/param"
)
// message mc
const (
ArcComplainDealMC = "1_13_1"
ArcComplainRevMC = "1_15_1"
WkfNotifyMC = "1_5_3"
)
// DealArcComplainMsg generate archive complain deal message param
func DealArcComplainMsg(aid int64, mids []int64) *param.MessageParam {
return &param.MessageParam{
Type: "json",
Source: 1,
DataType: 4,
MC: ArcComplainDealMC,
Title: "您的投诉已被受理",
Context: fmt.Sprintf("您对稿件av%d的投诉已被受理。感谢您对 bilibili 社区秩序的维护,哔哩哔哩 (゜-゜)つロ 干杯~ ", aid),
MidList: mids,
}
}
// ReceivedArcComplainMsg generate archive complain received message param
func ReceivedArcComplainMsg(aid int64, mids []int64) *param.MessageParam {
return &param.MessageParam{
Type: "json",
Source: 1,
DataType: 4,
MC: ArcComplainRevMC,
Title: "您的投诉已收到",
Context: fmt.Sprintf("您对稿件av%d的投诉我们已经收到。感谢您对 bilibili 社区秩序的维护,哔哩哔哩 (゜-゜)つロ 干杯~", aid),
MidList: mids,
}
}

View File

@@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"callback.go",
"challenge.go",
"group.go",
"param.go",
"platform.go",
"wlog.go",
],
importpath = "go-common/app/admin/main/workflow/model/param",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/xstr: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,11 @@
package param
// AddCallbackParam is the model to create callback
type AddCallbackParam struct {
Business int8 `json:"business"`
URL string `json:"url"`
IsSobot bool `json:"is_sobot"`
State int8 `json:"state"`
ExternalAPI string `json:"external_api"`
SourceAPI string `json:"source_api"`
}

View File

@@ -0,0 +1,227 @@
package param
import (
"net/url"
"strconv"
"go-common/library/xstr"
)
// ChallengeListCommonParam .
type ChallengeListCommonParam struct {
Business int8 `form:"business" validate:"required"`
IDs []int64 `form:"ids,split"`
Oids []string `form:"oids,split"`
ObjectMids []int64 `form:"object_mids,split"`
Mids []int64 `form:"mids,split"`
Gids []int64 `form:"gids,split"`
States []int64 `form:"states,split"`
TypeIDs []int64 `form:"typeids,split"`
Tids []int64 `form:"tids,split" validate:"dive,gt=0"`
Rounds []int64 `form:"rounds,split"`
AssigneeAdminIDs []int64 `form:"assignee_adminids,split"`
AssigneeAdminNames []string `form:"assignee_adminnames,split"`
AdminIDs []int64 `form:"adminids,split"`
BusinessStates []int64 `form:"business_states,split"`
DispatchStates []int64 `form:"dispatch_states,split"`
Title string `form:"title"`
Content string `form:"content"`
AdminReply string `form:"admin_reply"`
UserReply string `form:"user_reply"`
CTimeFrom string `form:"ctime_from"`
CTimeTo string `form:"ctime_to"`
Order string `form:"order" default:"ctime"`
Sort string `form:"sort_order" default:"desc"`
PS int `form:"ps" default:"50"`
PN int `form:"pn" default:"1"`
}
// ChallengeListV3Param .
type ChallengeListV3Param struct {
Business int8 `form:"business" validate:"required"`
IDs []int64 `form:"cid,split"`
Oids []string `form:"oid,split"`
Mids []int64 `form:"mid,split"`
Gids []int64 `form:"gid,split"`
States []int64 `form:"state,split"`
TypeIDs []int64 `form:"typeid,split"`
Tids []int64 `form:"tid,split"`
Roles []int64 `form:"role,split"`
AssigneeAdminIDs []int64 `form:"assignee_adminid,split"`
AssigneeAdminNames []string `form:"assignee_admin_name,split"`
AdminIDs []int64 `form:"adminid,split"`
AdminNames []string `form:"admin_name,split"`
BusinessStates []int64 `form:"business_state,split"`
KW []string `form:"kw,split"`
KWField []string `form:"kw_field,split"`
CTimeFrom string `form:"ctime_from"`
CTimeTo string `form:"ctime_to"`
Order string `form:"order" default:"id"`
Sort string `form:"sort" default:"desc"`
PS int `form:"ps" default:"50"`
PN int `form:"pn" default:"1"`
}
// ChallRstParam describe the reset request params to a challenge row
type ChallRstParam struct {
Cid int64 `form:"cid" json:"cid" validate:"required,min=1"`
State int8 `form:"state" json:"state" validate:"min=0,max=14"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Reason string `form:"reason" json:"reason"`
Business int8 `form:"business" json:"business"`
}
// ChallUpParam describe the update request params to a challenge row
type ChallUpParam struct {
Cid int64 `form:"cid" json:"cid" validate:"required,min=1"`
Tid int64 `form:"tid" json:"tid"`
Note string `form:"note" json:"note"`
AdminID int64 `form:"adminid" json:"adminid"`
AdminName string `json:"admin_name"`
Business int8 `form:"business" json:"business"`
Role int8 `form:"role" json:"role"`
}
// ChallResParam describe the set result request params to a challenge row
type ChallResParam struct {
Cid int64 `json:"cid" form:"cid" validate:"required,min=1"`
State int8 `json:"state" form:"state" validate:"min=0,max=14"`
Reason string `json:"reason" form:"reason"`
AdminID int64 `json:"adminid" form:"adminid"`
AdminName string `json:"admin_name"`
}
// BatchChallResParam describe the set result request params to a set of challenges
type BatchChallResParam struct {
Cids []int64 `json:"cids" form:"cid,split" validate:"required,gt=0"`
State int8 `json:"state" form:"state" validate:"min=0,max=14"`
Business int8 `form:"business" json:"business"`
Role int8 `form:"role" json:"role"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Reason string `json:"reason" form:"reason"`
}
// ChallSetParamV3 .
type ChallSetParamV3 struct {
ID []int64 `json:"id" form:"id,split" validate:"required,gt=0"`
State int8 `json:"state" form:"state" validate:"min=0,max=14"`
AdminID int64 `json:"adminid"`
Reason string `json:"reason" form:"reason"`
}
// BatchChallBusStateParam .
type BatchChallBusStateParam struct {
Cids []int64 `form:"cid,split" json:"cid" validate:"required,gt=0"`
AssigneeAdminID int64 `json:"assignee_admin_id"`
AssigneeAdminName string `json:"assignee_admin_name"`
Business int8 `form:"business"`
Role int8 `form:"role"`
BusState int8 `form:"business_state" json:"business_state" validate:"min=0,max=14"`
}
// EventParam is used to parse user request
type EventParam struct {
Cid int64 `json:"cid" form:"cid" validate:"required,min=1"`
AdminID int64 `json:"adminid" form:"adminid"`
AdminName string `json:"admin_name"`
Content string `json:"content" form:"content"`
Attachments string `json:"attachments" form:"attachments"`
Event int8 `json:"event" form:"event" validate:"required,min=1"`
}
// BatchEventParam is used to parse user request
type BatchEventParam struct {
Cids []int64 `json:"cids,split" form:"cids,split" validate:"required,dive,gt=0"`
AdminID int64 `json:"adminid" form:"adminid"`
AdminName string `json:"admin_name"`
Content string `json:"content" form:"content"`
Attachments string `json:"attachments" form:"attachments"`
Event int8 `json:"event" form:"event" validate:"required,min=1"`
}
// ChallExtraParam describe the request params to update challenge extra data
type ChallExtraParam struct {
Cid int64 `json:"cid" validate:"required,min=1"`
AdminID int64 `json:"adminid" validate:"required,min=1"`
AdminName string `json:"admin_name"`
Extra map[string]interface{} `json:"extra" validate:"required"`
}
// ChallExtraParamV3 .
type ChallExtraParamV3 struct {
Cids []int64 `json:"cid" form:"cid,split" validate:"required,dive,gt=0"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Extra string `json:"extra" form:"extra" validate:"required"`
}
// BatchChallExtraParam describe the request params to batch update challenges extra data
type BatchChallExtraParam struct {
Cids []int64 `json:"cid" form:"cid" validate:"required,min=1"`
Business int8 `json:"business" form:"business"`
AdminID int64 `json:"adminid" validate:"required,min=1"`
AdminName string `json:"admin_name"`
Extra map[string]interface{} `json:"extra" form:"extra" validate:"required"`
}
// BusChallsBusStateParam describe the request params to update business state of challenges in business
type BusChallsBusStateParam struct {
Business int8 `json:"business" validate:"required,min=1"`
Oid int64 `json:"oid" validate:"required,min=1"`
AdminID int64 `json:"adminid" validate:"required,min=1"`
BusState int8 `json:"business_state" validate:"min=0,max=14"`
PreBusStates []int8 `json:"pre_business_states" validate:"dive,gt=-1"`
Extra map[string]interface{} `json:"extra"`
}
// ValidComponent will verify the component field is valid
func (e *EventParam) ValidComponent() bool {
if e.Cid > 0 &&
e.AdminID > 0 &&
e.Content != "" &&
e.Event > 0 {
return true
}
return false
}
// ValidComponent will verify the component field is valid
func (be *BatchEventParam) ValidComponent() bool {
if len(be.Cids) > 0 &&
be.AdminID > 0 &&
be.Content != "" &&
be.Event > 0 {
return true
}
return false
}
// MessageParam is the model to send message to end user
type MessageParam struct {
Type string
Source int8
DataType int8
MC string
Title string
Context string
MidList []int64
}
// Query method will serialize all conditions into a url.Values struct
func (mp *MessageParam) Query() (uv url.Values) {
uv = url.Values{}
uv.Set("type", mp.Type)
uv.Set("source", strconv.Itoa(int(mp.Source)))
uv.Set("data_type", strconv.Itoa(int(mp.DataType)))
uv.Set("mc", mp.MC)
uv.Set("title", mp.Title)
uv.Set("context", mp.Context)
uv.Set("mid_list", xstr.JoinInts(mp.MidList))
return uv
}

View File

@@ -0,0 +1,130 @@
package param
// GroupParam describe the group param
type GroupParam struct {
Oid int64 `form:"oid" json:"oid" validate:"required,min=1"`
Business int8 `form:"business" json:"business" validate:"required,min=1"`
Rid int8 `form:"rid" json:"rid"`
State int8 `form:"state" json:"state"`
Tid int64 `form:"tid" json:"tid" validate:"required,min=1"`
Note string `form:"note" json:"note"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
}
// GroupRoleSetParam .
type GroupRoleSetParam struct {
GID []int64 `form:"id,split" json:"id" validate:"required"`
AdminID int64 `json:"admin_id"`
AdminName string `json:"admin_name"`
BID int8 `form:"bid" json:"bid" validate:"required,min=1"`
RID int8 `json:"rid"`
TID int64 `form:"tid" json:"tid" validate:"min=-1"`
Note string `form:"note" json:"note"`
}
// GroupResParam describe the set result request params to a group row
type GroupResParam struct {
Oid int64 `form:"oid" json:"oid" validate:"required,min=1"`
Business int8 `form:"business" json:"business" validate:"required,min=1"`
State int8 `form:"state" json:"state" validate:"required,min=1,max=14"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Reason string `form:"reason" json:"reason"`
ISDisplay bool `form:"is_display" json:"is_display"`
IsMessage bool `form:"is_message" json:"is_message"`
ReviewState int `form:"review_state" json:"review_state"`
Extra *GroupExtra `json:"extra"`
}
// BatchGroupResParam describe the set result request params to a set of groups
type BatchGroupResParam struct {
GID []int64 `form:"id,split" json:"id"`
Oids []int64 `form:"oids,split" json:"oids" validate:"required,gt=0"`
Business int8 `form:"business" json:"business" validate:"required,min=1"`
Role int8 `form:"role" json:"role"`
State int8 `form:"state" json:"state" validate:"required,min=1,max=14"`
AdminID int64 `json:"adminid"`
AdminName string `json:"admin_name"`
Reason string `form:"reason" json:"reason"`
ISDisplay bool `form:"is_display" json:"is_display"`
IsMessage bool `form:"is_message" json:"is_message"`
ReviewState int `form:"review_state" json:"review_state"`
Extra *GroupExtra `json:"extra"`
}
// GroupExtra .
type GroupExtra struct {
ISDisplay bool `form:"is_display" json:"is_display"`
IsMessage bool `form:"is_message" json:"is_message"`
ReviewState int `form:"review_state" json:"review_state"`
}
// GroupListParamV3 .
type GroupListParamV3 struct {
Business int8 `form:"business" validate:"required"`
Oid []string `form:"oid,split"`
Rid []int8 `form:"rid,split"` //role
Fid []int64 `form:"fid,split"` //flow
Eid []int64 `form:"eid,split"`
Mid []int64 `form:"mid,split"` // workflow_business mid
ReportMid []int64 `form:"report_mid,split"` // workflow_chall mid
FirstUserTid []int64 `form:"first_user_tid"`
State []int8 `form:"state,split"`
Tid []int64 `form:"tid,split"`
Round []int64 `form:"round,split"`
TypeID []int64 `form:"typeid,split"`
KWPriority bool `form:"kw_priority"`
KW []string `form:"kw,split"`
KWField []string `form:"kw_field,split"`
Order string `form:"order" default:"lasttime"`
Sort string `form:"sort" default:"desc"`
PN int64 `form:"pn" default:"1"`
PS int64 `form:"ps" default:"50"`
AdminName []string `form:"admin_name,split"`
CTimeFrom string `form:"ctime_from"`
CTimeTo string `form:"ctime_to"`
}
// GroupStateSetParam .
type GroupStateSetParam struct {
ID []int64 `form:"id,split" json:"id" validate:"required"`
Business int8 `form:"business" json:"business" validate:"required"`
State int8 `form:"state" json:"state" validate:"required"`
Tid int64 `form:"tid" json:"tid"` //处理理由tag_id
Rid int8 `form:"rid" json:"rid" validate:"required"`
Reason string `form:"reason" json:"reason"`
IsDisplay bool `form:"is_display" json:"is_display"`
IsMessage bool `form:"is_message" json:"is_message"` //通知举报人
IsMessageUper bool `form:"is_message_uper" json:"is_message_uper"` //通知被举报人(up主)
ReviewState int `form:"review_state" json:"review_state"`
DecreaseMoral int64 `form:"decrease_moral" json:"decrease_moral" validate:"max=0"` //扣节操
DisposeMode int `form:"dispose_mode" json:"dispose_mode" validate:"min=0"` //内容处理方式,批量操作不支持处理内容
BlockDay int64 `form:"block_day" json:"block_day"` //封禁时间
BlockReason int8 `form:"block_reason" json:"block_reason"` //封禁理由
AdminID int64 `json:"admin_id"`
AdminName string `json:"admin_name"`
}
// GroupStatePublicReferee .
type GroupStatePublicReferee struct {
ID []int64 `form:"id,split" json:"id" validate:"required"`
Business int8 `form:"business" json:"business" validate:"required"`
AdminID int64 `json:"admin_id"`
AdminName string `json:"admin_name"`
State int8 `json:"state"`
}
// UpExtraParam describe the request params to batch update group extra data
type UpExtraParam struct {
Gids []int64 `form:"gid,split" json:"gid" validate:"required,min=1"`
Extra string `form:"extra" json:"extra" validate:"required"`
AdminID int64 `json:"admin_id"`
AdminName string `json:"admin_name"`
}
// GroupPendingParam .
type GroupPendingParam struct {
Business int8 `form:"business" json:"business" validate:"required,min=1"`
Rid []int8 `form:"rid,split" json:"rid"`
}

View File

@@ -0,0 +1,44 @@
package param
// AddBusAttrParam describe params of add or update business attrs
type AddBusAttrParam struct {
ID int64 `form:"id" json:"id"`
Bid int64 `form:"bid" json:"bid"`
Name string `form:"name" json:"name"`
DealType int8 `form:"deal_type" json:"deal_type"`
ExpireTime int64 `form:"expire_time" json:"expire_time"`
AssignType int8 `form:"assign_type" json:"assign_type"`
AssignMax int8 `form:"assign_max" json:"assign_max"`
GroupType int8 `form:"group_type" json:"group_type"`
BusinessName string `form:"business_name" json:"business_name"`
}
// BusAttrButtonSwitch .
type BusAttrButtonSwitch struct {
Bid int8 `form:"bid" json:"bid" validate:"required"`
Index uint8 `form:"index" json:"index" validate:"min=0,max=7"`
Switch uint8 `form:"switch" json:"switch" validate:"min=0,max=1"`
}
// BusAttrButtonShortCut .
type BusAttrButtonShortCut struct {
Bid int8 `form:"bid" json:"bid" validate:"required"`
Index int8 `form:"index" json:"index" validate:"min=0,max=7"`
ShortCut string `form:"short_cut" json:"short_cut" validate:"required"`
}
// BusAttrExtAPI .
type BusAttrExtAPI struct {
Bid int8 `form:"bid" json:"bid" validate:"required"`
ExternalAPI string `form:"external_api" json:"external_api"`
}
// BlockInfo .
type BlockInfo struct {
Mid int64 `form:"mid" json:"mid" validate:"required"`
}
// Source .
type Source struct {
Bid int8 `form:"bid" json:"bid" validate:"required"`
}

View File

@@ -0,0 +1,36 @@
package param
// ChallListParam describe challenge list search params of a business
type ChallListParam struct {
Businesses []int8 `form:"businesses" validate:"required,min=1"`
AssignNum []int8 `form:"assign_num" validate:"required,min=0"`
Order string `form:"order" default:"id"`
Sort string `form:"sort" default:"desc"`
PN int `form:"pn"`
PS int `form:"ps"`
R int64 `form:"r" validate:"required"`
}
// ChallHandlingDoneListParam describe params challenge list handling of admin
type ChallHandlingDoneListParam struct {
Businesses int8 `form:"businesses" validate:"required,min=1"`
Order string `form:"order"`
Sort string `form:"sort"`
PN int `form:"pn"`
PS int `form:"ps"`
}
// ChallCountParam describe challenge count in some states of a business
type ChallCountParam struct {
Business int64 `form:"business" validate:"required,min=1"`
States []int64 `form:"states,split" validate:"dive,gt=-1"`
}
// ChallCreatedListParam return challenge list created by an admin
type ChallCreatedListParam struct {
Businesses int8 `form:"businesses" validate:"required,min=1"`
Order string `form:"order"`
Sort string `form:"sort"`
PN int `form:"pn"`
PS int `form:"ps"`
}

View File

@@ -0,0 +1,22 @@
package param
// TargetLogParam log/target/list api param
type TargetLogParam struct {
Target int64 `form:"target" validate:"required,min=1"`
Module int8 `form:"module" validate:"required,min=1"`
}
// LogListParam .
type LogListParam struct {
Business int64 `form:"business" validate:"required"`
AdminIDs []int64 `form:"adminid,split"`
OIDs []int64 `form:"oid,split"`
MIDs []int64 `form:"up_mid,split"`
TypeIDs []int64 `form:"typeids,split"`
CTimeFrom string `form:"optctime_from"`
CTimeTo string `form:"optctime_to"`
Order string `form:"order" default:"opt_ctime"`
Sort string `form:"sort_order" default:"desc"`
PN int64 `form:"page" default:"1"`
PS int64 `form:"ps" default:"50"`
}

View File

@@ -0,0 +1,64 @@
package model
// challenge audit state mapping to platform state map[business][platform_state][flow]chall_state
// business > 100 means feedback flow of business%100
// platform state 0.pending 1.handling 2.done 3.close
const (
_AuditFlow = 0
_FeedbcakFlow = 1
_PlatPending = 0
_PlatHandling = 1
_PlatDone = 2
_PlatClosed = 3
)
var pMetas map[int8]map[int8]map[int64][]int64
func init() {
pMetas = map[int8]map[int8]map[int64][]int64{
// 稿件申诉
2: {
_PlatPending: {
_AuditFlow: {},
_FeedbcakFlow: {0, 1},
},
_PlatHandling: {
_AuditFlow: {},
_FeedbcakFlow: {2, 6},
},
_PlatDone: {
_AuditFlow: {},
_FeedbcakFlow: {3, 4, 5, 7, 8},
},
_PlatClosed: { // 关闭状态合并到已办
_AuditFlow: {},
_FeedbcakFlow: {5, 7, 8},
},
},
//稿件审核
6: {
_PlatPending: {
_AuditFlow: {0},
_FeedbcakFlow: {},
},
_PlatDone: {
_AuditFlow: {1, 2, 3},
_FeedbcakFlow: {},
},
_PlatClosed: {
_AuditFlow: {3},
_FeedbcakFlow: {},
},
},
}
}
// PlatformMetas return platform meta data
func PlatformMetas() map[int8]map[int8]map[int64][]int64 {
return pMetas
}

View File

@@ -0,0 +1,18 @@
package model
const (
// NotifyComRules 社区规则
NotifyComRules = `评论区是公众场所,而非私人场所,具体规范烦请参阅#{《社区规则》}{"http://www.bilibili.com/blackboard/blackroom.html"},良好的社区氛围需要大家一起维护!`
// NotifyComRulesReport NotifyComRulesReport
NotifyComRulesReport = "感谢您对bilibili社区秩序的维护哔哩哔哩 (゜-゜)つロ 干杯~"
// NotifyComUnrelated NotifyComUnrelated
NotifyComUnrelated = "bilibili倡导发送与视频相关的评论希望大家尊重作品尊重UP主。良好的社区氛围需要大家一起维护"
// NotifyComProvoke NotifyComProvoke
NotifyComProvoke = "bilibili倡导平等友善的交流。良好的社区氛围需要大家一起维护"
// NofityComProhibited NofityComProhibited
NofityComProhibited = `请自觉遵守国家相关法律法规及#{《社区规则》}{"http://www.bilibili.com/blackboard/blackroom.html"}bilibili良好的社区氛围需要大家一起维护`
)
const (
ReplyFidManga = 22
)

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 = [
"audit_log.go",
"report.go",
"search.go",
"search2.go",
],
importpath = "go-common/app/admin/main/workflow/model/search",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/main/workflow/model:go_default_library",
"//library/xstr: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,151 @@
package search
import (
"fmt"
"net/url"
"strconv"
"strings"
"go-common/app/admin/main/workflow/model"
"go-common/library/xstr"
)
// AuditLogGroupSearchCond is the common condition model to send challenge search request
type AuditLogGroupSearchCond struct {
Group []string
UNames []string
Uids []int64
Businesses []int64
Type []int64
Oids []int64
Actions []string
CTimeFrom string
CTimeTo string
Int0 []int64
Int0From int64
Int0To int64
Int1 []int64
Int1From int64
Int1To int64
Int2 []int64
Int2From int64
Int2To int64
Str0 string
Str1 string
Str2 string
PN int64
PS int64
Order string
Sort string
}
// AuditLogSearchResult is the model to parse search challenge common result
type AuditLogSearchResult struct {
Code int `json:"code"`
Message string `json:"message"`
TTL int32 `json:"ttl"`
Data struct {
Order string `json:"order"`
Sort string `json:"sort"`
Page model.Page `json:"page"`
Result []struct {
Action string `json:"action"`
Business int64 `json:"business"`
CTime string `json:"ctime"`
ExtraData string `json:"extra_data"`
Str0 string `json:"str_0"`
Str1 string `json:"str_1"`
Str2 string `json:"str_2"`
Oid int64 `json:"oid"`
Type int64 `json:"type"`
UID int64 `json:"uid"`
UName string `json:"uname"`
} `json:"result"`
} `json:"data"`
}
// ArchiveAuditLogExtra archive audit log extra message
type ArchiveAuditLogExtra struct {
Content struct {
ID int64 `json:"id"` //oid
UID int64 `json:"uid"`
UName string `json:"uname"`
Note string `json:"note"`
Content string `json:"content"`
} `json:"content"`
Diff string `json:"diff"`
}
// Query make query for AuditLogGroupSearchCond
func (alsc *AuditLogGroupSearchCond) Query() (uv url.Values) {
uv = url.Values{}
// AppID
uv.Set("appid", _auditLogSrhComID)
// Common
uv.Set("pn", "1")
uv.Set("ps", "20")
uv.Set("order", "ctime")
uv.Set("sort", "desc")
if alsc.PN != 0 {
uv.Set("pn", fmt.Sprintf("%d", alsc.PN))
}
if alsc.PS != 0 {
uv.Set("ps", fmt.Sprintf("%d", alsc.PS))
}
if alsc.Order != "" {
uv.Set("order", alsc.Order)
}
if alsc.Sort != "" {
uv.Set("sort", alsc.Sort)
}
if alsc.Int0From != 0 {
uv.Set("int_0_from", strconv.FormatInt(alsc.Int0From, 10))
}
if alsc.Int0To != 0 {
uv.Set("int_0_to", strconv.FormatInt(alsc.Int0To, 10))
}
if alsc.Int1From != 0 {
uv.Set("int_1_from", strconv.FormatInt(alsc.Int1From, 10))
}
if alsc.Int1To != 0 {
uv.Set("int_1_to", strconv.FormatInt(alsc.Int1To, 10))
}
if alsc.Int2From != 0 {
uv.Set("int_2_from", strconv.FormatInt(alsc.Int2From, 10))
}
if alsc.Int2To != 0 {
uv.Set("int_2_to", strconv.FormatInt(alsc.Int2To, 10))
}
// Group related
uv.Set("group", strings.Join(alsc.Group, ","))
uv.Set("uname", strings.Join(alsc.UNames, ","))
uv.Set("uid", xstr.JoinInts(alsc.Uids))
uv.Set("business", xstr.JoinInts(alsc.Businesses))
uv.Set("type", xstr.JoinInts(alsc.Type))
uv.Set("oid", xstr.JoinInts(alsc.Oids))
uv.Set("action", strings.Join(alsc.Actions, ","))
uv.Set("ctime_from", alsc.CTimeFrom)
uv.Set("ctime_to", alsc.CTimeTo)
uv.Set("int_0", xstr.JoinInts(alsc.Int0))
uv.Set("int_1", xstr.JoinInts(alsc.Int1))
uv.Set("int_2", xstr.JoinInts(alsc.Int2))
uv.Set("str_0", alsc.Str0)
uv.Set("str_1", alsc.Str1)
uv.Set("str_2", alsc.Str2)
return uv
}

View File

@@ -0,0 +1,75 @@
package search
import (
"time"
"go-common/app/admin/main/workflow/model"
)
// business id const
const (
Archive = 3
Workflow = 11
LogAuditAction = "log_audit"
_auditLogSrhComID = "log_audit_group"
//// IndexTypeYear index by year
//IndexTypeYear indexType = "year"
//// IndexTypeMonth index by month
//IndexTypeMonth indexType = "month"
//// IndexTypeWeek index by week
//IndexTypeWeek indexType = "week"
//// IndexTypeDay index by day
//IndexTypeDay indexType = "day"
)
// AuditReportSearchCond .
type AuditReportSearchCond struct {
AppID string `json:"app_id"`
Fields []string `json:"fields"`
IndexTimeType string `json:"index_time_type"`
IndexTimeFrom time.Time `json:"index_time_from"`
IndexTimeEnd time.Time `json:"index_time_end"`
Business int `json:"business"`
UName string `json:"uname"`
UID []int64 `json:"uid"`
Oid []int64 `json:"oid"`
Type []int `json:"type"`
Action string `json:"action"`
CTime string `json:"ctime"`
Order string `json:"order"`
Sort string `json:"sort"`
Int0 []int64 `json:"int_0"`
Int1 []int64 `json:"int_1"`
Int2 []int64 `json:"int_2"`
Str0 string `json:"str_0"`
Str1 string `json:"str_1"`
Str2 string `json:"str_2"`
Group string `json:"group"`
Distinct string `json:"distinct"`
}
// AuditLogSearchCommonResult .
type AuditLogSearchCommonResult struct {
Page *model.Page `json:"page"`
Result []*ReportLog `json:"result"`
}
// ReportLog .
type ReportLog struct {
Action string `json:"action"`
Business int64 `json:"business"`
CTime string `json:"ctime"`
ExtraData string `json:"extra_data"`
Str0 string `json:"str_0"`
Str1 string `json:"str_1"`
Str2 string `json:"str_2"`
Int0 int64 `json:"int_0"`
Int1 int64 `json:"int_1"`
Int2 int64 `json:"int_2"`
Int3 int64 `json:"int_3"`
Oid int64 `json:"oid"`
Type int64 `json:"type"`
UID int64 `json:"uid"`
UName string `json:"uname"`
}

View File

@@ -0,0 +1,92 @@
package search
import (
"go-common/app/admin/main/workflow/model"
)
// ChallSearchCond is the condition model to send challenge search request
type ChallSearchCond struct {
// Using int64 directly
Cids []int64
Gids []int64
Mids []int64
Tids []int64
TagRounds []int64
States []int64
Keyword string
CTimeFrom string
CTimeTo string
PN int64
PS int64
Order string
Sort string
}
// FormatState .
func (cc *ChallSearchCond) FormatState() {
for _, st := range cc.States {
if st == model.QueueStateBefore {
cc.States = append(cc.States, model.QueueState)
}
}
}
// ArcSearchResult is the model to parse search archive appeal result
type ArcSearchResult struct {
Code int32 `json:"code"`
Message string `json:"message"`
TTL int32 `json:"ttl"`
Data struct {
Page *model.Page `json:"page"`
Result []GroupSearchCommonData `json:"result"`
} `json:"data"`
}
// ChallSearchResult is the model to parse search challenge result
type ChallSearchResult struct {
Code int32 `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"`
Gid int64 `json:"gid"`
Mid int64 `json:"mid"`
Tid int64 `json:"tid"`
CTime string `json:"ctime"`
} `json:"result"`
} `json:"data"`
}
// ChallListPage is the model for challenge list result
type ChallListPage struct {
Items []*model.Chall `json:"items"`
TotalCount int32 `json:"total_count"`
PN int32 `json:"pn"`
PS int32 `json:"ps"`
}
// ChallListPageCommon model for challenge/list2 result
type ChallListPageCommon struct {
Items []*model.Chall `json:"items"`
Page *model.Page `json:"page"`
}
// ChallCount is the model for challenge count result
type ChallCount struct {
TotalCount int64 `json:"total_count"`
BusinessCount map[int8]int64 `json:"business_count"`
}

View File

@@ -0,0 +1,148 @@
package search
import (
"go-common/app/admin/main/workflow/model"
)
// search appid
const (
GroupSrhComID = "workflow_group_common"
ChallSrhComID = "workflow_chall_common"
)
// 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
FirstUserTid []int64
ReportMID []int64 // report_mid
AuthorMID []int64 // mid
KWPriority bool
KW []string
KWFields []string
CTimeFrom string
CTimeTo string
PN int64
PS int64
Order string
Sort string
}
// GroupSearchCommonData .
type GroupSearchCommonData struct {
ID int64 `json:"id"`
Oid int64 `json:"oid"`
Mid int64 `json:"mid"`
TypeID int64 `json:"typeid"`
Eid int64 `json:"eid"`
FirstUserTid int64 `json:"first_user_tid"`
}
// GroupSearchCommonResp .
type GroupSearchCommonResp struct {
Page *model.Page `json:"page"`
Result []*GroupSearchCommonData `json:"result"`
}
// ChallSearchCommonCond is the common condition model to send challenge search request
type ChallSearchCommonCond struct {
// Using int64 directly
Fields []string
Business int8
IDs []int64
Gids []int64
Oids []string
Tids []int64
Mids []int64
ObjectMids []int64
Rounds []int64
TypeIDs []int64
AssigneeAdminIDs []int64
AssigneeAdminNames []string
AdminIDs []int64
States []int64
BusinessStates []int64
CTimeFrom string
CTimeTo string
KW []string
KWFields []string
Distinct []string
PN int
PS int
Order string
Sort string
}
// FormatState transform add state in queue into search cond
func (csc *ChallSearchCommonCond) FormatState() {
for _, busState := range csc.BusinessStates {
if busState == model.QueueBusinessStateBefore {
csc.BusinessStates = append(csc.BusinessStates, model.QueueBusinessState)
}
}
for _, st := range csc.States {
if st == model.QueueStateBefore {
csc.States = append(csc.States, model.QueueState)
}
}
}
// ChallSearchCommonData .
type ChallSearchCommonData struct {
ID int64 `json:"id"`
Oid int64 `json:"oid"`
Mid int64 `json:"mid"`
Gid int64 `json:"gid"`
Tid int64 `json:"tid"`
CountTid int64 `json:"count_tid"`
State interface{} `json:"state"` //兼容 int string
Title string `json:"title"`
Business int8 `json:"business"`
TypeID int64 `json:"typeid"`
CTime string `json:"ctime"`
}
// ChallSearchCommonResp .
type ChallSearchCommonResp struct {
Page *model.Page `json:"page"`
Result []*ChallSearchCommonData `json:"result"`
}
// ChallReleaseUpSearchCond .
type ChallReleaseUpSearchCond struct {
Cids []int64 `json:"id"`
AssigneeAdminID int64 `json:"assignee_adminid"`
BusinessState int64 `json:"business_state"`
}
// ChallReleaseUpSearchCondItem .
type ChallReleaseUpSearchCondItem struct {
Cid int64 `json:"id"`
AssigneeAdminID int64 `json:"assignee_adminid"`
BusinessState int64 `json:"business_state"`
}
// ChallUpSearchResult .
type ChallUpSearchResult struct {
Code int32 `json:"code"`
Message string `json:"message"`
TTL int32 `json:"ttl"`
}

View File

@@ -0,0 +1,39 @@
package model
// consts for state
const (
// Group and Challenge State field
Pending = int8(0) // 未处理
Effective = int8(1) // 有效
Invalid = int8(2) // 无效
RoleShift = int8(3) // 流转
Deleted = int8(9) // 已删除
PublicReferee = int8(10) // 移交众裁
// dispatch_state offset bit
AuditorStateOffset = 0
CSStateOffset = 4
QueueState = 15 // 队列中审核状态
QueueBusinessState = 15 // 队列中客服状态
QueueStateBefore = 0 // 默认审核状态
QueueBusinessStateBefore = 1 // 默认客服状态
// 反馈状态
FeedbackReplyNotRead = 6 // 已回复未读
)
// platform state
const (
PlatformStateHandling = iota + 1
PlatformStateDone
PlatformStateClosed
)
// business round
const (
AuditRoundMin = 1
AuditRoundMax = 10
FeedbackRound = 11
)

View File

@@ -0,0 +1,29 @@
package model
// TagMeta tag from manager/list
type TagMeta struct {
ID int64 `json:"id"`
Bid int8 `json:"bid"`
Tid int64 `json:"tid"`
TagID int64 `json:"tag_id"` //map to old workflow tag id
TName string `json:"tname"`
RID int8 `json:"rid"`
RName string `json:"rname"`
Name string `json:"name"`
Weight int64 `json:"weight"`
State int8 `json:"state"`
UID int64 `json:"uid"`
UName string `json:"uname"`
Description string `json:"description"`
CTime int `json:"ctime"`
MTime int `json:"mtime"`
}
// TagListResult .
type TagListResult struct {
*CommonResponse
Data struct {
Tags []*TagMeta `json:"data"`
Page `json:"page"`
} `json:"data"`
}

View File

@@ -0,0 +1,53 @@
package model
import (
xtime "go-common/library/time"
)
// Log Module Field defination
const (
// report business = 12
WLogModuleChallenge = 1
WLogModuleTag = 2
WLogModuleControl = 3
WLogModuleGroup = 4
WLogModuleReply = 5 // modify business_state
WLogModulePublicReferee = 6
WLogModuleRoleShift = 7 // 流转 (同一个执行方)
WLogModuleDispose = 8 // content dispose 操作内容对象
WLogModuleAddMoral = 20 // 扣节操
WLogModuleBlock = 21 // 封禁
// report business = 11
FeedBackTypeNotifyUserReceived = 2
FeedBackTypeNotifyUserDisposed = 3
FeedBackTypeReply = 5
)
// LogSlice is a Log slice struct
type LogSlice []*WLog
// Log model is the universal model
// Will record any management actions
type WLog struct {
Lid int32 `json:"lid"`
AdminID int64 `json:"adminid"`
Admin string `json:"admin"`
Oid int64 `json:"oid"`
Business int8 `json:"business"`
Target int64 `json:"target"`
Module int8 `json:"module"`
Remark string `json:"remark"`
Note string `json:"note"`
CTime xtime.Time `json:"ctime"`
MTime xtime.Time `json:"mtime"`
Meta interface{} `json:"meta"`
ReportCTime string `json:"report_ctime"`
Mid int64 `json:"mid"`
TypeID int64 `json:"type_id"`
TimeConsume int64 `json:"time_consume"`
OpType string `json:"op_type"`
PreRid string `json:"pre_rid"`
Param interface{} `json:"param"`
Mids []int64 `json:"mids"` //对被举报人的批量操作
}

View File

@@ -0,0 +1,79 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"activity_test.go",
"business_test.go",
"challenge_test.go",
"platform_test.go",
"service_test.go",
"wlog_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/admin/main/workflow/model:go_default_library",
"//app/admin/main/workflow/model/param:go_default_library",
"//app/admin/main/workflow/model/search:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"activity.go",
"business.go",
"callback.go",
"challenge.go",
"event.go",
"group.go",
"meta.go",
"platform.go",
"service.go",
"tag.go",
"task.go",
"wlog.go",
],
importpath = "go-common/app/admin/main/workflow/service",
tags = ["automanaged"],
deps = [
"//app/admin/main/credit/model/blocked:go_default_library",
"//app/admin/main/workflow/dao:go_default_library",
"//app/admin/main/workflow/model:go_default_library",
"//app/admin/main/workflow/model/param:go_default_library",
"//app/admin/main/workflow/model/search:go_default_library",
"//app/job/main/credit/model:go_default_library",
"//library/conf/paladin:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/queue/databus/report:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/jinzhu/gorm:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/golang.org/x/sync/errgroup:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,54 @@
package service
import (
"context"
"sort"
"go-common/app/admin/main/workflow/model"
"go-common/library/log"
)
// ActivityList will list activities by given conditions
func (s *Service) ActivityList(c context.Context, business int8, cid int64) (acts *model.Activities, err error) {
var (
logs []*model.WLog
events map[int64]*model.Event
uids []int64
uNames map[int64]string
)
if logs, err = s.AllAuditLog(c, cid, []int{model.WLogModuleChallenge}); err != nil {
log.Error("s.AllAuditLog(%d) error(%v)", cid, err)
}
log.Info("audit log cid(%d) logs(%+v)", cid, logs)
if events, err = s.dao.EventsByCid(c, cid); err != nil {
log.Error("Failed to s.dao.EventsByCid(%d): %v", cid, err)
return
}
acts = new(model.Activities)
acts.Events = make([]*model.Event, 0, len(events))
acts.Logs = logs
for _, e := range events {
acts.Events = append(acts.Events, e)
uids = append(uids, e.AdminID)
}
if uNames, err = s.dao.BatchUNameByUID(c, uids); err != nil {
log.Error("s.dao.SearchUNameByUid(%v) error(%v)", uids, err)
err = nil
} else {
for i := range acts.Events {
acts.Events[i].Admin = uNames[acts.Events[i].AdminID]
}
}
//sort.Sort(model.LogSlice(acts.Logs))
//sort.Sort(model.EventSlice(acts.Events))
sort.Slice(acts.Logs, func(i, j int) bool {
return acts.Logs[i].CTime < acts.Logs[j].CTime
})
sort.Slice(acts.Events, func(i, j int) bool {
return acts.Events[i].CTime < acts.Events[j].CTime
})
return
}

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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