chore: refresh gramsrv public release

This commit is contained in:
A 2026-06-30 14:37:43 +08:00
parent 75cebe8dbf
commit 70b6820474
1274 changed files with 378751 additions and 59919 deletions

View file

@ -110,6 +110,185 @@ func (s *Service) CanSee(ctx context.Context, ownerUserID, viewerUserID int64, k
return Evaluate(rules, evalCtx), nil
}
// CanSeeBatch 批量评估多个 owner 对同一 viewer 在多个 key 上的可见性,结果等价于对每个
// (owner,key) 调一次 CanSee,但只用一次 ListPrivacyRules + 一次 GetReverseContacts + 内存
// Evaluate(消除 projectBatch / fan-out 投影里 per-user 3×CanSee×2行 的 N+1)。返回
// map[ownerUserID]map[key]bool;owner==viewer 恒 true(与 CanSee 一致)。
func (s *Service) CanSeeBatch(ctx context.Context, ownerUserIDs []int64, viewerUserID int64, keys []domain.PrivacyKey) (map[int64]map[domain.PrivacyKey]bool, error) {
out := make(map[int64]map[domain.PrivacyKey]bool, len(ownerUserIDs))
if viewerUserID == 0 || len(ownerUserIDs) == 0 || len(keys) == 0 {
return out, nil
}
for _, k := range keys {
if !ValidKey(k) {
return nil, domain.ErrPrivacyKeyInvalid
}
}
owners := make([]int64, 0, len(ownerUserIDs))
seen := make(map[int64]struct{}, len(ownerUserIDs))
for _, id := range ownerUserIDs {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
if id == viewerUserID {
// 自己恒可见全部 key(与 CanSee 的 ownerUserID==viewerUserID 分支一致)。
m := make(map[domain.PrivacyKey]bool, len(keys))
for _, k := range keys {
m[k] = true
}
out[id] = m
continue
}
owners = append(owners, id)
}
if len(owners) == 0 {
return out, nil
}
// 批量取 rules:存在的行进 map,缺失的 (owner,key) 用 defaultRules(复刻 GetRules 兜底)。
rulesByOwner := make(map[int64]map[domain.PrivacyKey]domain.PrivacyRules, len(owners))
if s != nil && s.rules != nil {
list, err := s.rules.ListPrivacyRules(ctx, owners, keys)
if err != nil {
return nil, err
}
for _, r := range list {
if !ValidKey(r.Key) {
continue
}
if len(r.Rules) == 0 {
r.Rules = domain.DefaultPrivacyRules(r.Key)
}
if rulesByOwner[r.OwnerUserID] == nil {
rulesByOwner[r.OwnerUserID] = make(map[domain.PrivacyKey]domain.PrivacyRules, len(keys))
}
rulesByOwner[r.OwnerUserID][r.Key] = cloneRules(r)
}
}
// 批量取「viewer 是否在 owner 的联系人里」(owner→viewer 方向,对应 CanSee 的
// contacts.Get(owner, viewer))。
var reverse map[int64]domain.Contact
if s != nil && s.contacts != nil {
var err error
reverse, err = s.contacts.GetReverseContacts(ctx, viewerUserID, owners)
if err != nil {
return nil, err
}
}
for _, owner := range owners {
_, isContact := reverse[owner]
m := make(map[domain.PrivacyKey]bool, len(keys))
for _, k := range keys {
rules, ok := rulesByOwner[owner][k]
if !ok {
rules = defaultRules(owner, k)
}
m[k] = Evaluate(rules, domain.PrivacyContext{
OwnerUserID: owner,
ViewerUserID: viewerUserID,
ViewerIsContact: isContact,
})
}
out[owner] = m
}
return out, nil
}
// CanSeeMatrix 批量评估 owners × viewers × keys 的可见性矩阵,结果等价于逐 (owner,viewer,key)
// 调 CanSee,但只用一次 ListPrivacyRules + 每 owner 一次 GetMany(owner,viewers) + 内存 Evaluate
// (把 fan-out 投影从 O(viewer) 次 privacy 查询降到 O(owner))。返回 map[owner]map[viewer]map[key]bool。
func (s *Service) CanSeeMatrix(ctx context.Context, ownerUserIDs, viewerUserIDs []int64, keys []domain.PrivacyKey) (map[int64]map[int64]map[domain.PrivacyKey]bool, error) {
out := make(map[int64]map[int64]map[domain.PrivacyKey]bool, len(ownerUserIDs))
if len(ownerUserIDs) == 0 || len(viewerUserIDs) == 0 || len(keys) == 0 {
return out, nil
}
for _, k := range keys {
if !ValidKey(k) {
return nil, domain.ErrPrivacyKeyInvalid
}
}
owners := dedupNonZero(ownerUserIDs)
viewers := dedupNonZero(viewerUserIDs)
if len(owners) == 0 || len(viewers) == 0 {
return out, nil
}
rulesByOwner := make(map[int64]map[domain.PrivacyKey]domain.PrivacyRules, len(owners))
if s != nil && s.rules != nil {
list, err := s.rules.ListPrivacyRules(ctx, owners, keys)
if err != nil {
return nil, err
}
for _, r := range list {
if !ValidKey(r.Key) {
continue
}
if len(r.Rules) == 0 {
r.Rules = domain.DefaultPrivacyRules(r.Key)
}
if rulesByOwner[r.OwnerUserID] == nil {
rulesByOwner[r.OwnerUserID] = make(map[domain.PrivacyKey]domain.PrivacyRules, len(keys))
}
rulesByOwner[r.OwnerUserID][r.Key] = cloneRules(r)
}
}
for _, owner := range owners {
// owner 的联系人中哪些是本批 viewer(= privacy 的 ViewerIsContact,对应 contacts.Get(owner,viewer))。
var ownerContacts map[int64]domain.Contact
if s != nil && s.contacts != nil {
var err error
ownerContacts, err = s.contacts.GetMany(ctx, owner, viewers)
if err != nil {
return nil, err
}
}
perViewer := make(map[int64]map[domain.PrivacyKey]bool, len(viewers))
for _, viewer := range viewers {
m := make(map[domain.PrivacyKey]bool, len(keys))
if owner == viewer {
for _, k := range keys {
m[k] = true
}
perViewer[viewer] = m
continue
}
_, isContact := ownerContacts[viewer]
for _, k := range keys {
rules, ok := rulesByOwner[owner][k]
if !ok {
rules = defaultRules(owner, k)
}
m[k] = Evaluate(rules, domain.PrivacyContext{
OwnerUserID: owner,
ViewerUserID: viewer,
ViewerIsContact: isContact,
})
}
perViewer[viewer] = m
}
out[owner] = perViewer
}
return out, nil
}
func dedupNonZero(ids []int64) []int64 {
seen := make(map[int64]struct{}, len(ids))
out := make([]int64, 0, len(ids))
for _, id := range ids {
if id == 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
out = append(out, id)
}
return out
}
func Evaluate(rules domain.PrivacyRules, ctx domain.PrivacyContext) bool {
if ctx.OwnerUserID != 0 && ctx.OwnerUserID == ctx.ViewerUserID {
return true