// Package dao 提供 transcribe 模块的数据库访问操作 package dao import ( "context" "errors" "time" "github.com/echochat/backend/app/transcribe/model" "gorm.io/gorm" ) // TranscriptDAO 转写记录数据访问对象(PostgreSQL 主库) type TranscriptDAO struct { db *gorm.DB } // NewTranscriptDAO 创建实例 func NewTranscriptDAO(db *gorm.DB) *TranscriptDAO { return &TranscriptDAO{db: db} } // GetByRecordingID 按 recording_id 获取转写记录(不存在返回 nil, nil) // // 之所以 nil 不视为错误:调用方常见模式是"取不到 → 创建新行",避免 ErrRecordNotFound 包装 func (d *TranscriptDAO) GetByRecordingID(ctx context.Context, recordingID int64) (*model.Transcript, error) { var t model.Transcript err := d.db.WithContext(ctx). Where("recording_id = ?", recordingID). First(&t).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return nil, err } return &t, nil } // ListByRoomID 拉取一场会议下所有转写(按 recording_id 升序) // 用于会议详情页一次性带回多段录制的转写状态 func (d *TranscriptDAO) ListByRoomID(ctx context.Context, roomID int64) ([]model.Transcript, error) { var list []model.Transcript err := d.db.WithContext(ctx). Where("room_id = ?", roomID). Order("recording_id ASC"). Find(&list).Error return list, err } // Upsert 插入或更新(按 recording_id unique 索引) // 用于"重新转写"路径:保留同一行 ID,状态/文本就地刷新 func (d *TranscriptDAO) Upsert(ctx context.Context, t *model.Transcript) error { existing, err := d.GetByRecordingID(ctx, t.RecordingID) if err != nil { return err } if existing == nil { return d.db.WithContext(ctx).Create(t).Error } t.ID = existing.ID return d.db.WithContext(ctx).Save(t).Error } // MarkRunning 把指定 transcript 置为 running 状态,并填 started_at = now // // 用于异步任务起跑时的状态翻转,保证后端重启时 running 行不会卡死 // (配合 RescueStuckRunning 兜底)。 func (d *TranscriptDAO) MarkRunning(ctx context.Context, id int64) error { now := time.Now() return d.db.WithContext(ctx). Model(&model.Transcript{}). Where("id = ?", id). Updates(map[string]any{ "status": model.TranscriptStatusRunning, "started_at": now, "error_msg": "", }).Error } // MarkReady 转写成功 func (d *TranscriptDAO) MarkReady(ctx context.Context, id int64, text, segments, language string, durationSec int) error { now := time.Now() return d.db.WithContext(ctx). Model(&model.Transcript{}). Where("id = ?", id). Updates(map[string]any{ "status": model.TranscriptStatusReady, "text": text, "segments": segments, "language": language, "duration_sec": durationSec, "finished_at": now, "error_msg": "", }).Error } // MarkFailed 转写失败 func (d *TranscriptDAO) MarkFailed(ctx context.Context, id int64, errorMsg string) error { now := time.Now() if len(errorMsg) > 500 { errorMsg = errorMsg[:500] } return d.db.WithContext(ctx). Model(&model.Transcript{}). Where("id = ?", id). Updates(map[string]any{ "status": model.TranscriptStatusFailed, "error_msg": errorMsg, "finished_at": now, }).Error }