package region_sync import ( "fmt" "time" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting/system_setting" "github.com/bytedance/gopkg/util/gopool" ) // PushUserCreateToSlave 在 master 节点创建用户后,异步推送到 slave 节点 func PushUserCreateToSlave(user *model.User) { settings := system_setting.GetRegionSyncSettings() if !settings.Enabled || !settings.IsMaster { return } if len(settings.SlaveEndpoints) == 0 || settings.SyncApiKey == "" { return } gopool.Go(func() { req := &SyncUserRequest{ Username: user.Username, Email: user.Email, PasswordHash: user.Password, // 传输的是哈希值,非明文密码 DisplayName: user.DisplayName, Quota: user.Quota, RemoteUserId: user.Id, Group: user.Group, AffCode: user.AffCode, } for _, endpoint := range settings.SlaveEndpoints { client := NewSyncClient(endpoint, settings.SyncApiKey) resp, err := client.SyncUserCreate(req) if err != nil { logSyncError("user", user.Username, endpoint, "", err) continue } if !resp.Success { logSyncError("user", user.Username, endpoint, resp.Error, nil) continue } logSyncSuccess("user", user.Username, user.Id, endpoint) } recordSyncLog(user.Id, 0, model.SyncTypeUserCreate, "") }) } // PushQuotaUpdateToSlave 在 master 节点更新用户余额后,推送到 slave 节点 func PushQuotaUpdateToSlave(userId int, quota int) { settings := system_setting.GetRegionSyncSettings() if !settings.Enabled || !settings.IsMaster { return } if len(settings.SlaveEndpoints) == 0 || settings.SyncApiKey == "" { return } logger.LogDebug(nil, "[RegionSync] PushQuotaUpdateToSlave: userId=%d, quota=%d, endpoints=%v", userId, quota, settings.SlaveEndpoints) gopool.Go(func() { for _, endpoint := range settings.SlaveEndpoints { client := NewSyncClient(endpoint, settings.SyncApiKey) resp, err := client.UpdateQuota(userId, quota) if err != nil { logSyncError("quota update", fmt.Sprintf("user %d", userId), endpoint, "", err) continue } if !resp.Success { logSyncError("quota update", fmt.Sprintf("user %d", userId), endpoint, resp.Error, nil) continue } logSyncSuccess("quota update", fmt.Sprintf("user %d", userId), 0, endpoint) } }) } // logSyncError 记录同步错误日志 func logSyncError(operation, target, endpoint string, errMsg string, err error) { if err != nil { common.SysError(fmt.Sprintf("[RegionSync] Failed to push %s %s to slave %s: %v", operation, target, endpoint, err)) } else { common.SysError(fmt.Sprintf("[RegionSync] Slave %s rejected %s %s: %s", endpoint, operation, target, errMsg)) } } // logSyncSuccess 记录同步成功日志 func logSyncSuccess(operation, target string, id int, endpoint string) { if id > 0 { common.SysLog(fmt.Sprintf("[RegionSync] Successfully pushed %s %s (id=%d) to slave %s", operation, target, id, endpoint)) } else { common.SysLog(fmt.Sprintf("[RegionSync] Successfully pushed %s %s to slave %s", operation, target, endpoint)) } } // recordSyncLog 创建同步日志记录 func recordSyncLog(userId, remoteUserId int, syncType string, requestId string) { syncLog := &model.QuotaSyncLog{ UserId: userId, RemoteUserId: remoteUserId, SyncType: syncType, Direction: model.SyncDirectionCnToOv, Status: model.SyncStatusSuccess, RequestId: requestId, CreatedAt: time.Now().Unix(), } if err := model.CreateSyncLog(syncLog); err != nil { common.SysError(fmt.Sprintf("[RegionSync] Failed to create sync log: %v", err)) } }