приём и чтение записей сведены к одному контракту приложения
- адреса приложения переехали в своё пространство `/app/`, опрос готовности убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи, текст — отдельным адресом названного вида - заведена единая точка отображения доменной ошибки и слой, приводящий к той же форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением - у записи появились имя файла отправителя, длительность и размер своими колонками, а у ленты владельца — свой индекс: без него страница сканировала весь архив сервиса
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package entity
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"git.vakhrushev.me/av/transcriber/internal/clock"
|
||||
)
|
||||
@@ -62,6 +64,33 @@ type AudioRecord struct {
|
||||
Title *string
|
||||
Brief *string
|
||||
|
||||
// OriginalFilename — имя файла, данное отправителем. Лежит **отдельно от
|
||||
// заголовка**: заголовок несёт название, которое дал человек либо посчитала
|
||||
// языковая модель, а имя файла — то, по чему человек узнаёт свою запись, пока
|
||||
// заголовка нет. Одной колонкой на оба смысла посчитанное название затирало бы
|
||||
// имя, и вернуть затёртое было бы неоткуда.
|
||||
//
|
||||
// Значение приходит извне: приём режет его по MaxOriginalFilenameLen и убирает
|
||||
// управляющие знаки. В имя файла хранилища и в журнал оно не идёт — инвариант
|
||||
// приватности.
|
||||
OriginalFilename *string
|
||||
|
||||
// DurationMs и SizeBytes — величины **принятого**, снимок с момента приёма.
|
||||
// Со строкой файла они намеренно не сверяются: там лежат величины той копии,
|
||||
// которой файл является сейчас, и уточнение длительности меняет их, не трогая
|
||||
// эти. Нужны колонками записи, потому что показываются в списке.
|
||||
//
|
||||
// Указатели здесь не выражают «неизвестно»: числовая колонка хранилища
|
||||
// пустого значения не держит, и пустое кладётся нулём. Обе величины ставит
|
||||
// приём и ставит всегда — запись с непрочитанными метаданными отвергается
|
||||
// отказом и не заводится вовсе. Решение владельца 2026-08-15.
|
||||
DurationMs *int64
|
||||
SizeBytes *int64
|
||||
|
||||
// TopicIDs — темы записи. Ни приём, ни конвейер их не пишут: место заведено
|
||||
// вперёд, заполняет его задача, считающая темы языковой моделью.
|
||||
TopicIDs []string
|
||||
|
||||
State string
|
||||
// StateEnteredAt ставится только сменой рубежа и возвратом записи в работу.
|
||||
// Откладывание опроса его не двигает — иначе застревание в чужой операции
|
||||
@@ -99,6 +128,40 @@ type AudioRecord struct {
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// MaxOriginalFilenameLen — потолок длины имени файла, данного отправителем.
|
||||
//
|
||||
// Имя приходит извне и содержимым своим приёму не подконтрольно, поэтому длина
|
||||
// назначается сервисом. Число выведено из предела длины имени в распространённых
|
||||
// файловых системах: имя длиннее 255 знаков не приходит от системного диалога
|
||||
// выбора файла вовсе, и всё, что длиннее, — либо самодельный запрос, либо
|
||||
// попытка раздуть строку записи.
|
||||
const MaxOriginalFilenameLen = 255
|
||||
|
||||
// SanitizeOriginalFilename приводит имя, данное отправителем, к пригодному для
|
||||
// хранения виду: убирает управляющие знаки и режет по потолку длины.
|
||||
//
|
||||
// Живёт в домене, а не в транспорте: имя доходит до колонки записи одним путём,
|
||||
// и правило чистки обязано быть одно. Управляющие знаки убираются потому, что
|
||||
// иначе доезжают до экрана и до панели владельца; резка идёт **после** уборки,
|
||||
// иначе потолок съедали бы знаки, которых в сохранённом имени всё равно не будет.
|
||||
//
|
||||
// Режется по знакам, а не по байтам: имя русское чаще, чем латинское, и обрезка
|
||||
// по байтам разрубила бы знак пополам.
|
||||
func SanitizeOriginalFilename(name string) string {
|
||||
cleaned := strings.Map(func(r rune) rune {
|
||||
if unicode.IsControl(r) {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, name)
|
||||
|
||||
runes := []rune(cleaned)
|
||||
if len(runes) > MaxOriginalFilenameLen {
|
||||
runes = runes[:MaxOriginalFilenameLen]
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
// AllStates — закрытый перечень рубежей для схемы хранилища.
|
||||
func AllStates() []string {
|
||||
out := make([]string, 0, len(stages))
|
||||
|
||||
Reference in New Issue
Block a user