feat: add im relationship flows

This commit is contained in:
richjohnrambo
2026-08-12 11:31:16 +08:00
parent 5bd35f97a6
commit d02041659e
11 changed files with 1100 additions and 12 deletions
+152
View File
@@ -995,6 +995,157 @@ func (messagesMapper) Edit(tenantID types.TenantID, topic string, seq int, actor
return tenantAdp.TenantMessageEdit(tenantID, topic, seq, actor, expectedVersion, head, content, reason, editedAt)
}
type RelationsPersistenceInterface interface {
CreateFriendRequest(tenantID types.TenantID, req *types.FriendRequest) (*types.FriendRequest, error)
ResolveFriendRequest(tenantID types.TenantID, requestID string, actor types.Uid, status string, handledAt time.Time) (*types.FriendRequest, error)
GetRelationshipContext(tenantID types.TenantID, userID, peerID types.Uid, topic string) (*types.RelationshipContext, error)
CanSendP2P(tenantID types.TenantID, userID, peerID types.Uid) (bool, string, error)
BlockUser(tenantID types.TenantID, blockerID, blockedID types.Uid, now time.Time) (*types.UserBlock, error)
UnblockUser(tenantID types.TenantID, blockerID, blockedID types.Uid) error
DeleteFriend(tenantID types.TenantID, userID, peerID types.Uid, now time.Time) error
}
type relationsMapper struct{}
var Relations RelationsPersistenceInterface
func (relationsMapper) CreateFriendRequest(tenantID types.TenantID, req *types.FriendRequest) (*types.FriendRequest, error) {
if req == nil || req.TenantID != tenantID || req.ApplicantID.IsZero() || req.TargetID.IsZero() || req.ApplicantID == req.TargetID {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantFriendRequestCreate(tenantID, req)
}
func (relationsMapper) ResolveFriendRequest(tenantID types.TenantID, requestID string, actor types.Uid, status string, handledAt time.Time) (*types.FriendRequest, error) {
if strings.TrimSpace(requestID) == "" || actor.IsZero() || (status != types.FriendRequestAccepted && status != types.FriendRequestRejected) {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
req, err := tenantAdp.TenantFriendRequestResolve(tenantID, requestID, actor, status, handledAt)
if err != nil {
return nil, err
}
if status == types.FriendRequestAccepted {
if err := tenantAdp.TenantFriendshipUpsertPair(tenantID, req.ApplicantID, req.TargetID, "", handledAt); err != nil {
return nil, err
}
}
return req, nil
}
func (relationsMapper) GetRelationshipContext(tenantID types.TenantID, userID, peerID types.Uid, topic string) (*types.RelationshipContext, error) {
if userID.IsZero() || peerID.IsZero() || userID == peerID {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return relationshipContext(tenantAdp, tenantID, userID, peerID, topic)
}
func (relationsMapper) CanSendP2P(tenantID types.TenantID, userID, peerID types.Uid) (bool, string, error) {
ctx, err := (relationsMapper{}).GetRelationshipContext(tenantID, userID, peerID, "")
if err != nil {
return false, "", err
}
if ctx.Relationship == types.RelationshipFriend {
return true, ctx.Relationship, nil
}
return false, ctx.Relationship, nil
}
func (relationsMapper) BlockUser(tenantID types.TenantID, blockerID, blockedID types.Uid, now time.Time) (*types.UserBlock, error) {
if blockerID.IsZero() || blockedID.IsZero() || blockerID == blockedID {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantUserBlockUpsert(tenantID, blockerID, blockedID, now)
}
func (relationsMapper) UnblockUser(tenantID types.TenantID, blockerID, blockedID types.Uid) error {
if blockerID.IsZero() || blockedID.IsZero() || blockerID == blockedID {
return types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantUserBlockDelete(tenantID, blockerID, blockedID)
}
func (relationsMapper) DeleteFriend(tenantID types.TenantID, userID, peerID types.Uid, now time.Time) error {
if userID.IsZero() || peerID.IsZero() || userID == peerID {
return types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantFriendshipDeletePair(tenantID, userID, peerID, now)
}
func relationshipContext(tenantAdp adapter.TenantBusinessAdapter, tenantID types.TenantID, userID, peerID types.Uid, topic string) (*types.RelationshipContext, error) {
ctx := &types.RelationshipContext{
TenantID: tenantID,
UserID: userID,
PeerID: peerID,
Topic: topic,
Relationship: types.RelationshipNone,
}
peer, err := tenantAdp.TenantUserGet(tenantID, peerID)
if err != nil {
return nil, err
}
ctx.Peer = peer
if block, err := tenantAdp.TenantUserBlockGet(tenantID, userID, peerID); err != nil {
return nil, err
} else if block != nil {
ctx.Relationship = types.RelationshipBlockedByMe
return ctx, nil
}
if block, err := tenantAdp.TenantUserBlockGet(tenantID, peerID, userID); err != nil {
return nil, err
} else if block != nil {
ctx.Relationship = types.RelationshipBlockedByPeer
return ctx, nil
}
if friendship, err := tenantAdp.TenantFriendshipGet(tenantID, userID, peerID); err != nil {
return nil, err
} else if friendship != nil && friendship.Status == types.RelationshipFriend {
ctx.Relationship = types.RelationshipFriend
ctx.CanSend = true
if ctx.Topic == "" {
ctx.Topic = friendship.Topic
}
return ctx, nil
}
req, err := tenantAdp.TenantFriendRequestLatestBetween(tenantID, userID, peerID)
if err != nil {
return nil, err
}
if req != nil && req.Status == types.FriendRequestPending && req.ExpiresAt.After(time.Now()) {
ctx.RequestID = req.RequestID
ctx.PendingStatus = req.Status
if req.ApplicantID == userID {
ctx.Relationship = types.RelationshipPendingOutbound
} else {
ctx.Relationship = types.RelationshipPendingInbound
}
}
return ctx, nil
}
// Save message
func (messagesMapper) Save(tenantID types.TenantID, msg *types.Message, attachmentURLs []string, readBySender bool) (*types.MessageSaveResult, error) {
if msg == nil || msg.TenantID != tenantID {
@@ -1814,4 +1965,5 @@ func init() {
PCache = pcacheMapper{}
Verifications = verificationMapper{}
Security = userSecurityMapper{}
Relations = relationsMapper{}
}