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,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"item_likes.go",
"like.go",
"service.go",
"user_likes.go",
],
importpath = "go-common/app/job/main/thumbup/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/main/thumbup/conf:go_default_library",
"//app/job/main/thumbup/dao:go_default_library",
"//app/job/main/thumbup/model:go_default_library",
"//app/service/main/thumbup/model:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
"//library/queue/databus/databusutil:go_default_library",
"//library/sync/pipeline: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,85 @@
package service
import (
"context"
"encoding/json"
"go-common/app/job/main/thumbup/model"
xmdl "go-common/app/service/main/thumbup/model"
"go-common/library/log"
"go-common/library/queue/databus"
)
func newItemLikeMsg(msg *databus.Message) (res interface{}, err error) {
itemLikesMsg := new(xmdl.ItemMsg)
if err = json.Unmarshal(msg.Value, &itemLikesMsg); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", msg.Value, err)
return
}
log.Info("get item like msg: %+v", itemLikesMsg)
res = itemLikesMsg
return
}
func itemLikesSplit(msg *databus.Message, data interface{}) int {
im, ok := data.(*xmdl.ItemMsg)
if !ok {
log.Error("user item msg err: message_id: 0 %s", msg.Value)
return 0
}
return int(im.MessageID)
}
func (s *Service) itemLikesDo(ms []interface{}) {
for _, m := range ms {
im, ok := m.(*xmdl.ItemMsg)
if !ok {
continue
}
var (
err error
businessID int64
ctx = context.Background()
)
if businessID, err = s.checkBusinessOrigin(im.Business, im.OriginID); err != nil {
continue
}
var exist bool
if exist, _ = s.dao.ExpireItemLikesCache(ctx, im.MessageID, businessID, im.State); exist {
continue
}
for i := 0; i < _retryTimes; i++ {
if err = s.addItemLikesCache(ctx, im); err == nil {
break
}
}
log.Info("itemLikes success params(%+v)", m)
}
}
// addCacheItemLikes .
func (s *Service) addItemLikesCache(c context.Context, p *xmdl.ItemMsg) (err error) {
var businessID int64
if businessID, err = s.checkBusinessOrigin(p.Business, p.OriginID); err != nil {
log.Error("s.checkBusinessOrigin business(%s) originID(%s)", p.Business, p.OriginID, err)
return
}
var items []*model.UserLikeRecord
var limit = s.businessIDMap[businessID].MessageLikesLimit
if items, err = s.dao.ItemLikes(c, businessID, p.OriginID, p.MessageID, p.State, limit); err != nil {
log.Error("s.dao.ItemLikes businessID(%d) originID(%d) messageID(%d) type(%d) error(%v)", businessID, p.OriginID, p.MessageID, p.State, err)
return
}
err = s.dao.AddItemLikesCache(c, businessID, p.MessageID, p.State, limit, items)
return
}
func (s *Service) addItemlikeRecord(c context.Context, businessID, messageID int64, state int8, item *model.UserLikeRecord) (err error) {
var exist bool
if exist, err = s.dao.ExpireItemLikesCache(c, messageID, businessID, state); (err != nil) || !exist {
return
}
limit := s.businessIDMap[businessID].MessageLikesLimit
err = s.dao.AppendCacheItemLikeList(c, messageID, item, businessID, state, limit)
return
}

View File

