business: fix contact projection and phone sharing
(cherry picked from commit c0a0e5b52240ed415d3b43ba77659821887bf50b)
This commit is contained in:
parent
d84fa6e126
commit
860e581d06
18 changed files with 703 additions and 36 deletions
|
|
@ -6,6 +6,7 @@ import (
|
|||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"telesrv/internal/app/userprojection"
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store"
|
||||
)
|
||||
|
|
@ -20,8 +21,9 @@ type ProfilePhotoProvider interface {
|
|||
|
||||
// Service 提供用户查询。
|
||||
type Service struct {
|
||||
users store.UserStore
|
||||
photos ProfilePhotoProvider
|
||||
users store.UserStore
|
||||
contacts store.ContactStore
|
||||
photos ProfilePhotoProvider
|
||||
}
|
||||
|
||||
// Option 调整用户服务可选依赖。
|
||||
|
|
@ -32,6 +34,11 @@ func WithPhotoProvider(p ProfilePhotoProvider) Option {
|
|||
return func(s *Service) { s.photos = p }
|
||||
}
|
||||
|
||||
// WithContactStore enables viewer-specific contact name/phone projection.
|
||||
func WithContactStore(c store.ContactStore) Option {
|
||||
return func(s *Service) { s.contacts = c }
|
||||
}
|
||||
|
||||
const (
|
||||
minUsernameLen = 5
|
||||
maxUsernameLen = 32
|
||||
|
|
@ -85,7 +92,12 @@ func (s *Service) ByID(ctx context.Context, currentUserID, userID int64) (domain
|
|||
if !found {
|
||||
return u, false, nil
|
||||
}
|
||||
return s.enrichOne(ctx, u), true, nil
|
||||
u = s.enrichOne(ctx, u)
|
||||
u, err = userprojection.One(ctx, s.contacts, currentUserID, u)
|
||||
if err != nil {
|
||||
return domain.User{}, false, err
|
||||
}
|
||||
return u, true, nil
|
||||
}
|
||||
|
||||
// ByIDs 批量返回指定用户。调用方必须已登录;缺失用户不会出现在结果中。
|
||||
|
|
@ -115,7 +127,8 @@ func (s *Service) ByIDs(ctx context.Context, currentUserID int64, userIDs []int6
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.enrich(ctx, users), nil
|
||||
users = s.enrich(ctx, users)
|
||||
return userprojection.ForViewer(ctx, s.contacts, currentUserID, users)
|
||||
}
|
||||
|
||||
// enrich 批量把当前头像富化到用户列表(best-effort:失败不影响用户查询)。
|
||||
|
|
@ -248,7 +261,12 @@ func (s *Service) ResolveUsername(ctx context.Context, currentUserID int64, user
|
|||
if err != nil || !found {
|
||||
return u, found, err
|
||||
}
|
||||
return s.enrichOne(ctx, u), true, nil
|
||||
u = s.enrichOne(ctx, u)
|
||||
u, err = userprojection.One(ctx, s.contacts, currentUserID, u)
|
||||
if err != nil {
|
||||
return domain.User{}, false, err
|
||||
}
|
||||
return u, true, nil
|
||||
}
|
||||
|
||||
// ResolvePhone 解析手机号到用户;当前阶段默认允许手机号深链解析,隐私规则后续接 account privacy。
|
||||
|
|
@ -264,7 +282,12 @@ func (s *Service) ResolvePhone(ctx context.Context, currentUserID int64, phone s
|
|||
if err != nil || !found {
|
||||
return u, found, err
|
||||
}
|
||||
return s.enrichOne(ctx, u), true, nil
|
||||
u = s.enrichOne(ctx, u)
|
||||
u, err = userprojection.One(ctx, s.contacts, currentUserID, u)
|
||||
if err != nil {
|
||||
return domain.User{}, false, err
|
||||
}
|
||||
return u, true, nil
|
||||
}
|
||||
|
||||
func normalizeUsername(username string) string {
|
||||
|
|
|
|||
|
|
@ -117,6 +117,48 @@ func TestServiceByIDDoesNotReloadSelf(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestServiceProjectsUsersForViewerContacts(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
contacts := memory.NewContactStore()
|
||||
owner, err := userStore.Create(ctx, domain.User{AccessHash: 1, Phone: "15550000001", FirstName: "Owner"})
|
||||
if err != nil {
|
||||
t.Fatalf("create owner: %v", err)
|
||||
}
|
||||
friend, err := userStore.Create(ctx, domain.User{AccessHash: 2, Phone: "15550000002", FirstName: "Public", LastName: "Name"})
|
||||
if err != nil {
|
||||
t.Fatalf("create friend: %v", err)
|
||||
}
|
||||
stranger, err := userStore.Create(ctx, domain.User{AccessHash: 3, Phone: "15550000003", FirstName: "Stranger"})
|
||||
if err != nil {
|
||||
t.Fatalf("create stranger: %v", err)
|
||||
}
|
||||
if _, err := contacts.Upsert(ctx, owner.ID, domain.ContactInput{
|
||||
ContactUserID: friend.ID,
|
||||
Phone: "15550000002",
|
||||
FirstName: "Remark",
|
||||
LastName: "Friend",
|
||||
}); err != nil {
|
||||
t.Fatalf("upsert contact: %v", err)
|
||||
}
|
||||
svc := NewService(userStore, WithContactStore(contacts))
|
||||
|
||||
contactUser, found, err := svc.ByID(ctx, owner.ID, friend.ID)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("ByID contact found=%v err=%v", found, err)
|
||||
}
|
||||
if !contactUser.Contact || contactUser.FirstName != "Remark" || contactUser.LastName != "Friend" || contactUser.Phone != "15550000002" {
|
||||
t.Fatalf("projected contact = %+v, want contact remark and phone", contactUser)
|
||||
}
|
||||
nonContact, found, err := svc.ByID(ctx, owner.ID, stranger.ID)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("ByID non-contact found=%v err=%v", found, err)
|
||||
}
|
||||
if nonContact.Contact || nonContact.Phone != "" || nonContact.FirstName != "Stranger" {
|
||||
t.Fatalf("projected non-contact = %+v, want name with hidden phone", nonContact)
|
||||
}
|
||||
}
|
||||
|
||||
type countingUserStore struct {
|
||||
*memory.UserStore
|
||||
byIDCalls int
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue