@@ -0,0 +1,312 @@
|
||||
package imser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/imclient"
|
||||
"91porn-server/models/v/imusermod"
|
||||
"91porn-server/models/v/usermod"
|
||||
)
|
||||
|
||||
const friendListFirstPageNow int64 = 1
|
||||
|
||||
type IMUserMapping struct {
|
||||
UID uint64
|
||||
IMUserID int64
|
||||
}
|
||||
|
||||
type IMFriendItem struct {
|
||||
FriendIMUserID int64 `json:"friendImUserId"`
|
||||
UID uint64 `json:"uid,omitempty"`
|
||||
NickName string `json:"nickName,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
Remark string `json:"remark,omitempty"`
|
||||
Status int `json:"status"`
|
||||
CreatedAt int64 `json:"createdAt,omitempty"`
|
||||
UpdatedAt int64 `json:"updatedAt,omitempty"`
|
||||
}
|
||||
|
||||
type IMHistoryMessage struct {
|
||||
MessageID string `json:"messageId"`
|
||||
SenderID int64 `json:"senderId"`
|
||||
ReceiverID int64 `json:"receiverId"`
|
||||
SenderUserID uint64 `json:"senderUserId,omitempty"`
|
||||
SenderNickName string `json:"senderNickName,omitempty"`
|
||||
SenderAvatar string `json:"senderAvatar,omitempty"`
|
||||
ReceiverUserID uint64 `json:"receiverUserId,omitempty"`
|
||||
ReceiverNickName string `json:"receiverNickName,omitempty"`
|
||||
ReceiverAvatar string `json:"receiverAvatar,omitempty"`
|
||||
Content string `json:"content"`
|
||||
MessageType string `json:"messageType"`
|
||||
Seq int64 `json:"seq"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
FromSelf bool `json:"fromSelf"`
|
||||
}
|
||||
|
||||
// ResolveStoredIMUserID 解析用户的 IM 平台 user_id:
|
||||
// 优先 usermod.ImUserID → imusermod 映射表 → 都没有则调用 IM 注册接口创建。
|
||||
// 保证返回的 imUserId > 0,或返回错误,调用方无需再判 0。
|
||||
func ResolveStoredIMUserID(uid uint64) (int64, error) {
|
||||
// 直接复用 EnsureSDKUser:它的"查→兜底注册"流程跟这里需要的语义一致。
|
||||
return EnsureSDKUser(uid)
|
||||
}
|
||||
|
||||
func ResolveUIDByIMUserID(imUserID int64) (uint64, error) {
|
||||
if imUserID <= 0 {
|
||||
return 0, fmt.Errorf("imUserId is empty")
|
||||
}
|
||||
// imUserId → uid 以 imusermod 为唯一真源
|
||||
mapping, err := imusermod.FindByIMUserIDQuiet(imUserID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if mapping.UID <= 0 {
|
||||
return 0, fmt.Errorf("im user not found")
|
||||
}
|
||||
return mapping.UID, nil
|
||||
}
|
||||
|
||||
func BusinessUsersByIMUserIDs(imUserIDs []int64) (map[int64]*usermod.User, error) {
|
||||
out := make(map[int64]*usermod.User)
|
||||
ids := uniquePositiveInt64(imUserIDs)
|
||||
if len(ids) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
// imUserId → uid 走 imusermod(唯一真源),再按 uid 批量取业务用户
|
||||
mappings, err := imusermod.FindByIMUserIDsQuiet(ids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uids := make([]uint64, 0, len(mappings))
|
||||
uidByIM := make(map[int64]uint64, len(mappings))
|
||||
for _, mapping := range mappings {
|
||||
if mapping.UID <= 0 || mapping.IMUserID <= 0 {
|
||||
continue
|
||||
}
|
||||
uids = append(uids, mapping.UID)
|
||||
uidByIM[mapping.IMUserID] = mapping.UID
|
||||
}
|
||||
if len(uids) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
usersByUID, err := usermod.FindUsersMapByUID(uids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for imUserID, uid := range uidByIM {
|
||||
if u := usersByUID[uid]; u != nil {
|
||||
out[imUserID] = u
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func EnsureFriendsBidirectional(uid, peerUID uint64) (selfIMUserID, peerIMUserID int64, added bool, err error) {
|
||||
if uid == 0 || peerUID == 0 {
|
||||
return 0, 0, false, fmt.Errorf("uid is empty")
|
||||
}
|
||||
if uid == peerUID {
|
||||
return 0, 0, false, fmt.Errorf("cannot add self")
|
||||
}
|
||||
c := newSDKClient()
|
||||
if !c.Enabled() {
|
||||
return 0, 0, false, nil
|
||||
}
|
||||
selfIMUserID, err = EnsureSDKUser(uid)
|
||||
if err != nil {
|
||||
return 0, 0, false, err
|
||||
}
|
||||
peerIMUserID, err = EnsureSDKUser(peerUID)
|
||||
if err != nil {
|
||||
return 0, 0, false, err
|
||||
}
|
||||
addedForward, err := ensureFriendDirect(c, selfIMUserID, peerIMUserID)
|
||||
if err != nil {
|
||||
return 0, 0, false, err
|
||||
}
|
||||
addedReverse, err := ensureFriendDirect(c, peerIMUserID, selfIMUserID)
|
||||
if err != nil {
|
||||
return 0, 0, false, err
|
||||
}
|
||||
return selfIMUserID, peerIMUserID, addedForward || addedReverse, nil
|
||||
}
|
||||
|
||||
func FriendList(uid uint64, now int64) (imUserID int64, usedNow int64, nextNow int64, friends []IMFriendItem, err error) {
|
||||
c := newSDKClient()
|
||||
if !c.Enabled() {
|
||||
return 0, 0, 0, []IMFriendItem{}, nil
|
||||
}
|
||||
imUserID, err = EnsureSDKUser(uid)
|
||||
if err != nil {
|
||||
return 0, 0, 0, nil, err
|
||||
}
|
||||
if now <= 0 {
|
||||
now = friendListFirstPageNow
|
||||
}
|
||||
var relations []imclient.Friend
|
||||
err = withAppToken(c, func(token string) error {
|
||||
var listErr error
|
||||
relations, listErr = c.FriendList(imUserID, now, token)
|
||||
return listErr
|
||||
})
|
||||
if err != nil {
|
||||
return 0, 0, 0, nil, err
|
||||
}
|
||||
friendIMIDs := make([]int64, 0, len(relations))
|
||||
for _, relation := range relations {
|
||||
if relation.FriendID > 0 {
|
||||
friendIMIDs = append(friendIMIDs, relation.FriendID)
|
||||
}
|
||||
}
|
||||
userByIM, err := BusinessUsersByIMUserIDs(friendIMIDs)
|
||||
if err != nil {
|
||||
return 0, 0, 0, nil, err
|
||||
}
|
||||
nextNow = now
|
||||
items := make([]IMFriendItem, 0, len(relations))
|
||||
for _, relation := range relations {
|
||||
if relation.UpdatedAt > nextNow {
|
||||
nextNow = relation.UpdatedAt
|
||||
}
|
||||
item := IMFriendItem{
|
||||
FriendIMUserID: relation.FriendID,
|
||||
Remark: relation.Remark,
|
||||
Status: relation.Status,
|
||||
CreatedAt: relation.CreatedAt,
|
||||
UpdatedAt: relation.UpdatedAt,
|
||||
}
|
||||
if u := userByIM[relation.FriendID]; u != nil {
|
||||
item.UID = u.UID
|
||||
item.NickName = u.Name
|
||||
item.Avatar = common.BindUrl(appg.Conf.URL.OriginUrl, u.Portrait)
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return imUserID, now, nextNow, items, nil
|
||||
}
|
||||
|
||||
func HistoryMessages(uid, peerUID uint64, req imclient.HistoryMessageRequest) (selfIMUserID, peerIMUserID int64, messages []IMHistoryMessage, err error) {
|
||||
c := newSDKClient()
|
||||
if !c.Enabled() {
|
||||
return 0, 0, []IMHistoryMessage{}, nil
|
||||
}
|
||||
selfIMUserID, err = EnsureSDKUser(uid)
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
peerIMUserID, err = ResolveStoredIMUserID(peerUID)
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
if peerIMUserID <= 0 {
|
||||
return 0, 0, nil, fmt.Errorf("peer im user not found")
|
||||
}
|
||||
req.UserID1 = selfIMUserID
|
||||
req.UserID2 = peerIMUserID
|
||||
var history []imclient.MessageDetail
|
||||
err = withAppToken(c, func(token string) error {
|
||||
var historyErr error
|
||||
history, historyErr = c.HistoryMessages(req, token)
|
||||
return historyErr
|
||||
})
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
messageIMIDs := make([]int64, 0, len(history)*2)
|
||||
for _, msg := range history {
|
||||
if msg.SenderID > 0 {
|
||||
messageIMIDs = append(messageIMIDs, msg.SenderID)
|
||||
}
|
||||
if msg.ReceiverID > 0 {
|
||||
messageIMIDs = append(messageIMIDs, msg.ReceiverID)
|
||||
}
|
||||
}
|
||||
userByIM, err := BusinessUsersByIMUserIDs(messageIMIDs)
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
out := make([]IMHistoryMessage, 0, len(history))
|
||||
for _, msg := range history {
|
||||
item := IMHistoryMessage{
|
||||
MessageID: msg.MessageID,
|
||||
SenderID: msg.SenderID,
|
||||
ReceiverID: msg.ReceiverID,
|
||||
Content: msg.Content,
|
||||
MessageType: msg.MessageType,
|
||||
Seq: msg.Seq,
|
||||
CreatedAt: msg.CreatedAt,
|
||||
FromSelf: msg.SenderID == selfIMUserID,
|
||||
}
|
||||
if u := userByIM[msg.SenderID]; u != nil {
|
||||
item.SenderUserID = u.UID
|
||||
item.SenderNickName = u.Name
|
||||
item.SenderAvatar = common.BindUrl(appg.Conf.URL.OriginUrl, u.Portrait)
|
||||
}
|
||||
if u := userByIM[msg.ReceiverID]; u != nil {
|
||||
item.ReceiverUserID = u.UID
|
||||
item.ReceiverNickName = u.Name
|
||||
item.ReceiverAvatar = common.BindUrl(appg.Conf.URL.OriginUrl, u.Portrait)
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
return selfIMUserID, peerIMUserID, out, nil
|
||||
}
|
||||
|
||||
func ensureFriendDirect(c *imclient.Client, userIMID, friendIMID int64) (bool, error) {
|
||||
var added bool
|
||||
err := withAppToken(c, func(token string) error {
|
||||
relations, err := c.FriendList(userIMID, friendListFirstPageNow, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, relation := range relations {
|
||||
if relation.FriendID == friendIMID && relation.Status == 0 {
|
||||
added = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = c.DirectAddFriend(imclient.DirectAddFriendRequest{
|
||||
UserID: userIMID,
|
||||
FriendID: friendIMID,
|
||||
}, token)
|
||||
if err == nil {
|
||||
added = true
|
||||
}
|
||||
return err
|
||||
})
|
||||
if isDuplicateFriendError(err) {
|
||||
return false, nil
|
||||
}
|
||||
return added, err
|
||||
}
|
||||
|
||||
func isDuplicateFriendError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
msg := strings.ToLower(err.Error())
|
||||
return strings.Contains(msg, "already") ||
|
||||
strings.Contains(msg, "exists") ||
|
||||
strings.Contains(msg, "重复") ||
|
||||
strings.Contains(msg, "已存在") ||
|
||||
strings.Contains(msg, "好友关系已存在")
|
||||
}
|
||||
|
||||
func uniquePositiveInt64(in []int64) []int64 {
|
||||
out := make([]int64, 0, len(in))
|
||||
seen := make(map[int64]struct{}, len(in))
|
||||
for _, v := range in {
|
||||
if v <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[v]; ok {
|
||||
continue
|
||||
}
|
||||
seen[v] = struct{}{}
|
||||
out = append(out, v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user