Merge pull request 'feat: 添加 PostgreSQL 服务并挂载数据库迁移脚本' #101
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/hhs/camtalk/internal/logger"
|
"github.com/hhs/camtalk/internal/logger"
|
||||||
"github.com/hhs/camtalk/internal/models"
|
"github.com/hhs/camtalk/internal/models"
|
||||||
|
"github.com/hhs/camtalk/internal/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -33,11 +34,23 @@ type MemoryManager struct {
|
|||||||
ttl time.Duration
|
ttl time.Duration
|
||||||
maxHistory int
|
maxHistory int
|
||||||
stopCleaner chan struct{}
|
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。
|
// NewMemoryManager 创建内存版 SessionManager。
|
||||||
// ttl 为会话过期时间,maxHistory 为对话历史上限(0 表示使用默认值 20)。
|
// 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 {
|
if ttl <= 0 {
|
||||||
ttl = defaultTTL
|
ttl = defaultTTL
|
||||||
}
|
}
|
||||||
@@ -52,6 +65,10 @@ func NewMemoryManager(ttl time.Duration, maxHistory int) *MemoryManager {
|
|||||||
stopCleaner: make(chan struct{}),
|
stopCleaner: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(m)
|
||||||
|
}
|
||||||
|
|
||||||
// 启动后台清理 goroutine,每分钟清除过期会话。
|
// 启动后台清理 goroutine,每分钟清除过期会话。
|
||||||
go m.cleanLoop()
|
go m.cleanLoop()
|
||||||
|
|
||||||
@@ -242,12 +259,13 @@ func (m *MemoryManager) GetHistory(_ context.Context, sessionID string, limit in
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AppendMessage 追加一条对话消息,同时刷新 TTL。
|
// AppendMessage 追加一条对话消息,同时刷新 TTL。
|
||||||
|
// 若配置了 MessageRepository,消息会异步写入 PostgreSQL(Write-Through)。
|
||||||
func (m *MemoryManager) AppendMessage(_ context.Context, sessionID string, msg models.Message) error {
|
func (m *MemoryManager) AppendMessage(_ context.Context, sessionID string, msg models.Message) error {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
|
||||||
|
|
||||||
entry, ok := m.sessions[sessionID]
|
entry, ok := m.sessions[sessionID]
|
||||||
if !ok || m.isExpired(entry) {
|
if !ok || m.isExpired(entry) {
|
||||||
|
m.mu.Unlock()
|
||||||
return ErrSessionNotFound
|
return ErrSessionNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +284,17 @@ func (m *MemoryManager) AppendMessage(_ context.Context, sessionID string, msg m
|
|||||||
now := time.Now()
|
now := time.Now()
|
||||||
entry.lastActive = now
|
entry.lastActive = now
|
||||||
entry.session.UpdatedAt = 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user