You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1218 lines
32 KiB
1218 lines
32 KiB
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 |
|
}
|
|
|