business: fix contact projection and phone sharing

(cherry picked from commit c0a0e5b52240ed415d3b43ba77659821887bf50b)
This commit is contained in:
A 2026-06-07 23:36:06 +08:00
parent d84fa6e126
commit 860e581d06
18 changed files with 703 additions and 36 deletions

View file

@ -0,0 +1,75 @@
package userprojection
import (
"context"
"telesrv/internal/domain"
"telesrv/internal/store"
)
// ForViewer applies the owner-specific user view that Telegram clients expect.
// In particular, phone is visible for self and contacts; non-contacts should not
// receive a phone field because TDesktop will prefer it over the public name.
func ForViewer(ctx context.Context, contacts store.ContactStore, viewerUserID int64, users []domain.User) ([]domain.User, error) {
if contacts == nil || viewerUserID == 0 || len(users) == 0 {
return users, nil
}
out := make([]domain.User, len(users))
copy(out, users)
cache := make(map[int64]domain.User, len(users))
for i := range out {
u := out[i]
if u.ID == 0 || u.ID == viewerUserID || u.ID == domain.OfficialSystemUserID {
continue
}
if projected, ok := cache[u.ID]; ok {
out[i] = projected
continue
}
projected, err := projectOne(ctx, contacts, viewerUserID, u)
if err != nil {
return nil, err
}
cache[u.ID] = projected
out[i] = projected
}
return out, nil
}
// One applies ForViewer to a single user.
func One(ctx context.Context, contacts store.ContactStore, viewerUserID int64, user domain.User) (domain.User, error) {
projected, err := ForViewer(ctx, contacts, viewerUserID, []domain.User{user})
if err != nil || len(projected) == 0 {
return domain.User{}, err
}
return projected[0], nil
}
func projectOne(ctx context.Context, contacts store.ContactStore, viewerUserID int64, user domain.User) (domain.User, error) {
contact, found, err := contacts.Get(ctx, viewerUserID, user.ID)
if err != nil {
return domain.User{}, err
}
if !found {
user.Phone = ""
user.Contact = false
user.Mutual = false
return user, nil
}
projected := user
projected.Contact = true
projected.Mutual = contact.Mutual || contact.User.Mutual
if contact.User.Phone != "" {
projected.Phone = contact.User.Phone
} else {
projected.Phone = contact.Phone
}
if contact.User.FirstName != "" || contact.User.LastName != "" {
projected.FirstName = contact.User.FirstName
projected.LastName = contact.User.LastName
} else if contact.FirstName != "" || contact.LastName != "" {
projected.FirstName = contact.FirstName
projected.LastName = contact.LastName
}
return projected, nil
}