Files
gochat/internal/repository/captain_document_repo.go
T

169 lines
5.5 KiB
Go

package repository
import (
"context"
"strings"
"time"
"github.com/gochat/gochat/internal/model"
"gorm.io/gorm"
)
// CaptainDocumentRepo provides data access for CaptainDocument.
type CaptainDocumentRepo struct {
db *gorm.DB
}
func NewCaptainDocumentRepo(db *gorm.DB) *CaptainDocumentRepo {
return &CaptainDocumentRepo{db: db}
}
func (r *CaptainDocumentRepo) DB() *gorm.DB {
return r.db
}
func (r *CaptainDocumentRepo) Create(ctx context.Context, doc *model.CaptainDocument) error {
return r.db.WithContext(ctx).Create(doc).Error
}
func (r *CaptainDocumentRepo) GetByID(ctx context.Context, id uint) (*model.CaptainDocument, error) {
var doc model.CaptainDocument
if err := r.db.WithContext(ctx).Preload("Assistant").First(&doc, id).Error; err != nil {
return nil, err
}
return &doc, nil
}
func (r *CaptainDocumentRepo) GetByAccountAndID(ctx context.Context, accountID, id uint) (*model.CaptainDocument, error) {
var doc model.CaptainDocument
if err := r.db.WithContext(ctx).Preload("Assistant").Where("account_id = ? AND id = ?", accountID, id).First(&doc).Error; err != nil {
return nil, err
}
return &doc, nil
}
func (r *CaptainDocumentRepo) Update(ctx context.Context, doc *model.CaptainDocument) error {
return r.db.WithContext(ctx).Save(doc).Error
}
func (r *CaptainDocumentRepo) Delete(ctx context.Context, id uint) error {
return r.db.WithContext(ctx).Delete(&model.CaptainDocument{}, id).Error
}
func (r *CaptainDocumentRepo) DeleteByAccount(ctx context.Context, accountID, id uint) error {
return r.db.WithContext(ctx).Where("account_id = ? AND id = ?", accountID, id).Delete(&model.CaptainDocument{}).Error
}
func (r *CaptainDocumentRepo) ListByAssistant(ctx context.Context, assistantID uint, offset, limit int) ([]model.CaptainDocument, int64, error) {
var docs []model.CaptainDocument
var count int64
db := r.db.WithContext(ctx).Model(&model.CaptainDocument{}).Where("assistant_id = ?", assistantID)
db.Count(&count)
if err := db.Preload("Assistant").Offset(offset).Limit(limit).Find(&docs).Error; err != nil {
return nil, 0, err
}
return docs, count, nil
}
type CaptainDocumentListFilters struct {
AssistantID uint
Source string
Filter string
SearchKey string
Sort string
Offset int
Limit int
}
func (r *CaptainDocumentRepo) ListByAccount(ctx context.Context, accountID uint, filters CaptainDocumentListFilters) ([]model.CaptainDocument, int64, error) {
var docs []model.CaptainDocument
var count int64
db := r.db.WithContext(ctx).Model(&model.CaptainDocument{}).Where("account_id = ?", accountID)
if filters.AssistantID > 0 {
db = db.Where("assistant_id = ?", filters.AssistantID)
}
switch filters.Source {
case "web":
db = db.Where("external_link <> ''")
case "pdf":
db = db.Where("external_link = ''")
}
switch filters.Filter {
case "syncing":
db = db.Where("sync_status = ?", model.DocumentSyncStatusPending)
case "failed":
db = db.Where("sync_status = ?", model.DocumentSyncStatusFailed)
case "synced":
db = db.Where("sync_status = ?", model.DocumentSyncStatusSynced)
}
if filters.SearchKey != "" {
query := "%" + strings.ToLower(filters.SearchKey) + "%"
db = db.Where("LOWER(name) LIKE ? OR LOWER(external_link) LIKE ?", query, query)
}
if err := db.Count(&count).Error; err != nil {
return nil, 0, err
}
order := "updated_at DESC"
if filters.Sort == "recently_created" {
order = "created_at DESC"
}
if filters.Limit <= 0 {
filters.Limit = 25
}
if err := db.Preload("Assistant").Order(order).Offset(filters.Offset).Limit(filters.Limit).Find(&docs).Error; err != nil {
return nil, 0, err
}
return docs, count, nil
}
func (r *CaptainDocumentRepo) FindByExternalLink(ctx context.Context, assistantID uint, externalLink string) (*model.CaptainDocument, error) {
var doc model.CaptainDocument
if err := r.db.WithContext(ctx).Where("assistant_id = ? AND external_link = ?", assistantID, externalLink).First(&doc).Error; err != nil {
return nil, err
}
return &doc, nil
}
func (r *CaptainDocumentRepo) ListDueForAutoSync(ctx context.Context, now time.Time, interval, staleTimeout time.Duration, limit int) ([]model.CaptainDocument, error) {
if interval <= 0 {
interval = 24 * time.Hour
}
if staleTimeout <= 0 {
staleTimeout = 10 * time.Minute
}
if limit <= 0 {
limit = 1000
}
syncDueBefore := now.Add(-interval / 2).Unix()
staleCutoff := now.Add(-staleTimeout).Unix()
var docs []model.CaptainDocument
err := r.db.WithContext(ctx).
Model(&model.CaptainDocument{}).
Where("status = ?", model.DocumentStatusCompleted).
Where(
"(sync_status = ? AND (last_synced_at IS NULL OR last_synced_at < ?)) OR "+
"(sync_status = ? AND (last_sync_attempted_at IS NULL OR last_sync_attempted_at < ?)) OR "+
"(sync_status = ? AND last_sync_attempted_at < ?)",
model.DocumentSyncStatusSynced, syncDueBefore,
model.DocumentSyncStatusFailed, syncDueBefore,
model.DocumentSyncStatusPending, staleCutoff,
).
Order("last_sync_attempted_at ASC, id ASC").
Limit(limit).
Find(&docs).Error
return docs, err
}
// FindByStatus retrieves documents matching a specific status.
func (r *CaptainDocumentRepo) FindByStatus(ctx context.Context, assistantID uint, status model.DocumentStatus, offset, limit int) ([]model.CaptainDocument, int64, error) {
var docs []model.CaptainDocument
var count int64
db := r.db.WithContext(ctx).Model(&model.CaptainDocument{}).Where("assistant_id = ? AND status = ?", assistantID, status)
db.Count(&count)
if err := db.Offset(offset).Limit(limit).Find(&docs).Error; err != nil {
return nil, 0, err
}
return docs, count, nil
}