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

View File

@@ -0,0 +1,21 @@
# v1.3.3
1. 直接使用 API 里的数据
# v1.3.2
1. member 与 block 变更时依旧全量更新
# v1.3.1
1. 根据不同 binlog 同步不同数据
# v1.3.0
1. 添加来自 passport 的部分注册信息
# v1.2.0
1. 去除 block 依赖
2. 去除 block 导入
# v1.0.0
1. 第一次上线
# v1.0.1
1. 暂时数据更新进行全同步

View File

@@ -0,0 +1,8 @@
# Owner
zhoujiahui
# Author
zhoujiahui
# Reviewer
zhoujiahui

View File

@@ -0,0 +1,12 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- zhoujiahui
labels:
- job
- job/main/account-summary
- main
options:
no_parent_owners: true
reviewers:
- zhoujiahui

View File

@@ -0,0 +1,12 @@
# account-summary
# 项目简介
1. 用户账号信息聚合 job
# 编译环境
# 依赖包
# 编译执行

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["test.toml"],
importpath = "go-common/app/job/main/account-summary/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/account-summary/conf:go_default_library",
"//app/job/main/account-summary/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,42 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"go-common/app/job/main/account-summary/conf"
"go-common/app/job/main/account-summary/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 {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("account-summary start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
ecode.Init(conf.Conf.Ecode)
http.Init(conf.Conf)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("account-summary get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
log.Info("account-summary exit")
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,133 @@
# This is a TOML document. Boom
[Log]
stdout=true
[MemberBinLog]
key = "4ba46ba31f9a44ef"
secret = "e4c5a7fce28695209e6b4f0af8cf91c5"
group = "MemberBinlog-MainAccount-AccountSummary-S"
topic = "MemberBinlog-T"
action = "sub"
offset = "old"
buffer = 2048
name = "account-summary/member-binlog"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 1
active = 1
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[BlockBinLog]
key = "4ba46ba31f9a44ef"
secret = "e4c5a7fce28695209e6b4f0af8cf91c5"
group = "BlockUserBinlog-MainAccount-S"
topic = "BlockUserBinlog-T"
action = "sub"
offset = "old"
buffer = 2048
name = "account-summary/member-binlog"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 1
active = 1
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[PassportBinLog]
key = "4ba46ba31f9a44ef"
secret = "e4c5a7fce28695209e6b4f0af8cf91c5"
group = "PassportPurgeCache-MainAccount-AccountSummary-S"
topic = "PassportPurgeCache-T"
action = "sub"
offset = "old"
buffer = 2048
name = "account-summary/member-binlog"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 1
active = 1
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[AccountSummaryProducer]
key = "4ba46ba31f9a44ef"
secret = "e4c5a7fce28695209e6b4f0af8cf91c5"
group = "AccountSummary-MainAccount-P"
topic = "AccountSummary-T"
action = "pub"
offset = "old"
buffer = 2048
name = "account-summary/account-summary-producer"
proto = "tcp"
addr = "172.18.33.50:6205"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[AccountSummaryHBase]
master = ""
meta = ""
dialTimeout = "1s"
readTimeout = "10s"
readsTimeout = "10s"
writeTimeout = "10s"
writesTimeout = "10s"
[AccountSummaryHBase.zookeeper]
root = ""
addrs = ["172.18.33.131:2181","172.18.33.168:2181","172.18.33.169:2181"]
timeout = "30s"
[MemberDB]
addr = "172.16.33.205"
dsn = "test:test@tcp(172.16.33.205:3308)/bilibili_member?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
IdleTimeout ="4h"
queryTimeout = "100ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[MemberDB.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[HTTPClient]
key = "c1a1cb2d89c33794"
secret = "dda47eeca111e03e6845017505baea13"
dial = "1s"
timeout = "3s"
keepAlive = "60s"
timer = 16
[HTTPClient.breaker]
window = "10s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[Host]
Passport="http://uat-passport.bilibili.co"
[AccountSummary]
SubProcessWorker=1
SyncRangeStart=0
SyncRangeEnd=300000000
SyncRangeWorker=4
[FeatureGate]
DisableSubProcess=false
SyncRange=false
Initial=true

View File

@@ -0,0 +1,40 @@
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/job/main/account-summary/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/conf:go_default_library",
"//library/database/hbase.v2: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",
"//library/queue/databus: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,130 @@
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"
"go-common/library/queue/databus"
"go-common/library/database/hbase.v2"
"github.com/BurntSushi/toml"
)
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
Tracer *trace.Config
Ecode *ecode.Config
AccountSummaryHBase *hbase.Config
MemberBinLog *databus.Config
BlockBinLog *databus.Config
PassportBinLog *databus.Config
RelationBinLog *databus.Config
AccountSummaryProducer *databus.Config
MemberService *rpc.ClientConfig
RelationService *rpc.ClientConfig
HTTPClient *bm.ClientConfig
Host *Host
FeatureGate *FeatureGate
AccountSummary *AccountSummary
MemberDB *sql.Config
RelationDB *sql.Config
PassportDB *sql.Config
}
// AccountSummary is
type AccountSummary struct {
SubProcessWorker uint64
SyncRangeStart int64
SyncRangeEnd int64
SyncRangeWorker uint64
InitialWriteWorker uint64
}
// FeatureGate is
type FeatureGate struct {
DisableSubProcess bool
Initial bool
InitialMemberBase bool
InitialMemberExp bool
InitialMemberOfficial bool
InitialRelationStat bool
InitialBlock bool
InitialPassport bool
SyncRange bool
}
// Host is
type Host struct {
Passport string
}
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,65 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"dao.go",
"member.go",
"passport.go",
"relation.go",
"summary.go",
],
importpath = "go-common/app/job/main/account-summary/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/account-summary/conf:go_default_library",
"//app/job/main/account-summary/model:go_default_library",
"//app/service/main/member/api/gorpc:go_default_library",
"//app/service/main/relation/rpc/client:go_default_library",
"//library/database/hbase.v2:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = [
"dao_test.go",
"member_test.go",
"passport_test.go",
"relation_test.go",
"summary_test.go",
],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/job/main/account-summary/conf:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)

View File

