- порядок беклога и роадмапа назначен слоями: проверки, которым можно верить → долги входа → владелец записи и контракт API → конвейер под тестами → приложение и возможности поверх; у каждого движения записана причина; - заведены восемь задач под пункты «Завершения», которых не закрывала ни одна запись, — цель any-audio-source была без задач вовсе; - у четырёх задач сняты критерии, требовавшие того, что делает задача ниже по очереди; исправлены ссылки на несуществующий repo/sqlite и на отменённую разведку об очереди.
44 lines
3.5 KiB
Markdown
44 lines
3.5 KiB
Markdown
# ✨ Узнавать уже загруженный файл по хеш-сумме
|
||
|
||
- **Тип:** feature
|
||
- **Категория:** Очередь — Дедупликация ищет совпадение в пределах пользователя — то есть после владельца записи, и экономит деньги с первого дня приложения.
|
||
- **Зачем:** Один и тот же файл, отправленный дважды, распознаётся дважды и оплачивается дважды: приём не смотрит на содержимое вовсе.
|
||
- **Теги:** goal:upload-reliability
|
||
|
||
Двигает пункт 1 «Завершения» цели: повторная отправка того же файла возвращает
|
||
прежнюю запись вместо второй задачи.
|
||
|
||
Совпадение ищется **в пределах одного пользователя**: чужая расшифровка по
|
||
совпадению хеш-суммы не отдаётся и о её существовании отправитель не узнаёт.
|
||
|
||
## Затрагивает
|
||
|
||
- `TranscribeService.createTranscribeJob` — единая точка приёма, через неё идут
|
||
оба входа;
|
||
- таблица `files`: колонка хеш-суммы, её миграция и индекс по паре «владелец,
|
||
хеш-сумма»;
|
||
- контракт `POST /api/audio`: ответ на повторный файл;
|
||
- ответ бота на повторно присланное голосовое;
|
||
- `docs/database.md` — представление данных.
|
||
|
||
## Критерии приёмки
|
||
|
||
- Повторная отправка того же файла тем же пользователем возвращает
|
||
идентификатор прежней задачи, второй задачи в базе не появляется. Оракул —
|
||
тест приёма: два вызова одним содержимым, в репозитории одна задача.
|
||
- Тот же файл от другого пользователя заводит свою задачу и своё распознавание.
|
||
Оракул — тест приёма с двумя владельцами: две задачи, тексты не разделяются.
|
||
- Повторный файл не остаётся вторым экземпляром в каталоге хранения. Оракул —
|
||
тест: после второго вызова в каталоге один файл.
|
||
- Незавершённая задача тоже узнаётся: повторная отправка отдаёт её состояние, а
|
||
не заводит соседнюю. Оракул — тест на задаче в состоянии `created`.
|
||
|
||
## Рамки
|
||
|
||
Владелец записи приходит из `record-ownership` — до неё дедупликация опирается
|
||
на того владельца, который уже есть. Хеш-сумма считается на сервере: подсчёт на
|
||
стороне приложения относится к `chunked-upload-choice`. Колонка добавляется во
|
||
всех четырёх местах пакета хранилища `internal/adapter/repo/pocketbase` —
|
||
`applyToRecord`, `recordToJob`, `acquireColumns`, `acquiredRow` с её `toJob`, —
|
||
плюс шаг схемы (инвариант `CLAUDE.md`).
|