Files
ai_site/platform/internal/logic/applogic/agent_sync_bind.go
whm 362dcee242 feat: heal dead sync channels and block deleting system default (Z12h)
Ensure default channel and rebind Agent/Binding on list, ticket, agents/me, and ensure-binding so SyncPage does not stay on deleted-channel errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 08:57:30 +08:00

106 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package applogic
import (
"fmt"
"strings"
"aijianzhan/platform/internal/agentstore"
"aijianzhan/platform/internal/dbsync"
"aijianzhan/platform/internal/svc"
"aijianzhan/platform/internal/types"
"aijianzhan/platform/internal/userstore"
)
// fillAgentSyncOnToken Z12a换票带回同步落点。
func fillAgentSyncOnToken(resp *types.TokenResp, acc *agentstore.Account) {
if resp == nil || acc == nil {
return
}
resp.ChannelID = strings.TrimSpace(acc.ChannelID)
resp.OnlineDBID = strings.TrimSpace(acc.OnlineDBID)
resp.DatabaseName = strings.TrimSpace(acc.DatabaseName)
resp.SyncBound = resp.ChannelID != "" && resp.OnlineDBID != ""
}
// fillUserSyncOnToken人类登录优先从本人 Binding 带回落点。
func fillUserSyncOnToken(svcCtx *svc.ServiceContext, resp *types.TokenResp, u *userstore.User) {
if resp == nil || u == nil || svcCtx == nil || svcCtx.DBSync == nil || u.TenantID <= 0 {
return
}
list, err := svcCtx.DBSync.Store().ListBindingsFiltered(u.TenantID, u.UserID, "")
if err != nil || len(list) == 0 {
return
}
b := list[0]
for i := range list {
if list[i].ChannelID != "" && list[i].OnlineDBID != "" {
b = list[i]
break
}
}
resp.ChannelID = strings.TrimSpace(b.ChannelID)
resp.OnlineDBID = strings.TrimSpace(b.OnlineDBID)
resp.DatabaseName = strings.TrimSpace(b.DatabaseName)
if resp.DatabaseName == "" {
resp.DatabaseName = strings.TrimSpace(b.DisplayName)
}
resp.SyncBound = resp.ChannelID != "" && resp.OnlineDBID != ""
}
// ensureAgentSyncBind Z12c/Z12h无通道或通道已删除时创建/复用默认同步通道并写回智能体。
func (l *AgentAdminLogic) ensureAgentSyncBind(acc *agentstore.Account) (*agentstore.Account, error) {
if acc == nil {
return nil, fmt.Errorf("agent nil")
}
if l.svcCtx.DBSync == nil {
return acc, nil
}
alive := channelAlive(l.svcCtx.DBSync.Store(), acc.TenantID, acc.ChannelID)
if alive && strings.TrimSpace(acc.OnlineDBID) != "" {
return acc, nil
}
if _, err := HealTenantSyncBind(l.ctx, l.svcCtx, acc.TenantID); err != nil {
return nil, fmt.Errorf("heal sync bind: %w", err)
}
st, err := l.store()
if err != nil {
return nil, err
}
updated, err := st.Get(l.ctx, acc.TenantID, acc.AgentID)
if err != nil {
return nil, err
}
// 若 heal 未覆盖该智能体(例如无 Binding 仅 Agent再显式挂一次
if !channelAlive(l.svcCtx.DBSync.Store(), updated.TenantID, updated.ChannelID) || strings.TrimSpace(updated.OnlineDBID) == "" {
cfg := l.svcCtx.Config.DBSync
driver := dbsync.Driver(strings.TrimSpace(cfg.DefaultRemoteDriver))
if driver == "" {
driver = dbsync.DriverPostgres
}
ch, err := l.svcCtx.DBSync.EnsureAndStartSystemDefaultChannel(dbsync.DefaultChannelOpts{
TenantID: acc.TenantID,
AgentID: acc.AgentID,
Name: fmt.Sprintf("默认同步 · %s", acc.Name),
RemoteDriver: driver,
RemoteDSN: strings.TrimSpace(cfg.DefaultRemoteDSN),
DatabaseName: acc.DatabaseName,
})
if err != nil {
return nil, fmt.Errorf("ensure default channel: %w", err)
}
online := strings.TrimSpace(updated.OnlineDBID)
if online == "" {
online = dbsync.ResolveOnlineDBID("", ch.ID)
}
dbName := strings.TrimSpace(updated.DatabaseName)
if dbName == "" {
dbName = fmt.Sprintf("agent_%d", updated.AgentID)
}
updated, err = st.AttachSyncBind(l.ctx, updated.AgentID, updated.TenantID, ch.ID, online, dbName, updated.Status == agentstore.StatusActive || updated.Status == "")
if err != nil {
return nil, err
}
}
return updated, nil
}