99ce07c1b0
把 JARVIS 做成每用户独立:名字(你叫JARVIS别人叫星期五)、人设(与主偏好记忆分开)、 可自带豆包配置(齐全则用用户的,否则回落系统)。 - store/user_jarvis.go: sundynix_user_jarvis(user_id唯一;name/persona/火山creds,APIKey密文) + Get/Save(upsert) + AutoMigrate 注册 - voice_config.go resolveJarvis(uid): 火山配置用户优先系统兜底 + 取名字/人设 - voice_task.go: voiceSystemPrompt(name,persona)——简短是硬基线,名字/语气由用户定; buildVoiceGraph 带 name+persona;voice.go 会话升级时按用户解析 - dispatcher compose_compiler: 语音任务(useVoice)不拉主偏好记忆,改用用户 persona(保留短期历史) - jarvis.go + 路由: GET/PUT /api/v1/me/jarvis(用户级,api_key 脱敏/留空沿用) 真机验证:设 name=星期五+干净人设→语音答"我是星期五…"(自称新名、无糙话、4.4s), has_own_voice=false 用系统豆包。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
50 lines
2.2 KiB
Go
50 lines
2.2 KiB
Go
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
|
||
}
|