Портал технической службы: наряды и чек-листы, задачи, отказы и неисправности, анализ надёжности, карты, склады, приложение для Android

This commit is contained in:
darkorion
2026-10-13 09:09:53 +03:00
commit c298f5a830
247 changed files with 23200 additions and 0 deletions
+138
View File
@@ -0,0 +1,138 @@
# Портал технической службы (чек-листы, наряды, инструмент)
FastAPI + SQLAlchemy, веб-интерфейс на чистом JS, JWT-API для будущего Android-приложения.
Документация API (Swagger): `/docs`.
## Модель
- **Должность** (`technician` / `engineer`) имеет **график** (по дням недели или цикл N/M, дневная/ночная смена).
**Пользователь** привязывается к должности администратором.
- **Чек-лист** — список задач; тип ответа: статус (в работе/выполнено), список, число (с границами), текст.
- **Шаблон наряда** — единожды настроенный наряд + периодичность (каждый день / дни недели / раз в месяц / каждые N дней).
Планировщик внутри приложения создаёт из него наряды.
- **Наряд** — копия чек-листа с оборудованием, местом, исполнителем и проверяющим.
Исполнитель: конкретный человек **или** должность (тогда наряд видят те, кто сейчас на смене; кто первый начал — тот исполнитель).
Статусы: `new → in_progress → review → completed` (проверяющий может вернуть на доработку), `cancelled`.
- Права: техник — только свои наряды; инженер — всё про наряды/чек-листы/оборудование + проверка; админ — ещё пользователи, должности, графики.
## Документация
- **[Руководство пользователя (PDF)](docs/user-guide.pdf)** — для техников, инженеров, модераторов и администраторов: скриншоты с пометками, работа без связи, проверка, графики, отчёты, FAQ. Пересборка: [tools/guide](tools/guide/README.md).
- [docs/DEPLOY.md](docs/DEPLOY.md) — установка на свой сервер (Docker Compose); [docs/API.md](docs/API.md) — описание API для разработчика клиентов; [android/README.md](android/README.md) — сборка приложения.
## Роли
- **Администратор** — всё, включая «Настройки сервера» (каталог фото, лимит размера, контакт push).
- **Модератор** — панель «Администрирование»: пользователи, должности, графики, чек-листы, оборудование, наряды; не меняет администраторов, не выдаёт права, не видит настройки сервера, сам наряды не выполняет.
- **Инженер / техник** — по типу должности (см. выше).
## Android-приложение
Нативное приложение (Kotlin + Compose) в каталоге [android/](android/README.md): список и выполнение нарядов, фото с камеры,
комментарии, проверка для инженеров, **полноценная работа без связи** (накопленное уходит на сервер само), уведомления.
Распространяется APK-файлом: готовая версия лежит в `release/` и раздаётся по `https://<домен>/app.apk` (ссылка на экране входа);
новый файл можно положить в `DATA_DIR/apk/` — он важнее.
## Мобильная версия в браузере (PWA)
Запасной вариант, если приложение недоступно (например, iPhone): **`https://<домен>/m`**. Устанавливается на экран «Домой»
(Android: «Установить приложение»; iPhone: «Поделиться → На экран Домой»), работает **полностью без связи**: наряды и чек-листы
хранятся в браузере (IndexedDB), ответы, фото, комментарии и сдача копятся в очереди и уходят сами (фото с `Idempotency-Key`,
ответы пачкой через `/orders/{id}/sync`). Все экраны: список с фильтрами и поиском, чек-лист с крупными кнопками, фото с камеры,
комментарии, проверка для инженеров, уведомления (Web Push), выход.
- С телефона на основном адресе предлагается перейти на `/m` (выбор «Остаться» запоминается); на ПК остаётся полная версия.
- Файлы: `app/static/m/` (m.js, m.css, manifest, иконки); service worker — `app/static/sw.js` (кэш оболочки + push).
- Ограничения iOS: нет фоновой синхронизации (отправка идёт, пока приложение открыто), push работает только у установленной PWA
(iOS 16.4+) и только по HTTPS. Данные браузера iOS может очистить, если не открывать сайт долго.
## Демонстрационный режим (личный)
«Администрирование → Демонстрационный режим». Демо включается **только для того, кто его включил**: его запросы идут в отдельную
SQLite-базу с тестовыми данными (сотрудники, оборудование и краны, чек-листы, наряды и задачи за 14 дней, карты, склады, инциденты),
остальные пользователи продолжают работать с рабочей базой. Администратор может включить или выключить демо любому сотруднику.
Тестовые учётные записи (пароль `demo1234`) работают параллельно с рабочими. Приложение для Android и мобильная версия при смене
режима очищают локальные данные. Подробнее — docs/API.md, раздел «Демонстрационный режим».
## Графики и отчёты
- Графики: по дням недели, простой цикл N через M или **конструктор цикла из шагов** (смена/выходной, любая длительность,
дневные, ночные и суточные смены, метки «отсыпной» и т. п.) с календарём-предпросмотром на 6 недель.
- Отчёты → «Выполненные наряды»: машина, чек-лист, кто выполнил, должность, когда. Период (дата+время) обязателен;
фильтры по машине, должности, исполнителю; экспорт в PDF и CSV. Шрифт DejaVu лежит в `app/fonts` (кириллица в PDF).
## Регламентное обслуживание и «Текущее состояние»
- **Узлы машины.** Карточка машины («Оборудование» → «Узлы и наработка») хранит наработку (моточасы) и список обслуживаемых узлов:
масло, фильтры, ремни и т. п. Для каждого узла выбирается, как он обслуживается: **по наработке** (каждые N м/ч), **по календарю**
(раз в N дней / недель / месяцев) или **что наступит раньше**; задаётся порог предупреждения «Скоро» (% от интервала).
При добавлении указывается, когда и при какой наработке узел обслуживался в последний раз.
- **Чек-лист «Регламентное обслуживание».** Тип чек-листа в редакторе. В наряд подставляются: показание наработки машины,
ваши обычные задачи и по пункту «обслужено?» на каждый узел выбранной машины (можно только «Требуется/Скоро»). Оборудование обязательно.
Когда наряд завершён (после проверки, если она есть), отмеченные узлы сбрасываются, а наработка машины обновляется.
Работает и в автозапуске (шаблоны нарядов). Android-приложение показывает пункты узлов с остатком и содержит экран «Состояние узлов» (наработка, сброс); мобильная версия — как обычные задачи.
- **Сброс узла** вручную — если замена или обслуживание выполнены отдельно либо при внештатной ситуации (дата, наработка, комментарий; всё в истории узла).
Показание счётчика не может уменьшаться, кроме «коррекции» (замена/обнуление счётчика).
- **Отчёт «Текущее состояние»** (Отчёты): узлы всех машин с состоянием **Требуется / Скоро / В норме**, остатком по наработке и календарю,
сроком и **прогнозом даты** (по средней наработке в сутки за 30 дней). Фильтры по машине, месту и состоянию; экспорт PDF и CSV.
- API: `/equipment/{id}/service-units`, `/service-units/{id}[/reset|/events]`, `/equipment/{id}/meter`, `/reports/equipment-state` (см. [docs/API.md](docs/API.md)).
## Портал: задания, инциденты, карта, инструмент, сотрудники
- **Боковое меню** вместо шапок: на компьютере всегда видно, на телефоне (сайт, /m, Android 1.1.0) — по кнопке ☰.
- **Суточные задания** (`/tasks`): назначен / в работе / приостановлен / выполнен / отменён; для трёх последних комментарий обязателен;
фото, привязка к машине или узлу. Другим ставят инженер, модератор, админ; техник — себе.
- **Инциденты** (`/incidents`): авария на машине — описание, затронутые узлы, была ли замена (замена сбрасывает ресурс узла).
Открытый инцидент — состояние машины «Авария». Сводка состояний: `/equipment-health`.
- **Иконки оборудования**: встроенный набор `app/static/icons` (генерируется `tools/make_icons.py`) или своё фото.
- **Карта завода** (`/map`): картинку и расстановку иконок ведёт админ; «!» — авария, жёлтое свечение — скоро/пора ТО.
**Карта складов** — зоны складов на том же плане (ведёт администратор склада), клик — содержимое склада.
- **Инструмент и склады** (`/inventory/*`, `/warehouses`): каталог, остатки, выдача лично сотруднику, возврат, перемещение, списание,
журнал. Роль **«Администратор склада»** (`is_storekeeper`). Сотрудник видит только выданное ему (`/me/inventory`).
- **Заявки на инструмент** (`/tool-requests`): что купить, зачем, ссылка на товар; статусы и ответ ответственного.
- **Карточка сотрудника** (`/users/{id}/card`): ФИО, фото, должность, ближайшие смены, «что умею», текущие задания и наряды, инструмент.
- **Передача задания** (`POST /tasks/{id}/transfer`): исполнитель, поставивший задание или инженер; причина обязательна, видна в истории.
- **Инциденты извне.** Публичная форма **`/report`** без входа: место → только машины этого места (и вложенных) → описание и контакты
(обязательно); лимит отправок по адресу, ловушка для ботов, выключается в «Настройках сервера». API для внешних систем:
`POST /external/incidents` с заголовком `X-API-Key` (машина по id или инвентарному номеру, `external_id` защищает от дублей),
справочник `GET /external/equipment`; ключи выпускает и отзывает админ в «Настройках сервера» (в БД — только хэш).
- **История машины** (`GET /equipment/{id}/history`, вкладка в карточке машины): инциденты, обслуживание и замены узлов, наработка,
наряды, задания; фильтры по типу, периоду и тексту.
- **Отделы** (`/departments`): вложенность, руководитель; отдел в карточке пользователя, фильтр в «Сотрудниках».
- **Мастер первичной настройки** (Администрирование → 🧭): графики, должности, отделы, места, оборудование, сотрудники
(пакетный ввод строками, логины и временные пароли — таблицей для печати), карта, склады и остатки, чек-листы, внешние обращения, копии.
- **Резервные копии** (Администрирование → «Резервные копии», `/backups`): база в переносимом формате + конфигурация (+ фото по желанию),
автокопии по расписанию, скачивание, загрузка, восстановление.
- **AI-помощник** (включается в «Настройках сервера»: провайдер, адрес, модель, ключ): кнопка в правом нижнем углу на всех
экранах; отвечает только о работе в портале, учитывает права пользователя и читает данные только от его имени.
- **Отчёт «Выполнение работ»** (`/reports/execution`): % выполнения суточных осмотров, чек-листов, ТО и заданий по машинам, отклонения; PDF/CSV.
## Комментарии, фото, уведомления
- Комментарии к наряду видят все, кто видит наряд; участникам (исполнитель, проверяющий, назначенный) уходит уведомление.
- Фото: у пункта чек-листа режим «без фото / по желанию / обязательно». Браузер уменьшает снимок до 1600 px. Файлы лежат в каталоге из настроек (по умолчанию `data/photos`, в Docker — `./photos` на хосте), в БД — только путь. API: `POST /api/orders/{id}/items/{item_id}/photos` (тело — сам файл, `Content-Type: image/jpeg|png|webp`), `GET /api/photos/{id}` (с токеном).
- Уведомления — Web Push: кнопка «🔔» в шапке. Нужен **HTTPS** (через Nginx Proxy Manager подойдёт). Приходят при назначении наряда (человеку или тем, кто в этот момент на смене в должности), на проверку, при возврате и при комментариях.
## API
Версионированный REST: `/api/v1`, Swagger на `/docs`. Короткий access-токен + refresh с ротацией, пакетная офлайн-синхронизация
(`POST /orders/{id}/sync`), курсор `updated_since`, идемпотентная загрузка фото, FCM для Android.
**Полное описание для разработчика приложения: [docs/API.md](docs/API.md).**
Переменные: `ACCESS_TTL_MIN` (60), `REFRESH_TTL_DAYS` (90), `LOGIN_MAX_FAILS` (5), `LOGIN_WINDOW_SEC` (300).
## Локальный запуск
```
python -m venv .venv && .venv\Scripts\pip install -r requirements.txt
.venv\Scripts\python -m uvicorn app.main:app --reload
```
Вход: `admin` / `admin` (создаётся при первом старте, **смените**). Тесты: `pytest tests`.
## Установка без Coolify (Docker Compose)
Пошаговая инструкция для любого Linux-сервера с Docker: **[docs/DEPLOY.md](docs/DEPLOY.md)** (шаблон настроек — `.env.example`).
Коротко: `cp .env.example .env` → заполнить → `docker compose up -d --build` → обратный прокси с HTTPS на `APP_PORT`.
## Деплой (Coolify + Nginx Proxy Manager)
Приложение разворачивается из `docker-compose.yml` (сервис приложения + PostgreSQL, тома `pgdata`, `photos`, `appdata`).
1. В Coolify: Project → New → Application из Git-репозитория, **Build Pack = Docker Compose**, файл `docker-compose.yml`.
2. Переменные окружения (Coolify → Environment Variables): `DB_PASSWORD`, `SECRET_KEY` (случайная строка), `ADMIN_PASSWORD`
(пароль администратора `admin`, применяется при первом старте), `FORWARDED_ALLOW_IPS` (**IP вашего NPM**, например `192.168.1.10`),
при желании `APP_TZ` (по умолчанию Europe/Moscow) и `APP_PORT` (порт хоста, по умолчанию **8080**).
В боевом режиме (`APP_ENV=production`, уже прописан в compose) приложение **не запустится**, если `SECRET_KEY` короче 32 символов
или пуст, а `ADMIN_PASSWORD` слабее 10 символов (проверяется при первом создании администратора). Пароли пользователей — от 10 символов.
3. Домен в Coolify не задаётся: прокси — NPM. Proxy Host → Forward `http://<IP сервера Coolify>:8080`, SSL (Let's Encrypt),
Force SSL. **В Advanced добавьте `client_max_body_size 20m;`** (иначе фото больше 1 МБ вернут 413).
Порт приложения обязательно закройте файрволом для всех, кроме адреса NPM, а в `FORWARDED_ALLOW_IPS` укажите этот адрес: только от него
принимаются заголовки `X-Forwarded-For`. Проверка: «Администрирование → Настройки сервера» показывает адрес клиента, каким его видит сервер.
4. Запуск строго в **одном процессе** (так и настроено): планировщик нарядов и демо-режим живут внутри процесса.
5. APK: готовый подписанный файл лежит в репозитории (`release/`) и входит в образ, поэтому ссылка «Скачать приложение для
Android» на экране входа и адрес `/app.apk` работают сразу после деплоя. Новую версию можно выложить без передеплоя: положить
файл в том `appdata` по пути `apk/` (он важнее файла из образа): `docker cp checklists.apk <контейнер приложения>:/srv/data/apk/`.
6. Резервные копии: `pg_dump` базы по расписанию + тома `photos` и `appdata`.