@@ -0,0 +1,330 @@
package service
import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"go-common/app/job/main/thumbup/model"
xmdl "go-common/app/service/main/thumbup/model"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/queue/databus"
xtime "go-common/library/time"
)
func newLikeMsg(msg *databus.Message) (res interface{}, err error) {
likeMsg := new(xmdl.LikeMsg)
if err = json.Unmarshal(msg.Value, &likeMsg); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", msg.Value, err)
return
}
log.Info("get like event msg: %+v", likeMsg)
res = likeMsg
return
}
func likeSplit(msg *databus.Message, data interface{}) int {
lm, ok := data.(*xmdl.LikeMsg)
if !ok {
log.Error("get like event msg: not ok %s", msg.Value)
return 0
}
return int(lm.Mid)
}
func (s *Service) likeDo(ms []interface{}) {
for _, m := range ms {
lm, ok := m.(*xmdl.LikeMsg)
if !ok {
log.Error("get like event msg: not ok %+v", m)
continue
}
var (
oldState int8
err error
businessID int64
ctx = context.Background()
)
if businessID, err = s.checkBusinessOrigin(lm.Business, lm.OriginID); err != nil {
log.Warn("like event: business(%v, %v) err: +v", lm.Business, lm.OriginID)
continue
}
if oldState, err = s.dao.LikeState(ctx, lm.Mid, businessID, lm.OriginID, lm.MessageID); err != nil {
log.Warn("like event: likeState(%+v) err: +v", lm)
time.Sleep(time.Millisecond * 50)
continue
}
var newState, likeType int8
if newState, likeType, err = s.checkState(ctx, oldState, lm); err != nil {
log.Warn("repeat like mid(%d) likeType(%d) oldState(%d) newState(%d) bid(%d) oid(%d) messageID(%d)",
lm.Mid, likeType, oldState, newState, businessID, lm.OriginID, lm.MessageID)
continue
}
var stat model.Stats
if stat, err = s.dao.UpdateLikeState(ctx, lm.Mid, businessID, lm.OriginID, lm.MessageID, newState, lm.LikeTime); err != nil {
log.Warn("like event: UpdateLikeState(%+v) err: +v", lm)
time.Sleep(time.Millisecond * 50)
continue
}
// 聚合数据
key := fmt.Sprintf("%d-%d-%d", businessID, lm.OriginID, lm.MessageID)
s.merge.Add(ctx, key, lm)
stat = calculateCount(stat, likeType)
s.updateCache(ctx, lm.Mid, businessID, lm.OriginID, lm.MessageID, likeType, lm.LikeTime, &stat)
s.dao.PubStatDatabus(ctx, lm.Business, lm.Mid, &stat, lm.UpMid)
log.Info("like event: like success params(%+v)", m)
// 拜年祭
target := s.mergeTarget(lm.Business, lm.MessageID)
if target <= 0 {
continue
}
if stat, err = s.dao.Stat(ctx, businessID, 0, target); err != nil {
continue
}
lm.MessageID = target
key = fmt.Sprintf("%d-%d-%d", businessID, 0, target)
s.merge.Add(ctx, key, lm)
stat = calculateCount(stat, likeType)
s.updateStatCache(ctx, businessID, 0, &stat)
s.dao.PubStatDatabus(ctx, lm.Business, lm.Mid, &stat, 0)
log.Info("like success params(%+v)", m)
}
}
func (s *Service) countsSplit(key string) int {
messageIDStr := strings.Split(key, "-")[2]
messageID, _ := strconv.Atoi(messageIDStr)
return messageID % s.c.Merge.Worker
}
func (s *Service) updateCountsDo(c context.Context, ch int, values map[string][]interface{}) {
mItem := make(map[model.LikeItem]*model.LikeCounts)
for _, vs := range values {
for _, v := range vs {
item := v.(*xmdl.LikeMsg)
stat := calculateCount(model.Stats{}, item.Type)
likesCount := stat.Likes
dislikesCount := stat.Dislikes
likeItem := model.LikeItem{
Business: item.Business,
OriginID: item.OriginID,
MessageID: item.MessageID,
}
if mItem[likeItem] == nil {
mItem[likeItem] = &model.LikeCounts{Like: likesCount, Dislike: dislikesCount, UpMid: item.UpMid}
} else {
mItem[likeItem].Like += likesCount
mItem[likeItem].Dislike += dislikesCount
if item.UpMid > 0 {
mItem[likeItem].UpMid = item.UpMid
}
}
}
}
for item, count := range mItem {
for i := 0; i < _retryTimes; i++ {
if err := s.dao.UpdateCounts(context.Background(), s.businessMap[item.Business].ID, item.OriginID, item.MessageID, count.Like, count.Dislike, count.UpMid); err == nil {
break
}
}
}
}
// checkBusiness .
func (s *Service) checkBusiness(business string) (id int64, err error) {
b := s.businessMap[business]
if b == nil {
err = ecode.ThumbupBusinessBlankErr
return
}
id = b.ID
return
}
// checkBusinessOrigin .
func (s *Service) checkBusinessOrigin(business string, originID int64) (id int64, err error) {
b := s.businessMap[business]
if b == nil {
err = ecode.ThumbupBusinessBlankErr
return
}
if (b.EnableOriginID == 1 && originID == 0) || (b.EnableOriginID == 0 && originID != 0) {
err = ecode.ThumbupOriginErr
return
}
id = b.ID
return
}
// updateCache .
func (s *Service) updateCache(c context.Context, mid, businessID, originID, messageID int64, likeType int8, likeTime time.Time, stat *model.Stats) {
if stat != nil {
s.updateStatCache(c, businessID, originID, stat)
}
business := s.businessIDMap[businessID]
likeRecord := &model.ItemLikeRecord{MessageID: messageID, Time: xtime.Time(likeTime.Unix())}
userRecord := &model.UserLikeRecord{Mid: mid, Time: xtime.Time(likeTime.Unix())}
switch likeType {
case model.TypeLike:
if business.EnableUserLikeList() {
s.addUserlikeRecord(c, mid, businessID, model.StateLike, likeRecord)
}
if business.EnableItemLikeList() {
s.addItemlikeRecord(c, businessID, messageID, model.StateLike, userRecord)
}
case model.TypeCancelLike:
if business.EnableUserLikeList() {
s.dao.DelUserLikeCache(c, mid, businessID, messageID, model.StateLike)
}
if business.EnableItemLikeList() {
s.dao.DelItemLikeCache(c, messageID, businessID, mid, model.StateLike)
}
case model.TypeDislike:
if business.EnableUserDislikeList() {
s.addUserlikeRecord(c, mid, businessID, model.StateDislike, likeRecord)
}
if business.EnableItemDislikeList() {
s.addItemlikeRecord(c, businessID, messageID, model.StateDislike, userRecord)
}
case model.TypeCancelDislike:
if business.EnableUserDislikeList() {
s.dao.DelUserLikeCache(c, mid, businessID, messageID, model.StateDislike)
}
if business.EnableItemDislikeList() {
s.dao.DelItemLikeCache(c, messageID, businessID, mid, model.StateDislike)
}
case model.TypeLikeReverse:
if business.EnableUserLikeList() {
s.dao.DelUserLikeCache(c, mid, businessID, messageID, model.StateLike)
}
if business.EnableItemLikeList() {
s.dao.DelItemLikeCache(c, messageID, businessID, mid, model.StateLike)
}
if business.EnableUserDislikeList() {
s.addUserlikeRecord(c, mid, businessID, model.StateDislike, likeRecord)
}
if business.EnableItemDislikeList() {
s.addItemlikeRecord(c, businessID, messageID, model.StateDislike, userRecord)
}
case model.TypeDislikeReverse:
if business.EnableUserDislikeList() {
s.dao.DelUserLikeCache(c, mid, businessID, messageID, model.StateDislike)
}
if business.EnableItemDislikeList() {
s.dao.DelItemLikeCache(c, messageID, businessID, mid, model.StateDislike)
}
if business.EnableUserLikeList() {
s.addUserlikeRecord(c, mid, businessID, model.StateLike, likeRecord)
}
if business.EnableItemLikeList() {
s.addItemlikeRecord(c, businessID, messageID, model.StateLike, userRecord)
}
}
}
// updateStateCache .
func (s *Service) updateStatCache(c context.Context, businessID, originID int64, stat *model.Stats) (err error) {
if stat == nil {
return
}
var ok bool
if originID == 0 {
err = s.dao.AddStatsCache(c, businessID, stat)
} else {
if ok, err = s.dao.ExpireHashStatsCache(c, businessID, originID); ok {
err = s.dao.AddHashStatsCache(c, businessID, originID, stat)
}
}
return
}
func calculateCount(stat model.Stats, typ int8) model.Stats {
var likesCount, dislikeCount int64
switch typ {
case model.TypeLike:
likesCount = 1
case model.TypeCancelLike:
likesCount = -1
case model.TypeDislike:
dislikeCount = 1
case model.TypeCancelDislike:
dislikeCount = -1
case model.TypeLikeReverse:
likesCount = -1
dislikeCount = 1
case model.TypeDislikeReverse:
likesCount = 1
dislikeCount = -1
}
stat.Likes += likesCount
stat.Dislikes += dislikeCount
return stat
}
// checkState .
func (s *Service) checkState(c context.Context, oldState int8, lm *xmdl.LikeMsg) (newState, likeType int8, err error) {
likeType = lm.Type
if oldState == model.StateBlank {
switch lm.Type {
case model.TypeLike:
newState = model.StateLike
case model.TypeCancelLike:
err = ecode.ThumbupCancelLikeErr
case model.TypeDislike:
newState = model.StateDislike
case model.TypeCancelDislike:
err = ecode.ThumbupCancelDislikeErr
}
} else if oldState == model.StateLike {
switch lm.Type {
case model.TypeLike:
err = ecode.ThumbupDupLikeErr
limit := s.businessMap[lm.Business].UserLikesLimit
bid := s.businessMap[lm.Business].ID
likeRecord := &model.ItemLikeRecord{MessageID: lm.MessageID, Time: xtime.Time(lm.LikeTime.Unix())}
if exists, err1 := s.dao.ExpireUserLikesCache(c, lm.Mid, bid, model.StateLike); err1 == nil && exists {
s.dao.AppendCacheUserLikeList(c, lm.Mid, likeRecord, bid, model.StateLike, limit)
}
case model.TypeCancelLike:
newState = model.StateBlank
case model.TypeDislike:
likeType = model.TypeLikeReverse
newState = model.StateDislike
case model.TypeCancelDislike:
err = ecode.ThumbupCancelDislikeErr
}
} else if oldState == model.StateDislike {
switch lm.Type {
case model.TypeLike:
likeType = model.TypeDislikeReverse
newState = model.StateLike
case model.TypeCancelLike:
err = ecode.ThumbupCancelLikeErr
case model.TypeDislike:
err = ecode.ThumbupDupDislikeErr
limit := s.businessMap[lm.Business].UserLikesLimit
bid := s.businessMap[lm.Business].ID
likeRecord := &model.ItemLikeRecord{MessageID: lm.MessageID, Time: xtime.Time(lm.LikeTime.Unix())}
if exists, err1 := s.dao.ExpireUserLikesCache(c, lm.Mid, bid, model.StateDislike); err1 == nil && exists {
s.dao.AppendCacheUserLikeList(c, lm.Mid, likeRecord, bid, model.StateDislike, limit)
}
case model.TypeCancelDislike:
newState = model.StateBlank
}
} else {
log.Warn("oldState abnormal mid:%d business:%v oid:%d messageID:%d oldState:%d", lm.Mid, lm.Business, lm.OriginID, lm.MessageID, oldState)
}
return
}
func (s *Service) mergeTarget(business string, aid int64) int64 {
if s.statMerge != nil && s.statMerge.Business == business && s.statMerge.Sources[aid] {
return s.statMerge.Target
}
return 0
}

