Create & Init Project...

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

View File

@@ -0,0 +1,21 @@
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//app/interface/main/feedback/cmd:all-srcs",
"//app/interface/main/feedback/conf:all-srcs",
"//app/interface/main/feedback/dao:all-srcs",
"//app/interface/main/feedback/http:all-srcs",
"//app/interface/main/feedback/model:all-srcs",
"//app/interface/main/feedback/service:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,234 @@
#### feedback 反馈系统
###### Version 4.2.1 - 2018.12.21
##### Features
> 1.H5用户反馈接口兼容buvid获取方式
###### Version 4.2.0 - 2018.11.28
##### Features
> 1.去掉net/ip的依赖
###### Version 4.1.8 - 2018.09.28
##### Features
> 1.新增H5专用用户反馈接口(支持多图片上传)
###### Version 4.1.7 - 2018.09.28
##### Features
> 1.新增H5专用tag接口
###### Version 4.1.6 - 2018.09.25
##### Features
> 1.为移动端新反馈途径增加tag
###### Version 4.1.5 - 2018.09.21
##### Features
> 1.新增云视听和创作中心反馈
###### Version 4.1.4 - 2018.09.20
##### Bugfix
> 1.修复播放器投屏兼容逻辑bug
###### Version 4.1.3 - 2018.08.27
##### Features
> 1.播放器投屏新增tagID并针对其入库数据status做特殊处理
###### Version 4.1.2 - 2018.08.30
##### Features
> 1.替换RemoteIP方法
###### Version 4.1.1 - 2018.08.23
##### Features
> 1.增加和完善Dao层UT测试用例
###### Version 4.1.0 - 2018.08.08
##### Features
> 1.修改bm
> 2.修改identiry
###### Version 4.0.6 - 2018.07.13
##### Features
> 1.删除bfs方法中的多余Fock
###### Version 4.0.5 - 2018.06.26
##### Features
> 1.补充完善粉版直播做兼容逻辑
###### Version 4.0.4 - 2018.06.26
##### Features
> 1.对粉版直播做兼容逻辑每条反馈都单独建立session
###### Version 4.0.3 - 2018.06.22
##### Features
> 1.修改粉版直播tag文案
###### Version 4.0.2 - 2018.06.08
##### Features
> 1.修改粉版直播tag顺序
###### Version 4.0.1 - 2018.05.14
##### Features
> 1.增加粉版直播逻辑
> 1.修改reply接口对buvid的判断
###### Version 4.0.0 - 2018.04.26
##### Features
> 1.项目整体迁目录
> 2.切bm
> 3.新增文件上传接口(content-type: octet-stream)
###### Version 3.8.0 - 2018.03.07
##### Features & Bug
> 1.ugc/reply增加mid参数和校验
###### Version 3.7.0 - 2017.12.15
##### Features & Bug
> 1.对接创作姬,为创作姬增加入口字段
> 2.修复了初次创建session时如果content或imgURL为空导致logURL无法写入的问题
###### Version 3.6.1 - 2017.11.27
##### Bug
> 1.恢复ecode(用到了具体错误码信息)
> 2.删除main的syscall.SIGSTOP
###### Version 3.6.0 - 2017.11.20
##### Features
> 1.添加播放器检测上报接口
> 2.删除无用代码(比如ecode)
###### Version 3.5.4
> 1.修复线上Tag下发
###### Version 3.5.3
> 1.调整tag位置
###### Version 3.5.2
> 1.增加session事务处理
###### Version 3.5.1
> 1.支持管理海外版搜索
###### Version 3.5.0
> 1.feedback合入Kratos
###### Version 3.4.4
> 1.兼容IOS播放器上报传参错误
###### Version 3.4.3
> 1.创作中心接入文章反馈
##### Version 3.4.2
> 1.修复seesion bug
##### Version 3.4.1
> 1.修复第一次番剧反馈多张图片bug
##### Version 3.4.0
> 1.更新vendor
> 2.兼容PGC反馈数据展示
##### Version 3.3.3
> 1.修复用户回复状态
##### Version 3.3.2
> 1.测试ci打tag
##### Version 3.3.1
> 1.修复emoji过滤
##### Version 3.3.0
> 1.修复内容过滤正则
> 2.创作中心子tag显示
##### Version 3.2.2
> 1.更新vendor
##### Version 3.2.0
> 1.接入新的配置中心
> 2.过滤content内容
> 3.去掉回复状态更新
##### Version 3.1.1
> 1.拆分mobi-feedback
##### Version 3.1.0
> 1.区分播放器与移动端反馈平台会话
> 2.修复tag显示
##### Version 3.0.2
> 1.兼容管理后台多tag
##### Version 3.0.1
> 1.过滤移动端session
##### Version 3.0.0
> 1.增加ugc反馈平台
##### Version 2.3.0
> 1.视频详情页提交新增会话
##### Version 2.2.0
> 1.升级vendor
> 2.返回客户端tag列表
> 3.增加tag字段写入
##### Version 2.1.3
> 1.升级vendor
##### Version 2.1.2
> 1.升级vendor
##### Version 2.1.1
> 1.升级vendor
##### Version 2.1.0
> 1.配置支持从环境变量获取配置
> 2.更新go-common与go-business依赖
##### Version 2.0.0
> 1.配置中心接入
> 2.go-business的错误码替换
> 3.更新go-common与go-business依赖
##### Version 1.0.4
> 1.获取IP地址修复
> 2.govendor代替glide
##### Version 1.0.3
> 1.限制用户发送内容的字符长度,配置文件可配
> 2.FeedbackSsnNotExist的错误过滤不打印日志
##### Version 1.0.2
> 1.更新会话去掉mid
> 2.会话创建更新laster_time
##### Version 1.0.1
> 1.登录用户的会话逻辑
> 2.登录用户的拉去反馈逻辑
##### Version 1.0.0
> 1.增加反馈
> 2.拉取反馈列表
> 3.上传文件

View File

@@ -0,0 +1,11 @@
# Owner
peiyifei
liweijia
# Author
xiequan
zhangxin
# Reviewer
peiyifei
haoguanwei

View File

@@ -0,0 +1,18 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- liweijia
- peiyifei
- xiequan
- zhangxin
labels:
- interface
- interface/main/feedback
- main
options:
no_parent_owners: true
reviewers:
- haoguanwei
- peiyifei
- xiequan
- zhangxin

View File

@@ -0,0 +1,13 @@
#### feedback 反馈系统
##### 项目简介
> 1.反馈系统接口
##### 编译环境
> 请只用golang v1.8.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common
##### 特别说明
> 1.model目录可能会被其他项目引用请谨慎请改并通知各方。

View File

@@ -0,0 +1,46 @@
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 = [
"convey-test.toml",
"feedback-test.toml",
],
importpath = "go-common/app/interface/main/feedback/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/feedback/conf:go_default_library",
"//app/interface/main/feedback/http:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,144 @@
# This is a TOML document. Boom.
version = "1.0.0"
user = "nobody"
pid = "/tmp/feedback.pid"
dir = "./"
perf = "0.0.0.0:6170"
checkFile = "/data/www/feedback.html"
family = "feedback"
trace = true
debug = false
env = "test"
[tracer]
family = "feedback"
proto = "unixgram"
addr = "172.16.33.46:5140"
[xlog]
dir = "/data/log/feedback/"
[xlog.agent]
family = "feedback"
taskID ="000069"
proto = "unixgram"
addr = "/var/run/lancer/collector.sock"
chan = 10240
timeout = "500ms"
[app]
key = "e7482d29be4a95b8"
secret = "9e803791cdef756e75faee68e12b7442"
[multiHttp]
[multiHttp.outer]
addrs = ["0.0.0.0:6171"]
maxListen = 1000
[multiHttp.local]
addrs = ["0.0.0.0:6172"]
maxListen = 1000
[ecode]
domain = "172.16.33.248:6401"
all = "1h"
diff = "10m"
[ecode.clientconfig]
key = "test"
secret = "e6c4c252dc7e3d8a90805eecd7c73396"
dial = "2s"
timeout = "2s"
keepAlive = "10s"
[ecode.clientconfig.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[ecode.app]
key = "test"
secret = "e6c4c252dc7e3d8a90805eecd7c73396"
[mysql]
[mysql.master]
addr = "172.16.33.205:3308"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_feedback?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
active = 5
idle = 2
idleTimeout ="4h"
queryTimeout = "100ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[mysql.master.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify]
whiteAccessKey = ""
whiteMid = 0
[identify.app]
key = "0dc647722719f2ea"
secret = "a3769b553680da10b40994265fb48d20"
[identify.memcache]
name = "go-business/identify"
proto = "tcp"
addr = "172.16.33.54:11211"
active = 5
idle = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "80s"
[identify.host]
auth = "http://passport.bilibili.com"
secret = "http://open.bilibili.com"
[identify.httpClient]
key = "0dc647722719f2ea"
secret = "a3769b553680da10b40994265fb48d20"
dial = "30ms"
timeout = "100ms"
keepAlive = "60s"
[identify.httpClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[identify.httpClient.url]
"http://passport.bilibili.co/intranet/auth/tokenInfo" = {timeout = "100ms"}
"http://passport.bilibili.co/intranet/auth/cookieInfo" = {timeout = "100ms"}
"http://open.bilibili.co/api/getsecret" = {timeout = "500ms"}
[bfs]
addr = "http://bfs.bilibili.co"
bucket = "test"
key = "221bce6492eba70f"
secret = "6eb80603e85842542f9736eb13b7e3"
# 上传文件大小限制为1M
maxFileSize = 1048576
[feedback]
# replys page size
replysNum = 50
# 发送内容长度限制为400字符
maxContentSize = 400
[locationRPC]
pullInterval = "10s"
[locationRPC.client]
timeout = "1s"
timer = 10
[locationRPC.client.breaker]
window ="3s"
sleep ="100ms"
bucket = 10
ratio = 0.5
request = 100
[locationRPC.zookeeper]
root = "/microservice/location-service/"
addrs = ["172.18.33.172:2181"]
timeout = "30s"

View File

@@ -0,0 +1,48 @@
env = "test"
[xlog]
dir = "/data/log/feedback/"
[app]
key = "e7482d29be4a95b8"
secret = "9e803791cdef756e75faee68e12b7442"
[bm]
[bm.outer]
addr = "0.0.0.0:6171"
timeout = "1s"
[bm.local]
addr = "0.0.0.0:6172"
timeout = "1s"
[mysql]
[mysql.master]
addr = "172.16.33.205:3308"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_feedback?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
active = 5
idle = 2
idleTimeout ="4h"
queryTimeout = "100ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[mysql.master.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[bfs]
addr = "http://bfs.bilibili.co"
bucket = "test"
key = "221bce6492eba70f"
secret = "6eb80603e85842542f9736eb13b7e3"
# 上传文件大小限制为1M
maxFileSize = 1048576
[feedback]
# replys page size
replysNum = 50
# 发送内容长度限制为400字符
maxContentSize = 400
imgLimit = 4

View File

@@ -0,0 +1,50 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/interface/main/feedback/conf"
"go-common/app/interface/main/feedback/http"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
log.Error("conf.Init() error(%v)", err)
panic(err)
}
// init log
log.Init(conf.Conf.XLog)
defer log.Close()
log.Info("feedback start")
// init trace
trace.Init(conf.Conf.Tracer)
defer trace.Close()
// ecode init
ecode.Init(conf.Conf.Ecode)
// service init
http.Init(conf.Conf)
// init pprof conf.Conf.Perf
// init signal
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("feedback get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("feedback exit")
return
case syscall.SIGHUP:
// TODO reload
default:
return
}
}
}

View File

