feat: 构建用户模块,实现用户对话历史持久化,完善接口文档 #96
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/hhs/camtalk/internal/logger"
|
||||
"github.com/hhs/camtalk/internal/models"
|
||||
"github.com/hhs/camtalk/internal/store"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,11 +34,23 @@ type MemoryManager struct {
|
||||
ttl time.Duration
|
||||
maxHistory int
|
||||
stopCleaner chan struct{}
|
||||
msgRepo store.MessageRepository // 可选,消息持久化(Write-Through)
|
||||
}
|
||||
|
||||
// Option MemoryManager 的函数式选项。
|
||||
type Option func(*MemoryManager)
|
||||
|
||||
// WithMessageRepository 注入消息持久化仓库,启用 Write-Through 模式。
|
||||
func WithMessageRepository(repo store.MessageRepository) Option {
|
||||
return func(m *MemoryManager) {
|
||||
m.msgRepo = repo
|
||||
}
|
||||
}
|
||||
|
||||
// NewMemoryManager 创建内存版 SessionManager。
|
||||
// ttl 为会话过期时间,maxHistory 为对话历史上限(0 表示使用默认值 20)。
|
||||
func NewMemoryManager(ttl time.Duration, maxHistory int) *MemoryManager {
|
||||
// opts 为可选配置,如 WithMessageRepository 启用消息持久化。
|
||||
func NewMemoryManager(ttl time.Duration, maxHistory int, opts ...Option) *MemoryManager {
|
||||
if ttl <= 0 {
|
||||
ttl = defaultTTL
|
||||
}
|
||||
@@ -52,6 +65,10 @@ func NewMemoryManager(ttl time.Duration, maxHistory int) *MemoryManager {
|
||||
stopCleaner: make(chan struct{}),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(m)
|
||||
}
|
||||
|
||||
// 启动后台清理 goroutine,每分钟清除过期会话。
|
||||
go m.cleanLoop()
|
||||
|
||||
@@ -242,12 +259,13 @@ func (m *MemoryManager) GetHistory(_ context.Context, sessionID string, limit in
|
||||
}
|
||||
|
||||
// AppendMessage 追加一条对话消息,同时刷新 TTL。
|
||||
// 若配置了 MessageRepository,消息会异步写入 PostgreSQL(Write-Through)。
|
||||
func (m *MemoryManager) AppendMessage(_ context.Context, sessionID string, msg models.Message) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
entry, ok := m.sessions[sessionID]
|
||||
if !ok || m.isExpired(entry) {
|
||||
m.mu.Unlock()
|
||||
return ErrSessionNotFound
|
||||
}
|
||||
|
||||
@@ -266,6 +284,17 @@ func (m *MemoryManager) AppendMessage(_ context.Context, sessionID string, msg m
|
||||
now := time.Now()
|
||||
entry.lastActive = now
|
||||
entry.session.UpdatedAt = now
|
||||
m.mu.Unlock()
|
||||
|
||||
// Write-Through:异步写冷存储,不阻塞调用方
|
||||
if m.msgRepo != nil {
|
||||
go func() {
|
||||
if err := m.msgRepo.SaveMessage(context.Background(), sessionID, msg, 0); err != nil {
|
||||
logger.Log.Warnw("persist message failed", "session", sessionID, "error", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user