- заведён internal/clock: Now даёт метку в UTC, Start — начало измерения длительности с монотонными часами; девять мест рабочего кода и запрос захвата переведены на него, долг «время time.Now() по месту» закрыт - клиент SpeechKit узнавал конец потока сравнением err.Error() == "EOF": отказ с тем же текстом вернул бы усечённую расшифровку как готовую, теперь errors.Is(err, io.EOF) - починены находки новых линтеров: две опечатки, два slog.DiscardHandler, четыре неэкранированные подстановки в docker/entrypoint.sh
95 lines
3.7 KiB
Go
95 lines
3.7 KiB
Go
package entity
|
|
|
|
import (
|
|
"time"
|
|
|
|
"git.vakhrushev.me/av/transcriber/internal/clock"
|
|
)
|
|
|
|
type TranscribeJob struct {
|
|
Id string
|
|
State string
|
|
Source string
|
|
FileID *string
|
|
ErrorText *string
|
|
AcquisitionID *string
|
|
AcquireTime *time.Time
|
|
DelayTime *time.Time
|
|
Attempts int // Число попыток: растёт при захвате, обнуляется на шаге без отказа
|
|
RecognitionOpID *string // ID операции распознавания в Yandex Cloud
|
|
TranscriptionText *string // Результат распознавания
|
|
TgChatId *int64 // Telegram: в какой чат отправить результат распознавания
|
|
TgReplyMessageId *int // Telegram: с каким сообщением связать результат распознавания
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
const (
|
|
StateCreated = "created"
|
|
StateConverted = "converted"
|
|
StateTranscribe = "transcribe"
|
|
StateDone = "done"
|
|
StateFailed = "failed"
|
|
// StateDead — задача, которую мы повторяли и перестали. От `failed` она
|
|
// отличается тем, чей это приговор: в `failed` задачу переводит шаг,
|
|
// рассудивший об этой записи окончательно, а сюда она уходит без такого
|
|
// суждения. Ни один шаг конвейера в неё не переводит сам.
|
|
StateDead = "dead"
|
|
)
|
|
|
|
const (
|
|
SourceUnknown = "unknown"
|
|
SourceApi = "api"
|
|
SourceTelegram = "telegram"
|
|
)
|
|
|
|
// Переводит задачу в новое состояние, при этом очищает все
|
|
// служебные поля предыдущего состояния, как-то время задержки, информацию о воркере и тд
|
|
func (j *TranscribeJob) MoveToState(state string) {
|
|
j.State = state
|
|
j.DelayTime = nil
|
|
j.AcquisitionID = nil
|
|
j.AcquireTime = nil
|
|
// Шаг, дошедший до перехода, завершился без отказа, а попытки считают
|
|
// именно отказавшие: иначе задача, прошедшая конвейер целиком, накопила бы
|
|
// их поштучно и умерла бы здоровой.
|
|
j.Attempts = 0
|
|
j.UpdatedAt = clock.Now()
|
|
}
|
|
|
|
func (j *TranscribeJob) MoveToStateAndDelay(state string, delay *time.Time) {
|
|
j.MoveToState(state)
|
|
j.DelayTime = delay
|
|
j.UpdatedAt = clock.Now()
|
|
}
|
|
|
|
func (j *TranscribeJob) Done(transcriptionText string) {
|
|
j.MoveToState(StateDone)
|
|
j.TranscriptionText = &transcriptionText
|
|
}
|
|
|
|
func (j *TranscribeJob) Fail(errText string) {
|
|
j.MoveToState(StateFailed)
|
|
j.ErrorText = &errText
|
|
}
|
|
|
|
// RetryAfter освобождает отказавшую задачу для повтора: захват снимается,
|
|
// пауза ставится, а число попыток сохраняется — по нему растёт пауза и
|
|
// наступает предел.
|
|
func (j *TranscribeJob) RetryAfter(delay time.Time) {
|
|
j.AcquisitionID = nil
|
|
j.AcquireTime = nil
|
|
j.DelayTime = &delay
|
|
j.UpdatedAt = clock.Now()
|
|
}
|
|
|
|
// Die переводит задачу, исчерпавшую попытки, в состояние «мертва». Число
|
|
// попыток при этом сохраняется: по нему видно, сколько раз мы пробовали, а
|
|
// возвращает задачу в работу владелец правкой состояния.
|
|
func (j *TranscribeJob) Die(errText string) {
|
|
attempts := j.Attempts
|
|
j.MoveToState(StateDead)
|
|
j.Attempts = attempts
|
|
j.ErrorText = &errText
|
|
}
|