Files
tech-service-portal/README.md
T
darkorion 0f239d5015 feat: документация из Nextcloud, график у сотрудника, автоочистка фото, мониторинг, итог мастера, фильтр по местам; доп. сервисы в docker-compose
- docker-compose.yml: профили docs (Nextcloud), office (ONLYOFFICE Document Server), ai (Ollama + скачивание модели)
- автонастройка: адреса Nextcloud, ONLYOFFICE, портала и модели подставляются из .env или по именам сервисов
  во внутренней сети Docker; значения из интерфейса важнее
- локальная модель в своей сети (имя сервиса, частный IP) подключается без ключа API
- .env.example, docs/DEPLOY.md (раздел 7а), README
2026-10-13 13:27:20 +03:00

163 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Портал технической службы (чек-листы, наряды, инструмент)
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`.
## Требования к серверу
Оценка по устройству портала (одно приложение на Python + PostgreSQL в Docker), нагрузочных замеров не было.
| | Минимум (до 20–30 сотрудников) | Рекомендуется (до 100–150 сотрудников, с запасом) |
|---|---|---|
| Процессор | 1 ядро (x86-64 или ARM64) | 2 ядра |
| Память | 2 ГБ (с 1 ГБ работает, но первая сборка образа может не пройти без файла подкачки) | 4 ГБ |
| Диск | 10 ГБ: система, Docker, образ ~1 ГБ, база, резервные копии | 40 ГБ |
| ОС | Linux (Debian 12/13, Ubuntu 22.04/24.04), Docker 24+ с Compose v2 | — |
- **Фото — главный расход места.** Приложение их сжимает, ориентир 0,3–0,5 МБ на снимок: при 50 фото в день — 6–9 ГБ в год,
и ещё столько же, если резервные копии делаются с фото.
- **Нужны домен и HTTPS** (обратный прокси): без них не работают уведомления, мобильная версия на iPhone и релизное приложение для Android.
- **Интернет** серверу нужен только для уведомлений Android (Firebase) и AI-помощника, если он включён; остальное работает в локальной сети.
- Тяжелее всего для процессора PDF-отчёты и анализ надёжности — секунды на запрос, не постоянная нагрузка.
Пошаговая установка — [docs/DEPLOY.md](docs/DEPLOY.md).
## Дополнительные сервисы
Рядом с порталом `docker-compose.yml` может поднять **Nextcloud** (документация оборудования), **ONLYOFFICE** (просмотр
Word/Excel/PowerPoint в портале) и **Ollama** (локальная модель для AI-помощника) — профилями `docs`, `office`, `ai`
(`COMPOSE_PROFILES=docs,office,ai` в `.env`). Портал сам находит их во внутренней сети Docker и заполняет адреса в настройках.
Подробно — [docs/DEPLOY.md](docs/DEPLOY.md), раздел 7а.
## Установка без 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`.