feat: add Yuheng ticket bind, trial SMS off, shared bindings
Ship ticket-exchange and bind/policy for Z13, keep trial binds SMS-free, allow shared company bindings, and align SyncPage plus sync docs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -19,6 +19,8 @@ type Binding struct {
|
||||
LocalDatabaseID string `json:"local_database_id"`
|
||||
OnlineDBID string `json:"online_db_id"`
|
||||
ChannelID string `json:"channel_id,omitempty"`
|
||||
// Shared:公司共享库(Z12c-2);同租户任意成员可 push/pull 该 online_db_id
|
||||
Shared bool `json:"shared,omitempty"`
|
||||
// 可读名(与宇恒 database_name / display_name 对齐;控制台优先展示)
|
||||
DatabaseName string `json:"database_name,omitempty"` // 本地库可读名
|
||||
DisplayName string `json:"display_name,omitempty"` // 线上库可读名
|
||||
@@ -70,6 +72,7 @@ func (s *FileStore) EnsureBinding(b Binding) (Binding, error) {
|
||||
if b.UserID > 0 {
|
||||
list[i].UserID = b.UserID
|
||||
}
|
||||
list[i].Shared = b.Shared
|
||||
list[i].UpdatedAt = now
|
||||
if err := s.writeBindingsUnlocked(list); err != nil {
|
||||
return b, err
|
||||
@@ -117,18 +120,33 @@ func (s *FileStore) ListBindingsFiltered(tenantID, userID int64, localDatabaseID
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UserOwnsOnlineDB 用户是否登记了该 online_db_id(可选限定 channel)。
|
||||
// UserOwnsOnlineDB 用户是否登记了该 online_db_id(可选限定 channel);或该库为公司 Shared。
|
||||
func (s *FileStore) UserOwnsOnlineDB(tenantID, userID int64, channelID, onlineDBID string) bool {
|
||||
if tenantID <= 0 || userID <= 0 || strings.TrimSpace(onlineDBID) == "" {
|
||||
return false
|
||||
}
|
||||
list, err := s.ListBindingsFiltered(tenantID, userID, "")
|
||||
online := strings.TrimSpace(onlineDBID)
|
||||
ch := strings.TrimSpace(channelID)
|
||||
own, err := s.ListBindingsFiltered(tenantID, userID, "")
|
||||
if err == nil {
|
||||
for _, b := range own {
|
||||
if strings.TrimSpace(b.OnlineDBID) != online {
|
||||
continue
|
||||
}
|
||||
if ch != "" && b.ChannelID != "" && b.ChannelID != ch {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
all, err := s.ListBindingsFiltered(tenantID, 0, "")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
online := strings.TrimSpace(onlineDBID)
|
||||
ch := strings.TrimSpace(channelID)
|
||||
for _, b := range list {
|
||||
for _, b := range all {
|
||||
if !b.Shared {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(b.OnlineDBID) != online {
|
||||
continue
|
||||
}
|
||||
@@ -140,17 +158,28 @@ func (s *FileStore) UserOwnsOnlineDB(tenantID, userID int64, channelID, onlineDB
|
||||
return false
|
||||
}
|
||||
|
||||
// UserCanAccessChannel 用户是否登记了指向该通道的 Binding(channel_id 空视为未限定通道)。
|
||||
// UserCanAccessChannel 用户是否登记了指向该通道的 Binding(channel_id 空视为未限定通道);含 Shared。
|
||||
func (s *FileStore) UserCanAccessChannel(tenantID, userID int64, channelID string) bool {
|
||||
if tenantID <= 0 || userID <= 0 || strings.TrimSpace(channelID) == "" {
|
||||
return false
|
||||
}
|
||||
ch := strings.TrimSpace(channelID)
|
||||
list, err := s.ListBindingsFiltered(tenantID, userID, "")
|
||||
if err != nil || len(list) == 0 {
|
||||
if err == nil {
|
||||
for _, b := range list {
|
||||
if b.ChannelID == "" || b.ChannelID == ch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
all, err := s.ListBindingsFiltered(tenantID, 0, "")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
ch := strings.TrimSpace(channelID)
|
||||
for _, b := range list {
|
||||
for _, b := range all {
|
||||
if !b.Shared {
|
||||
continue
|
||||
}
|
||||
if b.ChannelID == "" || b.ChannelID == ch {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user