go-common/app/interface/main/account/service/relation/relation.go
2019-04-22 18:49:16 +08:00

1219 lines
32 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package relation
import (
"context"
"fmt"
"hash/crc32"
"sort"
"strconv"
"sync"
"go-common/app/interface/main/account/conf"
"go-common/app/interface/main/account/dao/relation"
"go-common/app/interface/main/account/model"
acml "go-common/app/service/main/account/model"
account "go-common/app/service/main/account/rpc/client"
archive "go-common/app/service/main/archive/api/gorpc"
mrl "go-common/app/service/main/relation/model"
rlrpc "go-common/app/service/main/relation/rpc/client"
"go-common/library/ecode"
"go-common/library/log"
"go-common/library/net/metadata"
)
var (
_emptyFollowings = []*model.Following{}
_emptyTagInfos = []*model.Tag{}
_emptyTags = make(map[int64]string)
_allTagsStr = "all"
_specialTagsStr = "special"
_defaultTagsStr = "default"
_listTagsStr = "list"
_emptySpList = []int64{}
)
// Service struct of service.
type Service struct {
// conf
c *conf.Config
// rpc
relationRPC *rlrpc.Service
accountRPC *account.Service3
archiveRPC *archive.Service2
// dao
dao *relation.Dao
}
// New create service instance and return.
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
relationRPC: rlrpc.New(c.RPCClient2.Relation),
accountRPC: account.New3(c.RPCClient2.Account),
archiveRPC: archive.New2(c.RPCClient2.Archive),
dao: relation.New(c),
}
return
}
// Modify modify user relation.
func (s *Service) Modify(c context.Context, mid, fid int64, act int8, src uint8, ric map[string]string) (err error) {
if act < mrl.ActAddFollowing || act > mrl.ActDelFollower {
err = ecode.RequestErr
return
}
arg := &mrl.ArgFollowing{Mid: mid, Fid: fid, Source: src, Action: act, Infoc: ric}
if err = s.relationRPC.ModifyRelation(c, arg); err != nil {
log.Error("s.relationRPC.ModifyRelation(mid:%d,fid:%d,src:%d,act:%d) err(%v)", mid, fid, act, src, err)
}
return
}
// BatchModify batch modify user relation.
func (s *Service) BatchModify(c context.Context, mid int64, fids []int64, act int8, src uint8, ric map[string]string) (result *model.BatchModifyResult, err error) {
if len(fids) > 50 {
err = ecode.RequestErr
return
}
for _, fid := range fids {
if fid <= 0 || fid == mid {
err = ecode.RequestErr
return
}
}
// luoweiling: 把非加关注的动作全部拒绝掉
if act != mrl.ActAddFollowing {
err = ecode.RequestErr
return
}
if act < mrl.ActAddFollowing || act > mrl.ActDelFollower {
err = ecode.RequestErr
return
}
// zhangsusu: 批量关注里保持悄悄关注的状态
whispers, err := s.relationRPC.Whispers(c, &mrl.ArgMid{
Mid: mid,
RealIP: "",
})
if err != nil {
log.Error("Failed to get user whispers: mid: %d: %+v", mid, err)
return
}
whispersmap := make(map[int64]struct{}, len(whispers))
for _, w := range whispers {
whispersmap[w.Mid] = struct{}{}
}
filteredFids := make([]int64, 0, len(fids))
for _, fid := range fids {
if _, ok := whispersmap[fid]; ok {
continue
}
filteredFids = append(filteredFids, fid)
}
raiseErr := func(in error) error {
shouldRaise := map[int]struct{}{
ecode.RelFollowAlreadyBlack.Code(): {},
ecode.RelFollowReachTelLimit.Code(): {},
ecode.RelFollowReachMaxLimit.Code(): {},
}
ec := ecode.Cause(in)
if _, ok := shouldRaise[ec.Code()]; ok {
return ec
}
return nil
}
lock := sync.Mutex{}
result = &model.BatchModifyResult{
FailedFids: []int64{},
}
wg := sync.WaitGroup{}
for _, fid := range filteredFids {
fid := fid
wg.Add(1)
go func() {
defer wg.Done()
arg := &mrl.ArgFollowing{Mid: mid, Fid: fid, Source: src, Action: act, Infoc: ric}
rerr := s.relationRPC.ModifyRelation(c, arg)
if rerr == nil {
return
}
lock.Lock()
defer lock.Unlock()
err = raiseErr(rerr)
log.Error("s.relationRPC.ModifyRelation(mid:%d,fid:%d,src:%d,act:%d) err(%v)", mid, fid, act, src, rerr)
result.FailedFids = append(result.FailedFids, fid)
}()
}
wg.Wait()
return
}
// Relation get user relation.
func (s *Service) Relation(c context.Context, mid, fid int64) (f *mrl.Following, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgRelation{Mid: mid, Fid: fid, RealIP: ip}
if f, err = s.relationRPC.Relation(c, arg); err != nil {
log.Error("s.Relation(mid %d,fid %d) err(%v)", mid, fid, err)
}
return
}
// Relations get relations between users.
func (s *Service) Relations(c context.Context, mid int64, fids []int64) (f map[int64]*mrl.Following, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgRelations{Mid: mid, Fids: fids, RealIP: ip}
if f, err = s.relationRPC.Relations(c, arg); err != nil {
log.Error("s.Relations(mid %d,fids %d) err(%v)", mid, fids, err)
}
return
}
// Blacks get user black list.
func (s *Service) Blacks(c context.Context, mid int64, version uint64, pn, ps int64) (f []*model.Following, crc32v uint32, total int, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgMid{Mid: mid, RealIP: ip}
fr, err := s.relationRPC.Blacks(c, arg)
if err != nil {
log.Error("s.Blacks(mid %d) err(%v)", mid, err)
return
}
total = len(fr)
stat, err := s.relationRPC.Stat(c, arg)
if err != nil {
log.Error("s.Stat(mid %d) err(%v)", mid, err)
return
}
total = int(stat.Black)
start, end := (pn-1)*ps, pn*ps
switch {
case start >= int64(len(fr)):
fr = fr[:0]
case end >= int64(len(fr)):
fr = fr[start:]
default:
fr = fr[start:end]
}
if len(fr) == 0 {
f = _emptyFollowings
return
}
temp := []byte(fmt.Sprintf("%s", fr))
crc32v = crc32.Checksum(temp, crc32.IEEETable)
if uint64(crc32v) == version {
err = ecode.NotModified
return
}
var (
mids []int64
infos map[int64]*acml.Info
fi *mrl.Following
)
for _, fi = range fr {
mids = append(mids, fi.Mid)
}
accArg := &acml.ArgMids{Mids: mids}
if infos, err = s.accountRPC.Infos3(c, accArg); err != nil {
log.Error("s.accountRPC.Infos3(mid:%v) err(%v)", accArg, err)
return
}
for _, fi = range fr {
tmp := &model.Following{Following: fi}
info, ok := infos[fi.Mid]
if !ok {
log.Warn("Failed to fetch infos with mid: %d", fi.Mid)
continue
}
tmp.Face = info.Face
tmp.Uname = info.Name
tmp.Sign = info.Sign
f = append(f, tmp)
}
return
}
// Whispers get user Whispers.
func (s *Service) Whispers(c context.Context, mid int64, pn, ps int64, version uint64) (f []*model.Following, crc32v uint32, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgMid{Mid: mid, RealIP: ip}
fr, err := s.relationRPC.Whispers(c, arg)
if err != nil {
log.Error("s.Whispers(mid %d) err(%v)", mid, err)
return
}
start, end := (pn-1)*ps, pn*ps
switch {
case start >= int64(len(fr)):
fr = fr[:0]
case end >= int64(len(fr)):
fr = fr[start:]
default:
fr = fr[start:end]
}
if len(fr) == 0 {
f = _emptyFollowings
return
}
temp := []byte(fmt.Sprintf("%s", fr))
crc32v = crc32.Checksum(temp, crc32.IEEETable)
if uint64(crc32v) == version {
err = ecode.NotModified
return
}
var (
mids []int64
cards map[int64]*acml.Card
fi *mrl.Following
)
for _, fi = range fr {
mids = append(mids, fi.Mid)
}
accArg := &acml.ArgMids{Mids: mids}
if cards, err = s.accountRPC.Cards3(c, accArg); err != nil {
log.Error("s.accountRPC(mid:%v) err(%v)", accArg, err)
return
}
for _, fi = range fr {
tmp := &model.Following{Following: fi}
card, ok := cards[fi.Mid]
if !ok {
log.Warn("Failed to fetch card with mid: %d", mid)
continue
}
tmp.Face = card.Face
tmp.Uname = card.Name
tmp.Sign = card.Sign
of := card.Official
if of.Role == 0 {
tmp.OfficialVerify.Type = -1
} else {
if of.Role <= 2 {
tmp.OfficialVerify.Type = 0
} else {
tmp.OfficialVerify.Type = 1
}
tmp.OfficialVerify.Desc = of.Title
}
// tmp.Vip = cards[fi.Mid].Vip
tmp.Vip.Type = int(card.Vip.Type)
tmp.Vip.VipStatus = int(card.Vip.Status)
tmp.Vip.DueDate = card.Vip.DueDate
f = append(f, tmp)
}
return
}
// Friends get user friends list: follow eachother.
func (s *Service) Friends(c context.Context, mid int64, version uint64) (f []*model.Following, crc32v uint32, err error) {
var (
mids []int64
cards map[int64]*acml.Card
fi *mrl.Following
fo, fs []*mrl.Following
ip = metadata.String(c, metadata.RemoteIP)
)
arg := &mrl.ArgMid{Mid: mid, RealIP: ip}
if fo, err = s.relationRPC.Followings(c, arg); err != nil {
log.Error("s.Followings(mid %d) err(%v)", mid, err)
return
}
for _, fi = range fo {
if mrl.Attr(fi.Attribute) == mrl.AttrFriend {
fs = append(fs, fi)
}
}
if len(fs) == 0 {
f = _emptyFollowings
return
}
temp := []byte(fmt.Sprintf("%s", fo))
crc32v = crc32.Checksum(temp, crc32.IEEETable)
if uint64(crc32v) == version {
err = ecode.NotModified
return
}
for _, fi = range fs {
mids = append(mids, fi.Mid)
}
accArg := &acml.ArgMids{Mids: mids}
if cards, err = s.accountRPC.Cards3(c, accArg); err != nil {
log.Error("s.accountRPC.Cards3(mid:%v) err(%v)", accArg, err)
return
}
for _, fi = range fs {
tmp := &model.Following{Following: fi}
card, ok := cards[fi.Mid]
if !ok {
log.Warn("Failed to fetch card with mid: %d", fi.Mid)
continue
}
tmp.Face = card.Face
tmp.Uname = card.Name
tmp.Sign = card.Sign
// tmp.OfficialVerify = cards[fi.Mid].Official
of := card.Official
if of.Role == 0 {
tmp.OfficialVerify.Type = -1
} else {
if of.Role <= 2 {
tmp.OfficialVerify.Type = 0
} else {
tmp.OfficialVerify.Type = 1
}
tmp.OfficialVerify.Desc = of.Title
}
// tmp.Vip = infos[fi.Mid].Vip
tmp.Vip.Type = int(card.Vip.Type)
tmp.Vip.VipStatus = int(card.Vip.Status)
tmp.Vip.DueDate = card.Vip.DueDate
f = append(f, tmp)
}
return
}
// Followers get user followings.
func (s *Service) Followers(c context.Context, vmid, mid, pn, ps int64, version uint64) (f []*model.Following, crc32v uint32, total int, err error) {
var (
mids []int64
cards map[int64]*acml.Card
fi *mrl.Following
ip = metadata.String(c, metadata.RemoteIP)
)
arg := &mrl.ArgMid{Mid: vmid, RealIP: ip}
fr, err := s.relationRPC.Followers(c, arg)
if err != nil {
log.Error("s.Followers(mid %d) err(%v)", vmid, err)
return
}
stat, err := s.relationRPC.Stat(c, arg)
if err != nil {
log.Error("s.Stat(mid %d) err(%v)", vmid, err)
return
}
total = int(stat.Follower)
start, end := (pn-1)*ps, pn*ps
switch {
case start >= int64(len(fr)):
fr = fr[:0]
case end >= int64(len(fr)):
fr = fr[start:]
default:
fr = fr[start:end]
}
if len(fr) == 0 {
f = _emptyFollowings
return
}
for _, fi = range fr {
mids = append(mids, fi.Mid)
}
// !self, compute !self user and up's followings' attr
var frs map[int64]*mrl.Following
if mid != 0 {
argfrs := &mrl.ArgRelations{Mid: mid, Fids: mids, RealIP: ip}
frs, err = s.relationRPC.Relations(c, argfrs)
if err != nil {
log.Error("s.relationRPC.Relations(c, %v) error(%v)", argfrs, err)
return
}
}
temp := []byte(fmt.Sprintf("%s", fr))
crc32v = crc32.Checksum(temp, crc32.IEEETable)
if uint64(crc32v) == version {
err = ecode.NotModified
return
}
accArg := &acml.ArgMids{Mids: mids}
if cards, err = s.accountRPC.Cards3(c, accArg); err != nil {
log.Error("s.accountRPC.Cards3(mid:%v) err(%v)", accArg, err)
return
}
for _, fi = range fr {
tmp := &model.Following{Following: fi}
card, ok := cards[fi.Mid]
if !ok {
log.Warn("Failed to fetch card with mid: %d", mid)
continue
}
tmp.Face = card.Face
tmp.Uname = card.Name
tmp.Sign = card.Sign
if frst, ok := frs[fi.Mid]; ok {
tmp.Attribute = frst.Attribute
} else {
tmp.Attribute = mrl.AttrNoRelation
}
// tmp.OfficialVerify = cards[fi.Mid].Official
of := card.Official
if of.Role == 0 {
tmp.OfficialVerify.Type = -1
} else {
if of.Role <= 2 {
tmp.OfficialVerify.Type = 0
} else {
tmp.OfficialVerify.Type = 1
}
tmp.OfficialVerify.Desc = of.Title
}
// tmp.Vip = infos[fi.Mid].Vip
tmp.Vip.Type = int(card.Vip.Type)
tmp.Vip.VipStatus = int(card.Vip.Status)
tmp.Vip.DueDate = card.Vip.DueDate
f = append(f, tmp)
}
return
}
// Followings get user followings list.
func (s *Service) Followings(c context.Context, vmid, mid, pn, ps int64, version uint64, order string) (f []*model.Following, crc32v uint32, total int, err error) {
var (
mids []int64
cards map[int64]*acml.Card
fi *mrl.Following
ip = metadata.String(c, metadata.RemoteIP)
)
arg := &mrl.ArgMid{Mid: vmid, RealIP: ip}
fr, err := s.relationRPC.Followings(c, arg)
if err != nil {
log.Error("s.Followings(mid %d) err(%v)", vmid, err)
return
}
stat, err := s.relationRPC.Stat(c, arg)
if err != nil {
log.Error("s.Stat(mid %d) err(%v)", vmid, err)
return
}
total = int(stat.Following)
if order == "asc" {
sort.Sort(ByMTime(fr))
}
start, end := (pn-1)*ps, pn*ps
switch {
case start >= int64(len(fr)):
fr = fr[:0]
case end >= int64(len(fr)):
fr = fr[start:]
default:
fr = fr[start:end]
}
if len(fr) == 0 {
f = _emptyFollowings
return
}
for _, fi = range fr {
mids = append(mids, fi.Mid)
}
// !self, compute !self user and up's followings' attr
var frs map[int64]*mrl.Following
if mid != vmid && mid != 0 {
argfrs := &mrl.ArgRelations{Mid: mid, Fids: mids, RealIP: ip}
frs, err = s.relationRPC.Relations(c, argfrs)
if err != nil {
log.Error("s.relationRPC.Relations(c, %v) error(%v)", argfrs, err)
return
}
}
temp := []byte(fmt.Sprintf("%s", fr))
crc32v = crc32.Checksum(temp, crc32.IEEETable)
if uint64(crc32v) == version {
err = ecode.NotModified
return
}
accArg := &acml.ArgMids{Mids: mids}
if cards, err = s.accountRPC.Cards3(c, accArg); err != nil {
log.Error("s.accountRPC.Cards3(mid:%v) err(%v)", accArg, err)
return
}
for _, fi = range fr {
tmp := &model.Following{Following: fi}
card, ok := cards[fi.Mid]
if !ok {
log.Warn("Failed to fetch card with mid: %d", mid)
continue
}
tmp.Face = card.Face
tmp.Uname = card.Name
tmp.Sign = card.Sign
if mid != vmid {
if frst, ok := frs[fi.Mid]; ok {
tmp.Attribute = frst.Attribute
} else {
tmp.Attribute = mrl.AttrNoRelation
}
}
// tmp.OfficialVerify = cards[fi.Mid].Official
of := card.Official
if of.Role == 0 {
tmp.OfficialVerify.Type = -1
} else {
if of.Role <= 2 {
tmp.OfficialVerify.Type = 0
} else {
tmp.OfficialVerify.Type = 1
}
tmp.OfficialVerify.Desc = of.Title
}
// tmp.Vip = infos[fi.Mid].Vip
tmp.Vip.Type = int(card.Vip.Type)
tmp.Vip.VipStatus = int(card.Vip.Status)
tmp.Vip.DueDate = card.Vip.DueDate
f = append(f, tmp)
}
return
}
// Stat get user relation stat.
func (s *Service) Stat(c context.Context, mid int64, self bool) (st *mrl.Stat, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgMid{Mid: mid, RealIP: ip}
if st, err = s.relationRPC.Stat(c, arg); err != nil {
log.Error("s.Stat(mid %d) err(%v)", mid, err)
return
}
if !self {
st.Whisper = 0
st.Black = 0
}
return
}
// Stats get users relation stat.
func (s *Service) Stats(c context.Context, mids []int64) (st map[int64]*mrl.Stat, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgMids{Mids: mids, RealIP: ip}
return s.relationRPC.Stats(c, arg)
}
// Ping check server ok.
func (s *Service) Ping(c context.Context) (err error) {
return
}
// ByMTime implements sort.Interface for []model.Following based on the MTime field.
type ByMTime []*mrl.Following
func (mt ByMTime) Len() int { return len(mt) }
func (mt ByMTime) Swap(i, j int) { mt[i], mt[j] = mt[j], mt[i] }
func (mt ByMTime) Less(i, j int) bool { return mt[i].MTime < mt[j].MTime }
// Tag get tag info by tag.
func (s *Service) Tag(c context.Context, mid int64, tagid int64, pn int64, ps int64) (tagInfo []*model.Tag, err error) {
var (
mids []int64
cards map[int64]*acml.Card
ip = metadata.String(c, metadata.RemoteIP)
)
arg := &mrl.ArgTagId{Mid: mid, TagId: tagid, RealIP: ip}
if mids, err = s.relationRPC.Tag(c, arg); err != nil {
log.Error("s.relationRPC(%d).Arg(%v) error(%v)", mid, arg, err)
return
}
var tmpMids []int64
start, end := (pn-1)*ps, pn*ps
switch {
case start >= int64(len(mids)):
tmpMids = mids[:0]
case end >= int64(len(mids)):
tmpMids = mids[start:]
default:
tmpMids = mids[start:end]
}
if len(tmpMids) == 0 {
tagInfo = _emptyTagInfos
return
}
accArg := &acml.ArgMids{Mids: mids}
if cards, err = s.accountRPC.Cards3(c, accArg); err != nil {
log.Error("s.accountRPC.Cards3(mid:%v) err(%v)", accArg, err)
return
}
for _, mid = range tmpMids {
tmp := &model.Tag{Mid: mid}
card, ok := cards[mid]
if !ok {
log.Warn("Failed to fetch card with mid: %d", mid)
continue
}
tmp.Mid = mid
tmp.Face = card.Face
tmp.Uname = card.Name
tmp.Sign = card.Sign
// tmp.OfficialVerify = cards[mid].Official
of := card.Official
if of.Role == 0 {
tmp.OfficialVerify.Type = -1
} else {
if of.Role <= 2 {
tmp.OfficialVerify.Type = 0
} else {
tmp.OfficialVerify.Type = 1
}
tmp.OfficialVerify.Desc = of.Title
}
// tmp.Vip = infos[mid].Vip
tmp.Vip.Type = int(card.Vip.Type)
tmp.Vip.VipStatus = int(card.Vip.Status)
tmp.Vip.DueDate = card.Vip.DueDate
tagInfo = append(tagInfo, tmp)
}
return
}
// Tags is.
func (s *Service) Tags(c context.Context, mid int64) (tagsCount []*mrl.TagCount, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgMid{Mid: mid, RealIP: ip}
if tagsCount, err = s.relationRPC.Tags(c, arg); err != nil {
log.Error("s.relationRPC(%d).Arg(%v) error(%v)", mid, arg, err)
return
}
return
}
// MobileTags is.
func (s *Service) MobileTags(c context.Context, mid int64) (tagsCount map[string][]*mrl.TagCount, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgMid{Mid: mid, RealIP: ip}
tags, err := s.relationRPC.Tags(c, arg)
if err != nil {
log.Error("s.relationRPC(%d).Arg(%v) error(%v)", mid, arg, err)
return
}
var st *mrl.Stat
argStat := &mrl.ArgMid{Mid: mid, RealIP: ip}
if st, err = s.relationRPC.Stat(c, argStat); err != nil {
log.Error("s.Stat(mid %d) err(%v)", mid, err)
return
}
tagsCount = map[string][]*mrl.TagCount{
_allTagsStr: {{
Tagid: -1,
Name: "公开关注",
Count: st.Following,
}},
_specialTagsStr: {{
Tagid: -10,
Name: "特别关注",
Count: 0,
}},
_listTagsStr: make([]*mrl.TagCount, 0, len(tags)),
_defaultTagsStr: make([]*mrl.TagCount, 0, 1),
}
for _, v := range tags {
if v.Tagid == 0 {
tagsCount[_defaultTagsStr] = append(tagsCount[_defaultTagsStr], v)
} else if v.Tagid == -10 {
tagsCount[_specialTagsStr][0].Count = v.Count
} else {
tagsCount[_listTagsStr] = append(tagsCount[_listTagsStr], v)
}
}
return
}
// UserTag is.
func (s *Service) UserTag(c context.Context, mid int64, fid int64) (tags map[int64]string, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgRelation{Mid: mid, Fid: fid, RealIP: ip}
if tags, err = s.relationRPC.UserTag(c, arg); err != nil {
log.Error("s.relationRPC.Arg(%v) error(%v)", arg, err)
return
}
if tags == nil {
tags = _emptyTags
}
return
}
// CreateTag is.
func (s *Service) CreateTag(c context.Context, mid int64, tag string) (tagInfo int64, err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgTag{Mid: mid, Tag: tag, RealIP: ip}
if tagInfo, err = s.relationRPC.CreateTag(c, arg); err != nil {
log.Error("s.relationRPC.Arg(%v) error(%v)", arg, err)
return
}
return
}
// UpdateTag is.
func (s *Service) UpdateTag(c context.Context, mid int64, tagID int64, new string) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgTagUpdate{Mid: mid, TagId: tagID, New: new, RealIP: ip}
if err = s.relationRPC.UpdateTag(c, arg); err != nil {
log.Error("s.relationRPC.Arg(%v) error(%v)", arg, err)
return
}
return
}
// DelTag is.
func (s *Service) DelTag(c context.Context, mid int64, tagID int64) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgTagDel{Mid: mid, TagId: tagID, RealIP: ip}
if err = s.relationRPC.DelTag(c, arg); err != nil {
log.Error("s.relationRPC.Arg(%v) error(%v)", arg, err)
return
}
return
}
// TagsAddUsers is.
func (s *Service) TagsAddUsers(c context.Context, mid int64, tagIds string, fids string) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgTagsMoveUsers{Mid: mid, BeforeID: 0, AfterTagIds: tagIds, Fids: fids, RealIP: ip}
if err = s.relationRPC.TagsAddUsers(c, arg); err != nil {
log.Error("s.relationRPC.Arg(%v) error(%v)", arg, err)
return
}
return
}
// TagsCopyUsers is.
func (s *Service) TagsCopyUsers(c context.Context, mid int64, tagIds string, fids string) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgTagsMoveUsers{Mid: mid, BeforeID: 0, AfterTagIds: tagIds, Fids: fids, RealIP: ip}
if err = s.relationRPC.TagsCopyUsers(c, arg); err != nil {
log.Error("s.relationRPC.Arg(%v) error(%v)", arg, err)
return
}
return
}
// TagsMoveUsers is.
func (s *Service) TagsMoveUsers(c context.Context, mid, beforeid int64, afterTagIdsStr, fidsStr string) (err error) {
ip := metadata.String(c, metadata.RemoteIP)
arg := &mrl.ArgTagsMoveUsers{Mid: mid, BeforeID: beforeid, AfterTagIds: afterTagIdsStr, Fids: fidsStr, RealIP: ip}
if err = s.relationRPC.TagsMoveUsers(c, arg); err != nil {
log.Error("s.relationRPC.Arg(%v) error(%v)", arg, err)
return
}
return
}
// Prompt report and get prompt status.
func (s *Service) Prompt(c context.Context, arg *mrl.ArgPrompt) (b bool, err error) {
return s.relationRPC.Prompt(c, arg)
}
// ClosePrompt close prompt.
func (s *Service) ClosePrompt(c context.Context, arg *mrl.ArgPrompt) (err error) {
return s.relationRPC.ClosePrompt(c, arg)
}
// AddSpecial add fid into special.
func (s *Service) AddSpecial(c context.Context, arg *mrl.ArgFollowing) (err error) {
return s.relationRPC.AddSpecial(c, arg)
}
// DelSpecial del fid from sepcial.
func (s *Service) DelSpecial(c context.Context, arg *mrl.ArgFollowing) (err error) {
return s.relationRPC.DelSpecial(c, arg)
}
// Special get user special list.
func (s *Service) Special(c context.Context, mid int64) (l []int64, err error) {
arg := &mrl.ArgMid{
Mid: mid,
}
l, err = s.relationRPC.Special(c, arg)
if len(l) == 0 {
l = _emptySpList
}
return
}
// Unread check unread status, for the 'show red point' function.
func (s *Service) Unread(c context.Context, mid int64, disableAutoReset bool) (show bool, err error) {
arg := &mrl.ArgMid{
Mid: mid,
}
// if !disableAutoReset {
// defer s.ResetUnread(c, mid)
// }
return s.relationRPC.FollowersUnread(c, arg)
}
// ResetUnread is
func (s *Service) ResetUnread(c context.Context, mid int64) (err error) {
arg := &mrl.ArgMid{
Mid: mid,
}
return s.relationRPC.ResetFollowersUnread(c, arg)
}
// UnreadCount unread count.
func (s *Service) UnreadCount(c context.Context, mid int64, disableAutoReset bool) (count int64, err error) {
arg := &mrl.ArgMid{
Mid: mid,
}
// if !disableAutoReset {
// defer func() {
// s.ResetUnread(c, mid)
// s.ResetUnreadCount(c, mid)
// }()
// }
return s.relationRPC.FollowersUnreadCount(c, arg)
}
// ResetUnreadCount is
func (s *Service) ResetUnreadCount(c context.Context, mid int64) (err error) {
arg := &mrl.ArgMid{
Mid: mid,
}
return s.relationRPC.ResetFollowersUnreadCount(c, arg)
}
// RecommendTagSuggestDetail is
func (s *Service) RecommendTagSuggestDetail(c context.Context, arg *model.ArgTagSuggestRecommend) (*model.TagSuggestRecommendInfo, error) {
result, err := s.RecommendTagSuggest(c, arg)
if err != nil {
return nil, err
}
if len(result) <= 0 {
empty := &model.TagSuggestRecommendInfo{
TagName: arg.TagName,
UpList: []*model.RecommendInfo{},
MatchCnt: 0,
}
return empty, nil
}
detail := result[0]
upMids := func() []int64 {
mids := make([]int64, 0, len(detail.UpList))
for _, up := range detail.UpList {
mid, perr := strconv.ParseInt(up.Mid, 10, 64)
if perr != nil {
log.Warn("Failed to parse mid: %s: %+v", up.Mid, perr)
continue
}
mids = append(mids, mid)
}
return mids
}()
rels, err := s.relationRPC.Relations(c, &mrl.ArgRelations{
Mid: arg.Mid,
Fids: upMids,
RealIP: arg.RemoteIP,
})
if err != nil {
return nil, err
}
for _, up := range detail.UpList {
mid, err := strconv.ParseInt(up.Mid, 10, 64)
if err != nil {
log.Warn("Failed to parse mid: %s: %+v", err, up.Mid)
continue
}
r, ok := rels[mid]
if !ok {
log.Warn("Failed to get relation between %d and %d", arg.Mid, mid)
up.Relation = &mrl.Following{Mid: mid} // empty relation
continue
}
up.Relation = r
}
return detail, nil
}
// RecommendTagSuggest is
func (s *Service) RecommendTagSuggest(c context.Context, arg *model.ArgTagSuggestRecommend) ([]*model.TagSuggestRecommendInfo, error) {
resp, err := s.dao.TagSuggestRecommend(c, arg.Mid, arg.ContextID, arg.TagName, arg.Device, arg.PageSize, arg.RemoteIP)
if err != nil {
return nil, err
}
allrecs := make([]*model.RecommendContent, 0)
for _, rec := range resp.Data {
allrecs = append(allrecs, rec.UpList...)
}
allrecinfos, err := s.collectionAsRecommendUserInfo(c, allrecs, resp.TrackID, arg.RemoteIP)
if err != nil {
return nil, err
}
allrecinfomap := make(map[string]*model.RecommendInfo, len(allrecinfos))
for _, recinfo := range allrecinfos {
allrecinfomap[recinfo.Mid] = recinfo
}
getRecInfos := func(mids ...int64) []*model.RecommendInfo {
out := make([]*model.RecommendInfo, 0, len(mids))
for _, mid := range mids {
smid := strconv.FormatInt(mid, 10)
recinfo, ok := allrecinfomap[smid]
if !ok {
log.Warn("Failed to get user info with mid: %d", mid)
continue
}
out = append(out, recinfo)
}
return out
}
result := make([]*model.TagSuggestRecommendInfo, 0, len(resp.Data))
for _, rec := range resp.Data {
trecinfo := &model.TagSuggestRecommendInfo{
TagName: rec.TagName,
MatchCnt: rec.MatchCnt,
}
rinfos := getRecInfos(rec.UpIDs()...)
trecinfo.UpList = rinfos
if len(rinfos) != len(rec.UpIDs()) {
log.Warn("Inconsistent user info and recommend match count: %d, %d", len(rinfos), len(rec.UpIDs()))
trecinfo.MatchCnt -= int64(len(rec.UpIDs()) - len(rinfos))
}
result = append(result, trecinfo)
}
return result, nil
}
// RecommendFollowlistEmpty is
func (s *Service) RecommendFollowlistEmpty(c context.Context, arg *model.ArgRecommend) ([]*model.RecommendInfo, error) {
return s.recommend(c, "followlist_empty", arg)
}
// RecommendAnswerOK is
func (s *Service) RecommendAnswerOK(c context.Context, arg *model.ArgRecommend) ([]*model.RecommendInfo, error) {
return s.recommend(c, "answer_ok", arg)
}
func (s *Service) collectionAsRecommendUserInfo(c context.Context, recs []*model.RecommendContent, trackID string, ip string) ([]*model.RecommendInfo, error) {
recmap := make(map[int64]*model.RecommendContent, len(recs))
mids := make([]int64, 0, len(recs))
for _, r := range recs {
mids = append(mids, r.UpID)
recmap[r.UpID] = r
}
cards, err := s.accountRPC.Cards3(c, &acml.ArgMids{Mids: mids})
if err != nil {
return nil, err
}
stats, err := s.relationRPC.Stats(c, &mrl.ArgMids{Mids: mids, RealIP: ip})
if err != nil {
return nil, err
}
// TODO: cache types
types, err := s.archiveRPC.Types2(c)
if err != nil {
return nil, err
}
typeName := func(tid int16) string {
t := types[tid]
if t == nil {
return ""
}
return t.Name
}
ris := make([]*model.RecommendInfo, 0, len(recs))
for _, rec := range recs {
if rec == nil {
log.Warn("Invalid recommend content: %+v", rec)
continue
}
card, ok := cards[rec.UpID]
if !ok {
log.Warn("Failed to get user card with mid: %d", rec.UpID)
continue
}
stat, ok := stats[rec.UpID]
if !ok {
log.Warn("Failed to get stat with mid: %d", rec.UpID)
continue
}
ri := &model.RecommendInfo{}
ri.FromCard(card)
ri.RecommendContent = *rec
ri.TrackID = trackID
ri.Fans = stat.Follower
ri.TypeName = typeName(ri.Tid)
ri.SecondTypeName = typeName(ri.SecondTid)
// zhangsusu: 拼粉丝数作为推荐理由(后来又下线了)
// fs := followerString(stat.Follower)
// if fs != "" {
// parts := []string{}
// if ri.RecReason != "" {
// parts = append(parts, ri.RecReason)
// }
// parts = append(parts, fs)
// ri.RecReason = strings.Join(parts, "")
// }
ris = append(ris, ri)
}
return ris, nil
}
func (s *Service) recommend(c context.Context, serviceArea string, arg *model.ArgRecommend) ([]*model.RecommendInfo, error) {
resp, err := s.dao.Recommend(c, arg.Mid, serviceArea, arg.MainTids, arg.SubTids, arg.Device, arg.PageSize, arg.RemoteIP)
if err != nil {
return nil, err
}
return s.collectionAsRecommendUserInfo(c, resp.Data, resp.TrackID, arg.RemoteIP)
}
// func followerString(follower int64) string {
// if follower <= 0 {
// return ""
// }
// if follower < 10000 {
// return fmt.Sprintf("%d粉丝", follower)
// }
// return fmt.Sprintf("%.1f万粉丝", float64(follower)/float64(10000))
// }
// AchieveGet is
func (s *Service) AchieveGet(c context.Context, arg *model.ArgAchieveGet) (*mrl.AchieveGetReply, error) {
rpcArg := &mrl.ArgAchieveGet{
Award: arg.Award,
Mid: arg.Mid,
}
return s.relationRPC.AchieveGet(c, rpcArg)
}
// Achieve is
func (s *Service) Achieve(c context.Context, arg *model.ArgAchieve) (*model.AchieveReply, error) {
rpcArg := &mrl.ArgAchieve{
AwardToken: arg.AwardToken,
}
achieve, err := s.relationRPC.Achieve(c, rpcArg)
if err != nil {
return nil, err
}
reply := &model.AchieveReply{
Achieve: *achieve,
Metadata: make(map[string]interface{}),
}
info, err := s.accountRPC.Info3(c, &acml.ArgMid{Mid: achieve.Mid})
if err != nil {
return nil, err
}
reply.Metadata["mid"] = info.Mid
reply.Metadata["name"] = info.Name
return reply, nil
}
// FollowerNotifySetting get new-follower-notification setting
func (s *Service) FollowerNotifySetting(c context.Context, mid int64) (followerNotify *mrl.FollowerNotifySetting, err error) {
arg := &mrl.ArgMid{
Mid: mid,
RealIP: metadata.String(c, metadata.RemoteIP),
}
return s.relationRPC.FollowerNotifySetting(c, arg)
}
// EnableFollowerNotify enable new-follower-notification
func (s *Service) EnableFollowerNotify(c context.Context, mid int64) (err error) {
arg := &mrl.ArgMid{
Mid: mid,
RealIP: metadata.String(c, metadata.RemoteIP),
}
return s.relationRPC.EnableFollowerNotify(c, arg)
}
// DisableFollowerNotify enable new-follower-notification
func (s *Service) DisableFollowerNotify(c context.Context, mid int64) (err error) {
arg := &mrl.ArgMid{
Mid: mid,
RealIP: metadata.String(c, metadata.RemoteIP),
}
return s.relationRPC.DisableFollowerNotify(c, arg)
}
// SameFollowings is
func (s *Service) SameFollowings(c context.Context, arg *model.ArgSameFollowing) (f []*model.Following, crc32v uint32, total int, err error) {
sfArg := &mrl.ArgSameFollowing{Mid1: arg.VMid, Mid2: arg.Mid}
fr, err := s.relationRPC.SameFollowings(c, sfArg)
if err != nil {
log.Error("s.SameFollowings(%+v) err(%v)", arg, err)
return
}
total = len(fr)
if arg.Order == "asc" {
// 直接倒序即可
for i := len(fr)/2 - 1; i >= 0; i-- {
opp := len(fr) - 1 - i
fr[i], fr[opp] = fr[opp], fr[i]
}
}
start, end := (arg.PN-1)*arg.PS, arg.PN*arg.PS
switch {
case start >= int64(len(fr)):
fr = fr[:0]
case end >= int64(len(fr)):
fr = fr[start:]
default:
fr = fr[start:end]
}
if len(fr) == 0 {
f = _emptyFollowings
return
}
mids := make([]int64, 0, len(fr))
for _, fi := range fr {
mids = append(mids, fi.Mid)
}
temp := []byte(fmt.Sprintf("%s", fr))
crc32v = crc32.Checksum(temp, crc32.IEEETable)
if uint64(crc32v) == arg.ReVersion {
err = ecode.NotModified
return
}
accArg := &acml.ArgMids{Mids: mids}
cards, err := s.accountRPC.Cards3(c, accArg)
if err != nil {
log.Error("s.accountRPC.Cards3(mid:%v) err(%v)", accArg, err)
return
}
for _, fi := range fr {
tmp := &model.Following{Following: fi}
card, ok := cards[fi.Mid]
if !ok {
log.Warn("Failed to fetch card with mid: %d", fi.Mid)
continue
}
tmp.Face = card.Face
tmp.Uname = card.Name
tmp.Sign = card.Sign
of := card.Official
if of.Role == 0 {
tmp.OfficialVerify.Type = -1
} else {
if of.Role <= 2 {
tmp.OfficialVerify.Type = 0
} else {
tmp.OfficialVerify.Type = 1
}
tmp.OfficialVerify.Desc = of.Title
}
tmp.Vip.Type = int(card.Vip.Type)
tmp.Vip.VipStatus = int(card.Vip.Status)
tmp.Vip.DueDate = card.Vip.DueDate
f = append(f, tmp)
}
return
}