feat: add im tenant db routing and chapter seven flows

This commit is contained in:
richjohnrambo
2026-08-13 11:30:48 +08:00
parent 774a448c12
commit 31f82d9c41
19 changed files with 5261 additions and 47 deletions
+184
View File
@@ -149,6 +149,13 @@ func tenantAuthAdapter(tenantID types.TenantID) (adapter.TenantAuthAdapter, erro
if !tenantID.IsValid() {
return nil, types.ErrMalformed
}
if routed, ok := adp.(adapter.TenantRoutedAdapter); ok {
_, authAdp, err := routed.TenantAdapterFor(tenantID)
if err != nil {
return nil, err
}
return authAdp, nil
}
tenantAdp, ok := adp.(adapter.TenantAuthAdapter)
if !ok {
return nil, types.ErrUnsupported
@@ -160,6 +167,17 @@ func tenantBusinessAdapter(tenantID types.TenantID) (adapter.TenantBusinessAdapt
if !tenantID.IsValid() {
return nil, types.ErrMalformed
}
if routed, ok := adp.(adapter.TenantRoutedAdapter); ok {
businessAdp, _, err := routed.TenantAdapterFor(tenantID)
if err != nil {
return nil, err
}
if !businessAdp.TenantBusinessReady() {
return nil, types.ErrUnsupported
}
activeTenantIDs.Store(tenantID, struct{}{})
return businessAdp, nil
}
tenantAdp, ok := adp.(adapter.TenantBusinessAdapter)
if !ok {
return nil, types.ErrUnsupported
@@ -960,6 +978,7 @@ type MessagesPersistenceInterface interface {
DeleteList(tenantID types.TenantID, topic string, delID int, forUser types.Uid, msgDelAge time.Duration, ranges []types.Range) error
GetAll(tenantID types.TenantID, topic string, forUser types.Uid, opt *types.QueryOpt) ([]types.Message, error)
GetDeleted(tenantID types.TenantID, topic string, forUser types.Uid, opt *types.QueryOpt) ([]types.Range, int, error)
Search(tenantID types.TenantID, topic string, forUser types.Uid, query, messageType, cursor string, limit int) ([]types.Message, string, error)
}
// messagesMapper is a concrete type implementing MessagesPersistenceInterface.
@@ -1003,14 +1022,75 @@ type RelationsPersistenceInterface interface {
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)
ListBlocks(tenantID types.TenantID, blockerID types.Uid, cursor string, limit int) ([]types.BlockListItem, string, error)
UnblockUser(tenantID types.TenantID, blockerID, blockedID types.Uid) error
DeleteFriend(tenantID types.TenantID, userID, peerID types.Uid, now time.Time) error
UpdateFriendRemark(tenantID types.TenantID, userID, peerID types.Uid, remark string, now time.Time) (*types.Friendship, error)
SearchUsers(tenantID types.TenantID, viewer types.Uid, query, searchType, cursor string, limit int) ([]types.UserSearchItem, string, error)
}
type relationsMapper struct{}
var Relations RelationsPersistenceInterface
type FavoritesPersistenceInterface interface {
Create(tenantID types.TenantID, item *types.FavoriteItem) (*types.FavoriteItem, error)
List(tenantID types.TenantID, uid types.Uid, itemType, query, cursor string, limit int) ([]types.FavoriteItem, string, error)
Get(tenantID types.TenantID, uid types.Uid, favoriteID string) (*types.FavoriteItem, error)
Delete(tenantID types.TenantID, uid types.Uid, favoriteID string) error
}
type favoritesMapper struct{}
var Favorites FavoritesPersistenceInterface
func (favoritesMapper) Create(tenantID types.TenantID, item *types.FavoriteItem) (*types.FavoriteItem, error) {
if item == nil || item.TenantID != tenantID || item.UserID.IsZero() || item.FavoriteID == "" {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantFavoriteCreate(tenantID, item)
}
func (favoritesMapper) List(tenantID types.TenantID, uid types.Uid, itemType, query, cursor string, limit int) ([]types.FavoriteItem, string, error) {
if uid.IsZero() {
return nil, "", types.ErrMalformed
}
if limit <= 0 || limit > 100 {
limit = 50
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, "", err
}
return tenantAdp.TenantFavoriteList(tenantID, uid, itemType, query, cursor, limit)
}
func (favoritesMapper) Get(tenantID types.TenantID, uid types.Uid, favoriteID string) (*types.FavoriteItem, error) {
if uid.IsZero() || strings.TrimSpace(favoriteID) == "" {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantFavoriteGet(tenantID, uid, favoriteID)
}
func (favoritesMapper) Delete(tenantID types.TenantID, uid types.Uid, favoriteID string) error {
if uid.IsZero() || strings.TrimSpace(favoriteID) == "" {
return types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return err
}
return tenantAdp.TenantFavoriteDelete(tenantID, uid, favoriteID)
}
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
@@ -1111,6 +1191,20 @@ func (relationsMapper) BlockUser(tenantID types.TenantID, blockerID, blockedID t
return tenantAdp.TenantUserBlockUpsert(tenantID, blockerID, blockedID, now)
}
func (relationsMapper) ListBlocks(tenantID types.TenantID, blockerID types.Uid, cursor string, limit int) ([]types.BlockListItem, string, error) {
if blockerID.IsZero() {
return nil, "", types.ErrMalformed
}
if limit <= 0 || limit > 100 {
limit = 50
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, "", err
}
return tenantAdp.TenantUserBlockList(tenantID, blockerID, cursor, limit)
}
func (relationsMapper) UnblockUser(tenantID types.TenantID, blockerID, blockedID types.Uid) error {
if blockerID.IsZero() || blockedID.IsZero() || blockerID == blockedID {
return types.ErrMalformed
@@ -1133,6 +1227,68 @@ func (relationsMapper) DeleteFriend(tenantID types.TenantID, userID, peerID type
return tenantAdp.TenantFriendshipDeletePair(tenantID, userID, peerID, now)
}
func (relationsMapper) UpdateFriendRemark(tenantID types.TenantID, userID, peerID types.Uid, remark string, now time.Time) (*types.Friendship, error) {
if userID.IsZero() || peerID.IsZero() || userID == peerID || len([]rune(remark)) > 64 {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantFriendshipUpdateRemark(tenantID, userID, peerID, remark, now)
}
func (relationsMapper) SearchUsers(tenantID types.TenantID, viewer types.Uid, query, searchType, cursor string, limit int) ([]types.UserSearchItem, string, error) {
if viewer.IsZero() || strings.TrimSpace(query) == "" {
return nil, "", types.ErrMalformed
}
switch searchType {
case "", "account", "exact_account", "phone":
default:
return nil, "", types.ErrMalformed
}
if limit <= 0 || limit > 50 {
limit = 20
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, "", err
}
users, nextCursor, err := tenantAdp.TenantUserSearch(tenantID, viewer, query, searchType, cursor, limit)
if err != nil {
return nil, "", err
}
items := make([]types.UserSearchItem, 0, len(users))
for i := range users {
uid := users[i].Uid()
ctx, err := relationshipContext(tenantAdp, tenantID, viewer, uid, viewer.P2PName(uid))
if err != nil {
return nil, "", err
}
items = append(items, types.UserSearchItem{
User: &users[i],
Relationship: ctx.Relationship,
RequestID: ctx.RequestID,
CanAdd: ctx.Relationship == types.RelationshipNone,
Action: relationshipAction(ctx.Relationship),
})
}
return items, nextCursor, nil
}
func relationshipAction(relationship string) string {
switch relationship {
case types.RelationshipFriend:
return "chat"
case types.RelationshipPendingInbound, types.RelationshipPendingOutbound:
return "pending"
case types.RelationshipBlockedByMe, types.RelationshipBlockedByPeer:
return "none"
default:
return "add"
}
}
func relationshipContext(tenantAdp adapter.TenantBusinessAdapter, tenantID types.TenantID, userID, peerID types.Uid, topic string) (*types.RelationshipContext, error) {
ctx := &types.RelationshipContext{
TenantID: tenantID,
@@ -1381,6 +1537,20 @@ func (messagesMapper) GetAll(tenantID types.TenantID, topic string, forUser type
return tenantAdp.TenantMessageGetAll(tenantID, topic, forUser, opt)
}
func (messagesMapper) Search(tenantID types.TenantID, topic string, forUser types.Uid, query, messageType, cursor string, limit int) ([]types.Message, string, error) {
if topic == "" || forUser.IsZero() {
return nil, "", types.ErrMalformed
}
if limit <= 0 || limit > 100 {
limit = 50
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, "", err
}
return tenantAdp.TenantMessageSearch(tenantID, topic, forUser, query, messageType, cursor, limit)
}
// GetDeleted returns the ranges of deleted messages and the largest DelId reported in the list.
func (messagesMapper) GetDeleted(tenantID types.TenantID, topic string, forUser types.Uid, opt *types.QueryOpt) ([]types.Range, int, error) {
tenantAdp, err := tenantBusinessAdapter(tenantID)
@@ -1649,6 +1819,8 @@ type FilePersistenceInterface interface {
// LinkAttachments connects earlier uploaded attachments to a message or topic to prevent it
// from being garbage collected.
LinkAttachments(tenantID types.TenantID, topic string, msgId types.Uid, attachments []string) error
// UserHasAccess checks if user can download a file through an active upload, user, topic, or message reference.
UserHasAccess(tenantID types.TenantID, fid string, uid types.Uid) (bool, error)
}
// fileMapper is concrete type which implements FilePersistenceInterface.
@@ -1738,6 +1910,17 @@ func (fileMapper) LinkAttachments(tenantID types.TenantID, topic string, msgId t
return nil
}
func (fileMapper) UserHasAccess(tenantID types.TenantID, fid string, uid types.Uid) (bool, error) {
if fid == "" || uid.IsZero() {
return false, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return false, err
}
return tenantAdp.TenantFileUserHasAccess(tenantID, fid, uid)
}
// PersistentCacheInterface is an interface which defines methods used for accessing persistent key-value cache.
type PersistentCacheInterface interface {
// Get reads a persistent cache entry.
@@ -2004,4 +2187,5 @@ func init() {
Verifications = verificationMapper{}
Security = userSecurityMapper{}
Relations = relationsMapper{}
Favorites = favoritesMapper{}
}