View File

@@ -0,0 +1,144 @@
package service
import (
"context"
"sync"
"time"
"go-common/app/job/main/thumbup/conf"
"go-common/app/job/main/thumbup/dao"
"go-common/app/job/main/thumbup/model"
"go-common/library/queue/databus"
"go-common/library/queue/databus/databusutil"
"go-common/library/sync/pipeline"
)
const (
_retryTimes = 3
)
// Service .Service
type Service struct {
c *conf.Config
dao *dao.Dao
waiter *sync.WaitGroup
merge *pipeline.Pipeline
likeGroup *databusutil.Group
itemLikesGroup *databusutil.Group
userLikesGroup *databusutil.Group
// businessMap
businessMap map[string]*model.Business
businessIDMap map[int64]*model.Business
// for 拜年祭
statMerge *statMerge
}
type statMerge struct {
Business string
Target int64
Sources map[int64]bool
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
// waitGroup
waiter: new(sync.WaitGroup),
}
if c.StatMerge != nil {
s.statMerge = &statMerge{
Business: c.StatMerge.Business,
Target: c.StatMerge.Target,
Sources: make(map[int64]bool),
}
for _, id := range c.StatMerge.Sources {
s.statMerge.Sources[id] = true
}
}
s.loadBusiness()
s.initLikeGroup(c)
s.initItemLikesGroup(c)
s.initUserLikesGroup(c)
s.initCountsMerge()
go s.loadBusinessproc()
return
}
func (s *Service) loadBusinessproc() {
for {
s.loadBusiness()
time.Sleep(time.Minute * 5)
}
}
func (s *Service) initItemLikesGroup(c *conf.Config) {
ig := databusutil.NewGroup(c.ItemLikesDatabusutil, databus.New(c.Databus.ItemLikes).Messages())
ig.New = newItemLikeMsg
ig.Split = itemLikesSplit
ig.Do = s.itemLikesDo
ig.Start()
s.itemLikesGroup = ig
}
func (s *Service) initUserLikesGroup(c *conf.Config) {
ug := databusutil.NewGroup(c.UserLikesDatabusutil, databus.New(c.Databus.UserLikes).Messages())
ug.New = newUserLikeMsg
ug.Split = userLikesSplit
ug.Do = s.userLikesDo
ug.Start()
s.userLikesGroup = ug
}
func (s *Service) initLikeGroup(c *conf.Config) {
lg := databusutil.NewGroup(c.LikeDatabusutil, databus.New(c.Databus.Like).Messages())
lg.New = newLikeMsg
lg.Split = likeSplit
lg.Do = s.likeDo
lg.Start()
s.likeGroup = lg
}
func (s *Service) initCountsMerge() {
s.merge = pipeline.NewPipeline(s.c.Merge)
s.merge.Split = s.countsSplit
s.merge.Do = s.updateCountsDo
s.merge.Start()
}
func (s *Service) loadBusiness() {
var (
err error
business []*model.Business
)
businessMap := make(map[string]*model.Business)
businessIDMap := make(map[int64]*model.Business)
for {
if business, err = s.dao.Business(context.TODO()); err != nil {
time.Sleep(time.Second)
continue
}
for _, b := range business {
businessMap[b.Name] = b
businessIDMap[b.ID] = b
}
s.businessMap = businessMap
s.businessIDMap = businessIDMap
return
}
}
// Ping .
func (s *Service) Ping(ctx context.Context) error {
return s.dao.Ping(ctx)
}
// Close .
func (s *Service) Close() {
s.waiter.Wait()
s.itemLikesGroup.Close()
s.userLikesGroup.Close()
s.likeGroup.Close()
s.dao.Close()
}

