Files
ttstd-infinite-canvas/backend/internal/repository/agent_lesson.go
ddCat d9abe41ceb feat(agent): Agent 工作台 - 计划插话记忆、统一模型选择器并修复摘要与视频入库
将计划清单、运行中插话和个人记忆接入云端 Agent;全站模型选择器统一为左右二级菜单。画布摘要按预算截断不再整轮拒绝,首页视频未知宽高可入库。README 补充贡献者 morrow。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 20:21:25 +08:00

262 lines
8.4 KiB
Go

package repository
import (
"strings"
"time"
"infinite-canvas/backend/internal/model"
"gorm.io/gorm"
)
type AgentLessonCategoryCount struct {
Category string
Count int64
}
func (r *Repository) ApprovedAgentLessonCategoryCounts(userID string) ([]AgentLessonCategoryCount, error) {
var rows []AgentLessonCategoryCount
err := r.db.Model(&model.AgentLesson{}).
Select("category, count(1) as count").
Where("author_user_id = ? AND status = ?", strings.TrimSpace(userID), model.AgentLessonStatusApproved).
Group("category").
Find(&rows).Error
return rows, err
}
type AgentLessonCandidate struct {
ID string
Topic string
Category string
StepsJSON string
}
func (r *Repository) ApprovedAgentLessonCandidates(userID string, limit int) ([]AgentLessonCandidate, error) {
if limit <= 0 || limit > 2000 {
limit = 2000
}
var rows []AgentLessonCandidate
err := r.db.Model(&model.AgentLesson{}).
Select("id, topic, category, steps_json").
Where("author_user_id = ? AND status = ?", strings.TrimSpace(userID), model.AgentLessonStatusApproved).
Order("hits DESC, updated_at DESC").
Limit(limit).
Find(&rows).Error
return rows, err
}
func (r *Repository) AgentLessonsByIDs(userID string, ids []string) ([]model.AgentLesson, error) {
if len(ids) == 0 {
return nil, nil
}
query := r.db.Where("id IN ? AND status = ?", ids, model.AgentLessonStatusApproved)
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
var lessons []model.AgentLesson
err := query.Find(&lessons).Error
return lessons, err
}
func (r *Repository) AgentLessonsByCategory(userID, category string, limit int) ([]model.AgentLesson, error) {
if limit <= 0 || limit > 500 {
limit = 500
}
var lessons []model.AgentLesson
err := r.db.Where("author_user_id = ? AND status = ? AND category = ?", strings.TrimSpace(userID), model.AgentLessonStatusApproved, category).
Order("hits DESC, updated_at DESC").Limit(limit).Find(&lessons).Error
return lessons, err
}
func (r *Repository) AgentLessonByTopic(userID, topic string) (*model.AgentLesson, error) {
var lesson model.AgentLesson
err := r.db.Where("author_user_id = ? AND status = ? AND topic = ?", strings.TrimSpace(userID), model.AgentLessonStatusApproved, topic).
Order("updated_at DESC").First(&lesson).Error
if err != nil {
return nil, err
}
return &lesson, nil
}
func (r *Repository) UserAgentLessons(userID, status string, limit int) ([]model.AgentLesson, error) {
query := r.db.Model(&model.AgentLesson{}).Where("author_user_id = ?", strings.TrimSpace(userID))
if trimmed := strings.TrimSpace(status); trimmed != "" {
query = query.Where("status = ?", trimmed)
}
if limit <= 0 || limit > 500 {
limit = 100
}
var lessons []model.AgentLesson
err := query.Order("updated_at DESC").Limit(limit).Find(&lessons).Error
return lessons, err
}
func (r *Repository) AgentLessonForUser(userID, id string) (*model.AgentLesson, error) {
var lesson model.AgentLesson
query := r.db.Where("id = ?", strings.TrimSpace(id))
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
if err := query.First(&lesson).Error; err != nil {
return nil, err
}
return &lesson, nil
}
const cloudAgentLessonScanMax = 500
func (r *Repository) ApprovedAgentLessons(userID string, limit int) ([]model.AgentLesson, error) {
if limit <= 0 || limit > cloudAgentLessonScanMax {
limit = cloudAgentLessonScanMax
}
var lessons []model.AgentLesson
err := r.db.Where("author_user_id = ? AND status = ?", strings.TrimSpace(userID), model.AgentLessonStatusApproved).
Order("hits DESC, updated_at DESC").Limit(limit).Find(&lessons).Error
return lessons, err
}
func (r *Repository) CountAgentLessonsByAuthor(userID, status string) (int64, error) {
query := r.db.Model(&model.AgentLesson{}).Where("author_user_id = ?", strings.TrimSpace(userID))
if trimmed := strings.TrimSpace(status); trimmed != "" {
query = query.Where("status = ?", trimmed)
}
var count int64
err := query.Count(&count).Error
return count, err
}
func (r *Repository) AdminAgentLessons(status, userID, keyword string, limit int) ([]model.AgentLesson, error) {
query := r.db.Model(&model.AgentLesson{})
if trimmed := strings.TrimSpace(status); trimmed != "" {
query = query.Where("status = ?", trimmed)
}
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
if trimmed := strings.TrimSpace(keyword); trimmed != "" {
like := "%" + trimmed + "%"
query = query.Where(
"topic LIKE ? OR situation LIKE ? OR lesson LIKE ? OR author_user_id LIKE ? OR author_user_id IN (SELECT id FROM users WHERE username LIKE ? OR display_name LIKE ?)",
like, like, like, like, like, like,
)
}
if limit <= 0 || limit > 200 {
limit = 100
}
var lessons []model.AgentLesson
err := query.Order("updated_at DESC").Limit(limit).Find(&lessons).Error
return lessons, err
}
func (r *Repository) SetAgentLessonStatus(userID, id, status string) error {
query := r.db.Model(&model.AgentLesson{}).Where("id = ?", strings.TrimSpace(id))
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
result := query.Update("status", status)
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return gorm.ErrRecordNotFound
}
return nil
}
func (r *Repository) DeleteAgentLesson(userID, id string) error {
query := r.db.Where("id = ?", strings.TrimSpace(id))
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
result := query.Delete(&model.AgentLesson{})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return gorm.ErrRecordNotFound
}
return nil
}
func (r *Repository) SaveAgentLesson(lesson *model.AgentLesson) error {
return r.db.Save(lesson).Error
}
func (r *Repository) BumpAgentLessonHits(userID string, ids []string) error {
if len(ids) == 0 {
return nil
}
query := r.db.Model(&model.AgentLesson{}).Where("id IN ?", ids)
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
return query.UpdateColumn("hits", gorm.Expr("hits + 1")).Error
}
func (r *Repository) TouchAgentLesson(userID, id string, verifiedAt time.Time) error {
query := r.db.Model(&model.AgentLesson{}).Where("id = ?", strings.TrimSpace(id))
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
return query.Updates(map[string]any{"last_verified_at": verifiedAt, "updated_at": verifiedAt}).Error
}
func (r *Repository) BumpAgentLessonInjected(userID string, ids []string) error {
if len(ids) == 0 {
return nil
}
query := r.db.Model(&model.AgentLesson{}).Where("id IN ?", ids)
if trimmed := strings.TrimSpace(userID); trimmed != "" {
query = query.Where("author_user_id = ?", trimmed)
}
return query.UpdateColumn("injected", gorm.Expr("injected + 1")).Error
}
func (r *Repository) ApplyUserAgentMemoryCompact(userID string, updates []model.AgentLesson, deleteIDs []string) error {
userID = strings.TrimSpace(userID)
if userID == "" {
return gorm.ErrRecordNotFound
}
return r.db.Transaction(func(tx *gorm.DB) error {
for index := range updates {
lesson := updates[index]
if strings.TrimSpace(lesson.AuthorUserID) != userID {
return gorm.ErrRecordNotFound
}
if err := tx.Save(&lesson).Error; err != nil {
return err
}
}
if len(deleteIDs) == 0 {
return nil
}
return tx.Where("author_user_id = ? AND id IN ?", userID, deleteIDs).Delete(&model.AgentLesson{}).Error
})
}
func (r *Repository) AgentMemorySetting(userID string) (*model.AgentMemorySetting, error) {
var setting model.AgentMemorySetting
err := r.db.Where("user_id = ?", strings.TrimSpace(userID)).First(&setting).Error
if err != nil {
return nil, err
}
return &setting, nil
}
func (r *Repository) SaveAgentMemorySetting(setting *model.AgentMemorySetting) error {
return r.db.Save(setting).Error
}
func (r *Repository) ScheduledAgentMemorySettings(limit int) ([]model.AgentMemorySetting, error) {
if limit <= 0 || limit > 200 {
limit = 50
}
var rows []model.AgentMemorySetting
err := r.db.Where("compact_interval IN ?", []string{
model.AgentMemoryCompactIntervalDaily,
model.AgentMemoryCompactIntervalWeekly,
model.AgentMemoryCompactIntervalMonthly,
}).Order("last_compact_at ASC").Limit(limit).Find(&rows).Error
return rows, err
}