@@ -0,0 +1,49 @@
package dao
import (
"context"
"go-common/app/job/main/account-summary/conf"
member "go-common/app/service/main/member/api/gorpc"
relation "go-common/app/service/main/relation/rpc/client"
"go-common/library/database/sql"
bm "go-common/library/net/http/blademaster"
"go-common/library/database/hbase.v2"
)
// Dao dao
type Dao struct {
c *conf.Config
AccountSumHBase *hbase.Client
MemberService *member.Service
RelationService *relation.Service
httpClient *bm.Client
MemberDB *sql.DB
RelationDB *sql.DB
PassportDB *sql.DB
}
// New init mysql db
func New(c *conf.Config) *Dao {
dao := &Dao{
c: c,
AccountSumHBase: hbase.NewClient(c.AccountSummaryHBase),
MemberService: member.New(c.MemberService),
RelationService: relation.New(c.RelationService),
httpClient: bm.NewClient(c.HTTPClient),
MemberDB: sql.NewMySQL(c.MemberDB),
RelationDB: sql.NewMySQL(c.RelationDB),
PassportDB: sql.NewMySQL(c.PassportDB),
}
return dao
}
// Close close the resource.
func (d *Dao) Close() {
}
// Ping dao ping
func (d *Dao) Ping(ctx context.Context) error {
return nil
}

View File

@@ -0,0 +1,33 @@
package dao
import (
"flag"
"go-common/app/job/main/account-summary/conf"
"os"
"testing"
)
var (
d *Dao
)
func TestMain(m *testing.M) {
if os.Getenv("DEPLOY_ENV") != "" {
flag.Set("app_id", "main.account.account-summary")
flag.Set("conf_token", "5761c1b876ef2941fe4b63f621b3d74a")
flag.Set("tree_id", "51697")
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")
}
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
d = New(conf.Conf)
m.Run()
os.Exit(0)
}

View File

@@ -0,0 +1,133 @@
package dao
// import (
// "context"
// "fmt"
// "go-common/app/job/main/account-summary/model"
// member "go-common/app/service/main/member/model"
// "go-common/library/log"
// "github.com/pkg/errors"
// )
// const (
// _AllBase = `SELECT mid,name,sex,face,sign,rank FROM user_base_%02d`
// _AllExp = `SELECT mid,exp FROM user_exp_%02d`
// _AllOfficial = `SELECT mid,role,title,description FROM user_official`
// )
// func (d *Dao) allMemberBaseFromTable(ctx context.Context, no int64) ([]*model.MemberBase, error) {
// rows, err := d.MemberDB.Query(ctx, fmt.Sprintf(_AllBase, no))
// if err != nil {
// return nil, errors.WithStack(err)
// }
// defer rows.Close()
// res := make([]*model.MemberBase, 0)
// for rows.Next() {
// mb := &member.BaseInfo{}
// if err = rows.Scan(&mb.Mid, &mb.Name, &mb.Sex, &mb.Face, &mb.Sign, &mb.Rank); err != nil {
// log.Error("Failed to scan row in query all member base: %+v", err)
// err = nil
// continue
// }
// mb.RandFaceURL()
// b := &model.MemberBase{
// EmbedMid: model.EmbedMid{Mid: mb.Mid},
// Birthday: model.Date(mb.Birthday),
// Face: mb.Face,
// Name: mb.Name,
// Rank: mb.Rank,
// Sex: mb.Sex,
// Sign: mb.Sign,
// }
// res = append(res, b)
// }
// return res, nil
// }
// func (d *Dao) allMemberExpFromTable(ctx context.Context, no int64) ([]*model.MemberExp, error) {
// rows, err := d.MemberDB.Query(ctx, fmt.Sprintf(_AllExp, no))
// if err != nil {
// return nil, err
// }
// defer rows.Close()
// res := make([]*model.MemberExp, 0)
// for rows.Next() {
// mid := int64(0)
// exp := int64(0)
// if err = rows.Scan(&mid, &exp); err != nil {
// log.Error("Failed to scan row in query all member exp: %+v", err)
// err = nil
// continue
// }
// e := &model.MemberExp{
// EmbedMid: model.EmbedMid{Mid: mid},
// Exp: exp / 100,
// }
// res = append(res, e)
// }
// return res, nil
// }
// // AllMemberBase is
// func (d *Dao) AllMemberBase(ctx context.Context) <-chan []*model.MemberBase {
// resCh := make(chan []*model.MemberBase)
// go func() {
// for i := 0; i < 100; i++ {
// res, err := d.allMemberBaseFromTable(context.Background(), int64(i))
// if err != nil {
// log.Error("Failed to get all member base from table with table id: %d: %+v", i, err)
// continue
// }
// resCh <- res
// }
// close(resCh)
// }()
// return resCh
// }
// // AllMemberExp is
// func (d *Dao) AllMemberExp(ctx context.Context) <-chan []*model.MemberExp {
// resCh := make(chan []*model.MemberExp)
// go func() {
// for i := 0; i < 100; i++ {
// res, err := d.allMemberExpFromTable(context.Background(), int64(i))
// if err != nil {
// log.Error("Failed to get all member exp from table with table id: %d: %+v", i, err)
// continue
// }
// resCh <- res
// }
// close(resCh)
// }()
// return resCh
// }
// // AllOfficial is
// func (d *Dao) AllOfficial(ctx context.Context) ([]*model.MemberOfficial, error) {
// rows, err := d.MemberDB.Query(ctx, _AllOfficial)
// if err != nil {
// return nil, err
// }
// defer rows.Close()
// res := make([]*model.MemberOfficial, 0)
// for rows.Next() {
// o := &model.MemberOfficial{}
// if err = rows.Scan(&o.Mid, &o.Role, &o.Title, &o.Description); err != nil {
// log.Error("Failed to scan row in query all member official: %+v", err)
// err = nil
// continue
// }
// res = append(res, o)
// }
// return res, nil
// }

View File

@@ -0,0 +1 @@
package dao

View File