View File

@@ -0,0 +1,91 @@
package service
import (
"context"
"encoding/json"
"go-common/app/job/main/thumbup/model"
xmdl "go-common/app/service/main/thumbup/model"
"go-common/library/log"
"go-common/library/queue/databus"
)
func newUserLikeMsg(msg *databus.Message) (res interface{}, err error) {
userLikesMsg := new(xmdl.UserMsg)
if err = json.Unmarshal(msg.Value, &userLikesMsg); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", msg.Value, err)
return
}
log.Info("get user like msg: %+v", userLikesMsg)
res = userLikesMsg
return
}
func userLikesSplit(msg *databus.Message, data interface{}) int {
um, ok := data.(*xmdl.UserMsg)
if !ok {
log.Error("user like msg err: mid: 0 %s", msg.Value)
return 0
}
return int(um.Mid)
}
func (s *Service) userLikesDo(ms []interface{}) {
for _, m := range ms {
um, ok := m.(*xmdl.UserMsg)
if !ok {
continue
}
var (
err error
businessID int64
ctx = context.Background()
)
if businessID, err = s.checkBusiness(um.Business); err != nil {
log.Warn("userlikes: checkBusiness(%s) err:%+v", um.Business, err)
continue
}
var exist bool
if exist, _ = s.dao.ExpireUserLikesCache(ctx, um.Mid, businessID, um.State); exist {
log.Warn("userlikes: ExpireUserLikesCache(%+v) exist ignore", um, err)
continue
}
for i := 0; i < _retryTimes; i++ {
if err = s.addUserLikesCache(context.Background(), um); err == nil {
break
}
}
if err != nil {
log.Error("userLikes fail params(%+v) err: %+v", m, err)
} else {
log.Info("userLikes success params(%+v)", m)
}
}
}
// addUserLikesCache .
func (s *Service) addUserLikesCache(c context.Context, p *xmdl.UserMsg) (err error) {
var businessID int64
if businessID, err = s.checkBusiness(p.Business); err != nil {
log.Error("s.checkBusiness business(%s) error(%v)", p.Business, err)
return
}
var items []*model.ItemLikeRecord
var limit = s.businessIDMap[businessID].UserLikesLimit
if items, err = s.dao.UserLikes(c, p.Mid, businessID, p.State, limit); err != nil {
log.Error("s.dao.UserLikes mid(%d) businessID(%d)(%d) type(%d) error(%v)", p.Mid, businessID, p.State, err)
return
}
err = s.dao.AddUserLikesCache(c, p.Mid, businessID, items, p.State, limit)
return
}
func (s *Service) addUserlikeRecord(c context.Context, mid, businessID int64, state int8, item *model.ItemLikeRecord) (err error) {
var exist bool
if exist, err = s.dao.ExpireUserLikesCache(c, mid, businessID, state); (err != nil) || !exist {
return
}
limit := s.businessIDMap[businessID].UserLikesLimit
err = s.dao.AppendCacheUserLikeList(c, mid, item, businessID, state, limit)
return
}