package store import ( "context" "gorm.io/gorm/clause" ) // UserJarvis 是**每用户**的 JARVIS 语音助手配置(客户端配、用户级)。 // 与系统级语音配置(Setting voice_config)、与主偏好记忆(user_profile) 都分开: // - Name/Persona:这个用户的助手叫什么、什么语气人设(persona 独立于主偏好记忆,互不污染)。 // - APIKey/…ResourceID/VoiceType:用户自带的豆包(火山)配置;齐全则语音走用户的,否则回落系统。 // APIKey 密文入库(AES-256-GCM,同 LLMModel/微信配置)。表名 sundynix_user_jarvis。 type UserJarvis struct { BaseModel UserID string `gorm:"size:32;uniqueIndex"` // 雪花 user.id,每用户唯一一条 Name string `gorm:"size:32"` // 助手名(空=用系统默认 "JARVIS") Persona string `gorm:"size:1024"` // 语气/人设(空=用系统默认) APIKey string `gorm:"size:255"` // 用户自带火山 API Key(密文;空=用系统) ASRResourceID string `gorm:"size:64"` TTSResourceID string `gorm:"size:64"` TTSVoiceType string `gorm:"size:64"` } func (UserJarvis) TableName() string { return "sundynix_user_jarvis" } // GetUserJarvis 取某用户的 JARVIS 配置;不存在返回 nil(调用方回落系统默认)。 func (p *Postgres) GetUserJarvis(ctx context.Context, uid string) *UserJarvis { if p.db == nil || uid == "" { return nil } var j UserJarvis if err := p.db.WithContext(ctx).Where("user_id = ?", uid).First(&j).Error; err != nil { return nil } return &j } // SaveUserJarvis 幂等写某用户的 JARVIS 配置(按 user_id 唯一,重复即覆盖)。 // APIKey 传空串表示"沿用已存"(由 handler 决定是否覆盖),此处只负责按传入值落库。 func (p *Postgres) SaveUserJarvis(ctx context.Context, j *UserJarvis) error { if p.db == nil { return errStoreDisabled } return p.db.WithContext(ctx).Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "user_id"}}, DoUpdates: clause.AssignmentColumns([]string{"name", "persona", "api_key", "asr_resource_id", "tts_resource_id", "tts_voice_type", "updated_at"}), }).Create(j).Error }