235 lines
7.6 KiB
Markdown
235 lines
7.6 KiB
Markdown
# Mila
|
||
|
||
Mila — небольшая Go-библиотека для обмена данными между БВК и
|
||
медиакомплексом.
|
||
|
||
Библиотека реализует HTTP-протокол, в котором БВК отправляет в медиакомплекс
|
||
текущее состояние отображаемого контента:
|
||
|
||
```text
|
||
GET /set-content?route=<R>&str=<S>&tpl_name=<T>
|
||
GET /get-content
|
||
```
|
||
|
||
Mila намеренно не знает ничего про снапшоты, геопозицию, прогресс маршрута,
|
||
фронтенд, правила воспроизведения медиа и структуру контента White Nights. Она
|
||
только принимает, валидирует, сохраняет, логирует и возвращает состояние,
|
||
полученное от БВК.
|
||
|
||
## Установка
|
||
|
||
```bash
|
||
go get gitea.unprism.ru/KRBL/mila
|
||
```
|
||
|
||
## Зона Ответственности
|
||
|
||
Mila — библиотека протокола. Ее нужно встраивать в прикладной сервис, например
|
||
в `wn-content`, и открывать на адресе, который требуется транспортной
|
||
интеграцией.
|
||
|
||
Mila делает:
|
||
|
||
- разбирает `GET /set-content`;
|
||
- разбирает `GET /get-content`;
|
||
- валидирует `route`, `str` и `tpl_name`;
|
||
- декодирует URL-параметры стандартным HTTP-парсером Go;
|
||
- хранит последнее состояние в памяти или JSON-файле;
|
||
- возвращает JSON, совместимый с протоколом;
|
||
- логирует каждый HTTP-запрос: метод, путь, статус, удаленный адрес и время;
|
||
- вызывает опциональный callback после успешного `set-content`.
|
||
|
||
Mila не делает:
|
||
|
||
- не считает прогресс маршрута;
|
||
- не работает с геопозицией или NMEA;
|
||
- не сопоставляет `tpl_name` с реальными плейлистами;
|
||
- не читает и не пишет снапшоты;
|
||
- не общается с фронтендом напрямую;
|
||
- не решает, как именно должен отображаться контент.
|
||
|
||
## Протокол
|
||
|
||
Медиакомплекс выступает HTTP-сервером. БВК выступает HTTP-клиентом.
|
||
|
||
Адрес по документу:
|
||
|
||
```text
|
||
172.16.16.2:13280
|
||
```
|
||
|
||
Ручки:
|
||
|
||
```text
|
||
GET /set-content?route=<R>&str=<S>&tpl_name=<T>
|
||
GET /get-content
|
||
```
|
||
|
||
`set-content` принимает URL-encoded query-параметры:
|
||
|
||
| Параметр | Обязательный | Описание |
|
||
| --- | --- | --- |
|
||
| `route` | да | номер маршрута, по умолчанию 1-3 символа |
|
||
| `str` | нет | текст бегущей строки, по умолчанию до 200 символов |
|
||
| `tpl_name` | да | название шаблона медиаконтента, по умолчанию до 200 символов |
|
||
|
||
Ответ `get-content`:
|
||
|
||
```json
|
||
{
|
||
"route": "3",
|
||
"str": "Гостиный двор - Gostiny dvor",
|
||
"tpl_name": "Гостиный двор"
|
||
}
|
||
```
|
||
|
||
Поле `updated_at` хранится внутри состояния, но не отдается через
|
||
`/get-content`, потому что ответ должен оставаться строгим относительно
|
||
протокола.
|
||
|
||
## Использование
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"log"
|
||
"net/http"
|
||
|
||
"gitea.unprism.ru/KRBL/mila"
|
||
)
|
||
|
||
func main() {
|
||
store := mila.NewFileStore("/srv/persistence/client_logs/bvk_content.json")
|
||
|
||
handler := mila.NewHandler(mila.DefaultConfig(), store,
|
||
mila.WithOnSet(func(state mila.ContentState) {
|
||
// Здесь прикладная интеграция:
|
||
// - передать route/str/tpl_name в состояние приложения;
|
||
// - сопоставить state.Template с локальным шаблоном;
|
||
// - записать дополнительные логи.
|
||
}),
|
||
)
|
||
|
||
srv := &http.Server{
|
||
Addr: "0.0.0.0:13280",
|
||
Handler: handler,
|
||
}
|
||
|
||
log.Fatal(srv.ListenAndServe())
|
||
}
|
||
```
|
||
|
||
## Конфигурация
|
||
|
||
Конфигурация валидации по умолчанию:
|
||
|
||
```go
|
||
cfg := mila.DefaultConfig()
|
||
```
|
||
|
||
Сейчас это:
|
||
|
||
```go
|
||
mila.Config{
|
||
MaxRouteLen: 3,
|
||
MaxTextLen: 200,
|
||
MaxTemplateLen: 200,
|
||
}
|
||
```
|
||
|
||
Лимиты для `str` и `tpl_name` нужно подтвердить у интегратора. В переданном PDF
|
||
текстовый слой неоднозначно распознает исходное значение.
|
||
|
||
## Хранилища
|
||
|
||
`MemoryStore` подходит, когда состояние не должно переживать рестарт процесса:
|
||
|
||
```go
|
||
store := mila.NewMemoryStore()
|
||
```
|
||
|
||
`FileStore` подходит для устройства, если последнее состояние от БВК нужно
|
||
сохранять на диске:
|
||
|
||
```go
|
||
store := mila.NewFileStore("/srv/persistence/client_logs/bvk_content.json")
|
||
```
|
||
|
||
`FileStore` пишет состояние атомарно через временный файл и держит копию в
|
||
памяти после первого успешного чтения или записи.
|
||
|
||
## Callback
|
||
|
||
`WithOnSet` позволяет приложению отреагировать на валидное обновление от БВК:
|
||
|
||
```go
|
||
handler := mila.NewHandler(mila.DefaultConfig(), store,
|
||
mila.WithOnSet(func(state mila.ContentState) {
|
||
// Возможные действия на стороне приложения:
|
||
// - опубликовать номер маршрута и бегущую строку;
|
||
// - сопоставить state.Template с локальным плейлистом;
|
||
// - записать прикладные логи.
|
||
}),
|
||
)
|
||
```
|
||
|
||
Callback выполняется синхронно. Его нужно держать быстрым. Если интеграционная
|
||
логика тяжелая, лучше положить состояние во внутреннюю очередь.
|
||
|
||
## Модель Состояния
|
||
|
||
```json
|
||
{
|
||
"route": "3",
|
||
"str": "Гостиный двор - Gostiny dvor",
|
||
"tpl_name": "Гостиный двор",
|
||
"updated_at": "2026-07-12T12:00:00Z"
|
||
}
|
||
```
|
||
|
||
Go-тип:
|
||
|
||
```go
|
||
type ContentState struct {
|
||
Route string `json:"route"`
|
||
Text string `json:"str"`
|
||
Template string `json:"tpl_name"`
|
||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||
}
|
||
```
|
||
|
||
## Коды Ответов
|
||
|
||
| Код | Когда возвращается |
|
||
| ---: | --- |
|
||
| 200 | валидный `set-content` или готовый `get-content` |
|
||
| 400 | невалидные параметры запроса |
|
||
| 404 | неизвестный путь |
|
||
| 405 | метод не `GET` |
|
||
| 500 | ошибка хранилища или внутренняя ошибка |
|
||
| 503 | `get-content` до первого состояния, отмененный контекст, недоступная зависимость |
|
||
|
||
Формат ошибки:
|
||
|
||
```json
|
||
{
|
||
"error": "route: must be at most 3 characters"
|
||
}
|
||
```
|
||
|
||
## Тестирование
|
||
|
||
```bash
|
||
go test ./...
|
||
```
|
||
|
||
Текущие тесты покрывают:
|
||
|
||
- успешный `set-content` + `get-content`;
|
||
- `get-content` до готовности состояния;
|
||
- невалидный `route`;
|
||
- UTF-8 и лимиты для текста/шаблона;
|
||
- сохранение и чтение через `FileStore`;
|
||
- отсутствие файла состояния в `FileStore`.
|