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/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"],
)

View File

@ -0,0 +1,27 @@
# v1.2.0
1. gorpc 调用account改成grpc
# v1.1.1
1. 通过调用rpc方式调用relation-service提供 statstats 接口
# 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

View File

@ -0,0 +1,9 @@
# Owner
linmiao
zhoujiahui
# Author
zhoujiahui
# Reviewer
zhoujiahui

View 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

View File

@ -0,0 +1,13 @@
# relation-admin
# 项目简介
1. 关系链后台服务
# 编译环境
# 依赖包
# 编译执行

View File

@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["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"],
)

View 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
}
}
}

View 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"

View 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"],
)

View 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
}

View 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"],
)

View 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
}

View 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)
})
}

View 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)
}

View 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())
}

View 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
}

View 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
}

View 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)
})
})
})
}

View 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)
})
}

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 = [
"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"],
)

View 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)
}

View 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))
}

View File

@ -0,0 +1,36 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"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"],
)

View 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
}

View 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
}

View 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"`
}

View 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"`
}

View File

@ -0,0 +1,54 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = [
"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"],
)

View 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
}

View 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)
}

View 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)
})
}

View 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()
}

View 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)
}