go-common/app/interface/main/account/service/relation/relation.go

1219 lines
32 KiB
Go
Raw Normal View History

2019-04-22 10:49:16 +00:00
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
}