Create & Init Project...
This commit is contained in:
21
app/admin/main/relation/BUILD
Normal file
21
app/admin/main/relation/BUILD
Normal file
@ -0,0 +1,21 @@
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//app/admin/main/relation/cmd:all-srcs",
|
||||
"//app/admin/main/relation/conf:all-srcs",
|
||||
"//app/admin/main/relation/dao:all-srcs",
|
||||
"//app/admin/main/relation/http:all-srcs",
|
||||
"//app/admin/main/relation/model:all-srcs",
|
||||
"//app/admin/main/relation/service:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
27
app/admin/main/relation/CHANGELOG.md
Normal file
27
app/admin/main/relation/CHANGELOG.md
Normal file
@ -0,0 +1,27 @@
|
||||
# v1.2.0
|
||||
1. gorpc 调用account改成grpc
|
||||
|
||||
# v1.1.1
|
||||
1. 通过调用rpc方式调用relation-service提供 stat,stats 接口
|
||||
|
||||
# v1.1.0
|
||||
1. 添加 stat 接口
|
||||
|
||||
# v1.0.5
|
||||
1. 删除remoteIP方法
|
||||
|
||||
# v1.0.4
|
||||
1. 使用新 hbase client
|
||||
|
||||
# v1.0.3
|
||||
1. 修改最大数量到 20000
|
||||
|
||||
# v1.0.2
|
||||
1. 增加最大数量到 100000
|
||||
|
||||
# v1.0.1
|
||||
1. 增加权限点限制
|
||||
|
||||
# v1.0.0
|
||||
1. 查询用户粉丝列表与关注列表
|
||||
2. 使用 account-service V7
|
9
app/admin/main/relation/CONTRIBUTORS.md
Normal file
9
app/admin/main/relation/CONTRIBUTORS.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Owner
|
||||
linmiao
|
||||
zhoujiahui
|
||||
|
||||
# Author
|
||||
zhoujiahui
|
||||
|
||||
# Reviewer
|
||||
zhoujiahui
|
13
app/admin/main/relation/OWNERS
Normal file
13
app/admin/main/relation/OWNERS
Normal file
@ -0,0 +1,13 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- linmiao
|
||||
- zhoujiahui
|
||||
labels:
|
||||
- admin
|
||||
- admin/main/relation
|
||||
- main
|
||||
options:
|
||||
no_parent_owners: true
|
||||
reviewers:
|
||||
- zhoujiahui
|
13
app/admin/main/relation/README.md
Normal file
13
app/admin/main/relation/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
# relation-admin
|
||||
|
||||
# 项目简介
|
||||
1. 关系链后台服务
|
||||
|
||||
# 编译环境
|
||||
|
||||
|
||||
# 依赖包
|
||||
|
||||
|
||||
# 编译执行
|
||||
|
42
app/admin/main/relation/cmd/BUILD
Normal file
42
app/admin/main/relation/cmd/BUILD
Normal file
@ -0,0 +1,42 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_binary",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_binary(
|
||||
name = "cmd",
|
||||
embed = [":go_default_library"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["main.go"],
|
||||
data = ["relation-test.toml"],
|
||||
importpath = "go-common/app/admin/main/relation/cmd",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/admin/main/relation/conf:go_default_library",
|
||||
"//app/admin/main/relation/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"],
|
||||
)
|
49
app/admin/main/relation/cmd/main.go
Normal file
49
app/admin/main/relation/cmd/main.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"go-common/app/admin/main/relation/conf"
|
||||
"go-common/app/admin/main/relation/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.Log)
|
||||
defer log.Close()
|
||||
log.Info("relation 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("relation get a signal %s", s.String())
|
||||
switch s {
|
||||
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
|
||||
log.Info("relation exit")
|
||||
return
|
||||
case syscall.SIGHUP:
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
74
app/admin/main/relation/cmd/relation-test.toml
Normal file
74
app/admin/main/relation/cmd/relation-test.toml
Normal file
@ -0,0 +1,74 @@
|
||||
# This is a TOML document. Boom
|
||||
|
||||
[log]
|
||||
stdout=true
|
||||
|
||||
[orm]
|
||||
[orm.read]
|
||||
dsn = "account:wx2U1MwXRyWEuURw@tcp(172.16.33.205:3306)/relation?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
|
||||
active = 20
|
||||
idle = 10
|
||||
idleTimeout = "4h"
|
||||
|
||||
[auth]
|
||||
managerHost = "http://uat-manager.bilibili.co"
|
||||
dashboardHost = "http://uat-dashboard-mng.bilibili.co"
|
||||
dashboardCaller = "manager-go"
|
||||
[auth.DsHTTPClient]
|
||||
key = "manager-go"
|
||||
secret = "949bbb2dd3178252638c2407578bc7ad"
|
||||
dial = "1s"
|
||||
timeout = "1s"
|
||||
keepAlive = "60s"
|
||||
[auth.DsHTTPClient.breaker]
|
||||
window = "3s"
|
||||
sleep = "100ms"
|
||||
bucket = 10
|
||||
ratio = 0.5
|
||||
request = 100
|
||||
[auth.MaHTTPClient]
|
||||
key = "f6433799dbd88751"
|
||||
secret = "36f8ddb1806207fe07013ab6a77a3935"
|
||||
dial = "1s"
|
||||
timeout = "1s"
|
||||
keepAlive = "60s"
|
||||
[auth.MaHTTPClient.breaker]
|
||||
window = "3s"
|
||||
sleep = "100ms"
|
||||
bucket = 10
|
||||
ratio = 0.5
|
||||
request = 100
|
||||
[auth.session]
|
||||
sessionIDLength = 32
|
||||
cookieLifeTime = 1800
|
||||
cookieName = "mng-go"
|
||||
domain = ".bilibili.co"
|
||||
[auth.session.Memcache]
|
||||
name = "go-business/auth"
|
||||
proto = "tcp"
|
||||
addr = "172.16.33.54:11211"
|
||||
active = 10
|
||||
idle = 10
|
||||
dialTimeout = "1s"
|
||||
readTimeout = "1s"
|
||||
writeTimeout = "1s"
|
||||
idleTimeout = "80s"
|
||||
|
||||
[RelationGRPC]
|
||||
timeout="3s"
|
||||
|
||||
[AccountGRPC]
|
||||
timeout="3s"
|
||||
|
||||
[loghbase]
|
||||
master = ""
|
||||
meta = ""
|
||||
dialTimeout = "1s"
|
||||
readTimeout = "10s"
|
||||
readsTimeout = "10s"
|
||||
writeTimeout = "10s"
|
||||
writesTimeout = "10s"
|
||||
[loghbase.zookeeper]
|
||||
root = ""
|
||||
addrs =["bigdata-test-02:2181","bigdata-test-03:2181","bigdata-test-04:2181"]
|
||||
timeout = "30s"
|
39
app/admin/main/relation/conf/BUILD
Normal file
39
app/admin/main/relation/conf/BUILD
Normal file
@ -0,0 +1,39 @@
|
||||
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/admin/main/relation/conf",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//library/conf:go_default_library",
|
||||
"//library/database/hbase.v2:go_default_library",
|
||||
"//library/database/orm:go_default_library",
|
||||
"//library/ecode/tip:go_default_library",
|
||||
"//library/log:go_default_library",
|
||||
"//library/net/http/blademaster:go_default_library",
|
||||
"//library/net/http/blademaster/middleware/permit:go_default_library",
|
||||
"//library/net/rpc/warden: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"],
|
||||
)
|
102
app/admin/main/relation/conf/conf.go
Normal file
102
app/admin/main/relation/conf/conf.go
Normal file
@ -0,0 +1,102 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
|
||||
"go-common/library/conf"
|
||||
"go-common/library/database/hbase.v2"
|
||||
"go-common/library/database/orm"
|
||||
ecode "go-common/library/ecode/tip"
|
||||
"go-common/library/log"
|
||||
bm "go-common/library/net/http/blademaster"
|
||||
"go-common/library/net/http/blademaster/middleware/permit"
|
||||
"go-common/library/net/rpc/warden"
|
||||
"go-common/library/net/trace"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
// global var
|
||||
var (
|
||||
confPath string
|
||||
client *conf.Client
|
||||
// Conf config
|
||||
Conf = &Config{}
|
||||
)
|
||||
|
||||
// Config config set
|
||||
type Config struct {
|
||||
// base
|
||||
// elk
|
||||
Log *log.Config
|
||||
// http
|
||||
BM *bm.ServerConfig
|
||||
// tracer
|
||||
Tracer *trace.Config
|
||||
// MySQL
|
||||
ORM *ORM
|
||||
// ecode
|
||||
Ecode *ecode.Config
|
||||
Auth *permit.Config
|
||||
LogTable string
|
||||
LogHBase *hbase.Config
|
||||
RelationGRPC *warden.ClientConfig
|
||||
AccountGRPC *warden.ClientConfig
|
||||
}
|
||||
|
||||
// ORM is the orm db config for relation
|
||||
type ORM struct {
|
||||
Read *orm.Config
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&confPath, "conf", "", "default 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
|
||||
}
|
66
app/admin/main/relation/dao/BUILD
Normal file
66
app/admin/main/relation/dao/BUILD
Normal file
@ -0,0 +1,66 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"account_test.go",
|
||||
"dao_test.go",
|
||||
"relation_log_test.go",
|
||||
"relation_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
rundir = ".",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/admin/main/relation/conf:go_default_library",
|
||||
"//library/database/hbase.v2:go_default_library",
|
||||
"//vendor/github.com/bouk/monkey:go_default_library",
|
||||
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
|
||||
"//vendor/github.com/tsuna/gohbase/hrpc:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"account.go",
|
||||
"dao.go",
|
||||
"relation.go",
|
||||
"relation_log.go",
|
||||
],
|
||||
importpath = "go-common/app/admin/main/relation/dao",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/admin/main/relation/conf:go_default_library",
|
||||
"//app/admin/main/relation/model:go_default_library",
|
||||
"//app/service/main/account/api:go_default_library",
|
||||
"//app/service/main/account/model:go_default_library",
|
||||
"//app/service/main/relation/api:go_default_library",
|
||||
"//library/database/hbase.v2:go_default_library",
|
||||
"//library/database/orm:go_default_library",
|
||||
"//library/log:go_default_library",
|
||||
"//library/net/metadata:go_default_library",
|
||||
"//library/sync/errgroup:go_default_library",
|
||||
"//vendor/github.com/jinzhu/gorm:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
76
app/admin/main/relation/dao/account.go
Normal file
76
app/admin/main/relation/dao/account.go
Normal file
@ -0,0 +1,76 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
accountApi "go-common/app/service/main/account/api"
|
||||
account "go-common/app/service/main/account/model"
|
||||
"go-common/library/log"
|
||||
"go-common/library/sync/errgroup"
|
||||
)
|
||||
|
||||
const (
|
||||
_perCall = 50
|
||||
)
|
||||
|
||||
func chain(ids ...[]int64) []int64 {
|
||||
res := make([]int64, 0, len(ids))
|
||||
for _, l := range ids {
|
||||
res = append(res, l...)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func uniq(ids ...[]int64) []int64 {
|
||||
hm := make(map[int64]struct{})
|
||||
for _, i := range chain(ids...) {
|
||||
hm[i] = struct{}{}
|
||||
}
|
||||
res := make([]int64, 0, len(ids))
|
||||
for i := range hm {
|
||||
res = append(res, i)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// RPCInfos rpc info get by muti mid .
|
||||
func (d *Dao) RPCInfos(c context.Context, mids []int64) (res map[int64]*account.Info, err error) {
|
||||
var (
|
||||
g errgroup.Group
|
||||
l sync.RWMutex
|
||||
)
|
||||
mids = uniq(mids)
|
||||
total := len(mids)
|
||||
pageNum := total / _perCall
|
||||
if total%_perCall != 0 {
|
||||
pageNum++
|
||||
}
|
||||
res = make(map[int64]*account.Info, total)
|
||||
for i := 0; i < pageNum; i++ {
|
||||
start := i * _perCall
|
||||
end := (i + 1) * _perCall
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
g.Go(func() (err error) {
|
||||
midsReq := &accountApi.MidsReq{Mids: mids[start:end]}
|
||||
infosReply, err := d.accountClient.Infos3(c, midsReq)
|
||||
if err != nil {
|
||||
log.Error("d.accountClient.Infos3(%+v) error(%v)", midsReq, err)
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
for mid, info := range infosReply.Infos {
|
||||
l.Lock()
|
||||
res[mid] = info
|
||||
l.Unlock()
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
if err = g.Wait(); err != nil {
|
||||
log.Error("g.Wait error(%v)", err)
|
||||
}
|
||||
return
|
||||
}
|
16
app/admin/main/relation/dao/account_test.go
Normal file
16
app/admin/main/relation/dao/account_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestRPCInfos(t *testing.T) {
|
||||
convey.Convey("RPCInfos", t, func() {
|
||||
infos, err := d.RPCInfos(context.TODO(), []int64{1, 2, 3})
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(infos, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
58
app/admin/main/relation/dao/dao.go
Normal file
58
app/admin/main/relation/dao/dao.go
Normal file
@ -0,0 +1,58 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go-common/app/admin/main/relation/conf"
|
||||
accountGRPC "go-common/app/service/main/account/api"
|
||||
relationGRPC "go-common/app/service/main/relation/api"
|
||||
"go-common/library/database/hbase.v2"
|
||||
"go-common/library/database/orm"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
// Dao dao
|
||||
type Dao struct {
|
||||
c *conf.Config
|
||||
ReadORM *gorm.DB
|
||||
accountClient accountGRPC.AccountClient
|
||||
relationClient relationGRPC.RelationClient
|
||||
hbase *hbase.Client
|
||||
}
|
||||
|
||||
// New init mysql db
|
||||
func New(c *conf.Config) (dao *Dao) {
|
||||
dao = &Dao{
|
||||
c: c,
|
||||
ReadORM: orm.NewMySQL(c.ORM.Read),
|
||||
hbase: hbase.NewClient(c.LogHBase),
|
||||
}
|
||||
relationGRPCClient, err := relationGRPC.NewClient(c.RelationGRPC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
accountGRPCClient, err := accountGRPC.NewClient(c.AccountGRPC)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dao.relationClient = relationGRPCClient
|
||||
dao.accountClient = accountGRPCClient
|
||||
dao.initORM()
|
||||
return
|
||||
}
|
||||
|
||||
func (dao *Dao) initORM() {
|
||||
dao.ReadORM.LogMode(true)
|
||||
}
|
||||
|
||||
// Close close the resource.
|
||||
func (dao *Dao) Close() {
|
||||
dao.ReadORM.Close()
|
||||
}
|
||||
|
||||
// Ping dao ping
|
||||
func (dao *Dao) Ping(c context.Context) error {
|
||||
return dao.ReadORM.DB().PingContext(c)
|
||||
}
|
35
app/admin/main/relation/dao/dao_test.go
Normal file
35
app/admin/main/relation/dao/dao_test.go
Normal file
@ -0,0 +1,35 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"go-common/app/admin/main/relation/conf"
|
||||
)
|
||||
|
||||
var (
|
||||
d *Dao
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if os.Getenv("DEPLOY_ENV") != "" {
|
||||
flag.Set("app_id", "main.account.relation-admin")
|
||||
flag.Set("conf_token", "d79d38fa305b01c1ce59099cb0385ebf")
|
||||
flag.Set("tree_id", "21423")
|
||||
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/relation-test.toml")
|
||||
}
|
||||
flag.Parse()
|
||||
if err := conf.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
d = New(conf.Conf)
|
||||
os.Exit(m.Run())
|
||||
}
|
85
app/admin/main/relation/dao/relation.go
Normal file
85
app/admin/main/relation/dao/relation.go
Normal file
@ -0,0 +1,85 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go-common/app/admin/main/relation/model"
|
||||
relationPB "go-common/app/service/main/relation/api"
|
||||
"go-common/library/log"
|
||||
"go-common/library/net/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
_shard = 500
|
||||
_maxSize = 20000
|
||||
)
|
||||
|
||||
func midTable(mid int64) string {
|
||||
return fmt.Sprintf("user_relation_mid_%03d", mid%_shard)
|
||||
}
|
||||
|
||||
func fidTable(fid int64) string {
|
||||
return fmt.Sprintf("user_relation_fid_%03d", fid%_shard)
|
||||
}
|
||||
|
||||
// Followers is
|
||||
func (d *Dao) Followers(ctx context.Context, fid int64, mid int64) (model.RelationList, error) {
|
||||
list := model.RelationList{}
|
||||
db := d.ReadORM.Table(fidTable(fid)).Where("status=?", 0).Where("fid=?", fid).Limit(_maxSize).Order("id desc")
|
||||
if mid > 0 {
|
||||
db = db.Where("mid=?", mid).Limit(1)
|
||||
}
|
||||
if err := db.Find(&list).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range list {
|
||||
f.ParseRelation()
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Followings is
|
||||
func (d *Dao) Followings(ctx context.Context, mid int64, fid int64) (model.RelationList, error) {
|
||||
list := model.RelationList{}
|
||||
db := d.ReadORM.Table(midTable(mid)).Where("status=?", 0).Where("mid=?", mid).Limit(_maxSize).Order("id desc")
|
||||
if fid > 0 {
|
||||
db = db.Where("fid=?", fid).Limit(1)
|
||||
}
|
||||
if err := db.Find(&list).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range list {
|
||||
f.ParseRelation()
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// Stat is
|
||||
func (d *Dao) Stat(ctx context.Context, mid int64) (*relationPB.StatReply, error) {
|
||||
stat, err := d.relationClient.Stat(ctx, &relationPB.MidReq{
|
||||
Mid: mid,
|
||||
RealIp: metadata.String(ctx, metadata.RemoteIP),
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("d.relationRPC.Stat err(%+v)", err)
|
||||
return nil, err
|
||||
}
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
// Stats is
|
||||
func (d *Dao) Stats(ctx context.Context, mids []int64) (map[int64]*relationPB.StatReply, error) {
|
||||
statReply, err := d.relationClient.Stats(ctx, &relationPB.MidsReq{
|
||||
Mids: mids,
|
||||
RealIp: metadata.String(ctx, metadata.RemoteIP),
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("d.relationRPC.Stats err(%+v)", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(statReply.StatReplyMap) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return statReply.StatReplyMap, nil
|
||||
}
|
84
app/admin/main/relation/dao/relation_log.go
Normal file
84
app/admin/main/relation/dao/relation_log.go
Normal file
@ -0,0 +1,84 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go-common/app/admin/main/relation/model"
|
||||
"go-common/library/log"
|
||||
)
|
||||
|
||||
// reverse returns its argument string reversed rune-wise left to right.
|
||||
func reverse(s string) string {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func rpad(s string, l int) string {
|
||||
dt := l - len(s)
|
||||
if dt <= 0 {
|
||||
return s
|
||||
}
|
||||
return s + strings.Repeat("0", dt)
|
||||
}
|
||||
|
||||
func logKey(mid, fid, ts int64) string {
|
||||
midStr := rpad(reverse(strconv.FormatInt(mid, 10)), 10)
|
||||
fidStr := rpad(reverse(strconv.FormatInt(fid, 10)), 10)
|
||||
tsStr := strconv.FormatInt(ts, 10)
|
||||
return midStr + fidStr + tsStr
|
||||
}
|
||||
|
||||
// RelationLogs is used to retriev relation log.
|
||||
func (d *Dao) RelationLogs(ctx context.Context, mid, fid int64, from time.Time, to time.Time) (model.RelationLogList, error) {
|
||||
scanner, err := d.hbase.ScanRangeStr(ctx, d.c.LogTable, logKey(mid, fid, from.Unix()), logKey(mid, fid, to.Unix()))
|
||||
if err != nil {
|
||||
log.Error("Failed to d.hbase.Scan(): %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logs := make(model.RelationLogList, 0)
|
||||
|
||||
for {
|
||||
r, err := scanner.Next()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
l := &model.RelationLog{
|
||||
Mid: mid,
|
||||
Fid: fid,
|
||||
}
|
||||
for _, c := range r.Cells {
|
||||
key := string(c.Row)
|
||||
qf := string(c.Qualifier)
|
||||
v := string(c.Value)
|
||||
log.Info("Retrieving relation log: mid(%d) fid(%d) key(%s) qualifier(%s) value(%s)", mid, fid, key, qf, v)
|
||||
|
||||
// fill fields
|
||||
switch qf {
|
||||
case "att":
|
||||
l.Attention = model.ParseAction(v)
|
||||
case "bl":
|
||||
l.Black = model.ParseAction(v)
|
||||
case "wh":
|
||||
l.Whisper = model.ParseAction(v)
|
||||
case "src":
|
||||
l.Source = model.ParseSource(v)
|
||||
case "mt":
|
||||
l.MTime, _ = model.ParseLogTime(v)
|
||||
}
|
||||
}
|
||||
l.FillAttrField()
|
||||
logs = append(logs, l)
|
||||
}
|
||||
return logs, nil
|
||||
}
|
85
app/admin/main/relation/dao/relation_log_test.go
Normal file
85
app/admin/main/relation/dao/relation_log_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go-common/library/database/hbase.v2"
|
||||
|
||||
"github.com/bouk/monkey"
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
"github.com/tsuna/gohbase/hrpc"
|
||||
)
|
||||
|
||||
func TestDaoreverse(t *testing.T) {
|
||||
convey.Convey("reverse", t, func(convCtx convey.C) {
|
||||
var (
|
||||
s = ""
|
||||
)
|
||||
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
|
||||
p1 := reverse(s)
|
||||
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
|
||||
convCtx.So(p1, convey.ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaorpad(t *testing.T) {
|
||||
convey.Convey("rpad", t, func(convCtx convey.C) {
|
||||
var (
|
||||
s = ""
|
||||
l = int(0)
|
||||
)
|
||||
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
|
||||
p1 := rpad(s, l)
|
||||
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
|
||||
convCtx.So(p1, convey.ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaologKey(t *testing.T) {
|
||||
convey.Convey("logKey", t, func(convCtx convey.C) {
|
||||
var (
|
||||
mid = int64(0)
|
||||
fid = int64(0)
|
||||
ts = int64(0)
|
||||
)
|
||||
convCtx.Convey("When everything goes positive", func(convCtx convey.C) {
|
||||
p1 := logKey(mid, fid, ts)
|
||||
convCtx.Convey("Then p1 should not be nil.", func(convCtx convey.C) {
|
||||
convCtx.So(p1, convey.ShouldNotBeNil)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaoRelationLogs(t *testing.T) {
|
||||
convey.Convey("RelationLogs", t, func(convCtx convey.C) {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
mid = int64(0)
|
||||
fid = int64(0)
|
||||
from = time.Now()
|
||||
to = time.Now()
|
||||
)
|
||||
convCtx.Convey("RelationLogs failed", func(convCtx convey.C) {
|
||||
monkey.PatchInstanceMethod(reflect.TypeOf(d.hbase), "ScanRangeStr", func(_ *hbase.Client, _ context.Context, _ string, _ string, _ string, _ ...func(hrpc.Call) error) (hrpc.Scanner, error) {
|
||||
return nil, fmt.Errorf("hbase scan err")
|
||||
})
|
||||
defer monkey.UnpatchAll()
|
||||
p1, err := d.RelationLogs(ctx, mid, fid, from, to)
|
||||
convCtx.Convey("Then err should be nil.p1 should not be nil.", func(convCtx convey.C) {
|
||||
convCtx.So(err, convey.ShouldNotBeNil)
|
||||
convCtx.So(p1, convey.ShouldBeNil)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
}
|
49
app/admin/main/relation/dao/relation_test.go
Normal file
49
app/admin/main/relation/dao/relation_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"testing"
|
||||
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestFollowers(t *testing.T) {
|
||||
convey.Convey("Followers", t, func() {
|
||||
rl, err := d.Followers(context.TODO(), 1, 2)
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(rl, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFollowings(t *testing.T) {
|
||||
convey.Convey("Followings", t, func() {
|
||||
rl, err := d.Followings(context.TODO(), 1, 2)
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(rl, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStat(t *testing.T) {
|
||||
var (
|
||||
c = context.Background()
|
||||
mid int64 = 2
|
||||
)
|
||||
convey.Convey("stat", t, func() {
|
||||
reply, err := d.Stat(c, mid)
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(reply, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStats(t *testing.T) {
|
||||
var (
|
||||
c = context.Background()
|
||||
mids = []int64{2, 3}
|
||||
)
|
||||
convey.Convey("stats", t, func() {
|
||||
replys, err := d.Stats(c, mids)
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(replys, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
38
app/admin/main/relation/http/BUILD
Normal file
38
app/admin/main/relation/http/BUILD
Normal 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 = [
|
||||
"http.go",
|
||||
"relation.go",
|
||||
],
|
||||
importpath = "go-common/app/admin/main/relation/http",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/admin/main/relation/conf:go_default_library",
|
||||
"//app/admin/main/relation/model:go_default_library",
|
||||
"//app/admin/main/relation/service:go_default_library",
|
||||
"//library/log:go_default_library",
|
||||
"//library/net/http/blademaster:go_default_library",
|
||||
"//library/net/http/blademaster/middleware/permit: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"],
|
||||
)
|
51
app/admin/main/relation/http/http.go
Normal file
51
app/admin/main/relation/http/http.go
Normal file
@ -0,0 +1,51 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"go-common/app/admin/main/relation/conf"
|
||||
"go-common/app/admin/main/relation/service"
|
||||
"go-common/library/log"
|
||||
bm "go-common/library/net/http/blademaster"
|
||||
"go-common/library/net/http/blademaster/middleware/permit"
|
||||
)
|
||||
|
||||
var (
|
||||
authSvc *permit.Permit
|
||||
svc *service.Service
|
||||
)
|
||||
|
||||
// Init init
|
||||
func Init(c *conf.Config) {
|
||||
initService(c)
|
||||
// init router
|
||||
engine := bm.DefaultServer(c.BM)
|
||||
initRouter(engine)
|
||||
if err := engine.Start(); err != nil {
|
||||
log.Error("engine.Start error(%v)", err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// initService init services.
|
||||
func initService(c *conf.Config) {
|
||||
authSvc = permit.New(c.Auth)
|
||||
svc = service.New(c)
|
||||
}
|
||||
|
||||
// initRouter init outer router api path.
|
||||
func initRouter(e *bm.Engine) {
|
||||
//init api
|
||||
e.Ping(ping)
|
||||
group := e.Group("/x/admin/relation")
|
||||
{
|
||||
group.GET("/follower/followers", authSvc.Permit("RELATION_INFO"), followers)
|
||||
group.GET("/following/followings", authSvc.Permit("RELATION_INFO"), followings)
|
||||
group.GET("/logs", authSvc.Permit("RELATION_INFO"), logs)
|
||||
group.GET("/stat", stat)
|
||||
group.GET("/stats", stats)
|
||||
}
|
||||
}
|
||||
|
||||
// ping check server ok.
|
||||
func ping(c *bm.Context) {
|
||||
svc.Ping(c)
|
||||
}
|
72
app/admin/main/relation/http/relation.go
Normal file
72
app/admin/main/relation/http/relation.go
Normal file
@ -0,0 +1,72 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go-common/app/admin/main/relation/model"
|
||||
bm "go-common/library/net/http/blademaster"
|
||||
)
|
||||
|
||||
func followers(c *bm.Context) {
|
||||
params := &model.FollowersParam{}
|
||||
if err := c.Bind(params); err != nil {
|
||||
return
|
||||
}
|
||||
if params.PN <= 0 {
|
||||
params.PN = 1
|
||||
}
|
||||
if params.PS <= 0 {
|
||||
params.PS = 50
|
||||
}
|
||||
params.Order = "mtime"
|
||||
if params.Sort != "desc" {
|
||||
params.Sort = "asc"
|
||||
}
|
||||
|
||||
c.JSON(svc.Followers(c, params))
|
||||
}
|
||||
|
||||
func followings(c *bm.Context) {
|
||||
params := &model.FollowingsParam{}
|
||||
if err := c.Bind(params); err != nil {
|
||||
return
|
||||
}
|
||||
if params.PN <= 0 {
|
||||
params.PN = 1
|
||||
}
|
||||
if params.PS <= 0 {
|
||||
params.PS = 50
|
||||
}
|
||||
params.Order = "mtime"
|
||||
if params.Sort != "desc" {
|
||||
params.Sort = "asc"
|
||||
}
|
||||
|
||||
c.JSON(svc.Followings(c, params))
|
||||
}
|
||||
|
||||
func logs(c *bm.Context) {
|
||||
params := &model.LogsParam{}
|
||||
if err := c.Bind(params); err != nil {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
from := time.Unix(0, 0)
|
||||
c.JSON(svc.RelationLog(c, params.Mid, params.Fid, from, now))
|
||||
}
|
||||
|
||||
func stat(ctx *bm.Context) {
|
||||
params := &model.ArgMid{}
|
||||
if err := ctx.Bind(params); err != nil {
|
||||
return
|
||||
}
|
||||
ctx.JSON(svc.Stat(ctx, params))
|
||||
}
|
||||
|
||||
func stats(ctx *bm.Context) {
|
||||
params := &model.ArgMids{}
|
||||
if err := ctx.Bind(params); err != nil {
|
||||
return
|
||||
}
|
||||
ctx.JSON(svc.Stats(ctx, params))
|
||||
}
|
36
app/admin/main/relation/model/BUILD
Normal file
36
app/admin/main/relation/model/BUILD
Normal file
@ -0,0 +1,36 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"log.go",
|
||||
"model.go",
|
||||
"page.go",
|
||||
"params.go",
|
||||
],
|
||||
importpath = "go-common/app/admin/main/relation/model",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/service/main/relation/model:go_default_library",
|
||||
"//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"],
|
||||
)
|
89
app/admin/main/relation/model/log.go
Normal file
89
app/admin/main/relation/model/log.go
Normal file
@ -0,0 +1,89 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
xtime "go-common/library/time"
|
||||
)
|
||||
|
||||
// RelationLog is
|
||||
type RelationLog struct {
|
||||
Mid int64 `json:"mid"`
|
||||
Fid int64 `json:"fid"`
|
||||
MemberName string `json:"member_name"`
|
||||
FollowingName string `json:"following_name"`
|
||||
|
||||
Source int32 `json:"source"`
|
||||
MTime xtime.Time `json:"mtime"`
|
||||
|
||||
Attention int32 `json:"attention"`
|
||||
Black int32 `json:"black"`
|
||||
Whisper int32 `json:"whisper"`
|
||||
AttrField string `json:"attr_field"`
|
||||
AttrChange string `json:"attr_change"`
|
||||
}
|
||||
|
||||
// FillAttrField is
|
||||
func (l *RelationLog) FillAttrField() {
|
||||
if l.Attention > 0 {
|
||||
l.AttrField = "attention"
|
||||
return
|
||||
}
|
||||
if l.Black > 0 {
|
||||
l.AttrField = "black"
|
||||
return
|
||||
}
|
||||
if l.Whisper > 0 {
|
||||
l.AttrField = "whisper"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// RelationLogList is
|
||||
type RelationLogList []*RelationLog
|
||||
|
||||
// Len is
|
||||
func (rl RelationLogList) Len() int {
|
||||
return len(rl)
|
||||
}
|
||||
|
||||
// Swap is
|
||||
func (rl RelationLogList) Swap(i, j int) {
|
||||
rl[i], rl[j] = rl[j], rl[i]
|
||||
}
|
||||
|
||||
// Less is
|
||||
func (rl RelationLogList) Less(i, j int) bool {
|
||||
return rl[i].MTime < rl[j].MTime
|
||||
}
|
||||
|
||||
// OrderByMTime is
|
||||
func (rl RelationLogList) OrderByMTime(desc bool) {
|
||||
sort.Sort(rl)
|
||||
}
|
||||
|
||||
// ParseAction is
|
||||
func ParseAction(act string) int32 {
|
||||
i, _ := strconv.ParseInt(act, 10, 32)
|
||||
return int32(i)
|
||||
}
|
||||
|
||||
// ParseSource is
|
||||
func ParseSource(src string) int32 {
|
||||
i, _ := strconv.ParseInt(src, 10, 64)
|
||||
return int32(i)
|
||||
}
|
||||
|
||||
// ParseLogTime is
|
||||
func ParseLogTime(ts string) (xt xtime.Time, err error) {
|
||||
var (
|
||||
t time.Time
|
||||
)
|
||||
if t, err = time.ParseInLocation("2006-01-02 15:04:05", ts, time.Local); err != nil {
|
||||
return
|
||||
}
|
||||
xt.Scan(t)
|
||||
return
|
||||
}
|
136
app/admin/main/relation/model/model.go
Normal file
136
app/admin/main/relation/model/model.go
Normal file
@ -0,0 +1,136 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
smodel "go-common/app/service/main/relation/model"
|
||||
"go-common/library/time"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Relation is
|
||||
type Relation struct {
|
||||
ID int64 `json:"id" gorm:"column:id"`
|
||||
Mid int64 `json:"mid" gorm:"column:mid"`
|
||||
Fid int64 `json:"fid" gorm:"column:fid"`
|
||||
Attribute uint32 `json:"attribute" gorm:"column:attribute"`
|
||||
Status int8 `json:"status" gorm:"column:status"`
|
||||
Source int8 `json:"source" gorm:"column:source"`
|
||||
CTime time.Time `json:"ctime" gorm:"column:ctime"`
|
||||
MTime time.Time `json:"mtime" gorm:"column:mtime"`
|
||||
|
||||
Relation uint32 `json:"relation"`
|
||||
}
|
||||
|
||||
// Stat is
|
||||
type Stat struct {
|
||||
ID int64 `json:"id" gorm:"column:id"`
|
||||
Mid int64 `json:"mid" gorm:"column:mid"`
|
||||
Following int64 `json:"following" gorm:"column:following"`
|
||||
Whisper int64 `json:"whisper" gorm:"column:whisper"`
|
||||
Black int64 `json:"black" gorm:"column:black"`
|
||||
Follower int64 `json:"follower" gorm:"column:follower"`
|
||||
CTime time.Time `json:"ctime" gorm:"column:ctime"`
|
||||
MTime time.Time `json:"mtime" gorm:"column:mtime"`
|
||||
}
|
||||
|
||||
// ParseRelation is
|
||||
func (r *Relation) ParseRelation() {
|
||||
r.Relation = smodel.Attr(r.Attribute)
|
||||
}
|
||||
|
||||
// Follower is
|
||||
type Follower struct {
|
||||
*Relation
|
||||
MemberName string `json:"member_name"`
|
||||
FollowerName string `json:"follower_name"`
|
||||
}
|
||||
|
||||
// Following is
|
||||
type Following struct {
|
||||
*Relation
|
||||
MemberName string `json:"member_name"`
|
||||
FollowingName string `json:"following_name"`
|
||||
}
|
||||
|
||||
// RelationList is
|
||||
type RelationList []*Relation
|
||||
|
||||
// FollowersList is
|
||||
type FollowersList []*Follower
|
||||
|
||||
// FollowingsList is
|
||||
type FollowingsList []*Following
|
||||
|
||||
func (rl RelationList) Len() int {
|
||||
return len(rl)
|
||||
}
|
||||
|
||||
func (rl RelationList) Swap(i, j int) {
|
||||
rl[i], rl[j] = rl[j], rl[i]
|
||||
}
|
||||
|
||||
func (rl RelationList) Less(i, j int) bool {
|
||||
return rl[i].MTime < rl[j].MTime
|
||||
}
|
||||
|
||||
// Paginate is
|
||||
func (rl RelationList) Paginate(skip int, size int) RelationList {
|
||||
if skip > len(rl) {
|
||||
skip = len(rl)
|
||||
}
|
||||
|
||||
end := skip + size
|
||||
if end > len(rl) {
|
||||
end = len(rl)
|
||||
}
|
||||
|
||||
return rl[skip:end]
|
||||
}
|
||||
|
||||
// FilterMTimeFrom is
|
||||
func (rl RelationList) FilterMTimeFrom(from time.Time) RelationList {
|
||||
res := make(RelationList, 0)
|
||||
for _, r := range rl {
|
||||
if r.MTime >= from {
|
||||
res = append(res, r)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// FilterMTimeTo is
|
||||
func (rl RelationList) FilterMTimeTo(to time.Time) RelationList {
|
||||
res := make(RelationList, 0)
|
||||
for _, r := range rl {
|
||||
if r.MTime <= to {
|
||||
res = append(res, r)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// OrderByMTime is
|
||||
func (rl RelationList) OrderByMTime(desc bool) {
|
||||
sort.Sort(rl)
|
||||
}
|
||||
|
||||
// FollowersList is
|
||||
func (rl RelationList) FollowersList() FollowersList {
|
||||
res := make(FollowersList, 0, len(rl))
|
||||
for _, r := range rl {
|
||||
res = append(res, &Follower{
|
||||
Relation: r,
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// FollowingsList is
|
||||
func (rl RelationList) FollowingsList() FollowingsList {
|
||||
res := make(FollowingsList, 0, len(rl))
|
||||
for _, r := range rl {
|
||||
res = append(res, &Following{
|
||||
Relation: r,
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
23
app/admin/main/relation/model/page.go
Normal file
23
app/admin/main/relation/model/page.go
Normal file
@ -0,0 +1,23 @@
|
||||
package model
|
||||
|
||||
// FollowersListPage is the model for followers list result
|
||||
type FollowersListPage struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PN int `json:"pn"`
|
||||
PS int `json:"ps"`
|
||||
List FollowersList `json:"list"`
|
||||
|
||||
Order string `json:"order"`
|
||||
Sort string `json:"sort"`
|
||||
}
|
||||
|
||||
// FollowingsListPage is the model for followings list result
|
||||
type FollowingsListPage struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
PN int `json:"pn"`
|
||||
PS int `json:"ps"`
|
||||
List FollowingsList `json:"list"`
|
||||
|
||||
Order string `json:"order"`
|
||||
Sort string `json:"sort"`
|
||||
}
|
63
app/admin/main/relation/model/params.go
Normal file
63
app/admin/main/relation/model/params.go
Normal file
@ -0,0 +1,63 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
xtime "go-common/library/time"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Pagination is
|
||||
type Pagination struct {
|
||||
Sort string `form:"sort"`
|
||||
Order string `form:"order"`
|
||||
PS int `form:"ps" validate:"min=0,max=50"`
|
||||
PN int `form:"pn" validate:"min=0"`
|
||||
MTimeFrom string `form:"mtime_from"`
|
||||
MTimeTo string `form:"mtime_to"`
|
||||
}
|
||||
|
||||
// FollowersParam is
|
||||
type FollowersParam struct {
|
||||
Pagination
|
||||
Fid int64 `form:"fid" validate:"min=1,required"`
|
||||
Mid int64 `form:"mid" validate:"min=0"`
|
||||
}
|
||||
|
||||
// FollowingsParam is
|
||||
type FollowingsParam struct {
|
||||
Pagination
|
||||
Mid int64 `form:"mid" validate:"min=1,required"`
|
||||
Fid int64 `form:"fid" validate:"min=0"`
|
||||
}
|
||||
|
||||
// LogsParam is
|
||||
type LogsParam struct {
|
||||
Mid int64 `form:"mid" validate:"min=1,required"`
|
||||
Fid int64 `form:"fid" validate:"min=1,required"`
|
||||
}
|
||||
|
||||
// ParseTime is
|
||||
func ParseTime(ts string) (xt xtime.Time, err error) {
|
||||
var (
|
||||
t time.Time
|
||||
)
|
||||
if t, err = time.Parse("2006-01-02 15:04:05", ts); err != nil {
|
||||
return
|
||||
}
|
||||
xt.Scan(t)
|
||||
return
|
||||
}
|
||||
|
||||
// Desc is
|
||||
func (p Pagination) Desc() bool {
|
||||
return p.Sort == "desc"
|
||||
}
|
||||
|
||||
// ArgMid is
|
||||
type ArgMid struct {
|
||||
Mid int64 `form:"mid" validate:"min=1,required"`
|
||||
}
|
||||
|
||||
// ArgMids is
|
||||
type ArgMids struct {
|
||||
Mids []int64 `form:"mids,split" validate:"dive,gt=0"`
|
||||
}
|
54
app/admin/main/relation/service/BUILD
Normal file
54
app/admin/main/relation/service/BUILD
Normal file
@ -0,0 +1,54 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"relations_test.go",
|
||||
"service_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
rundir = ".",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/admin/main/relation/conf:go_default_library",
|
||||
"//app/admin/main/relation/model:go_default_library",
|
||||
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"relation_log.go",
|
||||
"relations.go",
|
||||
"service.go",
|
||||
],
|
||||
importpath = "go-common/app/admin/main/relation/service",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//app/admin/main/relation/conf:go_default_library",
|
||||
"//app/admin/main/relation/dao:go_default_library",
|
||||
"//app/admin/main/relation/model:go_default_library",
|
||||
"//app/service/main/relation/api: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"],
|
||||
)
|
40
app/admin/main/relation/service/relation_log.go
Normal file
40
app/admin/main/relation/service/relation_log.go
Normal file
@ -0,0 +1,40 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go-common/app/admin/main/relation/model"
|
||||
)
|
||||
|
||||
// RelationLog is.
|
||||
func (s *Service) RelationLog(ctx context.Context, mid, fid int64, from time.Time, to time.Time) (model.RelationLogList, error) {
|
||||
logs, err := s.dao.RelationLogs(ctx, mid, fid, from, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// order log by mtime with desc
|
||||
logs.OrderByMTime(true)
|
||||
|
||||
uids := make([]int64, 0, len(logs)*2)
|
||||
for _, l := range logs {
|
||||
uids = append(uids, l.Mid)
|
||||
uids = append(uids, l.Fid)
|
||||
}
|
||||
uinfos, err := s.dao.RPCInfos(ctx, uids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, l := range logs {
|
||||
if mi, ok := uinfos[l.Mid]; ok {
|
||||
l.MemberName = mi.Name
|
||||
}
|
||||
}
|
||||
for _, l := range logs {
|
||||
if fi, ok := uinfos[l.Fid]; ok {
|
||||
l.FollowingName = fi.Name
|
||||
}
|
||||
}
|
||||
|
||||
return logs, nil
|
||||
}
|
115
app/admin/main/relation/service/relations.go
Normal file
115
app/admin/main/relation/service/relations.go
Normal file
@ -0,0 +1,115 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go-common/app/admin/main/relation/model"
|
||||
relationPB "go-common/app/service/main/relation/api"
|
||||
)
|
||||
|
||||
// Followers is.
|
||||
func (s *Service) Followers(ctx context.Context, param *model.FollowersParam) (*model.FollowersListPage, error) {
|
||||
list, err := s.dao.Followers(ctx, param.Fid, param.Mid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list.OrderByMTime(param.Desc())
|
||||
from, err := model.ParseTime(param.MTimeFrom)
|
||||
if err == nil {
|
||||
list = list.FilterMTimeFrom(from)
|
||||
}
|
||||
to, err := model.ParseTime(param.MTimeTo)
|
||||
if err == nil {
|
||||
list = list.FilterMTimeTo(to)
|
||||
}
|
||||
plist := list.Paginate(param.PS*(param.PN-1), param.PS)
|
||||
flist := plist.FollowersList()
|
||||
|
||||
uids := make([]int64, 0, len(flist)*2)
|
||||
for _, r := range flist {
|
||||
uids = append(uids, r.Mid)
|
||||
uids = append(uids, r.Fid)
|
||||
}
|
||||
uinfos, err := s.dao.RPCInfos(ctx, uids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, r := range flist {
|
||||
if mi, ok := uinfos[r.Mid]; ok {
|
||||
r.MemberName = mi.Name
|
||||
}
|
||||
}
|
||||
for _, r := range flist {
|
||||
if fi, ok := uinfos[r.Fid]; ok {
|
||||
r.FollowerName = fi.Name
|
||||
}
|
||||
}
|
||||
|
||||
page := &model.FollowersListPage{}
|
||||
page.Sort = param.Sort
|
||||
page.Order = param.Order
|
||||
page.List = flist
|
||||
page.TotalCount = len(list)
|
||||
page.PN = param.PN
|
||||
page.PS = param.PS
|
||||
return page, nil
|
||||
}
|
||||
|
||||
// Followings is.
|
||||
func (s *Service) Followings(ctx context.Context, param *model.FollowingsParam) (*model.FollowingsListPage, error) {
|
||||
list, err := s.dao.Followings(ctx, param.Mid, param.Fid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list.OrderByMTime(param.Desc())
|
||||
from, err := model.ParseTime(param.MTimeFrom)
|
||||
if err == nil {
|
||||
list = list.FilterMTimeFrom(from)
|
||||
}
|
||||
to, err := model.ParseTime(param.MTimeTo)
|
||||
if err == nil {
|
||||
list = list.FilterMTimeTo(to)
|
||||
}
|
||||
plist := list.Paginate(param.PS*(param.PN-1), param.PS)
|
||||
flist := plist.FollowingsList()
|
||||
|
||||
uids := make([]int64, 0, len(flist)*2)
|
||||
for _, r := range flist {
|
||||
uids = append(uids, r.Mid)
|
||||
uids = append(uids, r.Fid)
|
||||
}
|
||||
minfos, err := s.dao.RPCInfos(ctx, uids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range flist {
|
||||
if mi, ok := minfos[r.Mid]; ok {
|
||||
r.MemberName = mi.Name
|
||||
}
|
||||
}
|
||||
for _, r := range flist {
|
||||
if fi, ok := minfos[r.Fid]; ok {
|
||||
r.FollowingName = fi.Name
|
||||
}
|
||||
}
|
||||
|
||||
page := &model.FollowingsListPage{}
|
||||
page.Sort = param.Sort
|
||||
page.Order = param.Order
|
||||
page.List = flist
|
||||
page.TotalCount = len(list)
|
||||
page.PN = param.PN
|
||||
page.PS = param.PS
|
||||
return page, nil
|
||||
}
|
||||
|
||||
// Stat is
|
||||
func (s *Service) Stat(ctx context.Context, param *model.ArgMid) (*relationPB.StatReply, error) {
|
||||
return s.dao.Stat(ctx, param.Mid)
|
||||
}
|
||||
|
||||
// Stats is
|
||||
func (s *Service) Stats(ctx context.Context, param *model.ArgMids) (map[int64]*relationPB.StatReply, error) {
|
||||
return s.dao.Stats(ctx, param.Mids)
|
||||
}
|
52
app/admin/main/relation/service/relations_test.go
Normal file
52
app/admin/main/relation/service/relations_test.go
Normal file
@ -0,0 +1,52 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"go-common/app/admin/main/relation/model"
|
||||
|
||||
"github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestFollowers(t *testing.T) {
|
||||
convey.Convey("Followers", t, func() {
|
||||
rl, err := s.Followers(context.TODO(), &model.FollowersParam{
|
||||
Fid: 1,
|
||||
Mid: 2,
|
||||
})
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(rl, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFollowings(t *testing.T) {
|
||||
convey.Convey("Followings", t, func() {
|
||||
rl, err := s.Followings(context.TODO(), &model.FollowingsParam{
|
||||
Fid: 1,
|
||||
Mid: 2,
|
||||
})
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(rl, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStat(t *testing.T) {
|
||||
convey.Convey("stat", t, func() {
|
||||
rl, err := s.Stat(context.TODO(), &model.ArgMid{
|
||||
Mid: 2,
|
||||
})
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(rl, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStats(t *testing.T) {
|
||||
convey.Convey("stats", t, func() {
|
||||
rl, err := s.Stats(context.TODO(), &model.ArgMids{
|
||||
Mids: []int64{2, 3},
|
||||
})
|
||||
convey.So(err, convey.ShouldBeNil)
|
||||
convey.So(rl, convey.ShouldNotBeNil)
|
||||
})
|
||||
}
|
33
app/admin/main/relation/service/service.go
Normal file
33
app/admin/main/relation/service/service.go
Normal file
@ -0,0 +1,33 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go-common/app/admin/main/relation/conf"
|
||||
"go-common/app/admin/main/relation/dao"
|
||||
)
|
||||
|
||||
// Service struct
|
||||
type Service struct {
|
||||
c *conf.Config
|
||||
dao *dao.Dao
|
||||
}
|
||||
|
||||
// New init
|
||||
func New(c *conf.Config) (s *Service) {
|
||||
s = &Service{
|
||||
c: c,
|
||||
dao: dao.New(c),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Ping Service
|
||||
func (s *Service) Ping(c context.Context) (err error) {
|
||||
return s.dao.Ping(c)
|
||||
}
|
||||
|
||||
// Close Service
|
||||
func (s *Service) Close() {
|
||||
s.dao.Close()
|
||||
}
|
18
app/admin/main/relation/service/service_test.go
Normal file
18
app/admin/main/relation/service/service_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"go-common/app/admin/main/relation/conf"
|
||||
)
|
||||
|
||||
var s *Service
|
||||
|
||||
func init() {
|
||||
flag.Parse()
|
||||
|
||||
if err := conf.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
s = New(conf.Conf)
|
||||
}
|
Reference in New Issue
Block a user