@@ -0,0 +1,38 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/interface/main/feedback/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode/tip:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/rpc:go_default_library",
"//library/net/trace:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,123 @@
package conf
import (
"errors"
"flag"
"go-common/library/conf"
"go-common/library/database/sql"
ecode "go-common/library/ecode/tip"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/rpc"
"go-common/library/net/trace"
"github.com/BurntSushi/toml"
)
var (
confPath string
// Conf init Config struct.
Conf = &Config{}
client *conf.Client
)
// Config struct.
type Config struct {
// Env
Env string
// tracer
Tracer *trace.Config
// xlog
XLog *log.Config
// http
BM *HTTPServers
// ecode
Ecode *ecode.Config
// db
MySQL *MySQL
// Bfs
Bfs *Bfs
// Feedback
Feedback *Feedback
// rpc
LocationRPC *rpc.ClientConfig
}
// HTTPServers Http Servers
type HTTPServers struct {
Outer *bm.ServerConfig
Local *bm.ServerConfig
}
// MySQL struct.
type MySQL struct {
Master *sql.Config
}
// Bfs struct.
type Bfs struct {
Addr string
Bucket string
Key string
Secret string
MaxFileSize int
}
// Feedback struct.
type Feedback struct {
ReplysNum int
MaxContentSize int
ImgLimit int
}
func init() {
flag.StringVar(&confPath, "conf", "", "config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}

View File

@@ -0,0 +1,67 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"bfs_test.go",
"dao_test.go",
"playerCheck_test.go",
"reply_test.go",
"session_test.go",
"tag_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/feedback/conf:go_default_library",
"//app/interface/main/feedback/model:go_default_library",
"//library/database/sql:go_default_library",
"//vendor/github.com/bouk/monkey:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
"//vendor/gopkg.in/h2non/gock.v1:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"bfs.go",
"dao.go",
"playerCheck.go",
"reply.go",
"session.go",
"tag.go",
],
importpath = "go-common/app/interface/main/feedback/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/feedback/conf:go_default_library",
"//app/interface/main/feedback/model:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/time: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,82 @@
package dao
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"errors"
"fmt"
"hash"
"io"
"net/http"
"strconv"
"go-common/library/log"
)
const (
_uploadURL = "/%s/%s"
_template = "%s\n%s\n%s\n%d\n"
_method = "PUT"
)
var (
errUpload = errors.New("Upload failed")
)
// Upload upload picture or log file to bfs
func (d *Dao) Upload(c context.Context, fileName, fileType string, expire int64, body io.Reader) (location string, err error) {
var (
url string
req *http.Request
resp *http.Response
header http.Header
code string
)
bfsConf := d.c.Bfs
url = fmt.Sprintf(bfsConf.Addr+_uploadURL, bfsConf.Bucket, fileName)
if req, err = http.NewRequest(_method, url, body); err != nil {
log.Error("http.NewRequest() Upload(%v) error(%v)", url, err)
return
}
authorization := authorize(bfsConf.Key, bfsConf.Secret, _method, bfsConf.Bucket, fileName, expire)
req.Header.Set("Host", bfsConf.Addr)
req.Header.Add("Date", fmt.Sprint(expire))
req.Header.Add("Authorization", authorization)
req.Header.Add("Content-Type", fileType)
resp, err = d.bfsClient.Do(req)
if err != nil {
log.Error("httpClient.Do(%s) error(%v)", url, err)
return
}
if resp.StatusCode != http.StatusOK {
log.Error("Upload url(%s) http.statuscode:%d", url, resp.StatusCode)
err = errUpload
return
}
header = resp.Header
code = header.Get("Code")
if code != strconv.Itoa(http.StatusOK) {
log.Error("Upload url(%s) code:%s", url, code)
err = errUpload
return
}
location = header.Get("Location")
return
}
// authorize returns authorization for upload file to bfs
func authorize(key, secret, method, bucket, file string, expire int64) (authorization string) {
var (
content string
mac hash.Hash
signature string
)
content = fmt.Sprintf(_template, method, bucket, file, expire)
mac = hmac.New(sha1.New, []byte(secret))
mac.Write([]byte(content))
signature = base64.StdEncoding.EncodeToString(mac.Sum(nil))
authorization = fmt.Sprintf("%s:%s:%d", key, signature, expire)
return
}

View File

