fix: Z35 map Chinese display names to stable physical DB names

Keep Chinese on Binding for UI; Agents use user_{id} or db{hash}; sanitize on attach/heal/publish.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-08-07 02:12:55 +08:00
parent 7290f91d3a
commit 4b6c90904b
8 changed files with 146 additions and 32 deletions

View File

@@ -6,6 +6,7 @@ import (
"aijianzhan/platform/internal/agentstore"
"aijianzhan/platform/internal/dbsync"
"aijianzhan/platform/internal/schema"
"aijianzhan/platform/internal/svc"
"aijianzhan/platform/internal/types"
"aijianzhan/platform/internal/userstore"
@@ -19,6 +20,9 @@ func fillAgentSyncOnToken(resp *types.TokenResp, acc *agentstore.Account) {
resp.ChannelID = strings.TrimSpace(acc.ChannelID)
resp.OnlineDBID = strings.TrimSpace(acc.OnlineDBID)
resp.DatabaseName = strings.TrimSpace(acc.DatabaseName)
if resp.DatabaseName != "" && !schema.ValidDBName(resp.DatabaseName) {
resp.DatabaseName = ""
}
resp.SyncBound = resp.ChannelID != "" && resp.OnlineDBID != ""
}
@@ -40,9 +44,10 @@ func fillUserSyncOnToken(svcCtx *svc.ServiceContext, resp *types.TokenResp, u *u
}
resp.ChannelID = strings.TrimSpace(b.ChannelID)
resp.OnlineDBID = strings.TrimSpace(b.OnlineDBID)
// Z35database_name 仅物理库名;勿把中文 DisplayName 填进该字段
resp.DatabaseName = strings.TrimSpace(b.DatabaseName)
if resp.DatabaseName == "" {
resp.DatabaseName = strings.TrimSpace(b.DisplayName)
if resp.DatabaseName != "" && !schema.ValidDBName(resp.DatabaseName) {
resp.DatabaseName = ""
}
resp.SyncBound = resp.ChannelID != "" && resp.OnlineDBID != ""
}
@@ -92,10 +97,7 @@ func (l *AgentAdminLogic) ensureAgentSyncBind(acc *agentstore.Account) (*agentst
if online == "" {
online = dbsync.ResolveOnlineDBID("", ch.ID)
}
dbName := strings.TrimSpace(updated.DatabaseName)
if dbName == "" {
dbName = fmt.Sprintf("agent_%d", updated.AgentID)
}
dbName := schema.SafeSyncDatabaseName(updated.DatabaseName, updated.AgentID, 0)
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

View File

@@ -11,6 +11,7 @@ import (
"aijianzhan/platform/internal/authx"
"aijianzhan/platform/internal/bindcodestore"
"aijianzhan/platform/internal/dbsync"
"aijianzhan/platform/internal/schema"
"aijianzhan/platform/internal/smsstore"
"aijianzhan/platform/internal/userstore"
)
@@ -58,7 +59,18 @@ func (l *AuthLogic) CreateBindCode(req BindCodeCreateReq) (*bindcodestore.BindCo
}
channelID := strings.TrimSpace(req.ChannelID)
online := strings.TrimSpace(req.OnlineDBID)
dbName := strings.TrimSpace(req.DatabaseName)
rawDB := strings.TrimSpace(req.DatabaseName)
dbName := ""
displayNote := ""
if rawDB != "" {
if schema.ValidDBName(rawDB) {
dbName = rawDB
} else {
// 接受中文等展示名:落码存物理哈希;原文记入 note 便于对照
dbName = schema.HashLabelDBName(rawDB, 0)
displayNote = rawDB
}
}
if channelID == "" && l.svcCtx.DBSync != nil {
// Z12h生成绑定码前确保默认通道存在含误删自愈
if healed, err := HealTenantSyncBind(l.ctx, l.svcCtx, tid); err != nil {
@@ -76,6 +88,13 @@ func (l *AuthLogic) CreateBindCode(req BindCodeCreateReq) (*bindcodestore.BindCo
if online == "" {
online = dbsync.ResolveOnlineDBID("", channelID)
}
note := strings.TrimSpace(req.Note)
if displayNote != "" {
if note != "" {
note = note + " | "
}
note += "display:" + displayNote
}
exp := time.Duration(req.ExpiresHours) * time.Hour
return l.svcCtx.BindCodes.Create(l.ctx, tid, authx.UserID(l.ctx), bindcodestore.CreateInput{
ChannelID: channelID,
@@ -83,7 +102,7 @@ func (l *AuthLogic) CreateBindCode(req BindCodeCreateReq) (*bindcodestore.BindCo
DatabaseName: dbName,
MaxUses: req.MaxUses,
ExpiresIn: exp,
Note: req.Note,
Note: note,
})
}
@@ -151,10 +170,7 @@ func (l *AuthLogic) RedeemBindCode(req BindCodeRedeemReq) (*BindCodeRedeemResp,
if online == "" {
online = dbsync.ResolveOnlineDBID("", bc.ChannelID)
}
dbName := strings.TrimSpace(bc.DatabaseName)
if dbName == "" {
dbName = fmt.Sprintf("agent_%d", acc.AgentID)
}
dbName := schema.SafeSyncDatabaseName(bc.DatabaseName, acc.AgentID, 0)
updated, err := l.svcCtx.Agents.AttachSyncBind(l.ctx, acc.AgentID, bc.TenantID, bc.ChannelID, online, dbName, true)
if err != nil {
return nil, err

View File

@@ -9,6 +9,7 @@ import (
"aijianzhan/platform/internal/agentstore"
"aijianzhan/platform/internal/dbsync"
"aijianzhan/platform/internal/schema"
"aijianzhan/platform/internal/svc"
)
@@ -40,6 +41,10 @@ func channelAlive(store *dbsync.FileStore, tenantID int64, channelID string) boo
}
func agentNeedsSyncHeal(store *dbsync.FileStore, a agentstore.Account) bool {
// Z35非法物理库名如中文昵称也要纠正否则发布 EnsureDatabase 400
if dn := strings.TrimSpace(a.DatabaseName); dn != "" && !schema.ValidDBName(dn) {
return true
}
cid := strings.TrimSpace(a.ChannelID)
alive := channelAlive(store, a.TenantID, cid)
if cid != "" {
@@ -111,11 +116,12 @@ func HealTenantSyncBind(ctx context.Context, svcCtx *svc.ServiceContext, tenantI
if online == "" {
online = dbsync.ResolveOnlineDBID("", ch.ID)
}
dbName := strings.TrimSpace(a.DatabaseName)
if dbName == "" {
dbName = fmt.Sprintf("agent_%d", a.AgentID)
cid := strings.TrimSpace(a.ChannelID)
if !channelAlive(svcCtx.DBSync.Store(), tenantID, cid) {
cid = ch.ID
}
if _, e := svcCtx.Agents.AttachSyncBind(ctx, a.AgentID, tenantID, ch.ID, online, dbName, a.Status == agentstore.StatusActive || a.Status == ""); e != nil {
dbName := schema.SafeSyncDatabaseName(a.DatabaseName, a.AgentID, 0)
if _, e := svcCtx.Agents.AttachSyncBind(ctx, a.AgentID, tenantID, cid, online, dbName, a.Status == agentstore.StatusActive || a.Status == ""); e != nil {
log.Printf("dbsync heal agent tenant=%d agent=%d: %v", tenantID, a.AgentID, e)
continue
}

View File

@@ -8,6 +8,7 @@ import (
"aijianzhan/platform/internal/agentstore"
"aijianzhan/platform/internal/authx"
"aijianzhan/platform/internal/dbsync"
"aijianzhan/platform/internal/schema"
"aijianzhan/platform/internal/types"
"aijianzhan/platform/internal/userstore"
"aijianzhan/platform/internal/yuhticket"
@@ -191,12 +192,12 @@ func (l *AuthLogic) bindUserHostSync(u *userstore.User, hostKey, name, localID,
return nil, "", fmt.Errorf("host_key 已绑定其它公司")
}
online := fmt.Sprintf("%s_u%d", dbsync.ResolveOnlineDBID("", ch.ID), u.UserID)
// DisplayName 可中文Postgres 物理库名必须 [a-z][a-z0-9_]{1,47}Z35
// 中文昵称可接受:写入 Binding 展示字段;物理库名映射为 user_{id} / 哈希Z35
display := strings.TrimSpace(u.DisplayName)
if display == "" {
display = fmt.Sprintf("user_%d", u.UserID)
}
dbName := fmt.Sprintf("user_%d", u.UserID)
dbName := schema.SafeSyncDatabaseName(display, acc.AgentID, u.UserID)
updated, err := l.svcCtx.Agents.AttachSyncBind(l.ctx, acc.AgentID, u.TenantID, ch.ID, online, dbName, true)
if err != nil {
return nil, "", err
@@ -215,7 +216,7 @@ func (l *AuthLogic) bindUserHostSync(u *userstore.User, hostKey, name, localID,
LocalDatabaseID: localID,
OnlineDBID: online,
ChannelID: ch.ID,
DatabaseName: dbName,
DatabaseName: display, // 可读名(可中文)
DisplayName: display,
Note: note,
})