@@ -0,0 +1,51 @@
package dao
import (
"context"
"fmt"
"net/url"
"strconv"
"go-common/app/job/main/account-summary/model"
"go-common/library/ecode"
"go-common/library/log"
)
const (
queryByMidURI = "/intranet/acc/queryByMid"
// _AsoAccountByMid = `SELECT mid,email FROM aso_account WHERE mid=?`
// _AsoAccountInfoByMid = `SELECT mid,join_ip,join_time FROM aso_account_info%d WHERE mid=?`
_AsoAccountRegOriginByMid = `SELECT mid,origintype,regtype FROM aso_account_reg_origin_%d WHERE mid=?`
// _AllCountryID = `SELECT id,code FROM aso_country_code`
)
// PassportProfile is
func (d *Dao) PassportProfile(ctx context.Context, mid int64) (*model.PassportProfile, error) {
params := url.Values{}
params.Set("mid", strconv.FormatInt(mid, 10))
var res struct {
Code int `json:"code"`
Data *model.PassportProfile `json:"data"`
}
if err := d.httpClient.Get(ctx, d.c.Host.Passport+queryByMidURI, "", params, &res); err != nil {
log.Error("Failed to query by mid: %+v: %+v", params, err)
return nil, err
}
if res.Code != 0 {
log.Error("Failed to query by mid with code: %+v: %d", params, res.Code)
return nil, ecode.Int(res.Code)
}
return res.Data, nil
}
// AsoAccountRegOrigin is
func (d *Dao) AsoAccountRegOrigin(ctx context.Context, mid int64) (*model.AsoAccountRegOrigin, error) {
row := d.PassportDB.QueryRow(ctx, fmt.Sprintf(_AsoAccountRegOriginByMid, mid%20), mid)
origin := new(model.AsoAccountRegOrigin)
if err := row.Scan(&origin.Mid, &origin.OriginType, &origin.RegType); err != nil {
return nil, err
}
return origin, nil
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoPassportProfile(t *testing.T) {
var (
// ctx = context.Background()
mid = int64(0)
)
convey.Convey("PassportProfile", t, func(ctx convey.C) {
p1, err := d.PassportProfile(context.TODO(), mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}
func TestDaoAsoAccountRegOrigin(t *testing.T) {
var (
// ctx = context.Background()
mid = int64(0)
)
convey.Convey("AsoAccountRegOrigin", t, func(ctx convey.C) {
p1, err := d.AsoAccountRegOrigin(context.TODO(), mid)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, convey.ShouldNotBeNil)
})
})
}

View File

@@ -0,0 +1,51 @@
package dao
// import (
// "context"
// "fmt"
// "go-common/app/job/main/account-summary/model"
// "go-common/library/log"
// )
// var (
// _AllStat = `SELECT mid,following,whisper,black,follower FROM user_relation_stat_%02d`
// )
// func (d *Dao) allRelationStatFromTable(ctx context.Context, no int64) ([]*model.RelationStat, error) {
// rows, err := d.RelationDB.Query(ctx, fmt.Sprintf(_AllStat, no))
// if err != nil {
// return nil, err
// }
// res := make([]*model.RelationStat, 0)
// defer rows.Close()
// for rows.Next() {
// rs := &model.RelationStat{}
// if err = rows.Scan(&rs.Mid, &rs.Following, &rs.Whisper, &rs.Black, &rs.Follower); err != nil {
// log.Error("Failed to scan row in query all relation stat: %+v", err)
// err = nil
// continue
// }
// res = append(res, rs)
// }
// return res, nil
// }
// // AllRelationStat is
// func (d *Dao) AllRelationStat(ctx context.Context) <-chan []*model.RelationStat {
// resCh := make(chan []*model.RelationStat)
// go func() {
// for i := 0; i < 50; i++ {
// res, err := d.allRelationStatFromTable(ctx, int64(i))
// if err != nil {
// log.Error("Failed to get all relation stat from table with table id: %d: %+v", i, err)
// continue
// }
// resCh <- res
// }
// close(resCh)
// }()
// return resCh
// }

View File

@@ -0,0 +1 @@
package dao

View File

@@ -0,0 +1,100 @@
package dao
import (
"context"
"strconv"
"go-common/app/job/main/account-summary/model"
"github.com/pkg/errors"
)
const (
_SummaryTable = "ugc:AccountSum"
_ColFamily = "accountsum"
)
// Save is
func (d *Dao) Save(ctx context.Context, key string, data map[string][]byte) error {
values := map[string]map[string][]byte{
_ColFamily: data,
}
_, err := d.AccountSumHBase.PutStr(ctx, _SummaryTable, key, values)
return err
}
// GetByKey is
func (d *Dao) GetByKey(ctx context.Context, key string) (*model.AccountSummary, error) {
res, err := d.AccountSumHBase.GetStr(ctx, _SummaryTable, key)
if err != nil {
return nil, errors.WithStack(err)
}
sum := model.NewAccountSummary()
for _, c := range res.Cells {
v := string(c.Value)
switch string(c.Qualifier) {
case "birthday":
sum.Birthday = v
case "face":
sum.Face = v
case "mid":
sum.Mid, _ = strconv.ParseInt(v, 10, 64)
case "name":
sum.Name = v
case "rank":
sum.Rank, _ = strconv.ParseInt(v, 10, 64)
case "sex":
sum.Sex, _ = strconv.ParseInt(v, 10, 64)
case "sign":
sum.Sign = v
case "official.role":
sum.Official.Role, _ = strconv.ParseInt(v, 10, 64)
case "official.mid":
sum.Official.Mid, _ = strconv.ParseInt(v, 10, 64)
case "official.title":
sum.Official.Title = v
case "official.description":
sum.Official.Description = v
case "exp.mid":
sum.Exp.Mid, _ = strconv.ParseInt(v, 10, 64)
case "exp.exp":
sum.Exp.Exp, _ = strconv.ParseInt(v, 10, 64)
case "relation.mid":
sum.RelationStat.Mid, _ = strconv.ParseInt(v, 10, 64)
case "relation.follower":
sum.RelationStat.Follower, _ = strconv.ParseInt(v, 10, 64)
case "relation.following":
sum.RelationStat.Following, _ = strconv.ParseInt(v, 10, 64)
case "relation.black":
sum.RelationStat.Black, _ = strconv.ParseInt(v, 10, 64)
case "relation.whisper":
sum.RelationStat.Whisper, _ = strconv.ParseInt(v, 10, 64)
case "block.mid":
sum.Block.Mid, _ = strconv.ParseInt(v, 10, 64)
case "block.block_status":
sum.Block.BlockStatus, _ = strconv.ParseInt(v, 10, 64)
case "block.start_time":
sum.Block.StartTime = v
case "block.end_time":
sum.Block.EndTime = v
case "passport.mid":
sum.Passport.Mid, _ = strconv.ParseInt(v, 10, 64)
case "passport.tel_status":
sum.Passport.TelStatus, _ = strconv.ParseInt(v, 10, 64)
case "passport.country_id":
sum.Passport.CountryID, _ = strconv.ParseInt(v, 10, 64)
case "passport.join_ip":
sum.Passport.JoinIP = v
case "passport.join_time":
sum.Passport.JoinTime = v
case "passport.email_suffix":
sum.Passport.EmailSuffix = v
case "passport.origin_type":
sum.Passport.OriginType, _ = strconv.ParseInt(v, 10, 64)
case "passport.reg_type":
sum.Passport.RegType, _ = strconv.ParseInt(v, 10, 64)
}
}
return sum, nil
}

View File

@@ -0,0 +1,36 @@
package dao
import (
"context"
"testing"
"github.com/smartystreets/goconvey/convey"
)
func TestDaoSave(t *testing.T) {
var (
// ctx = context.Background()·
key = ""
data map[string][]byte
)
convey.Convey("Save", t, func(ctx convey.C) {
err := d.Save(context.TODO(), key, data)
ctx.Convey("Then err should be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
})
})
}
func TestDaoGetByKey(t *testing.T) {
var (
// ctx = context.Background()
key = ""
)
convey.Convey("GetByKey", t, func(ctx convey.C) {
p1, err := d.GetByKey(context.TODO(), key)
ctx.Convey("Then err should be nil.p1 should not be nil.", func(ctx convey.C) {
ctx.So(err, convey.ShouldBeNil)
ctx.So(p1, 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",
"summary.go",
],
importpath = "go-common/app/job/main/account-summary/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/account-summary/conf:go_default_library",
"//app/job/main/account-summary/model:go_default_library",
"//app/job/main/account-summary/service:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,46 @@
package http
import (
"net/http"
"go-common/app/job/main/account-summary/conf"
"go-common/app/job/main/account-summary/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
)
var (
srv *service.Service
)
// Init init
func Init(c *conf.Config) {
srv = service.New(c)
engine := bm.DefaultServer(c.BM)
router(engine)
if err := engine.Start(); err != nil {
log.Error("bm.DefaultServer error(%v)", err)
panic(err)
}
}
func router(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
g := e.Group("/x/internal/account-summary")
{
g.GET("/sync/one", syncOne)
g.GET("/get/one", getOne)
}
}
func ping(c *bm.Context) {
if err := srv.Ping(c); err != nil {
log.Error("account-summary-job ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}

View File

@@ -0,0 +1,27 @@
package http
import (
"go-common/app/job/main/account-summary/model"
bm "go-common/library/net/http/blademaster"
)
func syncOne(ctx *bm.Context) {
arg := &model.ArgMid{}
if err := ctx.Bind(arg); err != nil {
return
}
if err := srv.SyncOne(ctx, arg.Mid); err != nil {
ctx.JSON(nil, err)
return
}
ctx.JSON(srv.GetOne(ctx, arg.Mid))
}
func getOne(ctx *bm.Context) {
arg := &model.ArgMid{}
if err := ctx.Bind(arg); err != nil {
return
}
ctx.JSON(srv.GetOne(ctx, arg.Mid))
}

View File

@@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"canal.go",
"model.go",
"param.go",
"passport.go",
],
importpath = "go-common/app/job/main/account-summary/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,18 @@
package model
import (
"encoding/json"
)
// CanalBinLog is
type CanalBinLog struct {
Action string `json:"action"`
Table string `json:"table"`
Old json.RawMessage `json:"old"`
New json.RawMessage `json:"new"`
}
// MidBinLog is
type MidBinLog struct {
Mid int64 `json:"mid"`
}

View File

@@ -0,0 +1,208 @@
package model
import (
"encoding/json"
"errors"
xtime "go-common/library/time"
"strconv"
"strings"
)
// EmbedMid is
type EmbedMid struct {
Mid int64 `json:"mid"`
}
// PassportSummary is
type PassportSummary struct {
EmbedMid
TelStatus int64 `json:"tel_status"`
CountryID int64 `json:"country_id"`
JoinIP string `json:"join_ip"`
JoinTime string `json:"join_time"`
EmailSuffix string `json:"email_suffix"`
OriginType int64 `json:"origin_type"`
RegType int64 `json:"reg_type"`
}
// RelationStat is
type RelationStat struct {
EmbedMid
Following int64 `json:"following"`
Whisper int64 `json:"whisper"`
Black int64 `json:"black"`
Follower int64 `json:"follower"`
}
// BlockSummary is
type BlockSummary struct {
EmbedMid
BlockStatus int64 `json:"block_status"`
StartTime string `json:"start_time"`
EndTime string `json:"end_time"`
}
// MemberBase is
type MemberBase struct {
EmbedMid
Birthday string `json:"birthday"`
Face string `json:"face"`
Name string `json:"name"`
Rank int64 `json:"rank"`
Sex int64 `json:"sex"`
Sign string `json:"sign"`
}
// MemberOfficial is
type MemberOfficial struct {
EmbedMid
Role int64 `json:"role"`
Title string `json:"title"`
Description string `json:"description"`
}
// MemberExp is
type MemberExp struct {
EmbedMid
Exp int64 `json:"exp"`
}
// AccountSummary is
type AccountSummary struct {
MemberBase
Exp *MemberExp `json:"exp"`
Official *MemberOfficial `json:"official"`
RelationStat *RelationStat `json:"relation_stat"`
Block *BlockSummary `json:"block"`
Passport *PassportSummary `json:"passport"`
}
// NewAccountSummary is
func NewAccountSummary() *AccountSummary {
return &AccountSummary{
Exp: &MemberExp{},
Official: &MemberOfficial{},
Passport: &PassportSummary{},
RelationStat: &RelationStat{},
Block: &BlockSummary{},
}
}
func (sum *AccountSummary) String() string {
b, _ := json.Marshal(sum)
return string(b)
}
// Key is
func (e *EmbedMid) Key() (string, error) {
if e.Mid == 0 {
return "", errors.New("Empty mid")
}
return MidKey(e.Mid), nil
}
// Marshal is
func (b *MemberBase) Marshal() (map[string][]byte, error) {
data := map[string][]byte{
"birthday": []byte(b.Birthday),
"face": []byte(b.Face),
"mid": []byte(strconv.FormatInt(b.Mid, 10)),
"name": []byte(b.Name),
"rank": []byte(strconv.FormatInt(b.Rank, 10)),
"sex": []byte(strconv.FormatInt(b.Sex, 10)),
"sign": []byte(b.Sign),
}
return data, nil
}
// Marshal is
func (o *MemberOfficial) Marshal() (map[string][]byte, error) {
data := map[string][]byte{
"official.mid": []byte(strconv.FormatInt(o.Mid, 10)),
"official.role": []byte(strconv.FormatInt(o.Role, 10)),
"official.title": []byte(o.Title),
"official.description": []byte(o.Description),
}
return data, nil
}
// Marshal is
func (e *MemberExp) Marshal() (map[string][]byte, error) {
data := map[string][]byte{
"exp.mid": []byte(strconv.FormatInt(e.Mid, 10)),
"exp.exp": []byte(strconv.FormatInt(e.Exp, 10)),
}
return data, nil
}
// 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, c string, l int) string {
dt := l - len(s)
if dt <= 0 {
return s
}
return s + strings.Repeat(c, dt)
}
// MidKey is
func MidKey(mid int64) string {
ms := strconv.FormatInt(mid, 10)
return rpad(reverse(ms), "0", 16)
}
// Marshal is
func (r *RelationStat) Marshal() (map[string][]byte, error) {
data := map[string][]byte{
"relation.mid": []byte(strconv.FormatInt(r.Mid, 10)),
"relation.following": []byte(strconv.FormatInt(r.Following, 10)),
"relation.whisper": []byte(strconv.FormatInt(r.Whisper, 10)),
"relation.black": []byte(strconv.FormatInt(r.Black, 10)),
"relation.follower": []byte(strconv.FormatInt(r.Follower, 10)),
}
return data, nil
}
// Marshal is
func (b *BlockSummary) Marshal() (map[string][]byte, error) {
data := map[string][]byte{
"block.mid": []byte(strconv.FormatInt(b.Mid, 10)),
"block.block_status": []byte(strconv.FormatInt(b.BlockStatus, 10)),
"block.start_time": []byte(b.StartTime),
"block.end_time": []byte(b.EndTime),
}
return data, nil
}
// Marshal is
func (p *PassportSummary) Marshal() (map[string][]byte, error) {
data := map[string][]byte{
"passport.mid": []byte(strconv.FormatInt(p.Mid, 10)),
"passport.tel_status": []byte(strconv.FormatInt(p.TelStatus, 10)),
"passport.country_id": []byte(strconv.FormatInt(p.CountryID, 10)),
"passport.join_ip": []byte(p.JoinIP),
"passport.join_time": []byte(p.JoinTime),
"passport.email_suffix": []byte(p.EmailSuffix),
"passport.reg_type": []byte(strconv.FormatInt(p.RegType, 10)),
"passport.origin_type": []byte(strconv.FormatInt(p.OriginType, 10)),
}
return data, nil
}
// Date convert timestamp to date
func Date(in xtime.Time) string {
return in.Time().Format("2006-01-02")
}
// Datetime convert timestamp to date time
func Datetime(in xtime.Time) string {
return in.Time().Format("2006-01-02 15:04:05")
}

View File

@@ -0,0 +1,6 @@
package model
// ArgMid is
type ArgMid struct {
Mid int64 `form:"mid" validate:"required"`
}

View File

@@ -0,0 +1,53 @@
package model
import (
xtime "go-common/library/time"
"strings"
)
// PassportProfile is
type PassportProfile struct {
Mid int64 `json:"mid"`
UName string `json:"uname"`
UserID string `json:"user_id"`
Telphone string `json:"telphone"`
Email string `json:"email"`
NickLock int `json:"nick_lock"`
BindQQ bool `json:"bind_qq"`
BindSina bool `json:"bind_sina"`
SpaceSta int `json:"spacesta"`
LoginTime xtime.Time `json:"login_time"`
LoginIP string `json:"login_ip"`
JoinIP string `json:"join_ip"`
JoinTime xtime.Time `json:"join_time"`
SafeQuestion int `json:"safe_question"`
CountryCode int64 `json:"country_code"`
}
func bool2int(in bool) int64 {
if in {
return 1
}
return 0
}
// TelStatus is
func (p *PassportProfile) TelStatus() int64 {
return bool2int(p.Telphone != "")
}
// EmailSuffix is
func (p *PassportProfile) EmailSuffix() string {
if !strings.Contains(p.Email, "@") {
return p.Email
}
parts := strings.SplitN(p.Email, "@", 2)
return parts[1]
}
// AsoAccountRegOrigin is
type AsoAccountRegOrigin struct {
Mid int64 `json:"mid"`
OriginType int64 `json:"origin_type"`
RegType int64 `json:"reg_type"`
}

View File

@@ -0,0 +1,48 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"block.go",
"initial.go",
"member.go",
"passport.go",
"relation.go",
"service.go",
"summary.go",
"sync.go",
],
importpath = "go-common/app/job/main/account-summary/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/account-summary/conf:go_default_library",
"//app/job/main/account-summary/dao:go_default_library",
"//app/job/main/account-summary/model:go_default_library",
"//app/service/main/member/model:go_default_library",
"//app/service/main/member/model/block:go_default_library",
"//app/service/main/relation/model:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus: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,47 @@
package service
import (
"context"
"encoding/json"
"go-common/app/job/main/account-summary/model"
"go-common/library/log"
)
func (s *Service) blockBinLogproc(ctx context.Context) {
for msg := range s.BlockBinLog.Messages() {
blog := &model.CanalBinLog{}
if err := json.Unmarshal(msg.Value, blog); err != nil {
log.Error("Failed to unmarshal canal bin log: %+v, value: %s: %+v", msg, string(msg.Value), err)
msg.Commit()
continue
}
log.Info("Handling message key: %s, value: %s", msg.Key, string(msg.Value))
s.blockBinLogHandle(ctx, blog)
msg.Commit()
}
}
func (s *Service) blockBinLogHandle(ctx context.Context, blog *model.CanalBinLog) {
if len(blog.New) == 0 {
log.Error("Failed to sync to hbase with empty new field: %+v", blog)
return
}
switch blog.Table {
case "block_user":
midl := &model.MidBinLog{}
if err := json.Unmarshal(blog.New, midl); err != nil {
log.Error("Failed to unmarsha new data: %s: %+v", string(blog.New), err)
return
}
// FIXME: 一段时间后改用 syncBlock
if err := s.SyncOne(ctx, midl.Mid); err != nil {
log.Error("Failed to sync block with mid: %d: %+v", midl.Mid, err)
return
}
default:
log.Warn("Unable to hanlde binlog: %+v, old: %s, new: %s", blog, string(blog.Old), string(blog.New))
}
}

View File

@@ -0,0 +1,122 @@
package service
// import (
// "context"
// "sync"
// "time"
// "go-common/library/log"
// )
// func (s *Service) initialproc(ctx context.Context) {
// dataWg := sync.WaitGroup{}
// workerWg := sync.WaitGroup{}
// jobQueue := make(chan func(), 4096)
// initialWorker := func() {
// worker := uint64(50)
// if s.c.AccountSummary.InitialWriteWorker > 0 {
// worker = s.c.AccountSummary.InitialWriteWorker
// }
// log.Info("Start %d initial write worker", worker)
// for i := uint64(0); i < worker; i++ {
// workerWg.Add(1)
// go func() {
// defer workerWg.Done()
// for job := range jobQueue {
// job()
// }
// }()
// }
// }
// initialWorker()
// initBase := func() {
// log.Info("Start to initial member base")
// defer dataWg.Done()
// baseCh := s.dao.AllMemberBase(ctx)
// for chunk := range baseCh {
// for _, b := range chunk {
// b := b
// jobQueue <- func() {
// if err := s.SyncToHBase(ctx, b); err != nil {
// log.Error("Failed to sync member base in initial process: base: %+v: %+v", b, err)
// }
// }
// }
// }
// }
// initExp := func() {
// log.Info("Start to initial member exp")
// defer dataWg.Done()
// expCh := s.dao.AllMemberExp(ctx)
// for chunk := range expCh {
// for _, e := range chunk {
// e := e
// jobQueue <- func() {
// if err := s.SyncToHBase(ctx, e); err != nil {
// log.Error("Failed to sync member exp in initial process: exp: %+v: %+v", e, err)
// }
// }
// }
// time.Sleep(time.Second)
// }
// }
// initOfficial := func() {
// log.Info("Start to initial member official")
// defer dataWg.Done()
// official, err := s.dao.AllOfficial(ctx)
// if err != nil {
// log.Error("Failed to get all member official: %+v", err)
// return
// }
// for _, o := range official {
// o := o
// jobQueue <- func() {
// if err := s.SyncToHBase(ctx, o); err != nil {
// log.Error("Failed to sync member official in initial process: official: %+v: %+v", o, err)
// }
// }
// }
// }
// initStat := func() {
// log.Info("Start to initial relation stat")
// defer dataWg.Done()
// statsCh := s.dao.AllRelationStat(ctx)
// for chunk := range statsCh {
// for _, stat := range chunk {
// stat := stat
// jobQueue <- func() {
// if err := s.SyncToHBase(ctx, stat); err != nil {
// log.Error("Failed to sync relation stat in initial process: stat: %+v: %+v", stat, err)
// }
// }
// }
// time.Sleep(time.Second)
// }
// }
// if s.c.FeatureGate.InitialMemberBase {
// dataWg.Add(1)
// go initBase()
// }
// if s.c.FeatureGate.InitialMemberExp {
// dataWg.Add(1)
// go initExp()
// }
// if s.c.FeatureGate.InitialMemberOfficial {
// dataWg.Add(1)
// go initOfficial()
// }
// if s.c.FeatureGate.InitialRelationStat {
// dataWg.Add(1)
// go initStat()
// }
// dataWg.Wait()
// close(jobQueue) // all job is enqueued
// workerWg.Wait()
// }

View File

@@ -0,0 +1,48 @@
package service
import (
"context"
"encoding/json"
"strings"
"go-common/app/job/main/account-summary/model"
"go-common/library/log"
)
func (s *Service) memberBinLogproc(ctx context.Context) {
for msg := range s.MemberBinLog.Messages() {
blog := &model.CanalBinLog{}
if err := json.Unmarshal(msg.Value, blog); err != nil {
log.Error("Failed to unmarshal canal bin log: %+v, value: %s: %+v", msg, string(msg.Value), err)
msg.Commit()
continue
}
log.Info("Handling message key: %s, value: %s", msg.Key, string(msg.Value))
s.memberBinLogHandle(ctx, blog)
msg.Commit()
}
}
func (s *Service) memberBinLogHandle(ctx context.Context, blog *model.CanalBinLog) {
if len(blog.New) == 0 {
log.Error("Failed to sync to hbase with empty new field: %+v", blog)
return
}
switch {
case strings.HasPrefix(blog.Table, "user_base_") || strings.HasPrefix(blog.Table, "user_official") || strings.HasPrefix(blog.Table, "user_exp_"):
midl := &model.MidBinLog{}
if err := json.Unmarshal(blog.New, midl); err != nil {
log.Error("Failed to unmarsha new data: %s: %+v", string(blog.New), err)
return
}
// FIXME: 一段时间后该用 syncMember
if err := s.SyncOne(ctx, midl.Mid); err != nil {
log.Error("Failed to sync member with mid: %d: %+v", midl.Mid, err)
return
}
default:
log.Warn("Unable to hanlde binlog: %+v, old: %s, new: %s", blog, string(blog.Old), string(blog.New))
}
}

View File

@@ -0,0 +1,47 @@
package service
import (
"context"
"encoding/json"
"strings"
"go-common/app/job/main/account-summary/model"
"go-common/library/log"
)
func (s *Service) passportBinLogproc(ctx context.Context) {
for msg := range s.PassportBinLog.Messages() {
blog := &model.CanalBinLog{}
if err := json.Unmarshal(msg.Value, blog); err != nil {
log.Error("Failed to unmarshal canal bin log: %+v, value: %s: %+v", msg, string(msg.Value), err)
msg.Commit()
continue
}
log.Info("Handling message key: %s, value: %s", msg.Key, string(msg.Value))
s.passportBinLogHandle(ctx, blog)
msg.Commit()
}
}
func (s *Service) passportBinLogHandle(ctx context.Context, blog *model.CanalBinLog) {
if len(blog.New) == 0 {
log.Error("Failed to sync to hbase with empty new field: %+v", blog)
return
}
switch {
case strings.HasPrefix(blog.Table, "aso_account"):
midl := &model.MidBinLog{}
if err := json.Unmarshal(blog.New, midl); err != nil {
log.Error("Failed to unmarsha new data: %s: %+v", string(blog.New), err)
return
}
if err := s.syncPassportSummary(ctx, midl.Mid); err != nil {
log.Error("Failed to sync passport summary with mid: %d: %+v", midl.Mid, err)
return
}
default:
log.Warn("Unable to hanlde binlog: %+v, old: %s, new: %s", blog, string(blog.Old), string(blog.New))
}
}

View File

@@ -0,0 +1,47 @@
package service
import (
"context"
"encoding/json"
"strings"
"go-common/app/job/main/account-summary/model"
"go-common/library/log"
)
func (s *Service) relationBinLogproc(ctx context.Context) {
for msg := range s.RelationBinLog.Messages() {
blog := &model.CanalBinLog{}
if err := json.Unmarshal(msg.Value, blog); err != nil {
log.Error("Failed to unmarshal canal bin log: %+v, value: %s: %+v", msg, string(msg.Value), err)
msg.Commit()
continue
}
log.Info("Handling message key: %s, value: %s", msg.Key, string(msg.Value))
s.relationBinLogHandle(ctx, blog)
msg.Commit()
}
}
func (s *Service) relationBinLogHandle(ctx context.Context, blog *model.CanalBinLog) {
if len(blog.New) == 0 {
log.Error("Failed to sync to hbase with empty new field: %+v", blog)
return
}
switch {
case strings.HasPrefix(blog.Table, "user_relation_stat_"):
midl := &model.MidBinLog{}
if err := json.Unmarshal(blog.New, midl); err != nil {
log.Error("Failed to unmarsha new data: %s: %+v", string(blog.New), err)
return
}
if err := s.syncRelationStat(ctx, midl.Mid); err != nil {
log.Error("Failed to sync relation stat with mid: %d: %+v", midl.Mid, err)
return
}
default:
log.Warn("Unable to hanlde binlog: %+v, old: %s, new: %s", blog, string(blog.Old), string(blog.New))
}
}

View File

@@ -0,0 +1,96 @@
package service
import (
"context"
"go-common/app/job/main/account-summary/conf"
"go-common/app/job/main/account-summary/dao"
"go-common/library/log"
"go-common/library/queue/databus"
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
MemberBinLog *databus.Databus
BlockBinLog *databus.Databus
PassportBinLog *databus.Databus
RelationBinLog *databus.Databus
AccountSummaryProducer *databus.Databus
}
// New init
func New(c *conf.Config) *Service {
s := &Service{
c: c,
dao: dao.New(c),
RelationBinLog: databus.New(c.RelationBinLog),
MemberBinLog: databus.New(c.MemberBinLog),
BlockBinLog: databus.New(c.BlockBinLog),
PassportBinLog: databus.New(c.PassportBinLog),
AccountSummaryProducer: databus.New(c.AccountSummaryProducer),
}
s.Main()
return s
}
// Ping Service
func (s *Service) Ping(c context.Context) error {
return s.dao.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.dao.Close()
}
// Main is
func (s *Service) Main() {
subproc := func() {
worker := s.c.AccountSummary.SubProcessWorker
if worker <= 0 {
worker = 1
}
log.Info("Starting sub process with %d workers", worker)
for i := uint64(0); i < worker; i++ {
go s.memberBinLogproc(context.Background())
go s.blockBinLogproc(context.Background())
go s.passportBinLogproc(context.Background())
go s.relationBinLogproc(context.Background())
}
}
syncrange := func() {
start := s.c.AccountSummary.SyncRangeStart
if start <= 0 {
start = 1
}
end := s.c.AccountSummary.SyncRangeEnd
if end <= 0 {
end = 1
}
worker := s.c.AccountSummary.SyncRangeWorker
if worker <= 0 {
worker = 1
}
go s.syncRangeproc(context.Background(), start, end, worker)
}
// initial := func() {
// go s.initialproc(context.Background())
// }
if !s.c.FeatureGate.DisableSubProcess {
subproc()
}
if s.c.FeatureGate.SyncRange {
syncrange()
}
// if s.c.FeatureGate.Initial {
// initial()
// }
}

View File

@@ -0,0 +1,65 @@
package service
import (
"context"
"go-common/app/job/main/account-summary/model"
"go-common/library/log"
)
// Syncable is
type Syncable interface {
Key() (string, error)
Marshal() (map[string][]byte, error)
}
// SyncToHBase is
func (s *Service) SyncToHBase(ctx context.Context, body Syncable) error {
key, err := body.Key()
if err != nil {
log.Error("Failed to struct key with body: %+v: %+v", body, err)
return err
}
data, err := body.Marshal()
if err != nil {
log.Error("Failed to sync to hbase with key: %s, body: %+v: %+v", key, body, err)
return err
}
defer s.logging(ctx, key)
return s.dao.Save(ctx, key, data)
}
func (s *Service) logging(ctx context.Context, key string) {
sum, _ := s.dao.GetByKey(ctx, key)
log.Info("Sync to hbase result: key: %s, summary: %+v", key, sum)
}
// SyncOne is
func (s *Service) SyncOne(ctx context.Context, mid int64) error {
// member
if err := s.syncMember(ctx, mid); err != nil {
log.Error("Failed to sync member with mid: %d: %+v", mid, err)
}
// relation
if err := s.syncRelationStat(ctx, mid); err != nil {
log.Error("Failed to sync relation stat with mid: %d: %+v", mid, err)
}
// block
if err := s.syncBlock(ctx, mid); err != nil {
log.Error("Failed to sync block with mid: %d: %+v", mid, err)
}
// passport
if err := s.syncPassportSummary(ctx, mid); err != nil {
log.Error("Failed to sync passport summary with mid: %d: %+v", mid, err)
}
return nil
}
// GetOne is
func (s *Service) GetOne(ctx context.Context, mid int64) (*model.AccountSummary, error) {
return s.dao.GetByKey(ctx, model.MidKey(mid))
}

View File

@@ -0,0 +1,208 @@
package service
import (
"context"
"sync"
"go-common/app/job/main/account-summary/model"
member "go-common/app/service/main/member/model"
"go-common/app/service/main/member/model/block"
relation "go-common/app/service/main/relation/model"
"go-common/library/log"
xtime "go-common/library/time"
)
func (s *Service) block(ctx context.Context, mid int64) (*model.BlockSummary, error) {
bl, err := s.dao.MemberService.BlockInfo(ctx, &block.RPCArgInfo{MID: mid})
if err != nil {
return nil, err
}
blSummary := &model.BlockSummary{
EmbedMid: model.EmbedMid{Mid: bl.MID},
BlockStatus: int64(bl.BlockStatus),
StartTime: model.Datetime(xtime.Time(bl.StartTime)),
EndTime: model.Datetime(xtime.Time(bl.EndTime)),
}
return blSummary, nil
}
func (s *Service) relationStat(ctx context.Context, mid int64) (*model.RelationStat, error) {
stat, err := s.dao.RelationService.Stat(ctx, &relation.ArgMid{Mid: mid})
if err != nil {
return nil, err
}
reStat := &model.RelationStat{
EmbedMid: model.EmbedMid{Mid: stat.Mid},
Follower: stat.Follower,
Following: stat.Following,
Whisper: stat.Whisper,
Black: stat.Black,
}
return reStat, nil
}
func (s *Service) passportSummary(ctx context.Context, mid int64) (*model.PassportSummary, error) {
ps := &model.PassportSummary{
EmbedMid: model.EmbedMid{Mid: mid},
}
wg := &sync.WaitGroup{}
wg.Add(1)
go func() error {
defer wg.Done()
pp, err := s.dao.PassportProfile(ctx, mid)
if err != nil {
log.Error("Failed to fetch passport profile: %+v", err)
return err
}
ps.TelStatus = pp.TelStatus()
ps.CountryID = pp.CountryCode
ps.JoinIP = pp.JoinIP
ps.JoinTime = model.Datetime(pp.JoinTime)
ps.EmailSuffix = pp.EmailSuffix()
return nil
}()
wg.Add(1)
go func() error {
defer wg.Done()
origin, err := s.dao.AsoAccountRegOrigin(ctx, mid)
if err != nil {
log.Error("Failed to fetch passport aso account reg origin: %+v", err)
return err
}
ps.RegType = origin.RegType
ps.OriginType = origin.OriginType
return nil
}()
wg.Wait()
return ps, nil
}
func (s *Service) member(ctx context.Context, mid int64) (*model.MemberBase, *model.MemberExp, *model.MemberOfficial, error) {
mb, err := s.dao.MemberService.Member(ctx, &member.ArgMemberMid{Mid: mid})
if err != nil {
return nil, nil, nil, err
}
var base *model.MemberBase
if mb.BaseInfo != nil {
base = &model.MemberBase{
EmbedMid: model.EmbedMid{Mid: mb.Mid},
Name: mb.Name,
Face: mb.Face,
Rank: int64(mb.Rank),
Sex: mb.Sex,
Sign: mb.Sign,
Birthday: model.Date(mb.Birthday),
}
}
var exp *model.MemberExp
if mb.LevelInfo != nil {
exp = &model.MemberExp{
EmbedMid: model.EmbedMid{Mid: mb.Mid},
Exp: int64(mb.NowExp),
}
}
var of *model.MemberOfficial
if mb.OfficialInfo != nil {
of = &model.MemberOfficial{
EmbedMid: model.EmbedMid{Mid: mb.Mid},
Role: int64(mb.Role),
Title: mb.Title,
Description: mb.Desc,
}
}
return base, exp, of, nil
}
func (s *Service) syncMember(ctx context.Context, mid int64) error {
base, exp, of, err := s.member(ctx, mid)
if err != nil {
return err
}
syncable := make([]Syncable, 0, 3)
if base != nil {
syncable = append(syncable, base)
}
if exp != nil {
syncable = append(syncable, exp)
}
if of != nil {
syncable = append(syncable, of)
}
for _, data := range syncable {
if err := s.SyncToHBase(ctx, data); err != nil {
return err
}
}
return nil
}
func (s *Service) syncRelationStat(ctx context.Context, mid int64) error {
reStat, err := s.relationStat(ctx, mid)
if err != nil {
return err
}
if err := s.SyncToHBase(ctx, reStat); err != nil {
return err
}
return nil
}
func (s *Service) syncBlock(ctx context.Context, mid int64) error {
blSummary, err := s.block(ctx, mid)
if err != nil {
return err
}
if err := s.SyncToHBase(ctx, blSummary); err != nil {
return err
}
return nil
}
func (s *Service) syncPassportSummary(ctx context.Context, mid int64) error {
ps, err := s.passportSummary(ctx, mid)
if err != nil {
return err
}
if err := s.SyncToHBase(ctx, ps); err != nil {
return err
}
return nil
}
func (s *Service) syncRangeproc(ctx context.Context, start, end int64, worker uint64) {
log.Info("Sync with range: start: %d, end %d, worker: %d", start, end, worker)
syncChan := make(chan int64, worker*128)
defer close(syncChan)
// initial
wg := sync.WaitGroup{}
wg.Add(1)
for i := uint64(0); i < worker; i++ {
go func() {
defer wg.Done()
for mid := range syncChan {
if err := s.SyncOne(context.Background(), mid); err != nil {
log.Error("Failed to sync user with mid: %d: %+v", mid, err)
}
}
}()
}
for j := start; j <= end; j++ {
syncChan <- j
}
wg.Wait()
}