@@ -0,0 +1,84 @@
package dao
import (
"context"
"fmt"
"io"
"net/http"
"reflect"
"testing"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoUpload(t *testing.T) {
convey.Convey("Upload", t, func(ctx convey.C) {
var (
c = context.Background()
fileName = ""
fileType = ""
expire = int64(0)
body io.Reader
)
bfsConf := d.c.Bfs
url := fmt.Sprintf(bfsConf.Addr+_uploadURL, bfsConf.Bucket, fileName)
ctx.Convey("When everything is correct", func(ctx convey.C) {
httpMock("PUT", url).Reply(200).SetHeaders(map[string]string{
"Code": "200",
"Location": "SomePlace",
})
location, err := d.Upload(c, fileName, fileType, expire, body)
ctx.Convey("Then err should be nil.location should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(location, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.bfsClient.Do gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.bfsClient), "Do",
func(_ *http.Client, _ *http.Request) (*http.Response, error) {
return nil, fmt.Errorf("d.bfsClient.Do Error")
})
defer guard.Unpatch()
_, err := d.Upload(c, fileName, fileType, expire, body)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
ctx.Convey("When http request status != 200", func(ctx convey.C) {
httpMock("PUT", url).Reply(404)
_, err := d.Upload(c, fileName, fileType, expire, body)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
ctx.Convey("When http request Code in header != 200", func(ctx convey.C) {
httpMock("PUT", url).Reply(404).SetHeaders(map[string]string{
"Code": "404",
"Location": "SomePlace",
})
_, err := d.Upload(c, fileName, fileType, expire, body)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoauthorize(t *testing.T) {
var (
key = "1234"
secret = "2345"
method = ""
bucket = ""
file = ""
expire = int64(0)
)
convey.Convey("authorize", t, func(ctx convey.C) {
authorization := authorize(key, secret, method, bucket, file, expire)
ctx.Convey("Then authorization should not be nil.", func(ctx convey.C) {
ctx.So(authorization, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,72 @@
package dao
import (
"context"
"go-common/app/interface/main/feedback/conf"
"go-common/library/database/sql"
"net/http"
)
// Dao is feedback dao.
type Dao struct {
// conf
c *conf.Config
// db
dbMs *sql.DB
//db stmt
// session
selSsn *sql.Stmt
selSsnByMid *sql.Stmt
selTagID *sql.Stmt
inSsn *sql.Stmt
inSsnTag *sql.Stmt
upSsn *sql.Stmt
upSsnMtime *sql.Stmt
upSsnSta *sql.Stmt
selSSnID *sql.Stmt
selSSnCntByMid *sql.Stmt
// reply
selReply *sql.Stmt
selReplyByMid *sql.Stmt
selReplyBySid *sql.Stmt
inReply *sql.Stmt
// tag
selTagBySsnID *sql.Stmt
// bfs
bfsClient *http.Client
}
// New dao.
func New(c *conf.Config) (d *Dao) {
d = &Dao{
c: c,
dbMs: sql.NewMySQL(c.MySQL.Master),
}
// session
d.selSsn = d.dbMs.Prepared(_selSsn)
d.selSsnByMid = d.dbMs.Prepared(_selSsnByMid)
d.inSsn = d.dbMs.Prepared(_inSsn)
d.inSsnTag = d.dbMs.Prepared(_inSsnTag)
d.upSsn = d.dbMs.Prepared(_upSsn)
d.upSsnMtime = d.dbMs.Prepared(_upSsnMtime)
d.upSsnSta = d.dbMs.Prepared(_upSsnState)
d.selSSnID = d.dbMs.Prepared(_selSSnID)
d.selSSnCntByMid = d.dbMs.Prepared(_selSSnCntByMid)
// reply
d.selReply = d.dbMs.Prepared(_selReply)
d.selReplyByMid = d.dbMs.Prepared(_selReplyByMid)
d.inReply = d.dbMs.Prepared(_inReply)
d.selTagID = d.dbMs.Prepared(_selTagID)
d.selReplyBySid = d.dbMs.Prepared(_selReplyBySid)
d.selTagBySsnID = d.dbMs.Prepared(_selTagBySsnID)
// init bfs http client
d.bfsClient = http.DefaultClient
return
}
// BeginTran begin tran.
func (d *Dao) BeginTran(c context.Context) (tx *sql.Tx, err error) {
tx, err = d.dbMs.Begin(c)
return
}

View File

@@ -0,0 +1,45 @@
package dao
import (
"flag"
"go-common/app/interface/main/feedback/conf"
"os"
"strings"
"testing"
"gopkg.in/h2non/gock.v1"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.app-svr.feedback")
flag.Set("conf_token", "a0b043ef740cfe08097a2353f6e086f5")
flag.Set("tree_id", "2179")
flag.Set("conf_version", "docker-1")
flag.Set("deploy_env", "uat")
flag.Set("conf_host", "config.bilibili.co")
flag.Set("conf_path", "/tmp")
flag.Set("region", "sh")
flag.Set("zone", "sh001")
} else {
flag.Set("conf", "../cmd/convey-test.toml")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
//d.bfsClient.SetTransport(gock.DefaultTransport)
m.Run()
os.Exit(0)
}
func httpMock(method, url string) *gock.Request {
r := gock.New(url)
r.Method = strings.ToUpper(method)
return r
}

View File

@@ -0,0 +1,28 @@
package dao
import (
"context"
"time"
"go-common/library/log"
xtime "go-common/library/time"
)
const (
_inPlayCheckSQL = `INSERT INTO campus_network_check_record (platform,check_time,isp,region,school,mid,ip,ip_change_times,cdn,connect_speed,io_speed,aid,ctime,mtime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
)
// InPlayCheck insert player check info into db
func (d *Dao) InPlayCheck(c context.Context, platform, isp, ipChangeTimes int, mid, checkTime, aid, connectSpeed, ioSpeed int64, region, school, ip, cdn string) (rows int64, err error) {
var (
now = time.Now()
checkTime2 = xtime.Time(checkTime).Time()
)
res, err := d.dbMs.Exec(c, _inPlayCheckSQL, platform, checkTime2, isp, region, school, mid, ip, ipChangeTimes, cdn, connectSpeed, ioSpeed, aid, now, now)
if err != nil {
log.Error("d.InPlayCheck.Exec() error(%v)", err)
return
}
rows, err = res.RowsAffected()
return
}

View File

@@ -0,0 +1,52 @@
package dao
import (
"context"
xsql "database/sql"
"fmt"
"reflect"
"testing"
"go-common/library/database/sql"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoInPlayCheck(t *testing.T) {
convey.Convey("InPlayCheck", t, func(ctx convey.C) {
var (
c = context.TODO()
platform = int(0)
isp = int(0)
ipChangeTimes = int(0)
mid = int64(0)
checkTime = int64(0)
aid = int64(0)
connectSpeed = int64(0)
ioSpeed = int64(0)
region = ""
school = ""
ip = ""
cdn = ""
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
rows, err := d.InPlayCheck(c, platform, isp, ipChangeTimes, mid, checkTime, aid, connectSpeed, ioSpeed, region, school, ip, cdn)
ctx.Convey("Then err should be nil.rows should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(rows, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Exec",
func(_ *sql.DB, _ context.Context, _ string, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.dbMs.Exec Error")
})
defer guard.Unpatch()
_, err := d.InPlayCheck(c, platform, isp, ipChangeTimes, mid, checkTime, aid, connectSpeed, ioSpeed, region, school, ip, cdn)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,96 @@
package dao
import (
"context"
"go-common/app/interface/main/feedback/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_inReply = "INSERT INTO reply (session_id,reply_id,content,img_url,log_url,ctime,mtime) VALUES (?,?,?,?,?,?,?)"
_selReply = "SELECT reply_id,type,content,img_url,log_url,ctime FROM reply WHERE session_id=? ORDER BY id DESC LIMIT ?,?"
_selReplyByMid = "SELECT r.reply_id,r.type,r.content,r.img_url,r.log_url,r.ctime FROM reply r INNER JOIN session s ON r.session_id=s.id WHERE s.mid=? ORDER BY r.id DESC LIMIT ?,?"
_selReplyBySid = "SELECT r.reply_id,r.type,r.content,r.img_url,r.log_url,r.ctime FROM reply r INNER JOIN session s ON r.session_id=s.id WHERE r.session_id=? AND s.mid=? ORDER BY r.id"
)
// TxAddReply implements add a new reply record
func (d *Dao) TxAddReply(tx *sql.Tx, r *model.Reply) (id int64, err error) {
res, err := tx.Exec(_inReply, r.SessionID, r.ReplyID, r.Content, r.ImgURL, r.LogURL, r.CTime, r.MTime)
if err != nil {
log.Error("AddReply tx.Exec() error(%v)", err)
return
}
return res.LastInsertId()
}
// AddReply insert reply.
func (d *Dao) AddReply(c context.Context, r *model.Reply) (id int64, err error) {
res, err := d.inReply.Exec(c, r.SessionID, r.ReplyID, r.Content, r.ImgURL, r.LogURL, r.CTime, r.MTime)
if err != nil {
log.Error("AddReply error(%v)", err)
return
}
return res.LastInsertId()
}
// Replys returns corresponding user feedback reply records
func (d *Dao) Replys(c context.Context, ssnID int64, offset, limit int) (rs []model.Reply, err error) {
rows, err := d.selReply.Query(c, ssnID, offset, limit)
if err != nil {
log.Error("d.selReply.Query() error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var r = model.Reply{}
if err = rows.Scan(&r.ReplyID, &r.Type, &r.Content, &r.ImgURL, &r.LogURL, &r.CTime); err != nil {
log.Error("row.Scan() error(%s)", err)
rs = nil
return
}
rs = append(rs, r)
}
return
}
// WebReplys get by ssnID.
func (d *Dao) WebReplys(c context.Context, ssnID, mid int64) (rs []*model.Reply, err error) {
rows, err := d.selReplyBySid.Query(c, ssnID, mid)
if err != nil {
log.Error("d.selReply.Query(%d, %d) error(%v)", ssnID, mid, err)
return
}
defer rows.Close()
for rows.Next() {
var r = &model.Reply{}
if err = rows.Scan(&r.ReplyID, &r.Type, &r.Content, &r.ImgURL, &r.LogURL, &r.CTime); err != nil {
log.Error("row.Scan() error(%s)", err)
rs = nil
return
}
rs = append(rs, r)
}
return
}
// ReplysByMid returns corresponding user feedback reply records by mid
func (d *Dao) ReplysByMid(c context.Context, mid int64, offset, limit int) (rs []model.Reply, err error) {
rows, err := d.selReplyByMid.Query(c, mid, offset, limit)
if err != nil {
log.Error("d.selReplyByMid.Query() error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var r = model.Reply{}
if err = rows.Scan(&r.ReplyID, &r.Type, &r.Content, &r.ImgURL, &r.LogURL, &r.CTime); err != nil {
log.Error("row.Scan() error(%s)", err)
rs = nil
return
}
rs = append(rs, r)
}
return
}

View File

@@ -0,0 +1,158 @@
package dao
import (
"context"
xsql "database/sql"
"fmt"
"reflect"
"testing"
"go-common/app/interface/main/feedback/model"
"go-common/library/database/sql"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoTxAddReply(t *testing.T) {
convey.Convey("TxAddReply", t, func(ctx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
r = &model.Reply{}
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
id, err := d.TxAddReply(tx, r)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
ctx.Convey("When tx.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Exec",
func(_ *sql.Tx, _ string, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("tx.Exec Error")
})
defer guard.Unpatch()
_, err := d.TxAddReply(tx, r)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
ctx.Reset(func() {
tx.Rollback()
})
})
}
func TestDaoAddReply(t *testing.T) {
convey.Convey("AddReply", t, func(ctx convey.C) {
var (
c = context.TODO()
r = &model.Reply{}
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
id, err := d.AddReply(c, r)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.inReply.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.inReply), "Exec",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.inReply.Exec Error")
})
defer guard.Unpatch()
_, err := d.AddReply(c, r)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoReplys(t *testing.T) {
convey.Convey("Replys", t, func(ctx convey.C) {
var (
c = context.TODO()
ssnID = int64(3131)
offset = int(1)
limit = int(10)
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
rs, err := d.Replys(c, ssnID, offset, limit)
ctx.Convey("Then err should be nil.rs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.SkipSo(rs, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.selReply.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.selReply), "Query",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.selReply.Query Error")
})
defer guard.Unpatch()
_, err := d.Replys(c, ssnID, offset, limit)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoWebReplys(t *testing.T) {
convey.Convey("WebReplys", t, func(ctx convey.C) {
var (
c = context.TODO()
ssnID = int64(3131)
mid = int64(1313)
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
rs, err := d.WebReplys(c, ssnID, mid)
ctx.Convey("Then err should be nil.rs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.SkipSo(rs, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.selReplyBySid.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.selReplyBySid), "Query",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.selReplyBySid.Query Error")
})
defer guard.Unpatch()
_, err := d.WebReplys(c, ssnID, mid)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoReplysByMid(t *testing.T) {
convey.Convey("ReplysByMid", t, func(ctx convey.C) {
var (
c = context.TODO()
mid = int64(11424224)
offset = int(1)
limit = int(10)
)
ctx.Convey("ReplysByMid", func(ctx convey.C) {
rs, err := d.ReplysByMid(c, mid, offset, limit)
ctx.Convey("Then err should be nil.rs should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.SkipSo(rs, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.selReplyByMid.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.selReplyByMid), "Query",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.selReplyByMid.Query Error")
})
defer guard.Unpatch()
_, err := d.ReplysByMid(c, mid, offset, limit)
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,280 @@
package dao
import (
"context"
"fmt"
"time"
"go-common/app/interface/main/feedback/model"
"go-common/library/database/sql"
"go-common/library/log"
"go-common/library/xstr"
"strings"
)
const (
_selSsn = "SELECT id,mid,content,img_url,log_url,state,ctime FROM session WHERE buvid=? AND system=? AND version=? AND mid=?"
_selSsnByMid = "SELECT id,mid,content,img_url,log_url,state,ctime FROM session WHERE mid=? AND platform IN (%s)"
_selSSnCntByMid = `SELECT COUNT(id) AS count FROM session WHERE mid=? AND state IN (0,1,2) AND platform IN ("ugc","article")`
_inSsn = "INSERT INTO session (buvid,system,version,mid,aid,content,img_url,log_url,device,channel,ip,net_state,net_operator,agency_area,platform,browser,qq,email,state,laster_time,ctime,mtime) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
_upSsn = "UPDATE session SET device=?,channel=?,ip=?,net_state=?,net_operator=?,agency_area=?,platform=?,browser=?,qq=?,email=?,state=?,laster_time=?,mtime=? WHERE id=?"
_upSsnMtime = "UPDATE session SET mtime=? WHERE id=?"
_upSsnState = "UPDATE session SET state=? where id=?"
_selTagByPlat = "SELECT id,name,platform,type FROM tag where type=? AND platform IN (%s)"
_selSsnIDbByTagID = "SELECT session_id FROM session_tag WHERE tag_id IN (%s)"
_selSSnBySsnID = "SELECT id,content,ctime,state FROM session WHERE id IN (%s) AND state IN (%s) AND session.ctime>? AND session.ctime<? ORDER BY id DESC"
_selSSnBySsnIDAllSate = "SELECT id,content,ctime,state FROM session WHERE id IN (%s) AND session.ctime>? AND session.ctime<? ORDER BY id DESC"
_selSSnID = "select id from session where id=? limit 1"
)
// JudgeSsnRecord judge session is exist or not .
func (d *Dao) JudgeSsnRecord(c context.Context, sid int64) (cnt int, err error) {
res, err := d.selSSnID.Exec(c, sid)
if err != nil {
log.Error("d.upSsnSta.Exec error(%v)", err)
return
}
res.RowsAffected()
return
}
// Session select feedback session
func (d *Dao) Session(c context.Context, buvid, system, version string, mid int64) (ssn *model.Session, err error) {
row := d.selSsn.QueryRow(c, buvid, system, version, mid)
ssn = &model.Session{}
if err = row.Scan(&ssn.ID, &ssn.Mid, &ssn.Content, &ssn.ImgURL, &ssn.LogURL, &ssn.State, &ssn.CTime); err != nil {
if err == sql.ErrNoRows {
err, ssn = nil, nil
} else {
log.Error("row.Scan() error(%v)", err)
}
}
return
}
//SessionCount session count.
func (d *Dao) SessionCount(c context.Context, mid int64) (cnt int, err error) {
row := d.selSSnCntByMid.QueryRow(c, mid)
if err = row.Scan(&cnt); err != nil {
log.Error("row.Scan error(%v)", err)
}
return
}
// UpSsnMtime up ssn mtime.
func (d *Dao) UpSsnMtime(c context.Context, now time.Time, id int64) (err error) {
_, err = d.upSsnMtime.Exec(c, now, id)
if err != nil {
log.Error("d.upSsnMtime error(%v)", err)
}
return
}
// TxUpSsnMtime up ssn mtime.
func (d *Dao) TxUpSsnMtime(tx *sql.Tx, now time.Time, id int64) (err error) {
_, err = tx.Exec(_upSsnMtime, now, id)
if err != nil {
log.Error("d.upSsnMtime error(%v)", err)
}
return
}
// SessionIDByTagID session find by time state and mid .
func (d *Dao) SessionIDByTagID(c context.Context, tagID []int64) (sid []int64, err error) {
rows, err := d.dbMs.Query(c, fmt.Sprintf(_selSsnIDbByTagID, xstr.JoinInts(tagID)))
if err != nil {
log.Error("d.dbMs.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
var id int64
if err = rows.Scan(&id); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
sid = append(sid, id)
}
return
}
// SessionBySsnID get session by ssnID
func (d *Dao) SessionBySsnID(c context.Context, sid []int64, state string, start, end time.Time) (ssns []*model.Session, err error) {
rows, err := d.dbMs.Query(c, fmt.Sprintf(_selSSnBySsnID, xstr.JoinInts(sid), state), start, end)
if err != nil {
log.Error("d.selSSnBySsnID.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
ssn := &model.Session{}
if err = rows.Scan(&ssn.ID, &ssn.Content, &ssn.CTime, &ssn.State); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
ssns = append(ssns, ssn)
}
return
}
// SSnBySsnIDAllSate get all SSn by ssnID all sate.
func (d *Dao) SSnBySsnIDAllSate(c context.Context, sid []int64, start, end time.Time) (ssns []*model.Session, err error) {
rows, err := d.dbMs.Query(c, fmt.Sprintf(_selSSnBySsnIDAllSate, xstr.JoinInts(sid)), start, end)
if err != nil {
log.Error("d.selSSnBySsnID.Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
ssn := &model.Session{}
if err = rows.Scan(&ssn.ID, &ssn.Content, &ssn.CTime, &ssn.State); err != nil {
log.Error("rows.Scan error(%v)", err)
return
}
ssns = append(ssns, ssn)
}
return
}
// SessionByMid select feedback session by mid
func (d *Dao) SessionByMid(c context.Context, mid int64, platform string) (ssns []*model.Session, err error) {
rows, err := d.dbMs.Query(c, fmt.Sprintf(_selSsnByMid, platConvert(platform)), mid)
if err != nil {
log.Error("d.dbMs.Query error(%v)", err)
return
}
defer rows.Close()
ssns = []*model.Session{}
for rows.Next() {
ssn := &model.Session{}
if err = rows.Scan(&ssn.ID, &ssn.Mid, &ssn.Content, &ssn.ImgURL, &ssn.LogURL, &ssn.State, &ssn.CTime); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
ssns = append(ssns, ssn)
}
return
}
// TxUpdateSessionState up date session state.
func (d *Dao) TxUpdateSessionState(tx *sql.Tx, state int, sid int64) (err error) {
res, err := tx.Exec(_upSsnState, state, sid)
if err != nil {
log.Error("d.upSsnSta.Exec error(%v)", err)
return
}
_, err = res.RowsAffected()
return
}
// UpdateSessionState update session state.
func (d *Dao) UpdateSessionState(c context.Context, state int, sid int64) (err error) {
res, err := d.upSsnSta.Exec(c, state, sid)
if err != nil {
log.Error("d.upSsnSta.Exec error(%v)", err)
return
}
_, err = res.RowsAffected()
return
}
// Tags get tags.
func (d *Dao) Tags(c context.Context, mold int, platform string) (tMap map[string][]*model.Tag, err error) {
rows, err := d.dbMs.Query(c, fmt.Sprintf(_selTagByPlat, platConvert(platform)), mold)
if err != nil {
log.Error("d.selTagByPlat.Query error(%v)", err)
return
}
defer rows.Close()
tMap = make(map[string][]*model.Tag)
for rows.Next() {
tag := &model.Tag{}
if err = rows.Scan(&tag.ID, &tag.Name, &tag.Platform, &tag.Type); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
if tag.Type == 0 {
tMap[tag.Platform] = append(tMap[tag.Platform], tag)
}
}
return
}
// AddSession add feedback session
func (d *Dao) AddSession(c context.Context, s *model.Session) (id int64, err error) {
res, err := d.inSsn.Exec(c, s.Buvid, s.System, s.Version, s.Mid, s.Aid, s.Content, s.ImgURL, s.LogURL, s.Device, s.Channel, s.IP, s.NetState, s.NetOperator, s.AgencyArea, s.Platform, s.Browser, s.QQ, s.Email, s.State, s.LasterTime, s.CTime, s.MTime)
if err != nil {
log.Error("AddSession tx.Exec() error(%v)", err)
return
}
return res.LastInsertId()
}
// TxAddSession add session
func (d *Dao) TxAddSession(tx *sql.Tx, s *model.Session) (id int64, err error) {
res, err := tx.Exec(_inSsn, s.Buvid, s.System, s.Version, s.Mid, s.Aid, s.Content, s.ImgURL, s.LogURL, s.Device, s.Channel, s.IP, s.NetState, s.NetOperator, s.AgencyArea, s.Platform, s.Browser, s.QQ, s.Email, s.State, s.LasterTime, s.CTime, s.MTime)
if err != nil {
log.Error("AddSession tx.Exec() error(%v)", err)
return
}
return res.LastInsertId()
}
// AddSessionTag add feedback session and tag.
func (d *Dao) AddSessionTag(c context.Context, sessionID, tagID int64, now time.Time) (id int64, err error) {
res, err := d.inSsnTag.Exec(c, sessionID, tagID, now)
if err != nil {
log.Error("AddSession tx.Exec() error(%v)", err)
return
}
return res.LastInsertId()
}
// TxAddSessionTag add session tag.
func (d *Dao) TxAddSessionTag(tx *sql.Tx, sessionID, tagID int64, now time.Time) (id int64, err error) {
res, err := tx.Exec(_inSsnTag, sessionID, tagID, now)
if err != nil {
log.Error("TxAddSessionTag tx.Exec() error(%v)", err)
return
}
return res.LastInsertId()
}
// UpdateSession update feedback session
func (d *Dao) UpdateSession(c context.Context, s *model.Session) (affected int64, err error) {
res, err := d.upSsn.Exec(c, s.Device, s.Channel, s.IP, s.NetState, s.NetOperator, s.AgencyArea, s.Platform, s.Browser, s.QQ, s.Email, s.State, s.LasterTime, s.MTime, s.ID)
if err != nil {
log.Error("UpdateSession tx.Exec() error(%v)", err)
return
}
return res.RowsAffected()
}
// TagIDBySid get tagid by sid.
func (d *Dao) TagIDBySid(c context.Context, sids []int64) (tsMap map[int64]int64, err error) {
rows, err := d.dbMs.Query(c, fmt.Sprintf(_selTagID, xstr.JoinInts(sids)))
if err != nil {
log.Error("d.dbMs.Query() error(%v)", err)
return
}
defer rows.Close()
tsMap = make(map[int64]int64)
for rows.Next() {
var (
tid int64
sid int64
)
if err = rows.Scan(&tid, &sid); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
tsMap[tid] = sid
}
return
}
// platConvert plat covert.
func platConvert(platform string) (s string) {
s = `"` + strings.Replace(platform, ",", `","`, -1) + `"`
return
}

View File

@@ -0,0 +1,518 @@
package dao
import (
"context"
xsql "database/sql"
"fmt"
"reflect"
"testing"
"time"
"go-common/app/interface/main/feedback/model"
"go-common/library/database/sql"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoJudgeSsnRecord(t *testing.T) {
convey.Convey("JudgeSsnRecord", t, func(ctx convey.C) {
var (
c = context.Background()
sid = int64(1)
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
cnt, err := d.JudgeSsnRecord(c, sid)
ctx.Convey("Then err should be nil.cnt should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(cnt, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.selSSnID.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.selSSnID), "Exec",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.selSSnID.Exec Error")
})
defer guard.Unpatch()
_, err := d.JudgeSsnRecord(c, sid)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSession(t *testing.T) {
convey.Convey("Session", t, func(ctx convey.C) {
var (
c = context.Background()
buvid = ""
system = ""
version = ""
mid = int64(1)
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
ssn, err := d.Session(c, buvid, system, version, mid)
ctx.Convey("Then err should be nil.ssn should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.SkipSo(ssn, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSessionCount(t *testing.T) {
var (
c = context.Background()
mid = int64(0)
)
convey.Convey("SessionCount", t, func(ctx convey.C) {
cnt, err := d.SessionCount(c, mid)
ctx.Convey("Then err should be nil.cnt should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(cnt, convey.ShouldNotBeNil)
})
})
}
func TestDaoUpSsnMtime(t *testing.T) {
convey.Convey("UpSsnMtime", t, func(ctx convey.C) {
var (
c = context.Background()
now = time.Now()
id = int64(1)
)
convey.Convey("When everything is correct", func(ctx convey.C) {
err := d.UpSsnMtime(c, now, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
convey.Convey("When d.upSsnMtime.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.upSsnMtime), "Exec",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.upSsnMtime.Exec Error")
})
defer guard.Unpatch()
err := d.UpSsnMtime(c, now, id)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxUpSsnMtime(t *testing.T) {
convey.Convey("TxUpSsnMtime", t, func(ctx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
now = time.Now()
id = int64(0)
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
err := d.TxUpSsnMtime(tx, now, id)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
ctx.Convey("When tx.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Exec",
func(_ *sql.Tx, _ string, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("tx.Exec Error")
})
defer guard.Unpatch()
err := d.TxUpSsnMtime(tx, now, id)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
ctx.Reset(func() {
tx.Rollback()
})
})
}
func TestDaoSessionIDByTagID(t *testing.T) {
convey.Convey("SessionIDByTagID", t, func(ctx convey.C) {
var (
c = context.Background()
tagID = []int64{1, 2}
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
sid, err := d.SessionIDByTagID(c, tagID)
ctx.Convey("Then err should be nil.sid should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.SkipSo(sid, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Query",
func(_ *sql.DB, _ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.dbMs.Query Error")
})
defer guard.Unpatch()
_, err := d.SessionIDByTagID(c, tagID)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSessionBySsnID(t *testing.T) {
convey.Convey("SessionBySsnID", t, func(ctx convey.C) {
var (
c = context.Background()
sid = []int64{1, 2}
state = "3"
start = time.Now()
end = time.Now()
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
ssns, err := d.SessionBySsnID(c, sid, state, start, end)
ctx.Convey("Then err should be nil.ssns should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.SkipSo(ssns, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Query",
func(_ *sql.DB, _ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.dbMs.Query Error")
})
defer guard.Unpatch()
_, err := d.SessionBySsnID(c, sid, state, start, end)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSSnBySsnIDAllSate(t *testing.T) {
convey.Convey("SSnBySsnIDAllSate", t, func(ctx convey.C) {
var (
c = context.Background()
sid = []int64{1, 2}
start = time.Now()
end = time.Now()
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
ssns, err := d.SSnBySsnIDAllSate(c, sid, start, end)
ctx.Convey("Then err should be nil.ssns should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.SkipSo(ssns, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Query",
func(_ *sql.DB, _ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.dbMs.Query Error")
})
defer guard.Unpatch()
_, err := d.SSnBySsnIDAllSate(c, sid, start, end)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoSessionByMid(t *testing.T) {
convey.Convey("SessionByMid", t, func(ctx convey.C) {
var (
c = context.Background()
mid = int64(1)
platform = "ios"
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
ssns, err := d.SessionByMid(c, mid, platform)
ctx.Convey("Then err should be nil.ssns should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(ssns, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Query",
func(_ *sql.DB, _ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.dbMs.Query Error")
})
defer guard.Unpatch()
_, err := d.SessionByMid(c, mid, platform)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxUpdateSessionState(t *testing.T) {
convey.Convey("TxUpdateSessionState", t, func(ctx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
state = int(0)
sid = int64(0)
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
err := d.TxUpdateSessionState(tx, state, sid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
ctx.Convey("When tx.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Exec",
func(_ *sql.Tx, _ string, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("tx.Exec Error")
})
defer guard.Unpatch()
err := d.TxUpdateSessionState(tx, state, sid)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
ctx.Reset(func() {
tx.Rollback()
})
})
}
func TestDaoUpdateSessionState(t *testing.T) {
convey.Convey("UpdateSessionState", t, func(ctx convey.C) {
var (
c = context.Background()
state = int(0)
sid = int64(0)
)
ctx.Convey("UpdateSessionState", func(ctx convey.C) {
err := d.UpdateSessionState(c, state, sid)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
ctx.Convey("When d.upSsnSta.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.upSsnSta), "Exec",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.upSsnSta.Exec Error")
})
defer guard.Unpatch()
err := d.UpdateSessionState(c, state, sid)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTags(t *testing.T) {
convey.Convey("Tags", t, func(ctx convey.C) {
var (
c = context.Background()
mold = int(1)
platform = "ios"
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
tMap, err := d.Tags(c, mold, platform)
ctx.Convey("Then err should be nil.tMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(tMap, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Query",
func(_ *sql.DB, _ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.dbMs.Query Error")
})
defer guard.Unpatch()
_, err := d.Tags(c, mold, platform)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoAddSession(t *testing.T) {
convey.Convey("AddSession", t, func(ctx convey.C) {
var (
c = context.Background()
s = &model.Session{}
)
ctx.Convey("When everthing is correct", func(ctx convey.C) {
id, err := d.AddSession(c, s)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.inSsn.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.inSsn), "Exec",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.inSsn.Exec Error")
})
defer guard.Unpatch()
_, err := d.AddSession(c, s)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxAddSession(t *testing.T) {
convey.Convey("TxAddSession", t, func(ctx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
s = &model.Session{}
)
ctx.Convey("When everthing is correct", func(ctx convey.C) {
id, err := d.TxAddSession(tx, s)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
ctx.Convey("When tx.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Exec",
func(_ *sql.Tx, _ string, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("tx.Exec Error")
})
defer guard.Unpatch()
_, err := d.TxAddSession(tx, s)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
ctx.Reset(func() {
tx.Rollback()
})
})
}
func TestDaoAddSessionTag(t *testing.T) {
convey.Convey("AddSessionTag", t, func(ctx convey.C) {
var (
c = context.Background()
sessionID = int64(0)
tagID = int64(0)
now = time.Now()
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
id, err := d.AddSessionTag(c, sessionID, tagID, now)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.inSsnTag.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.inSsnTag), "Exec",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.inSsnTag.Exec Error")
})
defer guard.Unpatch()
_, err := d.AddSessionTag(c, sessionID, tagID, now)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTxAddSessionTag(t *testing.T) {
convey.Convey("TxAddSessionTag", t, func(ctx convey.C) {
var (
tx, _ = d.BeginTran(context.Background())
sessionID = int64(0)
tagID = int64(0)
now = time.Now()
)
ctx.Convey("When everthing is correct", func(ctx convey.C) {
id, err := d.TxAddSessionTag(tx, sessionID, tagID, now)
ctx.Convey("Then err should be nil.id should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(id, convey.ShouldNotBeNil)
})
})
ctx.Convey("When tx.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(tx), "Exec",
func(_ *sql.Tx, _ string, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("tx.Exec Error")
})
defer guard.Unpatch()
_, err := d.TxAddSessionTag(tx, sessionID, tagID, now)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
ctx.Reset(func() {
tx.Rollback()
})
})
}
func TestDaoUpdateSession(t *testing.T) {
convey.Convey("UpdateSession", t, func(ctx convey.C) {
var (
c = context.Background()
s = &model.Session{}
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
affected, err := d.UpdateSession(c, s)
ctx.Convey("Then err should be nil.affected should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(affected, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.upSsn.Exec gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.upSsn), "Exec",
func(_ *sql.Stmt, _ context.Context, _ ...interface{}) (xsql.Result, error) {
return nil, fmt.Errorf("d.upSsn.Exec Error")
})
defer guard.Unpatch()
_, err := d.UpdateSession(c, s)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoTagIDBySid(t *testing.T) {
convey.Convey("TagIDBySid", t, func(ctx convey.C) {
var (
c = context.Background()
sids = []int64{1, 2}
)
ctx.Convey("When everything is correct", func(ctx convey.C) {
tsMap, err := d.TagIDBySid(c, sids)
ctx.Convey("Then err should be nil.tsMap should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(tsMap, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Query",
func(_ *sql.DB, _ context.Context, _ string, _ ...interface{}) (*sql.Rows, error) {
return nil, fmt.Errorf("d.dbMs.Query Error")
})
defer guard.Unpatch()
_, err := d.TagIDBySid(c, sids)
ctx.Convey("Then err should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}
func TestDaoplatConvert(t *testing.T) {
convey.Convey("platConvert", t, func(ctx convey.C) {
var (
platform = "a,b"
)
ctx.Convey("When the original string is 'a,b'", func(ctx convey.C) {
s := platConvert(platform)
ctx.Convey("Then s should equal \"a\",\"b\".", func(ctx convey.C) {
ctx.So(s, convey.ShouldEqual, "\"a\",\"b\"")
})
})
})
}

View File

@@ -0,0 +1,41 @@
package dao
import (
"context"
"fmt"
"go-common/app/interface/main/feedback/model"
"go-common/library/log"
"go-common/library/xstr"
)
const (
_selTagBySsnID = "SELECT session_tag.session_id,tag.id,tag.name,tag.platform,tag.type FROM tag INNER JOIN session_tag ON tag.id =session_tag.tag_id WHERE session_id IN (%s)"
_selTagID = "SELECT tag_id,session_id FROM session_tag WHERE session_id IN (%s)"
_inSsnTag = "INSERT INTO session_tag (session_id,tag_id,ctime) VALUES (?,?,?)"
)
// TagBySsnID get tag by ssnID.
func (d *Dao) TagBySsnID(c context.Context, sids []int64) (tagMap map[int64][]*model.Tag, err error) {
rows, err := d.dbMs.Query(c, fmt.Sprintf(_selTagBySsnID, xstr.JoinInts(sids)))
if err != nil {
log.Error("d.dbMs.Query error(%v)", err)
return
}
defer rows.Close()
tagMap = make(map[int64][]*model.Tag)
for rows.Next() {
var (
tag = &model.Tag{}
sid int64
)
if err = rows.Scan(&sid, &tag.ID, &tag.Name, &tag.Platform, &tag.Type); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
if tag.Type == 0 {
tagMap[sid] = append(tagMap[sid], tag)
}
}
return
}

View File

@@ -0,0 +1,35 @@
package dao
import (
"context"
"fmt"
"reflect"
"testing"
xsql "go-common/library/database/sql"
"github.com/bouk/monkey"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoTagBySsnID(t *testing.T) {
convey.Convey("Given function TagBySsnID", t, func(ctx convey.C) {
ctx.Convey("When everyting is correct", func(ctx convey.C) {
tagMap, err := d.TagBySsnID(context.Background(), []int64{1, 2})
ctx.Convey("Then err should be nil.tapMap should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(tagMap, convey.ShouldNotBeNil)
})
})
ctx.Convey("When d.dbMs.Query gets error", func(ctx convey.C) {
guard := monkey.PatchInstanceMethod(reflect.TypeOf(d.dbMs), "Query", func(_ *xsql.DB, _ context.Context, _ string, _ ...interface{}) (*xsql.Rows, error) {
return nil, fmt.Errorf("d.dbMs.Query Error")
})
defer guard.Unpatch()
_, err := d.TagBySsnID(context.Background(), []int64{1, 2})
ctx.Convey("Then err should not be nil", func(ctx convey.C) {
ctx.So(err, convey.ShouldNotBeNil)
})
})
})
}

View File

@@ -0,0 +1,44 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"bfs.go",
"feedback.go",
"http.go",
"local.go",
],
importpath = "go-common/app/interface/main/feedback/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/feedback/conf:go_default_library",
"//app/interface/main/feedback/model:go_default_library",
"//app/interface/main/feedback/service:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/auth:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/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,62 @@
package http
import (
"io/ioutil"
"mime/multipart"
"net/http"
"path"
"strings"
"time"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
)
// upload
func upload(c *bm.Context) {
var (
fileTpye string
file multipart.File
header *multipart.FileHeader
fileName string
body []byte
location string
err error
)
// res := c.Result()
if file, header, err = c.Request.FormFile("file"); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
defer file.Close()
fileName = header.Filename
fileTpye = strings.TrimPrefix(path.Ext(fileName), ".")
if body, err = ioutil.ReadAll(file); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
location, err = feedbackSvr.Upload(c, "", fileTpye, time.Now(), body)
c.JSON(struct {
URL string `json:"url"`
}{location}, err)
}
// uploadFile
func uploadFile(c *bm.Context) {
var (
req = c.Request
fileTpye, location string
body []byte
err error
)
if body, err = ioutil.ReadAll(req.Body); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
req.Body.Close()
fileTpye = http.DetectContentType(body)
location, err = feedbackSvr.Upload(c, "", fileTpye, time.Now(), body)
c.JSON(struct {
URL string `json:"url"`
}{location}, err)
}

View File

@@ -0,0 +1,480 @@
package http
import (
"io/ioutil"
"mime/multipart"
"path"
"strconv"
"strings"
"sync"
"time"
"unicode/utf8"
"go-common/app/interface/main/feedback/conf"
"go-common/app/interface/main/feedback/model"
"go-common/library/ecode"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/sync/errgroup"
)
const (
_timeFromat = "2006-01-02 15:04:05"
)
// addReply
func addReply(c *bm.Context) {
header := c.Request.Header
params := c.Request.Form
// params
buvid := header.Get("Buvid")
system := params.Get("system")
version := params.Get("version")
midStr := params.Get("mid")
content := params.Get("content")
imgURL := params.Get("img_url")
logURL := params.Get("log_url")
device := params.Get("device")
channel := params.Get("channel")
entrance := params.Get("entrance")
netState := params.Get("net_state")
netOperator := params.Get("net_operator")
agencyArea := params.Get("agency_area")
platform := params.Get("platform")
browser := params.Get("browser")
qq := params.Get("qq")
mobiApp := params.Get("mobi_app")
email := params.Get("email")
tagStr := params.Get("tag_id")
// check params
if buvid == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if version == "" {
c.JSON(nil, ecode.RequestErr)
return
}
var (
mid int64
err error
)
if midStr != "" {
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
}
if content == "" && imgURL == "" && logURL == "" {
c.JSON(nil, ecode.RequestErr)
return
}
tagID, _ := strconv.ParseInt(tagStr, 10, 64)
c.JSON(feedbackSvr.AddReply(c, mid, tagID, buvid, system, version, mobiApp, filtered(content), imgURL, logURL, device, channel, entrance, netState, netOperator, agencyArea, platform, browser, qq, email, time.Now()))
}
func addWebReply(c *bm.Context) {
params := c.Request.Form
// system := params.Get("system")
version := params.Get("version")
midStr := params.Get("mid")
sidStr := params.Get("session_id")
content := params.Get("content")
imgURL := params.Get("img_url")
logURL := params.Get("log_url")
// device := params.Get("device")
buvid := params.Get("buvid")
// channel := params.Get("channel")
netState := params.Get("net_state")
netOperator := params.Get("net_operator")
agencyArea := params.Get("agency_area")
platform := params.Get("platform")
browser := params.Get("browser")
qq := params.Get("qq")
email := params.Get("email")
tagStr := params.Get("tag_id")
aidStr := params.Get("aid")
var (
mid int64
sid int64
err error
)
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
if content == "" && imgURL == "" && logURL == "" {
c.JSON(nil, ecode.RequestErr)
return
}
sid, _ = strconv.ParseInt(sidStr, 10, 64)
tagID, _ := strconv.ParseInt(tagStr, 10, 64)
if platform == "" {
platform = "ugc"
}
c.JSON(feedbackSvr.AddWebReply(c, mid, sid, tagID, aidStr, filtered(content), imgURL, netState, netOperator, agencyArea, platform, version, buvid, browser, qq, email, time.Now()))
}
//replys
func replys(c *bm.Context) {
header := c.Request.Header
params := c.Request.Form
buvid := header.Get("Buvid")
system := params.Get("system")
version := params.Get("version")
midStr := params.Get("mid")
platform := params.Get("platform")
mobiApp := params.Get("mobi_app")
device := params.Get("device")
pnStr := params.Get("pn")
psStr := params.Get("ps")
entrance := params.Get("entrance")
// check params
if system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if version == "" {
c.JSON(nil, ecode.RequestErr)
return
}
var (
mid int64
err error
)
if midStr != "" {
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
}
if mid == 0 && buvid == "" {
c.JSON(nil, ecode.RequestErr)
return
}
pn, err := strconv.Atoi(pnStr)
if err != nil || pn < 1 {
pn = 1
}
ps, err := strconv.Atoi(psStr)
if err != nil || ps < 1 || ps > conf.Conf.Feedback.ReplysNum {
ps = conf.Conf.Feedback.ReplysNum
}
rs, isEndReply, err := feedbackSvr.Replys(c, buvid, platform, mobiApp, device, system, version, entrance, mid, pn, ps)
if err != nil {
c.JSON(nil, err)
return
}
res := map[string]interface{}{
"data": rs,
"is_end_reply": isEndReply,
}
c.JSONMap(res, err)
}
func sessions(c *bm.Context) {
params := c.Request.Form
tagid := params.Get("tag_id")
platform := params.Get("platform")
stateStr := params.Get("state")
midStr := params.Get("mid")
start := params.Get("start")
end := params.Get("end")
pnStr := params.Get("pn")
psStr := params.Get("ps")
mid, err := strconv.ParseInt(midStr, 10, 64)
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
var (
etime time.Time
)
if start == "" {
start = "00-00-00 00:00:00"
}
stime, _ := time.Parse(_timeFromat, start)
etime, _ = time.Parse(_timeFromat, end)
if end == "" {
etime = time.Now()
}
pn, err := strconv.Atoi(pnStr)
if err != nil || pn < 1 {
pn = 1
}
ps, err := strconv.Atoi(psStr)
if err != nil || ps < 0 || ps > 10 {
ps = 10
}
total, sessions, err := feedbackSvr.Sessions(c, mid, stateStr, tagid, platform, stime, etime, ps, pn)
if err != nil {
c.JSON(nil, err)
return
}
res := map[string]interface{}{
"data": sessions,
"total": total,
}
c.JSONMap(res, err)
}
func sessionsClose(c *bm.Context) {
params := c.Request.Form
sidStr := params.Get("session_id")
sid, err := strconv.ParseInt(sidStr, 10, 64)
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, feedbackSvr.UpdateSessionState(c, 3, sid))
}
func replyTag(c *bm.Context) {
params := c.Request.Form
tp := params.Get("type") // NOTE: player
platform := params.Get("platform")
c.JSON(model.Tags[tp][platform], nil)
}
func ugcTag(c *bm.Context) {
params := c.Request.Form
tp := params.Get("type")
platform := params.Get("platform")
mold, err := strconv.Atoi(tp)
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
midStr := params.Get("mid")
mid, err := strconv.ParseInt(midStr, 10, 64)
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(feedbackSvr.Tags(c, mid, mold, platform))
}
func webReply(c *bm.Context) {
params := c.Request.Form
sidStr := params.Get("session_id")
sid, err := strconv.ParseInt(sidStr, 10, 64)
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
midStr := params.Get("mid")
mid, err := strconv.ParseInt(midStr, 10, 64)
if err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(feedbackSvr.WebReplys(c, sid, mid))
}
func filtered(content string) string {
v := make([]rune, 0, len(content))
for _, c := range content {
if c != utf8.RuneError {
v = append(v, c)
}
}
return string(v)
}
func playerCheck(c *bm.Context) {
var (
params = c.Request.Form
header = c.Request.Header
platform, ipChangeTimes int
mid, checkTime, aid, connectSpeed, ioSpeed int64
region, school string
err error
)
platformStr := params.Get("platform")
if platform = model.FormPlatForm(platformStr); platform == 0 {
c.JSON(nil, ecode.RequestErr)
return
}
if region = params.Get("region"); region == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if school = params.Get("school"); school == "" {
c.JSON(nil, ecode.RequestErr)
return
}
midStr := params.Get("mid")
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
checkTimeStr := params.Get("check_time")
if checkTime, err = strconv.ParseInt(checkTimeStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
aid, _ = strconv.ParseInt(params.Get("aid"), 10, 64)
ipChangeTimesStr := params.Get("ip_change_times")
if ipChangeTimes, err = strconv.Atoi(ipChangeTimesStr); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
connectSpeedStr := params.Get("connect_speed")
if connectSpeed, err = strconv.ParseInt(connectSpeedStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
ioSpeedStr := params.Get("io_speed")
if ioSpeed, err = strconv.ParseInt(ioSpeedStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
c.JSON(nil, feedbackSvr.PlayerCheck(c, platform, ipChangeTimes, mid, checkTime, aid, connectSpeed, ioSpeed, region, school, header.Get("X-Cache-Server-Addr")))
}
// addReplyH5 add from H5
func addReplyH5(c *bm.Context) {
c.Request.ParseMultipartForm(model.MaxUploadSize)
header := c.Request.Header
params := c.Request.MultipartForm
buvid := header.Get("Buvid")
var system, version, midStr, content, logURL, device, channel, entrance, netState, netOperator, agencyArea, platform, browser, qq, mobiApp, email, tagStr string
// params
if len(params.Value["system"]) > 0 {
system = params.Value["system"][0]
}
if len(params.Value["version"]) > 0 {
version = params.Value["version"][0]
}
if len(params.Value["mid"]) > 0 {
midStr = params.Value["mid"][0]
}
if len(params.Value["content"]) > 0 {
content = params.Value["content"][0]
}
if len(params.Value["log_url"]) > 0 {
logURL = params.Value["log_url"][0]
}
if len(params.Value["device"]) > 0 {
device = params.Value["device"][0]
}
if len(params.Value["channel"]) > 0 {
channel = params.Value["channel"][0]
}
if len(params.Value["entrance"]) > 0 {
entrance = params.Value["entrance"][0]
}
if len(params.Value["net_state"]) > 0 {
netState = params.Value["net_state"][0]
}
if len(params.Value["net_operator"]) > 0 {
netOperator = params.Value["net_operator"][0]
}
if len(params.Value["agency_area"]) > 0 {
agencyArea = params.Value["agency_area"][0]
}
if len(params.Value["platform"]) > 0 {
platform = params.Value["platform"][0]
}
if len(params.Value["browser"]) > 0 {
browser = params.Value["browser"][0]
}
if len(params.Value["qq"]) > 0 {
qq = params.Value["qq"][0]
}
if len(params.Value["mobi_app"]) > 0 {
mobiApp = params.Value["mobi_app"][0]
}
if len(params.Value["email"]) > 0 {
email = params.Value["email"][0]
}
if len(params.Value["buvid"]) > 0 && buvid == "" {
buvid = params.Value["buvid"][0]
}
if len(params.Value["tag_id"]) > 0 {
tagStr = params.Value["tag_id"][0]
}
// check params
if buvid == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if system == "" {
c.JSON(nil, ecode.RequestErr)
return
}
if version == "" {
c.JSON(nil, ecode.RequestErr)
return
}
var (
mid int64
err error
)
if midStr != "" {
if mid, err = strconv.ParseInt(midStr, 10, 64); err != nil {
c.JSON(nil, ecode.RequestErr)
return
}
}
imgs := c.Request.MultipartForm.File["imgs"]
var (
imgURL string
urls []string
mutex = sync.Mutex{}
)
g, ctx := errgroup.WithContext(c)
for k, fh := range imgs {
if k == conf.Conf.Feedback.ImgLimit {
break
}
var (
img multipart.File
url string
fileName string
fileTpye string
body []byte
)
if img, err = fh.Open(); err != nil {
log.Error("H5 addReply Open %s failed", fh.Filename)
err = nil
continue
}
defer img.Close()
fileName = fh.Filename
fileTpye = strings.TrimPrefix(path.Ext(fileName), ".")
if body, err = ioutil.ReadAll(img); err != nil {
log.Error("H5 addReply ioutil.ReadAll %s failed", fh.Filename)
err = nil
continue
}
g.Go(func() (err error) {
if url, err = feedbackSvr.Upload(ctx, "", fileTpye, time.Now(), body); err != nil {
log.Error("H5 addReply Upload %s failed", fh.Filename)
err = nil
return
}
mutex.Lock()
urls = append(urls, url)
mutex.Unlock()
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
if len(urls) > 0 {
imgURL = strings.Join(urls, ";")
}
if content == "" && imgURL == "" && logURL == "" {
c.JSON(nil, ecode.RequestErr)
return
}
tagID, _ := strconv.ParseInt(tagStr, 10, 64)
c.JSON(feedbackSvr.AddReply(c, mid, tagID, buvid, system, version, mobiApp, filtered(content), imgURL, logURL, device, channel, entrance, netState, netOperator, agencyArea, platform, browser, qq, email, time.Now()))
}

View File

@@ -0,0 +1,71 @@
package http
import (
"go-common/app/interface/main/feedback/conf"
"go-common/app/interface/main/feedback/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/auth"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
verifySvc *verify.Verify
authSvc *auth.Auth
feedbackSvr *service.Service
)
// Init init http
func Init(c *conf.Config) error {
initService(c)
engineOut := bm.DefaultServer(c.BM.Outer)
outerRouter(engineOut)
internalRouter(engineOut)
// init external server
if err := engineOut.Start(); err != nil {
log.Error("engineOut.Start() error(%v) | config(%v)", err, c)
panic(err)
}
engineLocal := bm.DefaultServer(c.BM.Local)
localRouter(engineLocal)
// init local server
if err := engineLocal.Start(); err != nil {
log.Error("engineLocal.Start() error(%v) | config(%v)", err, c)
panic(err)
}
return nil
}
// initService init services.
func initService(c *conf.Config) {
verifySvc = verify.New(nil)
authSvc = auth.New(nil)
feedbackSvr = service.New(c)
}
func outerRouter(e *bm.Engine) {
e.Ping(ping)
// init api
feedback := e.Group("/x/feedback")
feedback.POST("/add", addReply)
feedback.GET("/reply", verifySvc.Verify, replys)
feedback.GET("/tag", verifySvc.Verify, replyTag)
feedback.POST("/upload", upload)
feedback.POST("/uploadFile", uploadFile)
feedback.POST("/playerCheck", playerCheck)
feedback.GET("/h5/tag", replyTag)
feedback.POST("/h5/add", addReplyH5)
}
func internalRouter(e *bm.Engine) {
feedbackInner := e.Group("/x/internal/feedback")
feedbackInner.POST("/ugc/add", addWebReply)
feedbackInner.GET("/ugc/session", verifySvc.Verify, sessions)
feedbackInner.POST("/ugc/session/close", verifySvc.Verify, sessionsClose)
feedbackInner.GET("/ugc/tag", verifySvc.Verify, ugcTag)
feedbackInner.GET("/ugc/reply", verifySvc.Verify, webReply)
feedbackInner.POST("/upload", authSvc.User, upload)
}
func localRouter(e *bm.Engine) {
}

View File

@@ -0,0 +1,9 @@
package http
import (
bm "go-common/library/net/http/blademaster"
)
// ping check server ok.
func ping(c *bm.Context) {
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"const.go",
"feedback.go",
"tag.go",
],
importpath = "go-common/app/interface/main/feedback/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = ["//library/time:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,116 @@
package model
import "net"
const (
// PlatAndroid is int8 for android.
PlatAndroid = int8(0)
// PlatIPhone is int8 for iphone.
PlatIPhone = int8(1)
// PlatIPad is int8 for ipad.
PlatIPad = int8(2)
// PlatWPhone is int8 for wphone.
PlatWPhone = int8(3)
// PlatAndroidG is int8 for Android Googleplay.
PlatAndroidG = int8(4)
// PlatIPhoneI is int8 for Iphone Global.
PlatIPhoneI = int8(5)
// PlatIPadI is int8 for IPAD Global.
PlatIPadI = int8(6)
// PlatAndroidTV is int8 for AndroidTV Global.
PlatAndroidTV = int8(7)
// PlatAndroidI is int8 for Android Global.
PlatAndroidI = int8(8)
// PlatIpadHD is int8 for IpadHD
PlatIpadHD = int8(9)
)
// IsAndroid check plat is android or ipad.
func IsAndroid(plat int8) bool {
return plat == PlatAndroid || plat == PlatAndroidG || plat == PlatAndroidI
}
// IsIOS check plat is iphone or ipad.
func IsIOS(plat int8) bool {
return plat == PlatIPad || plat == PlatIPhone || plat == PlatIPadI || plat == PlatIPhoneI
}
// IsIPhone check plat is iphone.
func IsIPhone(plat int8) bool {
return plat == PlatIPhone || plat == PlatIPhoneI
}
// IsIPad check plat is pad.
func IsIPad(plat int8) bool {
return plat == PlatIPad || plat == PlatIPadI || plat == PlatIpadHD
}
// IsPlayerScreen check plat is iphone or ipad.
func IsPlayerScreen(tagID int64) bool {
return tagID == AndroidPlayerScreen || tagID == AndroidPlayerScreenNothing || tagID == AndroidPlayerScreenDlna || tagID == AndroidPlayerScreenTV || tagID == IOSPlayerScreen || tagID == IOSPlayerScreenNothing || tagID == IOSPlayerScreenDlna || tagID == IOSPlayerScreenTV
}
// Plat return plat by platStr or mobiApp
func Plat(mobiApp, device string) int8 {
switch mobiApp {
case "iphone":
if device == "pad" {
return PlatIPad
}
return PlatIPhone
case "white":
return PlatIPhone
case "ipad":
return PlatIpadHD
case "android":
return PlatAndroid
case "win", "winphone":
return PlatWPhone
case "android_G":
return PlatAndroidG
case "android_i":
return PlatAndroidI
case "iphone_i":
if device == "pad" {
return PlatIPadI
}
return PlatIPhoneI
case "ipad_i":
return PlatIPadI
case "android_tv":
return PlatAndroidTV
}
return PlatIPhone
}
// IsOverseas is overseas
func IsOverseas(plat int8) bool {
return plat == PlatAndroidI || plat == PlatIPhoneI || plat == PlatIPadI
}
// InetAtoN conver ip addr to uint32.
func InetAtoN(s string) (sum uint32) {
ip := net.ParseIP(s)
if ip == nil {
return
}
ip = ip.To4()
if ip == nil {
return
}
sum += uint32(ip[0]) << 24
sum += uint32(ip[1]) << 16
sum += uint32(ip[2]) << 8
sum += uint32(ip[3])
return sum
}
// InetNtoA conver uint32 to ip addr.
func InetNtoA(sum uint32) string {
ip := make(net.IP, net.IPv4len)
ip[0] = byte((sum >> 24) & 0xFF)
ip[1] = byte((sum >> 16) & 0xFF)
ip[2] = byte((sum >> 8) & 0xFF)
ip[3] = byte(sum & 0xFF)
return ip.String()
}

View File

@@ -0,0 +1,116 @@
package model
import "go-common/library/time"
const (
// EnvPro is pro.
EnvPro = "pro"
// EnvTest is env.
EnvTest = "test"
// EnvDev is env.
EnvDev = "dev"
// MaxUploadSize for h5 upload
MaxUploadSize = 20 * 1024 * 1024
)
// feedback model const.
const (
// StateNoReply 未回复
StateNoReply = 0
// StateReplied 已回复
StateReplied = 1
// StateRepeated 二次追问
StateRepeated = 2
// StateOther 其它
StateOther = 4
// TypeCustomer 客户
TypeCustomer = 0
// TypeServer 客服
TypeServer = 1
// player cast screen
AndroidPlayerScreen = int64(464)
AndroidPlayerScreenNothing = int64(465)
AndroidPlayerScreenDlna = int64(466)
AndroidPlayerScreenTV = int64(470)
IOSPlayerScreen = int64(467)
IOSPlayerScreenNothing = int64(468)
IOSPlayerScreenDlna = int64(469)
IOSPlayerScreenTV = int64(471)
)
// Session is feedback session
type Session struct {
ID int64 `json:"id"`
Buvid string `json:"-"`
System string `json:"-"`
Version string `json:"-"`
Mid int64 `json:"-"`
Aid string `json:"-"`
Content string `json:"content"`
ImgURL string `json:"-"`
LogURL string `json:"-"`
Device string `json:"-"`
Channel string `json:"-"`
IP uint32 `json:"-"`
NetState string `json:"-"`
NetOperator string `json:"-"`
AgencyArea string `json:"-"`
Platform string `json:"-"`
Browser string `json:"-"`
Email string `json:"-"`
QQ string `json:"-"`
State int8 `json:"state"`
ReplyID string `json:"-"`
ReplyTime time.Time `json:"-"`
LasterTime time.Time `json:"-"`
CTime time.Time `json:"ctime"`
MTime time.Time `json:"-"`
}
// WebSession web session.
type WebSession struct {
Session *Session `json:"session,omitempty"`
Tag *Tag `json:"tag,omitempty"`
}
// Reply is feedback reply
type Reply struct {
ID int64 `json:"-"`
SessionID int64 `json:"-"`
ReplyID string `json:"reply_id"`
Type int8 `json:"type"`
Content string `json:"content"`
ImgURL string `json:"img_url"`
LogURL string `json:"log_url"`
CTime time.Time `json:"ctime"`
MTime time.Time `json:"-"`
}
// SsnAndTagID ssn and tagid.
type SsnAndTagID struct {
TagID int64 `json:"tag_id"`
SessionID int64 `json:"session_id"`
}
// Replys for sort by ctime asc
type Replys []Reply
func (t Replys) Len() int { return len(t) }
func (t Replys) Less(i, j int) bool {
return t[i].CTime < t[j].CTime
}
func (t Replys) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
// FormPlatForm fro playerCheck.
func FormPlatForm(platStr string) (plat int) {
switch platStr {
case "web":
plat = 1
case "ios":
plat = 2
case "android":
plat = 3
}
return
}

View File

@@ -0,0 +1,95 @@
package model
var (
// Tags emmm...
Tags = map[string]map[string][]*Tag{
"main": map[string][]*Tag{
"ios": []*Tag{
{95, "账号相关", 0, ""},
{481, "播放相关", 0, ""},
{475, "投稿相关", 0, ""},
{163, "番剧相关", 0, ""},
{132, "弹幕", 0, ""},
{178, "首页推荐", 0, ""},
{152, "网站内容", 0, ""},
{192, "免流卡", 0, ""},
{365, "会员购", 0, ""},
{478, "创作中心", 0, ""},
{98, "其他功能建议", 0, ""},
},
"android": []*Tag{
{14, "账号相关", 0, ""},
{182, "播放相关", 0, ""},
{476, "投稿相关", 0, ""},
{162, "番剧相关", 0, ""},
{27, "弹幕", 0, ""},
{177, "首页推荐", 0, ""},
{150, "网站内容", 0, ""},
{191, "免流卡", 0, ""},
{363, "会员购", 0, ""},
{477, "创作中心", 0, ""},
{43, "其他功能建议", 0, ""},
},
},
"player": map[string][]*Tag{
"ios": []*Tag{
{100, "无限小电视", 0, ""},
//{5, "播放卡顿"},
//{11, "网页被劫持"},
//{11, "拖动进度条失败"},
{101, "黑屏", 0, ""},
{143, "音画不同步", 0, ""},
{134, "弹幕无法显示", 0, ""},
},
"android": []*Tag{
{183, "无限小电视", 0, ""},
{184, "播放卡顿", 0, ""},
{185, "网页劫持", 0, ""},
{186, "拖动进度条失败", 0, ""},
{187, "黑屏", 0, ""},
{188, "音画不同步", 0, ""},
{70, "弹幕无法显示", 0, ""},
},
},
"live": map[string][]*Tag{
"ios": []*Tag{
{444, "播放卡顿", 0, ""},
{445, "打开直播间速度慢", 0, ""},
{452, "无限小电视", 0, ""},
{450, "音画不同步", 0, ""},
{446, "黑屏", 0, ""},
{448, "画面比例异常", 0, ""},
{447, "绿屏", 0, ""},
{451, "片段循环", 0, ""},
{449, "弹幕卡顿", 0, ""},
{453, "其他问题或建议", 0, ""},
},
"android": []*Tag{
{454, "播放卡顿", 0, ""},
{455, "打开直播间速度慢", 0, ""},
{462, "无限小电视", 0, ""},
{460, "音画不同步", 0, ""},
{456, "黑屏", 0, ""},
{457, "画面比例异常", 0, ""},
{458, "绿屏", 0, ""},
{461, "片段循环", 0, ""},
{459, "弹幕卡顿", 0, ""},
{463, "其他问题或建议", 0, ""},
},
},
}
)
// UGCTag ugc tag.
type UGCTag struct {
Tags map[string][]*Tag `json:"tags"`
Limit int `json:"limit"`
}
// Tag tag.
type Tag struct {
ID int64 `json:"id"`
Name string `json:"name"`
Type int `json:"type"`
Platform string `json:"platform"`
}

View File

@@ -0,0 +1,61 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"feedback_test.go",
"service_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/interface/main/feedback/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"bfs.go",
"feedback.go",
"service.go",
],
importpath = "go-common/app/interface/main/feedback/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/interface/main/feedback/conf:go_default_library",
"//app/interface/main/feedback/dao:go_default_library",
"//app/interface/main/feedback/model:go_default_library",
"//app/service/main/location/model:go_default_library",
"//app/service/main/location/rpc/client:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/metadata:go_default_library",
"//library/time: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,27 @@
package service
import (
"bytes"
"context"
"time"
"go-common/library/ecode"
"go-common/library/log"
)
// Upload upload.
func (s *Service) Upload(c context.Context, fileName, fileType string, t time.Time, body []byte) (location string, err error) {
if len(body) == 0 {
err = ecode.FeedbackBodyNotExist
return
}
if len(body) > s.c.Bfs.MaxFileSize {
err = ecode.FeedbackBodyTooLarge
return
}
if location, err = s.dao.Upload(c, fileName, fileType, t.Unix(), bytes.NewReader(body)); err != nil {
log.Error("s.dao.Upload error(%v)", err)
return
}
return
}

View File

@@ -0,0 +1,646 @@
package service
import (
"context"
"net"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
"go-common/app/interface/main/feedback/model"
locmdl "go-common/app/service/main/location/model"
"go-common/library/database/sql"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
xtime "go-common/library/time"
"go-common/library/xstr"
)
const (
_androidFeedback = "android-feedback"
_androidFeedbackI = "android-fb-i"
_iosFeedback = "ios-feedback"
_iosFeedbackI = "ios-fb-i"
_androidPlayer = "android-player"
_iosPlayer = "ios-player"
_androidCreative = "android-creative"
_iosCreative = "ios-creative"
_androidLivePink = "android-live-pink"
_iosLivePink = "ios-live-pink"
_tvYST = "tv-yst"
_creativeCenter = "creative-center"
)
var (
defaultTag = &model.Tag{
ID: 314,
Name: "其它问题",
Type: 0,
}
)
// AddReply add feedback raply and create session if session isn't exist
func (s *Service) AddReply(c context.Context, mid, tagID int64, buvid, system, version, mobiApp, content, imgURL, logURL, device, channel, entrance,
netState, netOperator, agencyArea, platform, browser, qq, email string, now time.Time) (r *model.Reply, err error) {
// check size
if utf8.RuneCountInString(content) > s.c.Feedback.MaxContentSize {
err = ecode.FeedbackBodyTooLarge
return
}
var (
ssn *model.Session
reply *model.Reply
cTime xtime.Time
replyID = buvid
id int64
imgURLs []string
ip = metadata.String(c, metadata.RemoteIP)
)
if entrance == "view" {
if model.IsAndroid(model.Plat(mobiApp, device)) {
platform = _androidPlayer
} else if model.IsIOS(model.Plat(mobiApp, device)) {
platform = _iosPlayer
}
} else if entrance == _tvYST || entrance == _creativeCenter {
platform = entrance
} else if platform == "android" {
if entrance == _androidCreative {
platform = _androidCreative
} else if entrance == _androidLivePink {
platform = _androidLivePink
} else if model.Plat(mobiApp, device) == model.PlatAndroid {
platform = _androidFeedback
} else if model.Plat(mobiApp, device) == model.PlatAndroidI {
platform = _androidFeedbackI
}
} else if platform == "ios" {
if entrance == _iosCreative {
platform = _iosCreative
} else if entrance == _iosLivePink {
platform = _iosLivePink
} else if model.Plat(mobiApp, device) == model.PlatIPhone {
platform = _iosFeedback
} else if model.Plat(mobiApp, device) == model.PlatIPhoneI {
platform = _iosFeedbackI
}
}
if ssn, err = s.dao.Session(c, buvid, system, version, mid); err != nil {
log.Error("s.dao.Session(%s,%s,%s,%d) error(%v)", buvid, system, version, mid, err)
return
}
if mid != 0 {
replyID = strconv.FormatInt(mid, 10)
}
cTime = xtime.Time(now.Unix())
if ssn == nil {
if content != "" || logURL != "" || imgURL != "" {
// TODO delete ;
imgURLs = strings.Split(imgURL, ";")
var sid int64
sid, err = s.session(c, mid, tagID, buvid, system, version, "", content, imgURLs[0], logURL, device, channel, ip, entrance, netState, netOperator, agencyArea, platform, browser, qq, email, now)
if err == nil {
for _, v := range imgURLs[1:] {
if v != "" {
reply = &model.Reply{
SessionID: sid,
ReplyID: replyID,
Content: "",
ImgURL: v,
LogURL: "",
CTime: cTime,
MTime: cTime,
}
if id, err = s.dao.AddReply(c, reply); err != nil {
log.Error("s.dao.AddReply error(%v)", err)
continue
}
}
}
}
}
} else {
if entrance == "view" || entrance == _androidLivePink || entrance == _iosLivePink || platform == _androidLivePink || platform == _iosLivePink || model.IsPlayerScreen(tagID) || entrance == _tvYST || platform == _tvYST || entrance == _creativeCenter || platform == _creativeCenter {
_, err = s.session(c, mid, tagID, buvid, system, version, "", content, imgURL, logURL, device, channel, ip, entrance, netState, netOperator, agencyArea, platform, browser, qq, email, now)
if err != nil {
log.Error("s.session error (%v)", err)
return
}
} else {
stat := ssn.State
if ssn.State == model.StateReplied {
stat = model.StateRepeated
}
var ip32 uint32
ipv := net.ParseIP(ip)
if ip2 := ipv.To4(); ip2 != nil {
ip32 = model.InetAtoN(ip)
}
ssn = &model.Session{
ID: ssn.ID,
Device: device,
Channel: channel,
IP: ip32,
NetState: netState,
NetOperator: netOperator,
AgencyArea: agencyArea,
Platform: platform,
Browser: browser,
QQ: qq,
Email: email,
State: stat,
LasterTime: cTime,
MTime: cTime,
}
if _, err = s.dao.UpdateSession(c, ssn); err != nil {
log.Error("s.dao.UpdateSession error(%v)", err)
return
}
if content != "" || logURL != "" {
reply = &model.Reply{
SessionID: ssn.ID,
ReplyID: replyID,
Content: content,
ImgURL: "",
LogURL: logURL,
CTime: cTime,
MTime: cTime,
}
if id, err = s.dao.AddReply(c, reply); err != nil {
log.Error("s.dao.AddReply error(%v)", err)
return
}
}
for _, v := range strings.Split(imgURL, ";") {
if v != "" {
cTime = xtime.Time(now.Unix())
reply = &model.Reply{
SessionID: ssn.ID,
ReplyID: replyID,
Content: "",
ImgURL: v,
LogURL: "",
CTime: cTime,
MTime: cTime,
}
if id, err = s.dao.AddReply(c, reply); err != nil {
log.Error("s.dao.AddReply error(%v)", err)
continue
}
}
}
}
}
r = &model.Reply{
ID: id,
ReplyID: replyID,
Type: model.TypeCustomer,
Content: content,
ImgURL: imgURL,
LogURL: logURL,
CTime: cTime,
}
return
}
// AddWebReply add web reply.
func (s *Service) AddWebReply(c context.Context, mid, sid, tagID int64, aid, content, imgURL, netState, netOperator, agencyArea, platform, version, buvid, browser, qq, email string, now time.Time) (r *model.Reply, err error) {
var (
cTime xtime.Time
reply *model.Reply
tx *sql.Tx
rid int
id int64
sessionID int64
replyID string
ip = metadata.String(c, metadata.RemoteIP)
)
if mid != 0 {
replyID = strconv.FormatInt(mid, 10)
}
sessionID = sid
cTime = xtime.Time(now.Unix())
if buvid == "" {
buvid = strconv.FormatInt(now.Unix(), 10)
}
rid, err = s.dao.JudgeSsnRecord(c, sessionID)
if err != nil {
log.Error("s.dao.JudgeSsnRecord error(%v)", err)
return
}
if sessionID > 0 || rid > 0 {
reply = &model.Reply{
SessionID: sessionID,
ReplyID: replyID,
Content: content,
ImgURL: imgURL,
CTime: cTime,
MTime: cTime,
}
tx, err = s.dao.BeginTran(c)
if err != nil {
log.Error("s.dao.Begin error(%v)", err)
return
}
if id, err = s.dao.TxAddReply(tx, reply); err != nil {
log.Error("s.dao.TxAddReply error(%v)", err)
tx.Rollback()
return
}
if err = s.dao.TxUpdateSessionState(tx, model.StateNoReply, sessionID); err != nil {
log.Error("s.dao.TxUpdateSessionState error(%v)", err)
tx.Rollback()
return
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(), error(%v)", err)
return
}
r = &model.Reply{
ID: id,
ReplyID: replyID,
Type: model.TypeCustomer,
Content: content,
ImgURL: imgURL,
CTime: cTime,
}
} else {
sessionID, err = s.session(c, mid, tagID, buvid, "", version, aid, content, imgURL, "", "", "", ip, "", netState, netOperator, agencyArea, platform, browser, qq, email, now)
if err == nil {
reply = &model.Reply{
SessionID: sessionID,
ReplyID: replyID,
Content: content,
ImgURL: imgURL,
CTime: cTime,
MTime: cTime,
}
if _, err = s.dao.AddReply(c, reply); err != nil {
log.Error("s.dao.AddReplyNoTx error(%v)", err)
return
}
}
}
return
}
func (s *Service) session(c context.Context, mid, tagID int64, buvid, system, version string, aid, content, imgURL, logURL, device, channel, ip, entrance,
netState, netOperator, agencyArea, platform, browser, qq, email string, now time.Time) (sid int64, err error) {
if platform == "ugc" {
var count int
// 通过平台进行处理
count, err = s.dao.SessionCount(c, mid)
if err != nil {
log.Error("s.dao.SessionCount error(%v)", err)
return
}
if count >= 10 {
err = ecode.FeedbackContentOver
return
}
}
cTime := xtime.Time(now.Unix())
var ip32 uint32
ipv := net.ParseIP(ip)
if ip2 := ipv.To4(); ip2 != nil {
ip32 = model.InetAtoN(ip)
}
ssn := &model.Session{
Buvid: buvid,
System: system,
Version: version,
Mid: mid,
Aid: aid,
Content: content,
ImgURL: imgURL,
LogURL: logURL,
Device: device,
Channel: channel,
IP: ip32,
NetState: netState,
NetOperator: netOperator,
AgencyArea: agencyArea,
Platform: platform,
Browser: browser,
QQ: qq,
Email: email,
State: model.StateNoReply,
LasterTime: cTime,
CTime: cTime,
MTime: cTime,
}
if entrance == "view" || content == "播放器反馈日志" || entrance == _androidLivePink || entrance == _iosLivePink || platform == _androidLivePink || platform == _iosLivePink || model.IsPlayerScreen(tagID) || entrance == _tvYST || platform == _tvYST || entrance == _creativeCenter || platform == _creativeCenter {
ssn.State = model.StateOther
}
var tx *sql.Tx
tx, err = s.dao.BeginTran(c)
if err != nil {
log.Error("s.dao.Begin error(%v)", err)
return
}
sid, err = s.dao.TxAddSession(tx, ssn)
if err != nil {
log.Error("s.dao.TxAddSession error(%v)", err)
tx.Rollback()
return
}
if err = s.dao.TxUpSsnMtime(tx, now, sid); err != nil {
log.Error("s.dao.TxUpSsnMtime error(%v)", err)
tx.Rollback()
return
}
ssn.ID = sid
if tagID > 0 {
if _, err = s.dao.TxAddSessionTag(tx, ssn.ID, tagID, now); err != nil {
log.Error("s.dao.TxAddSessionTag error(%v)", err)
tx.Rollback()
return
}
}
if err = tx.Commit(); err != nil {
log.Error("tx.Commit(), error(%v)", err)
}
return
}
// Replys show all feedback replays
func (s *Service) Replys(c context.Context, buvid, platform, mobiApp, device, system, version, entrance string, mid int64, pn, ps int) (rs []model.Reply, isEndReply bool, err error) {
var (
replyID = buvid
ssns []*model.Session
ssn *model.Session
r model.Reply
tmp []model.Reply
offset int
limit int
start int
end int
rsl int
)
offset = (pn - 1) * ps
limit = ps
if mid != 0 {
if platform == "android" {
if entrance == _androidCreative {
platform = _androidCreative
} else if entrance == _androidLivePink {
platform = _androidLivePink
} else if model.Plat(mobiApp, device) == model.PlatAndroid {
platform = _androidFeedback
} else if model.Plat(mobiApp, device) == model.PlatAndroidI {
platform = _androidFeedbackI
}
} else if platform == "ios" {
if entrance == _iosCreative {
platform = _iosCreative
} else if entrance == _iosLivePink {
platform = _iosLivePink
} else if model.Plat(mobiApp, device) == model.PlatIPhone {
platform = _iosFeedback
} else if model.Plat(mobiApp, device) == model.PlatIPhoneI {
platform = _iosFeedbackI
}
}
if ssns, err = s.dao.SessionByMid(c, mid, platform); err != nil {
log.Error("s.dao.SessionByMid(%d) error(%v)", mid, err)
return
}
if len(ssns) == 0 {
err = ecode.FeedbackSsnNotExist
return
}
replyID = strconv.FormatInt(mid, 10)
if tmp, err = s.dao.ReplysByMid(c, mid, offset, limit); err != nil {
log.Error("s.dao.ReplysByMid(%d) error(%v)", mid, err)
return
}
if len(tmp) > 0 {
rs = tmp
}
for _, ssn = range ssns {
r = model.Reply{
ID: ssn.ID,
ReplyID: replyID,
Type: model.TypeCustomer,
Content: ssn.Content,
ImgURL: ssn.ImgURL,
LogURL: ssn.LogURL,
CTime: ssn.CTime,
}
rs = append(rs, r)
}
sort.Sort(model.Replys(rs))
rsl = len(rs)
end = rsl
if limit > rsl {
start = 0
isEndReply = true
} else {
start = end - limit
}
rs = rs[start:end]
} else {
if ssn, err = s.dao.Session(c, buvid, system, version, mid); err != nil {
log.Error("s.dao.Session(%s,%s,%s,%d) error(%v)", buvid, system, version, mid, err)
return
}
if ssn == nil {
err = ecode.FeedbackSsnNotExist
return
}
if tmp, err = s.dao.Replys(c, ssn.ID, offset, limit); err != nil {
log.Error("s.dao.Replays(%d) error(%v)", ssn.ID, err)
return
}
if len(tmp) == limit {
rs = tmp
} else {
r = model.Reply{
ID: ssn.ID,
ReplyID: replyID,
Type: model.TypeCustomer,
Content: ssn.Content,
ImgURL: ssn.ImgURL,
LogURL: ssn.LogURL,
CTime: ssn.CTime,
}
rs = append(tmp, r)
isEndReply = true
}
sort.Sort(model.Replys(rs))
}
return
}
// Sessions sessions.
func (s *Service) Sessions(c context.Context, mid int64, state string, tagID, platform string, start, end time.Time, ps, pn int) (total int, wssns []*model.WebSession, err error) {
var (
sids, sidsTmp, tids, intersect, sidTmp, sidCut []int64
limit, st, en, sls int
ssnMap map[int64]*model.Session
ssns []*model.Session
)
if mid > 0 {
ssns, err = s.dao.SessionByMid(c, mid, platform)
if err != nil {
log.Error("s.dao.SessionByMid error(%v)", err)
return
}
for _, v := range ssns {
sids = append(sids, v.ID)
}
if tagID != "" {
tids, err = xstr.SplitInts(tagID)
if err != nil {
log.Error("xstr.SplitInts error(%v)", err)
return
}
if len(tids) > 0 {
sidsTmp, err = s.dao.SessionIDByTagID(c, tids)
if err != nil {
log.Error("s.dao.SessionIDByTagID error(%v)", err)
return
}
}
for _, v := range sids {
if contains(sidsTmp, v) {
intersect = append(intersect, v)
}
}
} else {
intersect = sids
}
}
if len(intersect) > 0 {
if state == "" {
ssns, err = s.dao.SSnBySsnIDAllSate(c, intersect, start, end)
if err != nil {
log.Error("s.dao.sSessionBySsnID error(%v)", err)
return
}
} else {
ssns, err = s.dao.SessionBySsnID(c, intersect, state, start, end)
if err != nil {
log.Error("s.dao.sSessionBySsnID error(%v)", err)
return
}
}
ssnMap = make(map[int64]*model.Session, len(ssns))
for _, v := range ssns {
sidTmp = append(sidTmp, v.ID)
ssnMap[v.ID] = v
}
sls = len(sidTmp)
total = sls
limit = ps
if limit > sls {
st = 0
en = sls
} else {
st = (pn - 1) * limit
en = pn * limit
if en > sls {
en = st + (sls % limit)
}
}
}
sidCut = sidTmp[st:en]
if len(sidCut) > 0 {
var tagsMap map[int64][]*model.Tag
tagsMap, err = s.dao.TagBySsnID(c, sidCut)
if err != nil {
log.Error("s.dao.TagBySsnID error(%v)", err)
return
}
for _, v := range sidCut {
wssn := &model.WebSession{}
wssn.Session = ssnMap[v]
tags := tagsMap[v]
if len(tags) == 0 {
wssn.Tag = defaultTag
} else {
wssn.Tag = tags[len(tags)-1]
}
wssns = append(wssns, wssn)
}
}
return
}
// UpdateSessionState up session state.
func (s *Service) UpdateSessionState(c context.Context, state int, sid int64) (err error) {
if err = s.dao.UpdateSessionState(c, state, sid); err != nil {
log.Error("s.dao.UpdateSessionState error(%v)", err)
}
return
}
// Tags tags.
func (s *Service) Tags(c context.Context, mid int64, mold int, platform string) (tag *model.UGCTag, err error) {
tags, err := s.dao.Tags(c, mold, platform)
if err != nil {
log.Error("s.dao.Tags error(%v)", err)
return
}
cnt, err := s.dao.SessionCount(c, mid)
if err != nil {
log.Error("s.dao.SessionCount error(%v)", err)
return
}
tag = &model.UGCTag{
Tags: tags,
Limit: 10 - cnt,
}
return
}
// WebReplys web replys.
func (s *Service) WebReplys(c context.Context, sid, mid int64) (mp []*model.Reply, err error) {
if mp, err = s.dao.WebReplys(c, sid, mid); err != nil {
log.Error("s.dao.WebReplys(%d, %d) error(%v)", sid, mid, err)
return
}
if len(mp) == 0 {
err = ecode.NothingFound
}
return
}
func contains(s []int64, e int64) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
// PlayerCheck check for player.
func (s *Service) PlayerCheck(c context.Context, platform, ipChangeTimes int, mid, checkTime, aid, connectSpeed, ioSpeed int64, region, school, cdnip string) (err error) {
var (
ipinfo *locmdl.Info
isp int
ipAddr = metadata.String(c, metadata.RemoteIP)
)
if ipinfo, err = s.locationRPC.Info(c, &locmdl.ArgIP{IP: ipAddr}); err != nil {
log.Error("s.locationRPC.Info(%s) error(%v)", ipAddr, err)
}
if ipinfo != nil {
if strings.Contains(ipinfo.ISP, "移动") {
isp = 1
} else if strings.Contains(ipinfo.ISP, "联通") {
isp = 2
} else if strings.Contains(ipinfo.ISP, "电信") {
isp = 3
} else {
isp = 0
}
}
if _, err = s.dao.InPlayCheck(c, platform, isp, ipChangeTimes, mid, checkTime, aid, connectSpeed, ioSpeed, region, school, ipAddr, cdnip); err != nil {
log.Error("s.dao.InPlayCheck(%d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %s) error(%v)", platform, isp, ipChangeTimes, mid, checkTime, aid, connectSpeed, ioSpeed, region, school, ipAddr, cdnip, err)
}
return
}

View File

@@ -0,0 +1,37 @@
package service
import (
"context"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Replys(t *testing.T) {
Convey("Replys", t, WithService(func(s *Service) {
_, _, err := s.Replys(context.Background(), "1466046600", "ios", "0", "phone", "abc", "123", "android-creative", 11111, 1, 10)
So(err, ShouldBeNil)
}))
}
func Test_Sessions(t *testing.T) {
Convey("Sessions", t, WithService(func(s *Service) {
_, _, err := s.Sessions(context.Background(), 1, "2", "3", "ios", time.Now(), time.Now(), 1, 10)
So(err, ShouldBeNil)
}))
}
func Test_Tags(t *testing.T) {
Convey("Tags", t, WithService(func(s *Service) {
_, err := s.Tags(context.Background(), 1, 1, "ios")
So(err, ShouldBeNil)
}))
}
func Test_WebReplys(t *testing.T) {
Convey("WebReplys", t, WithService(func(s *Service) {
_, err := s.WebReplys(context.Background(), 1, 1)
So(err, ShouldBeNil)
}))
}

View File

@@ -0,0 +1,36 @@
package service
import (
"context"
"go-common/app/interface/main/feedback/conf"
"go-common/app/interface/main/feedback/dao"
locrpc "go-common/app/service/main/location/rpc/client"
)
// Service struct.
type Service struct {
// dao
dao *dao.Dao
// conf
c *conf.Config
// rpc
locationRPC *locrpc.Service
}
// New new Tag service.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
// rpc
locationRPC: locrpc.New(c.LocationRPC),
}
// init dao
s.dao = dao.New(c)
return
}
// Ping check server ok
func (s *Service) Ping(c context.Context) (err error) {
return
}

View File

@@ -0,0 +1,27 @@
package service
import (
"flag"
"path/filepath"
"time"
"go-common/app/interface/main/feedback/conf"
)
var (
s *Service
)
func init() {
dir, _ := filepath.Abs("../cmd/convey-test.toml")
flag.Set("conf", dir)
conf.Init()
s = New(conf.Conf)
time.Sleep(time.Second)
}
func WithService(f func(s *Service)) func() {
return func() {
f(s)
}
}