package dbsync import ( "fmt" "path/filepath" "strings" ) // DefaultChannelOpts 创建公司默认同步通道(Z12c)。 type DefaultChannelOpts struct { TenantID int64 AgentID int64 Name string RemoteDriver Driver RemoteDSN string OnlineDBID string // 空则用 channel id DatabaseName string } // FindSystemDefaultChannel 返回该公司 IsSystemDefault 通道(若有多条取最新启用的)。 func (s *FileStore) FindSystemDefaultChannel(tenantID int64) (*Channel, error) { s.mu.Lock() defer s.mu.Unlock() list, err := s.readChannels() if err != nil { return nil, err } var best *Channel for i := range list { ch := list[i] if ch.TenantID != tenantID || !ch.IsSystemDefault { continue } cp := ch if best == nil || cp.UpdatedAt.After(best.UpdatedAt) { best = &cp } } return best, nil } // EnsureSystemDefaultChannel 若无默认同步通道则创建;返回通道(已存在则复用)。 func (s *FileStore) EnsureSystemDefaultChannel(opts DefaultChannelOpts) (Channel, error) { if opts.TenantID <= 0 { return Channel{}, fmt.Errorf("tenant_id required") } if existing, err := s.FindSystemDefaultChannel(opts.TenantID); err == nil && existing != nil { ch := *existing if opts.AgentID > 0 && ch.AgentID == 0 { ch.AgentID = opts.AgentID saved, err := s.SaveChannel(ch) if err != nil { return Channel{}, err } return saved, nil } return ch, nil } driver := opts.RemoteDriver if driver == "" { driver = DriverPostgres } dsn := strings.TrimSpace(opts.RemoteDSN) if dsn == "" { driver = DriverSQLite dsn = filepath.ToSlash(filepath.Join(".", "data", "dbsync", fmt.Sprintf("tenant_%d_online.db", opts.TenantID))) dsn = "file:" + dsn + "?_pragma=busy_timeout(5000)" } name := strings.TrimSpace(opts.Name) if name == "" { name = fmt.Sprintf("公司默认同步 #%d", opts.TenantID) } ch := Channel{ TenantID: opts.TenantID, Name: name, Enabled: false, // 默认同步通道供 agent push 落点;不启本地 outbox 轮询 Direction: DirLocalToRemote, ConflictPolicy: PolicyLWWSource, IsSystemDefault: true, AgentID: opts.AgentID, Local: Endpoint{ Driver: DriverSQLite, DSN: "file:./data/dbsync/local_placeholder.db?_pragma=busy_timeout(5000)", Tables: nil, }, Remote: Endpoint{ Driver: driver, DSN: dsn, Tables: nil, }, PKColumns: map[string]string{}, } if err := ValidateChannelConfig(&ch); err != nil { return Channel{}, err } saved, err := s.SaveChannel(ch) if err != nil { return Channel{}, err } return saved, nil } // ResolveOnlineDBID 默认 online_db_id:优先显式值,否则通道 id。 func ResolveOnlineDBID(explicit, channelID string) string { if s := strings.TrimSpace(explicit); s != "" { return s } return strings.TrimSpace(channelID) }