feat: add Z12/Z13 bind APIs, stock import, and sync docs

Enable auto default sync channels on agent activate, bind-code/phone confirm flows, publish ALTER, and align admin/yuheng docs with the production bind path.
This commit is contained in:
whm
2026-08-05 11:47:20 +08:00
parent b04b180d30
commit cb56e6847e
31 changed files with 1882 additions and 91 deletions

View File

@@ -0,0 +1,91 @@
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启用/创建时若无通道则自动创建公司默认同步通道并写回智能体。
func (l *AgentAdminLogic) ensureAgentSyncBind(acc *agentstore.Account) (*agentstore.Account, error) {
if acc == nil {
return nil, fmt.Errorf("agent nil")
}
if strings.TrimSpace(acc.ChannelID) != "" && strings.TrimSpace(acc.OnlineDBID) != "" {
return acc, nil
}
if l.svcCtx.DBSync == nil {
return acc, nil
}
cfg := l.svcCtx.Config.DBSync
driver := dbsync.Driver(strings.TrimSpace(cfg.DefaultRemoteDriver))
if driver == "" {
driver = dbsync.DriverPostgres
}
ch, err := l.svcCtx.DBSync.Store().EnsureSystemDefaultChannel(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 := dbsync.ResolveOnlineDBID(acc.OnlineDBID, ch.ID)
dbName := strings.TrimSpace(acc.DatabaseName)
if dbName == "" {
dbName = fmt.Sprintf("agent_%d", acc.AgentID)
}
st, err := l.store()
if err != nil {
return nil, err
}
updated, err := st.AttachSyncBind(l.ctx, acc.AgentID, acc.TenantID, ch.ID, online, dbName, acc.Status == agentstore.StatusActive || acc.Status == "")
if err != nil {
return nil, err
}
return updated, nil
}