Compare commits

...

3 Commits

Author SHA1 Message Date
a559d3848a add questions 2026-01-13 08:23:03 +03:00
043b14b65f fix style 2026-01-12 22:55:12 +03:00
afd6b2c1fd refactor project 2026-01-12 22:42:22 +03:00
17 changed files with 1311 additions and 2342 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.claude/

225
ARCHITECTURE.md Normal file
View File

@ -0,0 +1,225 @@
# HotelTask - Архитектура системы
## Обзор системы
```mermaid
flowchart TB
subgraph ClientLayer["Client Layer"]
WebAdmin["Web Admin<br/>(TypeScript, React)"]
MobileApps["Mobile Apps"]
TelegramBot["Telegram Bot"]
end
subgraph APIGateway["API Gateway Layer"]
REST["REST API"]
gRPC["gRPC"]
WebSocket["WebSocket"]
end
subgraph ServiceLayer["Service Layer (Python)"]
tasks["tasks-service<br/>Управление задачами"]
users["users-service<br/>Управление пользователями"]
connections["connections-service<br/>Связи между адаптерами"]
notification["notification-service<br/>Уведомления"]
fileStorage["file-storage-service<br/>Файлы"]
audit["audit-service<br/>Аудит"]
scheduler["scheduler-service<br/>Расписание"]
permissions["permissions-service<br/>Права доступа"]
events["events-service<br/>Мероприятия"]
end
subgraph DataLayer["Data Layer"]
PostgreSQL[(PostgreSQL<br/>Основная БД)]
Redis[(Redis<br/>Кэш)]
S3[(S3<br/>Файлы)]
end
subgraph MessageBroker["Message Broker"]
Kafka["Apache Kafka"]
end
subgraph AdapterLayer["Adapter Layer (Python)"]
PMSAdapter["PMS Adapter"]
ERPAdapter["ERP Adapter"]
ResonlineAdapter["Resonline Adapter"]
TelegramAdapter["Telegram Bot Adapter"]
CustomAdapter["Custom Adapter #N"]
end
subgraph ExternalSystems["External Systems"]
PMS["PMS"]
ERP["ERP"]
Resonline["Resonline"]
end
%% Client -> API Gateway
WebAdmin --> REST
MobileApps --> REST
MobileApps --> WebSocket
TelegramBot --> REST
%% API Gateway -> Services
REST --> tasks
REST --> users
REST --> connections
gRPC --> tasks
gRPC --> users
WebSocket --> notification
%% Services -> Data
tasks --> PostgreSQL
users --> PostgreSQL
connections --> PostgreSQL
audit --> PostgreSQL
scheduler --> PostgreSQL
permissions --> PostgreSQL
events --> PostgreSQL
notification --> Redis
tasks --> Redis
fileStorage --> S3
%% Services <-> Kafka
tasks <--> Kafka
notification <--> Kafka
connections <--> Kafka
%% Kafka <-> Adapters
Kafka <--> PMSAdapter
Kafka <--> ERPAdapter
Kafka <--> ResonlineAdapter
Kafka <--> TelegramAdapter
Kafka <--> CustomAdapter
%% Adapters -> External Systems
PMSAdapter --> PMS
ERPAdapter --> ERP
ResonlineAdapter --> Resonline
```
---
## Описание сервисов
| Сервис | Назначение | Ключевые функции |
|--------|------------|------------------|
| **tasks-service** | Ядро системы | CRUD задач, статусы, назначение исполнителей, история изменений |
| **users-service** | Управление пользователями | Регистрация, авторизация, профили, привязка к отелям |
| **connections-service** | Маршрутизация событий | Настройка связей "событие A → система B", реестр адаптеров |
| **permissions-service** | Права доступа | RBAC, роли, проверка разрешений |
| **notification-service** | Уведомления | Push, email, SMS, Telegram, in-app |
| **file-storage-service** | Файлы | Загрузка/скачивание фото и документов в S3 |
| **audit-service** | Аудит | Логирование всех действий в системе |
| **scheduler-service** | Планировщик | Создание задач по расписанию (cron) |
| **events-service** | Мероприятия | Декомпозиция мероприятий на задачи, генерация "функшн" |
---
## Слои системы
### Client Layer
Клиентские приложения для взаимодействия с системой:
- **Web Admin** (React, TypeScript) — админка для управления системой
- **Mobile Apps** — мобильные приложения для сотрудников
- **Telegram Bot** — бот для управления задачами
### API Gateway Layer
Точки входа для клиентов:
- **REST API** — основной API для веб и мобильных клиентов
- **gRPC** — высокопроизводительное межсервисное взаимодействие
- **WebSocket** — real-time уведомления
### Service Layer
Микросервисы на Python (FastAPI), каждый отвечает за свою domain-область.
### Data Layer
- **PostgreSQL** — основное хранилище данных
- **Redis** — кэширование, сессии
- **S3** — файловое хранилище (фото, документы)
### Message Broker
**Apache Kafka** — асинхронный обмен сообщениями между сервисами и адаптерами.
### Adapter Layer
Адаптеры для интеграции с внешними системами. Каждый адаптер:
- Преобразует данные внешней системы в стандартный формат
- Работает как по push, так и по pull модели
- Изолирует логику интеграции от бизнес-логики
### External Systems
Внешние системы отеля: PMS, ERP, системы управления уборкой и другие.
---
## Потоки данных
### Клиенты → API Gateway
| Источник | Назначение | Данные |
|----------|------------|--------|
| Web Admin | REST API | Запросы на управление задачами, пользователями, настройками |
| Mobile Apps | REST API | Запросы на работу с задачами, профилем |
| Mobile Apps | WebSocket | Подписка на real-time уведомления |
| Telegram Bot | REST API | Команды от пользователей бота |
### API Gateway → Сервисы
| Источник | Назначение | Данные |
|----------|------------|--------|
| REST API | tasks-service | CRUD операции с задачами |
| REST API | users-service | Авторизация, управление профилями |
| REST API | connections-service | Настройка связей между адаптерами |
| gRPC | tasks-service, users-service | Межсервисные синхронные вызовы |
| WebSocket | notification-service | Подключения клиентов для push-уведомлений |
### Сервисы → Хранилища данных
| Источник | Назначение | Данные |
|----------|------------|--------|
| tasks-service | PostgreSQL | Задачи, статусы, история изменений |
| users-service | PostgreSQL | Пользователи, профили, привязки к отелям |
| connections-service | PostgreSQL | Правила маршрутизации, реестр адаптеров |
| permissions-service | PostgreSQL | Роли, права доступа |
| audit-service | PostgreSQL | Журнал действий |
| scheduler-service | PostgreSQL | Расписания, шаблоны задач |
| events-service | PostgreSQL | Мероприятия, шаблоны декомпозиции |
| notification-service | Redis | Очереди уведомлений, статусы доставки |
| tasks-service | Redis | Кэш часто запрашиваемых задач |
| file-storage-service | S3 | Фотографии, документы, вложения |
### Сервисы ↔ Kafka
| Сервис | Публикует | Подписан на |
|--------|-----------|-------------|
| tasks-service | События задач (создание, изменение статуса) | Команды на создание задач от адаптеров |
| notification-service | — | События задач для отправки уведомлений |
| connections-service | Маршрутизированные события | Входящие события от адаптеров |
### Kafka ↔ Адаптеры
| Адаптер | В Kafka | Из Kafka |
|---------|---------|----------|
| PMS Adapter | Бронирования, статусы номеров, данные гостей | Запросы на изменение статуса номера |
| ERP Adapter | Данные о материалах, заявки | Списания, обновления остатков |
| Resonline Adapter | Статусы уборки | Задачи на уборку |
| Telegram Bot Adapter | Сообщения от гостей | Уведомления для отправки |
### Адаптеры → Внешние системы
| Адаптер | Внешняя система | Данные |
|---------|-----------------|--------|
| PMS Adapter | PMS | Синхронизация бронирований и статусов номеров |
| ERP Adapter | ERP | Синхронизация складских данных |
| Resonline Adapter | Resonline | Синхронизация задач уборки |
---
## Технологический стек
| Компонент | Технология |
|-----------|------------|
| Backend | Python 3.11+ (FastAPI) |
| Frontend | TypeScript, React |
| База данных | PostgreSQL 15+ |
| Кэш | Redis 7+ |
| Файловое хранилище | S3-compatible (AWS S3 / MinIO) |
| Message Broker | Apache Kafka |
| API | REST, gRPC, WebSocket |
| Аутентификация | JWT / OAuth2 |
| Контейнеризация | Docker |
| Оркестрация | Kubernetes |

148
BUSINESS_REQUIREMENTS.md Normal file
View File

@ -0,0 +1,148 @@
# HotelTask - Бизнес-требования
> **MVP** — Таск-менеджер для сотрудников отеля.
> Платформа, состоящая из ряда сервисов, которые решают повседневные задачи, эффективно распределяют ресурсы объекта размещения, а также предоставляют аналитику для улучшения.
---
## 1. Пользователи и роли
Система должна поддерживать следующие роли:
### Администратор системы
- Управление пользователями
- Настройка ролей и прав доступа
- Просмотр всех задач и отчетов
### Менеджер / Супервайзер
- Создание и распределение задач
- Контроль статусов выполнения
- Приоритизация задач
### Линейный сотрудник
- Просмотр назначенных задач
- Изменение статуса задачи
- Добавление комментариев и отметок о выполнении
### API-интерфейс
- Постановка задач для сотрудников
- Изменение статуса задачи
---
## 2. Управление пользователями
Система должна обеспечивать:
- Регистрацию и авторизацию пользователей
- Назначение ролей пользователям
- Редактирование профиля пользователя
- Деактивацию пользователей
---
## 3. Управление задачами
### Создание задачи
Система должна позволять создавать задачу с указанием:
| Поле | Описание |
|------|----------|
| **Название** | Краткое название задачи |
| **Описание** | Детальное описание задачи |
| **Тип задачи** | Уборка, ремонт, обслуживание гостей и т.д. |
| **Приоритет** | Низкий / Средний / Высокий / Срочный |
| **Срок выполнения** | Дедлайн задачи |
| **Исполнитель** | Ответственный сотрудник |
| **Супервайзер** | Ответственный менеджер |
### Операции с задачами
- Редактировать задачу
- Удалять задачу *(при наличии прав)*
- Назначать и переназначать исполнителей
- Включать трекинг времени задачи
### Статусы задач
| Статус | Описание |
|--------|----------|
| **Новая** | Задача создана, ожидает выполнения |
| **В работе** | Сотрудник приступил к выполнению |
| **Ожидает** | Задача приостановлена (ждет чего-то) |
| **Выполнена** | Задача завершена |
| **Пауза** | Временно отложена |
| **Отменена** | Задача отменена |
> Сотрудник должен иметь возможность менять статус своих задач.
---
## 4. Уведомления
Система должна отправлять **push-уведомления**:
- Сотруднику — о назначении новой задачи
- Менеджеру — о завершении задачи
- Напоминания — о приближении срока выполнения
---
## 5. Комментарии и вложения
Система должна позволять:
- Добавлять комментарии к задачам
- Прикреплять фотографии или файлы *(например, фото выполненной уборки)*
- Просматривать историю изменений задачи
---
## 6. Поиск и фильтрация
Система должна обеспечивать:
**Поиск:**
- По названию задачи
- По описанию задачи
**Фильтрация:**
- По статусу
- По приоритету
- По сотруднику
- По дате выполнения
- По типу задачи
---
## 7. Отчеты и аналитика
Система должна предоставлять:
- Отчет по выполненным задачам за период
- Статистику по сотрудникам
- Статистику по типам задач
- Процент просроченных задач
---
## 8. Доступ с разных устройств
Система должна:
- Корректно работать в веб-браузере
- Поддерживать мобильные устройства *(адаптивный интерфейс)*
- Иметь мобильное приложение
---
## 9. Интеграции
### PMS (Property Management System)
Система должна взаимодействовать с внешними PMS:
- Получать бронирования с информацией о гостях и номерах
- Получать статус номера
- Изменять статус номера
### Консьерж Resonline
Система должна взаимодействовать с сервисом Консьерж Resonline:
- Получать данные о заказе в номер
- Передавать статус заказа

View File

@ -1,635 +0,0 @@
[Назад](/README.md)
# C4 Architecture Diagrams - TaskManager
## Содержание
1. [Level 1: System Context](#level-1-system-context)
2. [Level 2: Container Diagram](#level-2-container-diagram)
3. [Level 3: Component Diagram - Tasks Service](#level-3-component-diagram---tasks-service)
4. [Level 3: Component Diagram - Adapter Layer](#level-3-component-diagram---adapter-layer)
---
## Level 1: System Context
Показывает TaskManager в контексте пользователей и внешних систем.
```mermaid
C4Context
title System Context diagram for TaskManager
Person(hotel_staff, "Hotel Staff", "Сотрудники отеля (горничные, техники, менеджеры)")
Person(admin, "System Admin", "Администратор системы")
Person(guest, "Hotel Guest", "Гость отеля")
System(taskmanager, "TaskManager", "Платформа для управления задачами между подразделениями отеля и интеграции с внешними системами")
System_Ext(pms, "PMS System", "Система управления отелем (Opera, Fidelio)")
System_Ext(erp, "ERP System", "Система управления ресурсами")
System_Ext(housekeeping, "Housekeeping App", "Приложение для управления уборкой")
System_Ext(email, "Email System", "SMTP сервер для отправки email")
System_Ext(sms, "SMS Gateway", "Сервис отправки SMS (Twilio)")
System_Ext(telegram, "Telegram", "Telegram Bot API")
System_Ext(s3, "AWS S3", "Хранилище файлов")
Rel(hotel_staff, taskmanager, "Создает и управляет задачами", "Web/Mobile")
Rel(admin, taskmanager, "Настраивает систему, связи, роли", "Web Admin")
Rel(guest, taskmanager, "Создает запросы на обслуживание", "Telegram Bot")
Rel(taskmanager, pms, "Получает/отправляет данные о бронированиях, номерах", "REST API/Webhook")
Rel(taskmanager, erp, "Интегрируется с учетом ресурсов", "REST API")
Rel(taskmanager, housekeeping, "Отправляет задачи на уборку", "REST API/Push")
Rel(taskmanager, email, "Отправляет уведомления", "SMTP")
Rel(taskmanager, sms, "Отправляет SMS уведомления", "REST API")
Rel(taskmanager, telegram, "Получает запросы гостей, отправляет уведомления", "Webhook/Bot API")
Rel(taskmanager, s3, "Хранит файлы и фото", "S3 API")
UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2")
```
---
## Level 2: Container Diagram
Показывает основные контейнеры (приложения и хранилища данных) внутри TaskManager.
```mermaid
C4Container
title Container diagram for TaskManager
Person(hotel_staff, "Hotel Staff", "Сотрудники отеля")
Person(admin, "System Admin", "Администратор системы")
Person(guest, "Hotel Guest", "Гость отеля")
System_Ext(pms, "PMS System", "Opera/Fidelio")
System_Ext(erp, "ERP System", "ERP")
System_Ext(housekeeping_ext, "Housekeeping App", "External App")
System_Ext(email_ext, "Email System", "SMTP")
System_Ext(sms_ext, "SMS Gateway", "Twilio")
System_Ext(telegram_ext, "Telegram", "Bot API")
System_Ext(s3_ext, "AWS S3", "File Storage")
Container_Boundary(taskmanager, "TaskManager System") {
Container(web_admin, "Web Admin", "React, TypeScript", "Веб-приложение для настройки системы и управления")
Container(mobile_app, "Mobile App", "React Native/Flutter", "Мобильное приложение для сотрудников")
Container(api_gateway, "API Gateway", "Kong/Envoy", "Единая точка входа, маршрутизация, аутентификация")
Container(tasks_service, "Tasks Service", "Python, FastAPI, gRPC", "Управление задачами")
Container(connections_service, "Connections Service", "Python, FastAPI, gRPC", "Управление связями между адаптерами")
Container(users_service, "Users Service", "Python, FastAPI, gRPC", "Управление пользователями")
Container(permissions_service, "Permissions Service", "Python, FastAPI, gRPC", "Управление ролями и правами (RBAC)")
Container(notification_service, "Notification Service", "Python, FastAPI", "Отправка уведомлений через разные каналы")
Container(file_storage_service, "File Storage Service", "Python, FastAPI", "Управление файлами")
Container(audit_service, "Audit Service", "Python, FastAPI", "Логирование всех действий")
Container(scheduler_service, "Scheduler Service", "Python, FastAPI", "Управление периодическими задачами")
Container(events_service, "Events Service", "Python, FastAPI, gRPC", "Управление мероприятиями")
Container(pms_adapter, "PMS Adapter", "Python", "Адаптер для PMS системы")
Container(erp_adapter, "ERP Adapter", "Python", "Адаптер для ERP системы")
Container(housekeeping_adapter, "Housekeeping Adapter", "Python", "Адаптер для приложения уборки")
Container(telegram_adapter, "Telegram Bot Adapter", "Python", "Адаптер для Telegram бота")
ContainerQueue(kafka, "Message Broker", "Apache Kafka", "Асинхронная коммуникация между сервисами")
ContainerDb(postgres, "Database", "PostgreSQL", "Хранение данных системы")
ContainerDb(redis, "Cache", "Redis", "Кэширование данных, сессии")
}
Rel(hotel_staff, web_admin, "Использует", "HTTPS")
Rel(hotel_staff, mobile_app, "Использует", "HTTPS")
Rel(admin, web_admin, "Настраивает систему", "HTTPS")
Rel(guest, telegram_adapter, "Отправляет запросы", "Telegram")
Rel(web_admin, api_gateway, "Делает API запросы", "REST/gRPC-Web")
Rel(mobile_app, api_gateway, "Делает API запросы", "REST/gRPC")
Rel(api_gateway, tasks_service, "Маршрутизирует запросы", "gRPC")
Rel(api_gateway, users_service, "Маршрутизирует запросы", "gRPC")
Rel(api_gateway, connections_service, "Маршрутизирует запросы", "gRPC")
Rel(api_gateway, events_service, "Маршрутизирует запросы", "gRPC")
Rel(api_gateway, file_storage_service, "Маршрутизирует запросы", "REST")
Rel(tasks_service, kafka, "Публикует события", "Kafka Protocol")
Rel(connections_service, kafka, "Публикует/читает события", "Kafka Protocol")
Rel(scheduler_service, kafka, "Публикует триггеры", "Kafka Protocol")
Rel(events_service, kafka, "Публикует события", "Kafka Protocol")
Rel(notification_service, kafka, "Читает события", "Kafka Protocol")
Rel(audit_service, kafka, "Читает все события", "Kafka Protocol")
Rel(tasks_service, permissions_service, "Проверяет права", "gRPC")
Rel(users_service, permissions_service, "Проверяет права", "gRPC")
Rel(pms_adapter, kafka, "Публикует сообщения", "Kafka Protocol")
Rel(erp_adapter, kafka, "Публикует сообщения", "Kafka Protocol")
Rel(housekeeping_adapter, kafka, "Публикует сообщения", "Kafka Protocol")
Rel(telegram_adapter, kafka, "Публикует сообщения", "Kafka Protocol")
Rel(pms_adapter, pms, "Получает/отправляет данные", "REST API/Webhook")
Rel(erp_adapter, erp, "Получает/отправляет данные", "REST API")
Rel(housekeeping_adapter, housekeeping_ext, "Отправляет задачи", "REST API/Push")
Rel(telegram_adapter, telegram_ext, "Взаимодействует с ботом", "Webhook/Bot API")
Rel(notification_service, email_ext, "Отправляет email", "SMTP")
Rel(notification_service, sms_ext, "Отправляет SMS", "REST API")
Rel(notification_service, telegram_ext, "Отправляет сообщения", "Bot API")
Rel(file_storage_service, s3_ext, "Хранит файлы", "S3 API")
Rel(tasks_service, postgres, "Читает/пишет данные", "SQL")
Rel(users_service, postgres, "Читает/пишет данные", "SQL")
Rel(connections_service, postgres, "Читает/пишет данные", "SQL")
Rel(permissions_service, postgres, "Читает/пишет данные", "SQL")
Rel(scheduler_service, postgres, "Читает/пишет данные", "SQL")
Rel(events_service, postgres, "Читает/пишет данные", "SQL")
Rel(audit_service, postgres, "Пишет логи", "SQL")
Rel(file_storage_service, postgres, "Пишет метаданные", "SQL")
Rel(tasks_service, redis, "Кэширует данные", "Redis Protocol")
Rel(users_service, redis, "Кэширует данные", "Redis Protocol")
Rel(permissions_service, redis, "Кэширует права", "Redis Protocol")
UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="2")
```
---
## Level 3: Component Diagram - Tasks Service
Показывает внутреннюю структуру Tasks Service.
```mermaid
C4Component
title Component diagram for Tasks Service
Container_Ext(api_gateway, "API Gateway", "Kong/Envoy", "Точка входа")
Container_Ext(kafka, "Kafka", "Apache Kafka", "Message Broker")
ContainerDb_Ext(postgres, "PostgreSQL", "Database", "Основная БД")
ContainerDb_Ext(redis, "Redis", "Cache", "Кэш")
Container_Ext(permissions_service, "Permissions Service", "Python, gRPC", "Проверка прав")
Container_Ext(file_storage_service, "File Storage Service", "Python", "Управление файлами")
Container_Boundary(tasks_service, "Tasks Service") {
Component(grpc_api, "gRPC API", "Python, gRPC", "Обработка gRPC запросов")
Component(rest_api, "REST API", "FastAPI", "Обработка REST запросов (fallback)")
Component(task_controller, "Task Controller", "Python", "Обработка бизнес-логики задач")
Component(status_controller, "Status Controller", "Python", "Управление статусами задач")
Component(dependency_controller, "Dependency Controller", "Python", "Управление зависимостями")
Component(history_controller, "History Controller", "Python", "Управление историей изменений")
Component(task_repository, "Task Repository", "SQLAlchemy", "Работа с БД задач")
Component(status_repository, "Status Repository", "SQLAlchemy", "Работа с БД статусов")
Component(dependency_repository, "Dependency Repository", "SQLAlchemy", "Работа с БД зависимостей")
Component(history_repository, "History Repository", "SQLAlchemy", "Работа с БД истории")
Component(kafka_producer, "Kafka Producer", "aiokafka", "Публикация событий в Kafka")
Component(kafka_consumer, "Kafka Consumer", "aiokafka", "Чтение событий из Kafka")
Component(cache_manager, "Cache Manager", "Redis Client", "Управление кэшем")
Component(permission_client, "Permission Client", "gRPC Client", "Проверка прав доступа")
Component(task_validator, "Task Validator", "Pydantic", "Валидация данных задач")
Component(event_mapper, "Event Mapper", "Python", "Маппинг событий")
}
Rel(api_gateway, grpc_api, "Отправляет gRPC запросы", "gRPC")
Rel(api_gateway, rest_api, "Отправляет REST запросы", "HTTP/JSON")
Rel(grpc_api, task_controller, "Вызывает методы")
Rel(rest_api, task_controller, "Вызывает методы")
Rel(task_controller, task_validator, "Валидирует данные")
Rel(task_controller, permission_client, "Проверяет права")
Rel(task_controller, task_repository, "Работает с задачами")
Rel(task_controller, status_controller, "Изменяет статусы")
Rel(task_controller, dependency_controller, "Управляет зависимостями")
Rel(task_controller, history_controller, "Записывает историю")
Rel(task_controller, cache_manager, "Кэширует данные")
Rel(task_controller, kafka_producer, "Публикует события")
Rel(status_controller, status_repository, "Работает со статусами")
Rel(dependency_controller, dependency_repository, "Работает с зависимостями")
Rel(history_controller, history_repository, "Работает с историей")
Rel(kafka_consumer, event_mapper, "Маппит события")
Rel(event_mapper, task_controller, "Обрабатывает события")
Rel(task_repository, postgres, "Выполняет SQL запросы", "SQL")
Rel(status_repository, postgres, "Выполняет SQL запросы", "SQL")
Rel(dependency_repository, postgres, "Выполняет SQL запросы", "SQL")
Rel(history_repository, postgres, "Выполняет SQL запросы", "SQL")
Rel(cache_manager, redis, "Кэширует/читает данные", "Redis Protocol")
Rel(permission_client, permissions_service, "Проверяет права", "gRPC")
Rel(kafka_producer, kafka, "Публикует события", "Kafka Protocol")
Rel(kafka_consumer, kafka, "Читает события", "Kafka Protocol")
UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")
```
---
## Level 3: Component Diagram - Adapter Layer
Показывает внутреннюю структуру адаптера (на примере PMS Adapter).
```mermaid
C4Component
title Component diagram for PMS Adapter (Example)
System_Ext(pms_system, "PMS System", "Opera/Fidelio", "Внешняя PMS система")
Container_Ext(kafka, "Kafka", "Apache Kafka", "Message Broker")
Container_Ext(postgres, "PostgreSQL", "Database", "Хранение состояния адаптера")
Container_Boundary(pms_adapter, "PMS Adapter") {
Component(webhook_handler, "Webhook Handler", "FastAPI", "Принимает webhook от PMS")
Component(api_poller, "API Poller", "Python, APScheduler", "Опрашивает PMS API по расписанию")
Component(rest_client, "REST Client", "httpx", "HTTP клиент для PMS API")
Component(inbound_transformer, "Inbound Transformer", "Python", "PMS формат → Стандартный формат")
Component(outbound_transformer, "Outbound Transformer", "Python", "Стандартный формат → PMS формат")
Component(message_validator, "Message Validator", "Pydantic", "Валидация сообщений")
Component(event_publisher, "Event Publisher", "aiokafka", "Публикация событий в Kafka")
Component(event_consumer, "Event Consumer", "aiokafka", "Чтение событий из Kafka")
Component(state_manager, "State Manager", "SQLAlchemy", "Управление состоянием синхронизации")
Component(error_handler, "Error Handler", "Python", "Обработка ошибок и retry")
Component(config_manager, "Config Manager", "Python", "Управление конфигурацией")
}
Rel(pms_system, webhook_handler, "Отправляет webhook", "HTTP/JSON")
Rel(api_poller, rest_client, "Запрашивает данные")
Rel(rest_client, pms_system, "Делает API запросы", "REST/SOAP")
Rel(webhook_handler, inbound_transformer, "Передает данные")
Rel(api_poller, inbound_transformer, "Передает данные")
Rel(inbound_transformer, message_validator, "Валидирует сообщение")
Rel(message_validator, event_publisher, "Публикует валидное событие")
Rel(event_consumer, kafka, "Читает исходящие события", "Kafka Protocol")
Rel(event_consumer, outbound_transformer, "Передает событие")
Rel(outbound_transformer, rest_client, "Отправляет в PMS")
Rel(event_publisher, kafka, "Публикует входящие события", "Kafka Protocol")
Rel(state_manager, postgres, "Сохраняет состояние", "SQL")
Rel(error_handler, state_manager, "Логирует ошибки")
Rel(webhook_handler, error_handler, "Обрабатывает ошибки")
Rel(api_poller, error_handler, "Обрабатывает ошибки")
Rel(rest_client, error_handler, "Обрабатывает ошибки")
Rel(config_manager, api_poller, "Предоставляет конфигурацию")
Rel(config_manager, rest_client, "Предоставляет конфигурацию")
UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")
```
---
## Deployment Diagram (Kubernetes)
Показывает развертывание в Kubernetes кластере.
```mermaid
C4Deployment
title Deployment Diagram for TaskManager (Kubernetes)
Deployment_Node(k8s_cluster, "Kubernetes Cluster", "GKE/EKS/AKS") {
Deployment_Node(ingress_ns, "Namespace: ingress") {
Container(ingress, "Ingress Controller", "NGINX/Traefik", "Входная точка для внешнего трафика")
}
Deployment_Node(gateway_ns, "Namespace: gateway") {
Container(api_gateway, "API Gateway", "Kong/Envoy", "3 replicas", "Маршрутизация и аутентификация")
}
Deployment_Node(services_ns, "Namespace: services") {
Container(tasks_svc, "Tasks Service", "Python, FastAPI", "5 replicas", "Управление задачами")
Container(users_svc, "Users Service", "Python, FastAPI", "3 replicas", "Управление пользователями")
Container(connections_svc, "Connections Service", "Python, FastAPI", "2 replicas", "Управление связями")
Container(permissions_svc, "Permissions Service", "Python, FastAPI", "3 replicas", "RBAC")
Container(notification_svc, "Notification Service", "Python, FastAPI", "3 replicas", "Уведомления")
Container(file_storage_svc, "File Storage Service", "Python, FastAPI", "2 replicas", "Файлы")
Container(audit_svc, "Audit Service", "Python, FastAPI", "2 replicas", "Аудит")
Container(scheduler_svc, "Scheduler Service", "Python, FastAPI", "1 replica", "Планировщик")
Container(events_svc, "Events Service", "Python, FastAPI", "2 replicas", "Мероприятия")
}
Deployment_Node(adapters_ns, "Namespace: adapters") {
Container(pms_adapter, "PMS Adapter", "Python", "2 replicas", "PMS интеграция")
Container(erp_adapter, "ERP Adapter", "Python", "1 replica", "ERP интеграция")
Container(housekeeping_adapter, "Housekeeping Adapter", "Python", "2 replicas", "Уборка")
Container(telegram_adapter, "Telegram Adapter", "Python", "2 replicas", "Telegram бот")
}
Deployment_Node(data_ns, "Namespace: data") {
ContainerDb(postgres_primary, "PostgreSQL Primary", "PostgreSQL 15", "1 pod", "Основная БД (master)")
ContainerDb(postgres_replica, "PostgreSQL Replica", "PostgreSQL 15", "2 pods", "Реплики для чтения")
ContainerDb(redis_cluster, "Redis Cluster", "Redis 7", "3 pods", "Кэш и сессии")
ContainerQueue(kafka_cluster, "Kafka Cluster", "Apache Kafka", "3 brokers", "Message broker")
}
Deployment_Node(monitoring_ns, "Namespace: monitoring") {
Container(prometheus, "Prometheus", "Prometheus", "Сбор метрик")
Container(grafana, "Grafana", "Grafana", "Визуализация метрик")
Container(jaeger, "Jaeger", "Jaeger", "Distributed tracing")
}
}
Deployment_Node(external_services, "External Services") {
Container(s3, "AWS S3", "Object Storage", "Хранилище файлов")
Container(smtp, "SMTP Server", "Email", "Отправка email")
Container(sms_gateway, "SMS Gateway", "Twilio", "Отправка SMS")
}
Rel(ingress, api_gateway, "Routes traffic", "HTTPS")
Rel(api_gateway, tasks_svc, "Calls", "gRPC")
Rel(api_gateway, users_svc, "Calls", "gRPC")
Rel(api_gateway, connections_svc, "Calls", "gRPC")
Rel(api_gateway, events_svc, "Calls", "gRPC")
Rel(api_gateway, file_storage_svc, "Calls", "REST")
Rel(tasks_svc, kafka_cluster, "Publishes/Consumes", "Kafka Protocol")
Rel(connections_svc, kafka_cluster, "Publishes/Consumes", "Kafka Protocol")
Rel(scheduler_svc, kafka_cluster, "Publishes", "Kafka Protocol")
Rel(notification_svc, kafka_cluster, "Consumes", "Kafka Protocol")
Rel(audit_svc, kafka_cluster, "Consumes", "Kafka Protocol")
Rel(pms_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol")
Rel(erp_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol")
Rel(housekeeping_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol")
Rel(telegram_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol")
Rel(tasks_svc, postgres_primary, "Writes", "SQL")
Rel(tasks_svc, postgres_replica, "Reads", "SQL")
Rel(users_svc, postgres_primary, "Writes", "SQL")
Rel(users_svc, postgres_replica, "Reads", "SQL")
Rel(audit_svc, postgres_primary, "Writes logs", "SQL")
Rel(tasks_svc, redis_cluster, "Cache", "Redis Protocol")
Rel(users_svc, redis_cluster, "Cache", "Redis Protocol")
Rel(permissions_svc, redis_cluster, "Cache permissions", "Redis Protocol")
Rel(file_storage_svc, s3, "Stores files", "S3 API")
Rel(notification_svc, smtp, "Sends emails", "SMTP")
Rel(notification_svc, sms_gateway, "Sends SMS", "REST API")
Rel(prometheus, tasks_svc, "Scrapes metrics", "HTTP")
Rel(prometheus, users_svc, "Scrapes metrics", "HTTP")
Rel(grafana, prometheus, "Queries metrics", "HTTP")
UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")
```
---
## Sequence Diagram: Creating Task from External System
Показывает последовательность действий при создании задачи из внешней системы.
```mermaid
sequenceDiagram
participant PMS as PMS System (Opera)
participant Adapter as PMS Adapter
participant Kafka as Apache Kafka
participant Connections as Connections Service
participant Tasks as Tasks Service
participant Permissions as Permissions Service
participant Audit as Audit Service
participant Notification as Notification Service
participant DB as PostgreSQL
participant Housekeeping as Housekeeping Adapter
participant HK_App as Housekeeping App
Note over PMS,HK_App: Сценарий: Гость выехал из номера, нужна уборка
PMS->>Adapter: Webhook: Guest Checkout (Room 305)
Adapter->>Adapter: Validate webhook signature
Adapter->>Adapter: Transform to standard format
Adapter->>Kafka: Publish: adapters.pms.checkout
Note over Kafka: Topic: adapters.pms.checkout
Kafka->>Connections: Consume: adapters.pms.checkout
Connections->>DB: Get routing rules for PMS → TaskManager
DB-->>Connections: Routing rules
Connections->>Connections: Apply rules: checkout → create cleaning task
Connections->>Kafka: Publish: connections.route_task
Note over Kafka: Topic: connections.route_task
Kafka->>Tasks: Consume: connections.route_task
Tasks->>Permissions: CheckPermission(source_adapter, create_task)
Permissions->>DB: Query permissions
DB-->>Permissions: Permission granted
Permissions-->>Tasks: Allowed
Tasks->>Tasks: Create task object
Tasks->>DB: INSERT INTO tasks (...)
DB-->>Tasks: Task created (id: uuid)
Tasks->>Kafka: Publish: tasks.created
Note over Kafka: Topic: tasks.created
par Parallel Processing
Kafka->>Audit: Consume: tasks.created
Audit->>DB: INSERT INTO audit_log
Kafka->>Notification: Consume: tasks.created
Notification->>DB: Get user notification preferences
DB-->>Notification: User prefers Telegram
Notification->>Notification: Send Telegram notification
Kafka->>Connections: Consume: tasks.created
Connections->>DB: Get routing rules for TaskManager → Housekeeping
DB-->>Connections: Route to Housekeeping Adapter
Connections->>Kafka: Publish: connections.route_task
end
Note over Kafka: Topic: connections.route_task
Kafka->>Housekeeping: Consume: connections.route_task
Housekeeping->>Housekeeping: Transform to Housekeeping App format
Housekeeping->>HK_App: POST /api/tasks (Room 305 cleaning)
HK_App-->>Housekeeping: Task assigned (assignee: Maria)
Housekeeping->>Kafka: Publish: adapters.housekeeping.task_assigned
Kafka->>Tasks: Consume: adapters.housekeeping.task_assigned
Tasks->>DB: UPDATE tasks SET assignee_id = ...
Tasks->>Kafka: Publish: tasks.assigned
Note over PMS,HK_App: Task created and assigned to housekeeping staff
```
---
## Sequence Diagram: Creating Event with Auto-Generated Tasks
Показывает создание мероприятия и автоматическую генерацию задач.
```mermaid
sequenceDiagram
participant User as Sales Manager
participant Web as Web Admin
participant Gateway as API Gateway
participant Events as Events Service
participant Tasks as Tasks Service
participant Kafka as Apache Kafka
participant DB as PostgreSQL
participant Notification as Notification Service
Note over User,Notification: Сценарий: Создание конференции на 50 человек
User->>Web: Create Event: "Marketing Conference"
Web->>Gateway: POST /api/v1/events (event details)
Gateway->>Gateway: Authenticate & Authorize
Gateway->>Events: CreateEvent(gRPC)
Events->>DB: INSERT INTO events
DB-->>Events: Event created (id: uuid)
Events->>Events: Generate tasks from event template
Note over Events: Tasks: Setup chairs, Prepare projector,<br/>Organize coffee break, etc.
loop For each generated task
Events->>Tasks: CreateTask(gRPC)
Tasks->>DB: INSERT INTO tasks
DB-->>Tasks: Task created
Tasks->>Kafka: Publish: tasks.created
Tasks-->>Events: Task ID
end
Events->>DB: INSERT INTO event_tasks (event_id, task_id)
Events->>Kafka: Publish: events.created
Events->>Kafka: Publish: events.tasks_generated
Events-->>Gateway: Event + generated tasks
Gateway-->>Web: 201 Created (event + tasks)
Web-->>User: Show created event with tasks
par Notify departments
Kafka->>Notification: Consume: tasks.created
loop For each task
Notification->>Notification: Get assignee notification prefs
Notification->>Notification: Send notification (Telegram/Email)
end
end
Note over User,Notification: All tasks created and departments notified
```
---
## Data Flow Diagram
Показывает потоки данных в системе.
```mermaid
flowchart TB
subgraph External["External Systems"]
PMS["PMS System"]
ERP["ERP System"]
Guest["Guest (Telegram)"]
end
subgraph Adapters["Adapter Layer"]
PMS_A["PMS Adapter"]
ERP_A["ERP Adapter"]
TG_A["Telegram Adapter"]
end
subgraph Kafka["Apache Kafka"]
T1["adapters.*"]
T2["tasks.*"]
T3["connections.*"]
T4["notifications.*"]
T5["audit.*"]
end
subgraph Core["Core Services"]
Connections["Connections Service<br/>(Routing Rules)"]
Tasks["Tasks Service<br/>(Task Management)"]
Users["Users Service"]
Permissions["Permissions Service"]
end
subgraph Support["Support Services"]
Notification["Notification Service"]
Audit["Audit Service"]
Scheduler["Scheduler Service"]
end
subgraph Data["Data Layer"]
DB["PostgreSQL"]
Cache["Redis"]
end
%% External to Adapters
PMS -->|"Webhook/API"| PMS_A
ERP -->|"API"| ERP_A
Guest -->|"Telegram Bot"| TG_A
%% Adapters to Kafka
PMS_A -->|"Publish events"| T1
ERP_A -->|"Publish events"| T1
TG_A -->|"Publish events"| T1
%% Kafka to Connections
T1 -->|"Consume"| Connections
%% Connections routing
Connections -->|"Route messages"| T3
T3 -->|"Consume"| Tasks
%% Tasks operations
Tasks -->|"Publish events"| T2
Tasks -->|"Query permissions"| Permissions
Tasks -->|"Write/Read"| DB
Tasks -->|"Cache"| Cache
%% Support services
T2 -->|"Consume"| Notification
T2 -->|"Consume"| Audit
Scheduler -->|"Trigger tasks"| T2
Notification -->|"Send notifications"| External
Audit -->|"Write logs"| DB
%% Users and Permissions
Users -->|"Write/Read"| DB
Permissions -->|"Write/Read"| DB
Permissions -->|"Cache permissions"| Cache
style Kafka fill:#ff9900
style Core fill:#00cc66
style Support fill:#3399ff
style Data fill:#cc66ff
```
---
## Notes
### Как использовать эти диаграммы:
1. **System Context (Level 1)**: Показывает TaskManager как черный ящик в контексте пользователей и внешних систем. Используйте для презентации бизнес-заказчикам.
2. **Container Diagram (Level 2)**: Показывает основные приложения и хранилища данных. Используйте для обсуждения с архитекторами и DevOps командой.
3. **Component Diagrams (Level 3)**: Показывают внутреннюю структуру отдельных сервисов. Используйте для разработчиков при проектировании конкретного сервиса.
4. **Deployment Diagram**: Показывает как система развернута в Kubernetes. Используйте для DevOps и планирования инфраструктуры.
5. **Sequence Diagrams**: Показывают взаимодействие компонентов во времени для конкретных сценариев. Используйте для понимания бизнес-процессов.

36
CLAUDE.md Normal file
View File

@ -0,0 +1,36 @@
# HotelTask - Точка входа для агента
## Промт
Ты работаешь над проектом **HotelTask** — единой платформой управления задачами для отелей.
**Проблема:** В отелях разные подразделения (ресепшен, горничные, техслужба, ресторан) используют разрозненные приложения и мессенджеры. Это ненадежно, информация теряется, ресепшен перегружен.
**Решение:** Система-ядро (Task Manager) со стандартизированным контрактом задач. Внешние системы (PMS, ERP, Housekeeper) подключаются через адаптеры. Коммуникация через Apache Kafka. В админке настраиваются связи между системами.
**Стек:** Python (FastAPI), PostgreSQL, Redis, Kafka, React (админка).
---
## Файлы проекта
| Файл | Зона ответственности |
|------|---------------------|
| [CLAUDE.md](CLAUDE.md) | Точка входа агента, промт |
| [CONTEXT.md](CONTEXT.md) | Память между сессиями, текущий статус |
| [ROADMAP.md](ROADMAP.md) | Этапы разработки, границы MVP |
| [BUSINESS_REQUIREMENTS.md](BUSINESS_REQUIREMENTS.md) | Бизнес-требования, роли, функционал |
| [REQUIREMENTS.md](REQUIREMENTS.md) | Структурированные FR/NFR |
| [ARCHITECTURE.md](ARCHITECTURE.md) | Архитектура, сервисы, потоки данных |
---
## Инструкции
**При работе над аналитикой/разработкой** изучи:
1. [CONTEXT.md](CONTEXT.md) — восстанови контекст
2. [ROADMAP.md](ROADMAP.md) — пойми на каком этапе проект
3. [BUSINESS_REQUIREMENTS.md](BUSINESS_REQUIREMENTS.md) + [REQUIREMENTS.md](REQUIREMENTS.md) — требования
4. [ARCHITECTURE.md](ARCHITECTURE.md) — архитектура
После изучения скажи: "Изучил, жду инструкций"

43
CONTEXT.md Normal file
View File

@ -0,0 +1,43 @@
# HotelTask - Контекст проекта
## Текущий статус
**Этап:** Проектирование (в процессе)
Подробности по этапам и границам MVP см. в [ROADMAP.md](ROADMAP.md)
---
## Что сделано
- [x] Собраны бизнес-требования
- [x] Структурированы FR/NFR
- [x] Определены роли пользователей
- [x] Определен scope MVP
- [x] Высокоуровневая архитектура (9 микросервисов)
## Что в работе
- [ ] Схема БД (ERD)
- [ ] API контракты (OpenAPI)
- [ ] Модель данных Task
---
## Ключевые технические решения
| Решение | Выбор | Обоснование |
|---------|-------|-------------|
| Backend | Python (FastAPI) | Быстрая разработка, async support |
| DB | PostgreSQL | Надежность, JSON support |
| Cache | Redis | Скорость, pub/sub |
| Message Broker | Apache Kafka | Надежная доставка, масштабируемость |
| Frontend | React + TypeScript | Популярность, типизация |
| Auth | JWT | Stateless, масштабируемость |
| Storage | S3-compatible | Стандарт для файлов |
---
## Заметки для следующей сессии
*(Здесь агент может оставлять заметки для себя)*

464
NOTIONS.md Normal file
View File

@ -0,0 +1,464 @@
Проработка:
- Прототипы интерфейсов
- Контракты API (swagger)
- Пользовательские сценарии (use cases)
- Sequence диаграммы
- Сервис лицензий, или какой-то способ модульной поставки сервисов? Может с помощью helm чартов?
- UI спецификация? Обработка ошибок на UI
- Система логирования бека, фронта, дебагинг, мониторинг, алертнинг
- Схема деплоя. Ставимся в кластер? А что если нет кластера? Как упаковывать коробку?
- Аналитика фичей по отдельности, разделить MVP на небольшие кусочки, поставлять фичи этапами
- Нагрузочное тестирование
Вопросы от Леши:
- Оценка временных затрат
- Оценка денежных затрат (человеческий ресурс от минимум до комфорт, дать диапазон с ролями, стоимостью по сотрудникам)
---
## Архитектурные решения
### ADR-001: Стратегия работы с данными PMS
**Контекст:**
Система интегрируется с PMS (и другими системами) для получения данных о гостях, номерах и бронированиях. Нужно определить, как работать с этими данными.
#### Варианты
**Вариант 1: Pass-through (запрос по требованию)**
Каждый раз запрашиваем данные из PMS когда нужно.
| Плюсы | Минусы |
|-------|--------|
| Данные всегда актуальные | Зависимость от доступности PMS |
| Не нужно хранить и синхронизировать | Задержки на каждый запрос |
| Нет проблем с консистентностью | Нет истории (кто был в номере вчера?) |
**Вариант 2: Полная репликация**
Храним копию данных, синхронизируем по событиям или периодически.
| Плюсы | Минусы |
|-------|--------|
| Работаем автономно при падении PMS | Сложность синхронизации |
| Быстрый доступ к данным | Возможны расхождения данных |
| Есть история | Дублирование хранилища |
**Вариант 3: Гибридный**
Храним только то, что нужно для задач + кэшируем справочники.
*Что храним у себя:*
- Ссылку на номер/гостя (ID из PMS)
- Снапшот данных на момент создания задачи (имя гостя, номер комнаты)
- Историю задач с контекстом
*Что запрашиваем из PMS:*
- Актуальный статус номера (грязный/чистый/занят)
- Текущие бронирования
- Справочник номеров (с кэшированием)
#### Вопросы для принятия решения
1. **Автономность** — должна ли система работать при недоступности PMS?
2. **История** — нужно ли знать "для какого гостя делали задачу месяц назад"?
3. **Частота изменений** — как часто меняются данные в PMS (заезды/выезды)?
4. **API PMS** — поддерживает ли PMS push-уведомления (webhooks) или только pull?
5. **Объём** — сколько номеров/гостей в типичном отеле?
6. **Сценарий** — горничной нужен актуальный статус номера или достаточно того, что было при создании задачи?
#### Решение
*Ожидает обсуждения*
---
### ADR-002: Мультитенантность
**Статус:** Открыт
**Контекст:**
В NFR-SCALE-003 указано, что система должна поддерживать несколько отелей на одной инсталляции. Нужно определить архитектуру изоляции данных.
#### Варианты
**Вариант 1: Shared Database + tenant_id**
Все отели в одной БД, данные разделены по `hotel_id`.
| Плюсы | Минусы |
|-------|--------|
| Простота развёртывания | Риск утечки данных между отелями |
| Легко делать кросс-отельную аналитику | Сложнее масштабировать отдельный отель |
| Экономия ресурсов | Один отель может "положить" всех |
**Вариант 2: Database per Tenant**
Отдельная БД на каждый отель.
| Плюсы | Минусы |
|-------|--------|
| Полная изоляция данных | Сложность управления миграциями |
| Можно масштабировать независимо | Больше ресурсов |
| Проще соответствовать требованиям безопасности | Сложнее кросс-отельная аналитика |
**Вариант 3: Schema per Tenant**
Одна БД, но отдельная schema на отель.
| Плюсы | Минусы |
|-------|--------|
| Баланс изоляции и простоты | Ограничения PostgreSQL на количество schema |
| Проще бэкапы отдельных отелей | Всё ещё shared resources |
#### Вопросы для принятия решения
1. **Количество отелей** — сколько отелей планируется на одной инсталляции?
2. **Требования безопасности** — есть ли требования к физической изоляции данных?
3. **Кросс-отельная аналитика** — нужны ли отчёты по всем отелям сразу?
4. **Модель продаж** — SaaS (много мелких) или Enterprise (мало крупных)?
#### Решение
*Ожидает обсуждения*
---
### ADR-003: API Gateway — готовое решение или самописный
**Статус:** Открыт
**Контекст:**
В архитектуре указан API Gateway Layer, но не определено — это готовое решение или часть кода. От этого зависит где происходит аутентификация, rate limiting, routing.
#### Варианты
**Вариант 1: Готовое решение (Kong / Traefik / AWS API Gateway)**
| Плюсы | Минусы |
|-------|--------|
| Готовый rate limiting, auth, metrics | Дополнительная зависимость |
| Battle-tested | Может быть избыточно для MVP |
| Меньше кода | Vendor lock-in (для облачных) |
**Вариант 2: Самописный на FastAPI**
| Плюсы | Минусы |
|-------|--------|
| Полный контроль | Нужно писать и поддерживать |
| Нет лишних зависимостей | Rate limiting, auth — своими руками |
| Гибкость | Риск ошибок безопасности |
**Вариант 3: Nginx + Lua / OpenResty**
| Плюсы | Минусы |
|-------|--------|
| Высокая производительность | Lua — дополнительный язык |
| Проверенное решение | Сложнее отлаживать |
#### Вопросы для принятия решения
1. **Где аутентификация?** — Gateway проверяет JWT или каждый сервис сам?
2. **Rate limiting** — нужен ли в MVP?
3. **Инфраструктура** — есть ли уже Kubernetes Ingress / Load Balancer?
4. **Команда** — есть ли опыт с Kong/Traefik?
#### Решение
*Ожидает обсуждения*
---
### ADR-004: Разделение users-service и permissions-service
**Статус:** Открыт
**Контекст:**
В архитектуре есть два отдельных сервиса: `users-service` (пользователи, профили) и `permissions-service` (RBAC, роли). Возможно это избыточно для MVP.
#### Варианты
**Вариант 1: Два отдельных сервиса (текущий)**
| Плюсы | Минусы |
|-------|--------|
| Чёткое разделение ответственности | Overhead на межсервисное взаимодействие |
| Можно развивать независимо | Сложнее для MVP |
| Permissions можно переиспользовать | Больше точек отказа |
**Вариант 2: Объединить в один auth-service**
| Плюсы | Минусы |
|-------|--------|
| Проще для MVP | Может разрастись |
| Меньше latency | Сложнее разделить потом |
| Один источник правды о пользователе | — |
#### Вопросы для принятия решения
1. **Сложность RBAC** — насколько сложная система прав планируется?
2. **Переиспользование** — будут ли другие системы использовать permissions-service?
3. **MVP scope** — что минимально необходимо для запуска?
#### Решение
*Ожидает обсуждения*
---
### ADR-005: WebSocket масштабирование
**Статус:** Открыт
**Контекст:**
`notification-service` использует WebSocket для real-time уведомлений. При нескольких инстансах сервиса нужен механизм доставки сообщения на нужный инстанс.
#### Варианты
**Вариант 1: Sticky Sessions**
Клиент всегда идёт на один и тот же инстанс.
| Плюсы | Минусы |
|-------|--------|
| Просто реализовать | Неравномерная нагрузка |
| Не нужен pub/sub | При падении инстанса — переподключение |
**Вариант 2: Redis Pub/Sub**
Все инстансы подписаны на Redis, сообщение доставляется всем.
| Плюсы | Минусы |
|-------|--------|
| Равномерная нагрузка | Дополнительная зависимость |
| Отказоустойчивость | Больше трафика |
**Вариант 3: Kafka → все инстансы**
Каждый инстанс читает из Kafka и отправляет своим клиентам.
| Плюсы | Минусы |
|-------|--------|
| Уже есть Kafka | Дублирование сообщений |
| Персистентность | Нужна фильтрация на клиенте |
#### Вопросы для принятия решения
1. **Количество инстансов** — сколько реально будет в production?
2. **Частота сообщений** — сколько уведомлений в секунду?
3. **Критичность доставки** — что если уведомление не дошло?
#### Решение
*Ожидает обсуждения*
---
### ADR-006: Scheduler — гарантия единственного выполнения
**Статус:** Открыт
**Контекст:**
`scheduler-service` создаёт задачи по расписанию. При нескольких инстансах может создать дубликаты.
#### Варианты
**Вариант 1: Distributed Lock (Redis/PostgreSQL)**
Только один инстанс выполняет задачу.
| Плюсы | Минусы |
|-------|--------|
| Гарантия единственности | Сложность реализации |
| Отказоустойчивость | Lock может "застрять" |
**Вариант 2: Leader Election**
Один инстанс — лидер, остальные в standby.
| Плюсы | Минусы |
|-------|--------|
| Простая логика в коде | Нужен механизм выбора лидера |
| — | Single point of failure |
**Вариант 3: Один инстанс scheduler'а**
Просто не масштабируем этот сервис.
| Плюсы | Минусы |
|-------|--------|
| Максимально просто | Нет отказоустойчивости |
| — | Для MVP может быть достаточно |
#### Вопросы для принятия решения
1. **Критичность** — что если задача не создалась вовремя?
2. **Частота** — как часто запускаются scheduled задачи?
3. **MVP** — нужен ли scheduler в MVP вообще? (В ROADMAP он в этапе 3)
#### Решение
*Ожидает обсуждения*
---
### ADR-007: Консистентность данных между сервисами
**Статус:** Открыт
**Контекст:**
При создании задачи нужно: сохранить в БД, отправить уведомление, записать в аудит. Что если одна из операций упадёт?
#### Проблемы
1. **Частичное выполнение** — задача создана, но уведомление не отправлено
2. **Дубликаты** — retry создал задачу дважды
3. **Порядок событий** — аудит записал раньше, чем задача реально создалась
#### Варианты
**Вариант 1: Saga Pattern**
Цепочка компенсирующих транзакций.
**Вариант 2: Outbox Pattern**
Сначала пишем в локальную таблицу outbox, потом отдельный процесс отправляет в Kafka.
**Вариант 3: Event Sourcing**
Храним события, состояние вычисляется.
#### Вопросы для принятия решения
1. **Критичность** — насколько критична потеря уведомления?
2. **Идемпотентность** — как обрабатывать повторные запросы?
3. **Сложность** — готовы ли усложнять архитектуру ради гарантий?
#### Решение
*Ожидает обсуждения*
---
### ADR-008: Жизненный цикл файлов
**Статус:** Открыт
**Контекст:**
К задачам прикрепляются файлы (фото уборки). Нужно определить что с ними происходит при удалении/архивации задачи.
#### Вопросы для принятия решения
1. **Удаление задачи** — удалять файлы или оставлять?
2. **Архивация** — нужно ли переносить старые файлы в "холодное" хранилище?
3. **Дедупликация** — один файл может быть привязан к нескольким задачам?
4. **Квоты** — есть ли лимит на размер файлов / количество на задачу?
5. **Доступ** — как генерировать URL? Presigned URLs или через API?
#### Решение
*Ожидает обсуждения*
---
### ADR-009: gRPC vs REST — когда что использовать
**Статус:** Открыт
**Контекст:**
В архитектуре указаны оба протокола. Нужно определить границы применения.
#### Вопросы для принятия решения
1. **Внешний API** — только REST или gRPC тоже?
2. **Межсервисное** — всё через gRPC или только критичные вызовы?
3. **Команда** — есть ли опыт с gRPC?
4. **Клиенты** — мобильные приложения будут использовать gRPC?
#### Потенциальное решение
- **REST** — внешний API (клиенты, адаптеры)
- **gRPC** — межсервисное взаимодействие (tasks ↔ permissions)
#### Решение
*Ожидает обсуждения*
---
### ADR-010: Отсутствующие компоненты в архитектуре
**Статус:** Открыт
**Контекст:**
При анализе выявлены компоненты, которые не описаны, но могут быть необходимы.
#### Что не описано
| Компонент | Зачем нужен | Критичность для MVP |
|-----------|-------------|---------------------|
| **Health checks** | Kubernetes liveness/readiness probes | Высокая |
| **Circuit Breaker** | Защита от каскадных отказов | Средняя |
| **Rate Limiting** | Защита от DDoS/abuse | Средняя |
| **Service Discovery** | Как сервисы находят друг друга | Высокая (если не K8s) |
| **Distributed Tracing** | Отладка цепочек вызовов | Низкая для MVP |
| **Config Service** | Централизованная конфигурация | Низкая |
| **Secret Management** | Хранение credentials | Высокая |
#### Вопросы для принятия решения
1. **Kubernetes** — будет ли K8s? Если да, многое решается из коробки
2. **MVP scope** — что из этого критично для первого релиза?
#### Решение
*Ожидает обсуждения*
---
### ADR-011: Безопасность Kafka
**Статус:** Открыт
**Контекст:**
Через Kafka ходят данные о задачах, гостях, бронированиях. Нужно определить уровень защиты.
#### Вопросы для принятия решения
1. **Шифрование** — нужен ли TLS между сервисами и Kafka?
2. **Аутентификация** — SASL или открытый доступ внутри кластера?
3. **Авторизация** — ACL на топики (кто может читать/писать)?
4. **Сетевая изоляция** — Kafka только во внутренней сети?
#### Решение
*Ожидает обсуждения*
---
### ADR-012: Версионирование API
**Статус:** Открыт
**Контекст:**
API будет развиваться. Нужна стратегия обратной совместимости.
#### Варианты
| Подход | Пример | Плюсы | Минусы |
|--------|--------|-------|--------|
| URL versioning | `/api/v1/tasks` | Явно, просто | Дублирование кода |
| Header versioning | `Accept: application/vnd.api.v1+json` | Чистые URL | Сложнее тестировать |
| Query parameter | `/api/tasks?version=1` | Просто | Некрасиво |
#### Вопросы для принятия решения
1. **Клиенты** — кто будет потреблять API? Только свои или внешние?
2. **Частота изменений** — как часто планируются breaking changes?
#### Решение
*Ожидает обсуждения*

View File

@ -1,2 +0,0 @@
- [Вопросы](/Вопросы.md)
- [Анализ требований](/Анализ%20требований.md)

252
REQUIREMENTS.md Normal file
View File

@ -0,0 +1,252 @@
# Требования к приложению HotelTask
## Содержание
1. [Функциональные требования](#1-функциональные-требования-fr)
2. [Нефункциональные требования](#2-нефункциональные-требования-nfr)
---
## 1. Функциональные требования (FR)
### 1.1 Управление пользователями (FR-USER)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-USER-001 | Система должна обеспечивать регистрацию пользователей | High | Да |
| FR-USER-002 | Система должна обеспечивать авторизацию пользователей | High | Да |
| FR-USER-003 | Система должна поддерживать назначение ролей пользователям | High | Да |
| FR-USER-004 | Система должна позволять редактировать профиль пользователя | Medium | Да |
| FR-USER-005 | Система должна позволять деактивировать пользователей | Medium | Да |
| FR-USER-006 | Система должна поддерживать привязку пользователя к подразделению | High | Да |
### 1.2 Роли и права доступа (FR-ROLE)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-ROLE-001 | Система должна поддерживать роль "Администратор системы" с полным доступом | High | Да |
| FR-ROLE-002 | Система должна поддерживать роль "Менеджер/Супервайзер" для управления задачами | High | Да |
| FR-ROLE-003 | Система должна поддерживать роль "Линейный сотрудник" для выполнения задач | High | Да |
| FR-ROLE-004 | Система должна поддерживать API-доступ для внешних систем | High | Да |
| FR-ROLE-005 | Система должна позволять настраивать гранулярные права доступа | Medium | Нет |
### 1.3 Управление задачами (FR-TASK)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-TASK-001 | Система должна позволять создавать задачу с названием и описанием | High | Да |
| FR-TASK-002 | Система должна поддерживать типы задач (уборка, ремонт, обслуживание и т.д.) | High | Да |
| FR-TASK-003 | Система должна поддерживать приоритеты задач (низкий, средний, высокий, срочный) | High | Да |
| FR-TASK-004 | Система должна позволять указывать срок выполнения задачи | High | Да |
| FR-TASK-005 | Система должна позволять назначать ответственного сотрудника | High | Да |
| FR-TASK-006 | Система должна позволять назначать супервайзера задачи | Medium | Да |
| FR-TASK-007 | Система должна позволять редактировать задачу | High | Да |
| FR-TASK-008 | Система должна позволять удалять задачу (при наличии прав) | Medium | Да |
| FR-TASK-009 | Система должна позволять переназначать исполнителей | High | Да |
| FR-TASK-010 | Система должна поддерживать трекинг времени выполнения задачи | Medium | Нет |
| FR-TASK-011 | Система должна позволять создавать встречную задачу | Medium | Нет |
### 1.4 Статусы задач (FR-STATUS)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-STATUS-001 | Система должна поддерживать статус "Новая" | High | Да |
| FR-STATUS-002 | Система должна поддерживать статус "В работе" | High | Да |
| FR-STATUS-003 | Система должна поддерживать статус "Ожидает" | High | Да |
| FR-STATUS-004 | Система должна поддерживать статус "Выполнена" | High | Да |
| FR-STATUS-005 | Система должна поддерживать статус "Пауза" | Medium | Да |
| FR-STATUS-006 | Система должна поддерживать статус "Отменена" | Medium | Да |
| FR-STATUS-007 | Сотрудник должен иметь возможность менять статус своих задач | High | Да |
### 1.5 Уведомления (FR-NOTIFY)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-NOTIFY-001 | Система должна уведомлять сотрудника о назначении новой задачи (push) | High | Да |
| FR-NOTIFY-002 | Система должна уведомлять менеджера о завершении задачи | High | Да |
| FR-NOTIFY-003 | Система должна отправлять напоминания о приближении срока выполнения | Medium | Да |
| FR-NOTIFY-004 | Система должна поддерживать настройку типов уведомлений для пользователя | Low | Нет |
### 1.6 Комментарии и вложения (FR-ATTACH)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-ATTACH-001 | Система должна позволять добавлять комментарии к задачам | High | Да |
| FR-ATTACH-002 | Система должна позволять прикреплять фотографии к задачам | High | Да |
| FR-ATTACH-003 | Система должна позволять прикреплять файлы к задачам | Medium | Да |
| FR-ATTACH-004 | Система должна сохранять историю изменений задачи | Medium | Да |
### 1.7 Поиск и фильтрация (FR-SEARCH)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-SEARCH-001 | Система должна обеспечивать поиск задач по названию | High | Да |
| FR-SEARCH-002 | Система должна обеспечивать поиск задач по описанию | Medium | Да |
| FR-SEARCH-003 | Система должна обеспечивать фильтрацию по статусу | High | Да |
| FR-SEARCH-004 | Система должна обеспечивать фильтрацию по приоритету | High | Да |
| FR-SEARCH-005 | Система должна обеспечивать фильтрацию по сотруднику | High | Да |
| FR-SEARCH-006 | Система должна обеспечивать фильтрацию по дате выполнения | Medium | Да |
| FR-SEARCH-007 | Система должна обеспечивать фильтрацию по типу задачи | Medium | Да |
### 1.8 Отчеты и аналитика (FR-REPORT)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-REPORT-001 | Система должна предоставлять отчет по выполненным задачам за период | Medium | Нет |
| FR-REPORT-002 | Система должна предоставлять статистику по сотрудникам | Medium | Нет |
| FR-REPORT-003 | Система должна предоставлять статистику по типам задач | Medium | Нет |
| FR-REPORT-004 | Система должна предоставлять процент просроченных задач | Medium | Нет |
### 1.9 Интеграции (FR-INT)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-INT-001 | Система должна получать бронирования из PMS с информацией о гостях и номерах | High | Да |
| FR-INT-002 | Система должна получать статус номера из PMS | High | Да |
| FR-INT-003 | Система должна изменять статус номера в PMS | High | Да |
| FR-INT-004 | Система должна взаимодействовать с Консьерж Resonline | Medium | Нет |
| FR-INT-005 | Система должна поддерживать подключение произвольных адаптеров | High | Да |
| FR-INT-006 | Адаптер должен регистрировать поддерживаемые типы событий | High | Да |
### 1.10 Управление связями между системами (FR-CONN)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-CONN-001 | Система должна позволять настраивать маршрутизацию событий между адаптерами | High | Да |
| FR-CONN-002 | Система должна предоставлять интерфейс для настройки связей (событие из А -> Б, В, Г) | High | Да |
| FR-CONN-003 | Система должна хранить реестр подключенных адаптеров и их возможностей | High | Да |
### 1.11 Планировщик задач (FR-SCHED)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-SCHED-001 | Система должна поддерживать создание регулярных/периодических задач | Medium | Нет |
| FR-SCHED-002 | Система должна автоматически создавать задачи по расписанию | Medium | Нет |
### 1.12 Модуль мероприятий (FR-EVENT)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-EVENT-001 | Система должна позволять описывать мероприятие | Low | Нет |
| FR-EVENT-002 | Система должна автоматически декомпозировать мероприятие на задачи | Low | Нет |
| FR-EVENT-003 | Система должна поддерживать массовое создание связанных задач | Low | Нет |
| FR-EVENT-004 | Система должна формировать документ "функшн" на основе данных мероприятия | Low | Нет |
### 1.13 Специализированный интерфейс горничных (FR-HOUSE)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-HOUSE-001 | Система должна предоставлять интерфейс с пошаговым процессом уборки | Medium | Нет |
| FR-HOUSE-002 | Система должна требовать фото до и после уборки | Medium | Нет |
| FR-HOUSE-003 | Система должна фиксировать время начала и окончания уборки | Medium | Нет |
### 1.14 Интерфейс для гостей (FR-GUEST)
| ID | Требование | Приоритет | MVP |
|----|------------|-----------|-----|
| FR-GUEST-001 | Система должна предоставлять интерфейс для гостей (бот/веб) | Low | Нет |
| FR-GUEST-002 | Система должна предоставлять набор базовых услуг с кнопками | Low | Нет |
| FR-GUEST-003 | Система должна автоматически создавать задачи из запросов гостей | Low | Нет |
---
## 2. Нефункциональные требования (NFR)
### 2.1 Производительность (NFR-PERF)
| ID | Требование | Значение |
|----|------------|----------|
| NFR-PERF-001 | Время отклика API при стандартной нагрузке | < 500ms |
| NFR-PERF-002 | Время отклика API для критических операций | < 200ms |
| NFR-PERF-003 | Прогнозируемое количество пользователей на отель | 20-50 |
| NFR-PERF-004 | Одновременных подключений на отель | до 100 |
| NFR-PERF-005 | Пропускная способность системы | 1000 задач/час на отель |
### 2.2 Доступность (NFR-AVAIL)
| ID | Требование | Значение |
|----|------------|----------|
| NFR-AVAIL-001 | Доступность системы (SLA) | 99.5% |
| NFR-AVAIL-002 | Допустимое время простоя в месяц | < 3.6 часа |
| NFR-AVAIL-003 | RTO (Recovery Time Objective) | 1 час |
| NFR-AVAIL-004 | RPO (Recovery Point Objective) | 15 минут |
### 2.3 Масштабируемость (NFR-SCALE)
| ID | Требование | Описание |
|----|------------|----------|
| NFR-SCALE-001 | Система должна поддерживать горизонтальное масштабирование сервисов | Микросервисная архитектура |
| NFR-SCALE-002 | Система должна поддерживать добавление новых адаптеров без изменения ядра | Plugin architecture |
| NFR-SCALE-003 | Система должна поддерживать мультитенантность | Несколько отелей на одной инсталляции |
### 2.4 Безопасность (NFR-SEC)
| ID | Требование | Описание |
|----|------------|----------|
| NFR-SEC-001 | Все API-запросы должны быть аутентифицированы | JWT/OAuth2 |
| NFR-SEC-002 | Передача данных должна быть зашифрована | HTTPS/TLS 1.3 |
| NFR-SEC-003 | Пароли должны храниться в зашифрованном виде | bcrypt/argon2 |
| NFR-SEC-004 | Система должна вести журнал аудита | audit-service |
| NFR-SEC-005 | Система должна поддерживать RBAC | Ролевая модель доступа |
| NFR-SEC-006 | Система должна защищать от основных OWASP уязвимостей | SQL injection, XSS, CSRF |
### 2.5 Совместимость (NFR-COMPAT)
| ID | Требование | Описание |
|----|------------|----------|
| NFR-COMPAT-001 | Веб-интерфейс должен корректно работать в современных браузерах | Chrome, Firefox, Safari, Edge (последние 2 версии) |
| NFR-COMPAT-002 | Веб-интерфейс должен быть адаптивным | Mobile-first design |
| NFR-COMPAT-003 | Мобильное приложение должно поддерживать Android | Android 8.0+ |
| NFR-COMPAT-004 | Мобильное приложение должно поддерживать iOS | iOS 14+ |
### 2.6 Поддерживаемость (NFR-MAINT)
| ID | Требование | Описание |
|----|------------|----------|
| NFR-MAINT-001 | Код должен быть покрыт unit-тестами | > 70% coverage |
| NFR-MAINT-002 | Система должна предоставлять метрики для мониторинга | Prometheus-compatible |
| NFR-MAINT-003 | Система должна предоставлять структурированные логи | JSON format |
| NFR-MAINT-004 | Документация API должна быть в формате OpenAPI | Swagger/OpenAPI 3.0 |
| NFR-MAINT-005 | Модульная структура для возможности продажи отдельных модулей | Loosely coupled services |
### 2.7 Локализация (NFR-L10N)
| ID | Требование | Описание |
|----|------------|----------|
| NFR-L10N-001 | Интерфейс должен поддерживать русский язык | Primary |
| NFR-L10N-002 | Интерфейс должен поддерживать английский язык | Secondary |
| NFR-L10N-003 | Система должна поддерживать добавление новых языков | i18n ready |
### 2.8 Инфраструктура (NFR-INFRA)
| ID | Требование | Технология |
|----|------------|------------|
| NFR-INFRA-001 | База данных | PostgreSQL |
| NFR-INFRA-002 | Кэширование | Redis |
| NFR-INFRA-003 | Файловое хранилище | S3-compatible |
| NFR-INFRA-004 | Очередь сообщений | Apache Kafka |
| NFR-INFRA-005 | Backend-сервисы | Python |
| NFR-INFRA-006 | Web Admin Frontend | TypeScript, React |
| NFR-INFRA-007 | API Gateway | REST API, gRPC, WebSocket |
### 2.9 Надежность доставки сообщений (NFR-MSG)
| ID | Требование | Описание |
|----|------------|----------|
| NFR-MSG-001 | Гарантия доставки сообщений | At-least-once delivery |
| NFR-MSG-002 | Сообщения должны сохраняться при недоступности получателя | Kafka persistence |
| NFR-MSG-003 | Система должна поддерживать retry-механизм | Exponential backoff |
| NFR-MSG-004 | Система должна поддерживать dead-letter queue | DLQ для необработанных сообщений |
---
## 3. Глоссарий
| Термин | Описание |
|--------|----------|
| PMS | Property Management System - система управления отелем |
| Адаптер | Компонент, обеспечивающий интеграцию с внешней системой |
| Событие | Стандартизированное сообщение, передаваемое между системами через Kafka |
| Задача | Единица работы в системе с назначенным исполнителем и статусом |
| Функшн | Документ с описанием мероприятия и задач для служб отеля |
| Хаускипинг | Служба уборки номеров |
| Хаусмен | Сотрудник, отвечающий за перестановку мебели и оборудования |

142
ROADMAP.md Normal file
View File

@ -0,0 +1,142 @@
# HotelTask - Дорожная карта
## Текущий статус: Проектирование
---
## Этап 0: Аналитика
**Статус:** Завершен
- [x] Собраны бизнес-требования
- [x] Структурированы FR/NFR
- [x] Определены роли пользователей
- [x] Определен scope MVP
---
## Этап 1: Проектирование
**Статус:** В процессе
- [x] Высокоуровневая архитектура
- [ ] Схема БД (ERD)
- [ ] API контракты (OpenAPI)
- [ ] Модель данных Task
---
## Этап 2: MVP
**Статус:** Ожидает
### Что входит в MVP:
**Управление пользователями:**
- Регистрация и авторизация
- Роли: Администратор, Менеджер/Супервайзер, Линейный сотрудник
- Привязка к подразделениям
**Управление задачами:**
- CRUD операции с задачами
- Все статусы: новая, в работе, ожидает, выполнена, пауза, отменена
- Типы задач, приоритеты, сроки
- Назначение исполнителей
**Уведомления:**
- Push-уведомления о новых задачах
- Уведомления о завершении задач
**Комментарии и вложения:**
- Комментарии к задачам
- Прикрепление фото и файлов
- История изменений
**Поиск и фильтрация:**
- Поиск по названию и описанию
- Фильтры по статусу, приоритету, сотруднику, дате
**Интерфейсы:**
- Веб-интерфейс (адаптивный)
**Интеграции:**
- Базовая интеграция с PMS
- Система адаптеров
- Управление связями между системами
### Разработка MVP:
- [ ] Инфраструктура (Docker, Kafka, PostgreSQL, Redis)
- [ ] tasks-service
- [ ] users-service
- [ ] permissions-service
- [ ] notification-service
- [ ] file-storage-service
- [ ] API Gateway
- [ ] Web Admin
- [ ] PMS Adapter
---
## Этап 3: Расширение функционала
**Статус:** Планируется
### 3.1 Аудит и аналитика
- audit-service
- Отчет по выполненным задачам за период
- Статистика по сотрудникам
- Статистика по типам задач
- Процент просроченных задач
### 3.2 Планировщик
- scheduler-service
- Создание регулярных/периодических задач
- Автоматическое создание задач по расписанию
### 3.3 Трекинг времени
- Время начала и окончания задачи
- Учет времени выполнения
---
## Этап 4: Специализированные модули
**Статус:** Планируется
### 4.1 Модуль мероприятий (Events Management)
- events-service
- Описание мероприятия
- Автоматическая декомпозиция на задачи
- Массовое создание связанных задач
- Формирование документа "функшн"
### 4.2 Специализированный интерфейс горничных
- Пошаговый процесс уборки
- Обязательное фото до и после
- Фиксация времени уборки
### 4.3 Складской модуль
- Учет материалов по службам
- Списание при выполнении задач
---
## Этап 5: Расширение каналов
**Статус:** Планируется
### 5.1 Мобильные приложения
- Android приложение (8.0+)
- iOS приложение (14+)
### 5.2 Интерфейс для гостей
- Telegram-бот / веб-приложение
- Базовые услуги с кнопками
- Автоматическое создание задач из запросов
### 5.3 Голосовой ввод
- Транскрипция голосовых сообщений в текст
### 5.4 Интеграция с AI
- Обработка свободных текстовых запросов
---
## Что НЕ входит в ближайшие планы:
- Гранулярные права доступа (кастомные permissions)
- Настройка типов уведомлений для пользователя
- Интеграция с Консьерж Resonline

View File

@ -1,255 +0,0 @@
[Назад](/README.md)
# Анализ требований
## Приложение: Таск менеджер
### Потребители:
- Горничные (хаускипинг)
- Техническая (инженерная) служба
- ИТ служба
- Ресторанная служба
- Ресепшен
- Хаусмены
- Менеджеры по продажам
**Прогнозируемое количество пользователей:** 20-50 на отель
---
### Проблемы и требования:
#### **Проблема 1: Отсутствие единой системы для передачи задач между подразделениями**
**AS-IS (как есть):**
- Ресепшен выступает посредником между всеми службами
- Передача задач через звонки и мессенджеры (WhatsApp)
- Информация может теряться, забываться
- Ресепшен перегружен, особенно в часы пик (например, при заезде группы)
**TO-BE (как должно быть):**
- Автоматизированная система для передачи задач
- Каждое подразделение имеет прямой доступ к системе
- Возможность создать задачу и передать ее напрямую нужному подразделению
- Ресепшен убирается как обязательный источник синхронизации
**Базовый функционал (MVP):**
- Создание задачи
- Передача задачи нужному подразделению
- Принятие задачи исполнителем
- Отметка о выполнении
- Возможность создать встречную задачу (если требуется)
---
#### **Проблема 2: Невозможность документировать выполнение задач с помощью фото/видео**
**AS-IS:**
- Фотографии отправляются через мессенджеры (WhatsApp)
- Нет централизованного хранения
- Сложно найти историю выполненных работ
**TO-BE:**
- Возможность прикрепления фотографий к задаче
- Применимо для всех служб:
- **Горничные:** фото до и после уборки
- **Техническая служба:** фото выполненного ремонта (труба, унитаз и т.д.)
- **ИТ служба:** фото расставленного оборудования
- Возможность показать результат заказчику
- Фиксация состояния объекта
**Требование:** Возможность прикрепления файлов и фотографий к задачам
---
#### **Проблема 3: Сложность ввода информации в процессе работы**
**AS-IS:**
- Много времени тратится на набор текста
- Сотрудники вынуждены писать длинные сообщения
**TO-BE:**
- Голосовой ввод с автоматической транскрипцией в текст
- Ускорение процесса создания и комментирования задач
**Требование:** Интеграция системы распознавания речи (голосовые сообщения → текст)
**Примечание:** Потребуется использование платных сервисов (западных или отечественных)
---
#### **Проблема 4: Отсутствие автоматизации повторяющихся задач**
**AS-IS:**
- Регулярные задачи (раз в неделю, месяц) создаются вручную каждый раз
**TO-BE:**
- Автоматическое создание периодических задач по расписанию
- Применимо для:
- Горничных (регулярная уборка определенных зон)
- Технической службы (плановое обслуживание оборудования)
**Требование:** Функционал создания регулярных задач с настройкой расписания
---
#### **Проблема 5: Отсутствие учета расходных материалов**
**AS-IS:**
- Информация о материалах не фиксируется нигде
- Невозможно отследить остатки и планировать закупки
- Не хранится в других системах (в PMS такой информации нет)
**TO-BE:**
- Складской модуль внутри системы
- Учет материалов по службам:
- **Горничные:** полотенца (разных видов), косметика
- **Техническая служба:** лампочки, расходники
- Первоначальный ввод остатков
- Списание при выполнении задач
- Понимание объемов работы (например, работы прачки)
**Требование:** Модуль учета материалов
**Примечание:** Это НЕ MVP, но важная часть. Может использоваться не только в Таск менеджере.
---
## Приложение: Модуль управления мероприятиями (Events Management)
### Потребители:
- Менеджеры по продажам
- Служба хаусменов
- ИТ служба
- Ресторанная служба
- Инженерная служба
---
### Проблемы и требования:
#### **Проблема 6: Неэффективное управление задачами для мероприятий**
**AS-IS:**
- Менеджер по продажам вручную составляет "функшн" или "меморандум" (Word документ)
- В документе описываются все задачи для разных служб:
- Застройка зала (расстановка столов, стульев)
- Предоставление оборудования (проектор, экран)
- Организация кофе-брейка
- Перестройка зала
- Документ рассылается службам
- Задачи могут быть упущены или не замечены
- Отсутствует единый контроль выполнения
**TO-BE:**
- Менеджер описывает мероприятие в системе один раз
- Система автоматически создает задачи для каждой службы
- Автоматическая генерация "функшн"
- Все задачи отслеживаются в единой системе
- Массовое создание связанных задач для разных подразделений
**Требование:**
- Возможность массового создания задач (bulk task creation)
- Описание мероприятия с автоматической декомпозицией на задачи
- Привязка задач к конференц-залам и времени
- Формирование документа "функшн" на основе введенных данных
**Примечание:** Функционал, который можно продавать отдельным модулем. Пока не решен полноценно ни одной существующей системой.
---
## Приложение: Горничные (расширенный модуль для Таск менеджера)
### Потребители:
- Горничные
---
### Проблемы и требования:
#### **Проблема 7: Отсутствие контроля процесса уборки номеров**
**AS-IS:**
- Горничная получает распечатку номеров из PMS
- Выполняет уборку
- Сообщает о завершении устно или через мессенджер
- Нет фиксации времени начала/окончания уборки
- Нет фото-подтверждения качества
**TO-BE:**
- Горничная видит номер в приложении
- Нажимает "Начать уборку"
- Фотографирует номер до уборки
- Выполняет уборку
- Фотографирует номер после уборки
- Отмечает завершение
**Требование:** Специализированный интерфейс для горничных с пошаговым процессом уборки и обязательным фотографированием
---
## Приложение: Интерфейс для гостей (Guest Interface)
### Потребители:
- Гости отеля
---
### Проблемы и требования:
#### **Проблема 8: Перегрузка ресепшена запросами от гостей**
**AS-IS:**
- Гость звонит на ресепшен с запросом (принести халат, тапочки и т.д.)
- Ресепшен записывает запрос
- Ресепшен создает задачу в Таск менеджере или пишет в WhatsApp
- При большой загрузке (заезд группы):
- Трубки не берутся
- Запросы забываются
- Теряется информация о номере
**TO-BE:**
- Гость самостоятельно создает запрос через:
- Telegram-бота
- Готовый консьерж-сервис
- Другой интерфейс
- Базовые услуги доступны по кнопкам (принести халат, тапочки и т.д.)
- Возможна интеграция с AI для обработки свободных запросов
- Задача автоматически попадает нужному подразделению
- Ресепшен разгружается
**Требование:**
- Интерфейс для гостей (бот/веб-приложение)
- Набор базовых услуг с кнопками
- Автоматическое создание задач в Таск менеджере
- Опционально: интеграция с AI для обработки текстовых запросов
**Примечание:** Может быть интеграцией с существующим консьерж-сервисом, который нужно будет доработать
---
## Общие требования к архитектуре:
1. **Модульная структура:** Возможность продажи отдельных модулей
2. **Базовый функционал может быть бесплатным:** Freemium-модель для привлечения пользователей
3. **Единая точка доступа:** Все службы работают в одном приложении, а не в разных мессенджерах
4. **Масштабируемость:** Архитектура должна поддерживать добавление новых модулей
---
## Список всех подразделений (требуется уточнить полный список):
- Хаускипинг (горничные)
- Техническая (инженерная) служба
- ИТ служба
- Ресторанная служба
- Ресепшен
- Хаусмены
- Служба консьержа
- Прачечная
---
## Следующие шаги:
1. Уточнить полный список подразделений
2. Детализировать требования для каждого подразделения
3. Определить приоритеты функций (MVP vs расширенный функционал)
4. Создать архитектуру с учетом модульности
5. Получить доступ к тестовой среде существующих систем

File diff suppressed because it is too large Load Diff

View File

@ -1,240 +0,0 @@
<mxfile host="65bd71144e">
<diagram id="7gCwj9mQwRpnqfD4ykJp" name="Page-1">
<mxGraphModel dx="4779" dy="3497" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<object placeholders="1" c4Name="Message Broker" c4Type="Container" c4Technology="Apache Kafka" c4Description="Очередь сообщений" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%:&amp;nbsp;%c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="2">
<mxCell style="shape=cylinder3;size=15;direction=south;whiteSpace=wrap;html=1;boundedLbl=1;rounded=0;labelBackgroundColor=none;fillColor=#23A2D9;fontSize=12;fontColor=#ffffff;align=center;strokeColor=#0E7DAD;metaEdit=1;points=[[0.5,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.5,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];resizable=0;" vertex="1" parent="1">
<mxGeometry x="-780" y="-40" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="connections-service" c4Type="Service" c4Technology="Python" c4Description="Управляет связями между адаптерами системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="3">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-80" y="-200" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="PMS Adapter" c4Type="Service" c4Technology="Python" c4Description="Адаптер передачи сообщений в систему" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="4">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1690" y="-120" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Custom Adapter #N" c4Type="Service" c4Technology="Python" c4Description="Адаптер передачи сообщений в систему" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="6">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1410" y="40" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="PMS" c4Type="Software System" c4Description="" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#cccccc&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="7">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#8C8496;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#736782;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1690" y="-480" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="ERP" c4Type="Software System" c4Description="" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#cccccc&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="8">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#8C8496;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#736782;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1410" y="-480" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Housekeeper" c4Type="Software System" c4Description="" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#cccccc&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="9">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#8C8496;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#736782;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1130" y="-480" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="tasks-service" c4Type="Service" c4Technology="Python" c4Description="Управляет всеми тасками системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="21">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-360" y="-200" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="БД" c4Type="Container" c4Technology="Postgress" c4Description="Сохранение данных системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%:&amp;nbsp;%c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="22">
<mxCell style="shape=cylinder3;size=15;whiteSpace=wrap;html=1;boundedLbl=1;rounded=0;labelBackgroundColor=none;fillColor=#23A2D9;fontSize=12;fontColor=#ffffff;align=center;strokeColor=#0E7DAD;metaEdit=1;points=[[0.5,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.5,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];resizable=0;" vertex="1" parent="1">
<mxGeometry x="-360" y="480" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Web admin" c4Type="Container" c4Technology="Typescript, React" c4Description="Админка для управления системой" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%:&amp;nbsp;%c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="23">
<mxCell style="shape=mxgraph.c4.webBrowserContainer2;whiteSpace=wrap;html=1;boundedLbl=1;rounded=0;labelBackgroundColor=none;strokeColor=#118ACD;fillColor=#23A2D9;strokeColor=#118ACD;strokeColor2=#0E7DAD;fontSize=12;fontColor=#ffffff;align=center;metaEdit=1;points=[[0.5,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.5,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];resizable=0;" vertex="1" parent="1">
<mxGeometry x="-360" y="-960" width="240" height="160" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Service Layer" c4Type="SystemScopeBoundary" c4Application="Layer" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;&lt;div style=&quot;text-align: left&quot;&gt;%c4Name%&lt;/div&gt;&lt;/b&gt;&lt;/font&gt;&lt;div style=&quot;text-align: left&quot;&gt;[%c4Application%]&lt;/div&gt;" id="24">
<mxCell style="rounded=1;fontSize=11;whiteSpace=wrap;html=1;dashed=1;arcSize=20;fillColor=none;strokeColor=#666666;fontColor=#333333;labelBackgroundColor=none;align=left;verticalAlign=bottom;labelBorderColor=none;spacingTop=0;spacing=10;dashPattern=8 4;metaEdit=1;rotatable=0;perimeter=rectanglePerimeter;noLabel=0;labelPadding=0;allowArrows=0;connectable=0;expand=0;recursiveResize=0;editable=1;pointerEvents=0;absoluteArcSize=1;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-400" y="-280" width="880" height="600" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Mobile Apps" c4Type="Container" c4Technology="?" c4Description="Отдельные приложения для работников" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%:&amp;nbsp;%c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="25">
<mxCell style="shape=mxgraph.c4.webBrowserContainer2;whiteSpace=wrap;html=1;boundedLbl=1;rounded=0;labelBackgroundColor=none;strokeColor=#118ACD;fillColor=#23A2D9;strokeColor=#118ACD;strokeColor2=#0E7DAD;fontSize=12;fontColor=#ffffff;align=center;metaEdit=1;points=[[0.5,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.5,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];resizable=0;" vertex="1" parent="1">
<mxGeometry x="-80" y="-960" width="240" height="160" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Telegram Bot" c4Type="Container" c4Technology="?" c4Description="Бот для управления системой" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%:&amp;nbsp;%c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="26">
<mxCell style="shape=mxgraph.c4.webBrowserContainer2;whiteSpace=wrap;html=1;boundedLbl=1;rounded=0;labelBackgroundColor=none;strokeColor=#118ACD;fillColor=#23A2D9;strokeColor=#118ACD;strokeColor2=#0E7DAD;fontSize=12;fontColor=#ffffff;align=center;metaEdit=1;points=[[0.5,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.5,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];resizable=0;" vertex="1" parent="1">
<mxGeometry x="200" y="-960" width="240" height="160" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Client Layer" c4Type="SystemScopeBoundary" c4Application="Layer" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;&lt;div style=&quot;text-align: left&quot;&gt;%c4Name%&lt;/div&gt;&lt;/b&gt;&lt;/font&gt;&lt;div style=&quot;text-align: left&quot;&gt;[%c4Application%]&lt;/div&gt;" id="27">
<mxCell style="rounded=1;fontSize=11;whiteSpace=wrap;html=1;dashed=1;arcSize=20;fillColor=none;strokeColor=#666666;fontColor=#333333;labelBackgroundColor=none;align=left;verticalAlign=bottom;labelBorderColor=none;spacingTop=0;spacing=10;dashPattern=8 4;metaEdit=1;rotatable=0;perimeter=rectanglePerimeter;noLabel=0;labelPadding=0;allowArrows=0;connectable=0;expand=0;recursiveResize=0;editable=1;pointerEvents=0;absoluteArcSize=1;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-400" y="-1040" width="880" height="320" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="API Gateway Layer" c4Type="SystemScopeBoundary" c4Application="Layer" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;&lt;div style=&quot;text-align: left&quot;&gt;%c4Name%&lt;/div&gt;&lt;/b&gt;&lt;/font&gt;&lt;div style=&quot;text-align: left&quot;&gt;[%c4Application%]&lt;/div&gt;" id="31">
<mxCell style="rounded=1;fontSize=11;whiteSpace=wrap;html=1;dashed=1;arcSize=20;fillColor=none;strokeColor=#666666;fontColor=#333333;labelBackgroundColor=none;align=left;verticalAlign=bottom;labelBorderColor=none;spacingTop=0;spacing=10;dashPattern=8 4;metaEdit=1;rotatable=0;perimeter=rectanglePerimeter;noLabel=0;labelPadding=0;allowArrows=0;connectable=0;expand=0;recursiveResize=0;editable=1;pointerEvents=0;absoluteArcSize=1;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-400" y="-640" width="880" height="280" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="REST API" c4Type="Service" c4Technology="?" c4Description="" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="32">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-360" y="-560" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="gRPC" c4Type="Service" c4Technology="?" c4Description="" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="33">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-80" y="-560" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="WebSocket" c4Type="Service" c4Technology="?" c4Description="" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="34">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="200" y="-560" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="users-service" c4Type="Service" c4Technology="Python" c4Description="Управление пользователями системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="35">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="200" y="-200" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="notification-service" c4Type="Service" c4Technology="Python" c4Description="Управление уведомлениями системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="36">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-360" y="-40" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="file-storage-service" c4Type="Service" c4Technology="Python" c4Description="Управление файлами системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="37">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-80" y="-40" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="audit-service" c4Type="Service" c4Technology="Python" c4Description="Аудит действий пользователя" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="38">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="200" y="-40" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="scheduler-service" c4Type="Service" c4Technology="Python" c4Description="Создание задач по расписанию" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="39">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-360" y="120" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="permissions-service" c4Type="Service" c4Technology="Python" c4Description="Управление правами доступа пользователей" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="40">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-80" y="120" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="events-service" c4Type="Service" c4Technology="Python" c4Description="Управление мероприятиями" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="41">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="200" y="120" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Adapter Layer" c4Type="SystemScopeBoundary" c4Application="Layer" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;&lt;div style=&quot;text-align: left&quot;&gt;%c4Name%&lt;/div&gt;&lt;/b&gt;&lt;/font&gt;&lt;div style=&quot;text-align: left&quot;&gt;[%c4Application%]&lt;/div&gt;" id="42">
<mxCell style="rounded=1;fontSize=11;whiteSpace=wrap;html=1;dashed=1;arcSize=20;fillColor=none;strokeColor=#666666;fontColor=#333333;labelBackgroundColor=none;align=left;verticalAlign=bottom;labelBorderColor=none;spacingTop=0;spacing=10;dashPattern=8 4;metaEdit=1;rotatable=0;perimeter=rectanglePerimeter;noLabel=0;labelPadding=0;allowArrows=0;connectable=0;expand=0;recursiveResize=0;editable=1;pointerEvents=0;absoluteArcSize=1;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1730" y="-200" width="880" height="440" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="ERP Adapter" c4Type="Service" c4Technology="Python" c4Description="Адаптер передачи сообщений в систему" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="43">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1410" y="-120" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Housekeeping Adapter" c4Type="Service" c4Technology="Python" c4Description="Адаптер передачи сообщений в систему" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="44">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1130" y="-120" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Telegram Bot Adapter" c4Type="Service" c4Technology="Python" c4Description="Адаптер передачи сообщений в систему" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%: %c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="45">
<mxCell style="rounded=1;whiteSpace=wrap;html=1;fontSize=11;labelBackgroundColor=none;fillColor=#23A2D9;fontColor=#ffffff;align=center;arcSize=10;strokeColor=#0E7DAD;metaEdit=1;resizable=0;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1690" y="40" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Data Layer" c4Type="SystemScopeBoundary" c4Application="Layer" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;&lt;div style=&quot;text-align: left&quot;&gt;%c4Name%&lt;/div&gt;&lt;/b&gt;&lt;/font&gt;&lt;div style=&quot;text-align: left&quot;&gt;[%c4Application%]&lt;/div&gt;" id="46">
<mxCell style="rounded=1;fontSize=11;whiteSpace=wrap;html=1;dashed=1;arcSize=20;fillColor=none;strokeColor=#666666;fontColor=#333333;labelBackgroundColor=none;align=left;verticalAlign=bottom;labelBorderColor=none;spacingTop=0;spacing=10;dashPattern=8 4;metaEdit=1;rotatable=0;perimeter=rectanglePerimeter;noLabel=0;labelPadding=0;allowArrows=0;connectable=0;expand=0;recursiveResize=0;editable=1;pointerEvents=0;absoluteArcSize=1;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-400" y="400" width="880" height="280" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="Cache" c4Type="Container" c4Technology="Redis" c4Description="Сохранение данных системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%:&amp;nbsp;%c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="50">
<mxCell style="shape=cylinder3;size=15;whiteSpace=wrap;html=1;boundedLbl=1;rounded=0;labelBackgroundColor=none;fillColor=#23A2D9;fontSize=12;fontColor=#ffffff;align=center;strokeColor=#0E7DAD;metaEdit=1;points=[[0.5,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.5,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];resizable=0;" vertex="1" parent="1">
<mxGeometry x="-80" y="480" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="File Storage" c4Type="Container" c4Technology="S3" c4Description="Сохранение данных системы" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;%c4Name%&lt;/b&gt;&lt;/font&gt;&lt;div&gt;[%c4Type%:&amp;nbsp;%c4Technology%]&lt;/div&gt;&lt;br&gt;&lt;div&gt;&lt;font style=&quot;font-size: 11px&quot;&gt;&lt;font color=&quot;#E6E6E6&quot;&gt;%c4Description%&lt;/font&gt;&lt;/div&gt;" id="51">
<mxCell style="shape=cylinder3;size=15;whiteSpace=wrap;html=1;boundedLbl=1;rounded=0;labelBackgroundColor=none;fillColor=#23A2D9;fontSize=12;fontColor=#ffffff;align=center;strokeColor=#0E7DAD;metaEdit=1;points=[[0.5,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.5,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];resizable=0;" vertex="1" parent="1">
<mxGeometry x="200" y="480" width="240" height="120" as="geometry"/>
</mxCell>
</object>
<object placeholders="1" c4Name="External System" c4Type="SystemScopeBoundary" c4Application="Layer" label="&lt;font style=&quot;font-size: 16px&quot;&gt;&lt;b&gt;&lt;div style=&quot;text-align: left&quot;&gt;%c4Name%&lt;/div&gt;&lt;/b&gt;&lt;/font&gt;&lt;div style=&quot;text-align: left&quot;&gt;[%c4Application%]&lt;/div&gt;" id="52">
<mxCell style="rounded=1;fontSize=11;whiteSpace=wrap;html=1;dashed=1;arcSize=20;fillColor=none;strokeColor=#666666;fontColor=#333333;labelBackgroundColor=none;align=left;verticalAlign=bottom;labelBorderColor=none;spacingTop=0;spacing=10;dashPattern=8 4;metaEdit=1;rotatable=0;perimeter=rectanglePerimeter;noLabel=0;labelPadding=0;allowArrows=0;connectable=0;expand=0;recursiveResize=0;editable=1;pointerEvents=0;absoluteArcSize=1;points=[[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0.25,0],[1,0.5,0],[1,0.75,0],[0.75,1,0],[0.5,1,0],[0.25,1,0],[0,0.75,0],[0,0.5,0],[0,0.25,0]];" vertex="1" parent="1">
<mxGeometry x="-1730" y="-520" width="880" height="240" as="geometry"/>
</mxCell>
</object>
<mxCell id="55" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="43" target="8">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="56" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="4" target="7">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="57" style="edgeStyle=none;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="44" target="9">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<object placeholders="1" c4Type="Relationship" c4Technology="e.g. JSON/HTTP" c4Description="e.g. Makes API calls" label="&lt;div style=&quot;text-align: left&quot;&gt;&lt;div style=&quot;text-align: center&quot;&gt;&lt;b&gt;%c4Description%&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: center&quot;&gt;[%c4Technology%]&lt;/div&gt;&lt;/div&gt;" id="64">
<mxCell style="endArrow=blockThin;html=1;fontSize=10;fontColor=#404040;strokeWidth=1;endFill=1;strokeColor=#828282;elbow=vertical;metaEdit=1;endSize=14;startSize=14;jumpStyle=arc;jumpSize=16;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1">
<mxGeometry width="240" relative="1" as="geometry">
<mxPoint x="39.93" y="-774.5" as="sourcePoint"/>
<mxPoint x="39.56" y="-584.5" as="targetPoint"/>
</mxGeometry>
</mxCell>
</object>
<object placeholders="1" c4Type="Relationship" id="65">
<mxCell style="endArrow=blockThin;html=1;fontSize=10;fontColor=#404040;strokeWidth=1;endFill=1;strokeColor=#828282;elbow=vertical;metaEdit=1;endSize=14;startSize=14;jumpStyle=arc;jumpSize=16;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1">
<mxGeometry width="240" relative="1" as="geometry">
<mxPoint x="39.62" y="-420" as="sourcePoint"/>
<mxPoint x="40" y="-220" as="targetPoint"/>
</mxGeometry>
</mxCell>
</object>
<object placeholders="1" c4Type="Relationship" id="66">
<mxCell style="endArrow=blockThin;html=1;fontSize=10;fontColor=#404040;strokeWidth=1;endFill=1;strokeColor=#828282;elbow=vertical;metaEdit=1;endSize=14;startSize=14;jumpStyle=arc;jumpSize=16;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1">
<mxGeometry width="240" relative="1" as="geometry">
<mxPoint x="39.089999999999996" y="260" as="sourcePoint"/>
<mxPoint x="39.47" y="460" as="targetPoint"/>
</mxGeometry>
</mxCell>
</object>
<object placeholders="1" c4Type="Relationship" id="67">
<mxCell style="endArrow=blockThin;html=1;fontSize=10;fontColor=#404040;strokeWidth=1;endFill=1;strokeColor=#828282;elbow=vertical;metaEdit=1;endSize=14;startSize=14;jumpStyle=arc;jumpSize=16;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1">
<mxGeometry width="240" relative="1" as="geometry">
<mxPoint x="-930" y="20" as="sourcePoint"/>
<mxPoint x="-790" y="20" as="targetPoint"/>
</mxGeometry>
</mxCell>
</object>
<object placeholders="1" c4Type="Relationship" id="68">
<mxCell style="endArrow=blockThin;html=1;fontSize=10;fontColor=#404040;strokeWidth=1;endFill=1;strokeColor=#828282;elbow=vertical;metaEdit=1;endSize=14;startSize=14;jumpStyle=arc;jumpSize=16;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1">
<mxGeometry width="240" relative="1" as="geometry">
<mxPoint x="-790" y="40" as="sourcePoint"/>
<mxPoint x="-930" y="40" as="targetPoint"/>
</mxGeometry>
</mxCell>
</object>
<object placeholders="1" c4Type="Relationship" id="69">
<mxCell style="endArrow=blockThin;html=1;fontSize=10;fontColor=#404040;strokeWidth=1;endFill=1;strokeColor=#828282;elbow=vertical;metaEdit=1;endSize=14;startSize=14;jumpStyle=arc;jumpSize=16;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1">
<mxGeometry width="240" relative="1" as="geometry">
<mxPoint x="-520" y="9.23" as="sourcePoint"/>
<mxPoint x="-380" y="9.23" as="targetPoint"/>
</mxGeometry>
</mxCell>
</object>
<object placeholders="1" c4Type="Relationship" id="70">
<mxCell style="endArrow=blockThin;html=1;fontSize=10;fontColor=#404040;strokeWidth=1;endFill=1;strokeColor=#828282;elbow=vertical;metaEdit=1;endSize=14;startSize=14;jumpStyle=arc;jumpSize=16;rounded=0;edgeStyle=orthogonalEdgeStyle;" edge="1" parent="1">
<mxGeometry width="240" relative="1" as="geometry">
<mxPoint x="-380" y="29.23" as="sourcePoint"/>
<mxPoint x="-520" y="29.23" as="targetPoint"/>
</mxGeometry>
</mxCell>
</object>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -1,117 +0,0 @@
[Назад](/README.md)
# Вопросы по архитектуре TaskManager
## 1. Стандартизация сообщений
- Какие ключевые поля должны быть в стандартизированном сообщении?
- Например: task_id, source_system, target_system, task_type, priority, description, attachments, due_date
- Нужна ли поддержка разных типов задач (одноразовые, периодические, срочные)?
**Ответ:**
1. Пока не думал над контрактом, тк это уже детали. Наверно общие поля стоит указать, как минимум для примера. Но контракт точно поменяется, многое будет зависить от выбора решений.
2. Задачи точно будут одноразовые и периодические
---
## 2. Connections-service
- Связи между системами должны быть двусторонними или направленными?
- Нужна ли поддержка правил маршрутизации (например, "все задачи типа 'уборка' из System_1 идут в System_2")?
- Должны ли связи настраиваться динамически через админку без перезапуска сервисов?
**Ответ:**
1. Сложно сказать, наверное правильнее строить односторонние связи. В случае если нам нужна двусторонняя связь, то мы сперва деляем связь от системы 1 к системе 2, а потом отдельную связь от системы 2 к системе 1. Думаю так будет гибче?
2. Какие то правила наверняка потребуются в будующем, стоит заложится под это, но пока не понятно как это будет
3. Да связи настраиваются динамически через админку и без перезапуска.
---
## 3. Адаптеры
- Какие типы внешних систем предполагаются для первых адаптеров? (PMS системы, ERP, существующие task-менеджеры?)
- Адаптеры должны работать по push-модели (отправляют сами) или pull-модели (периодически опрашивают внешние системы)?
- Нужна ли поддержка WebHooks для интеграций?
**Ответ:**
1. PMS, ERP, управление уборщицами
2. Суть адаптера в том, что он адаптер. Системы бывают очень разные, гдето есть api, гдето надо будет самому пойти в базу, гдето нужно опрашивать систему, гдето система сама будет отправлять данные. Вообщем каждый адаптер будет уникален, но его задача адаптировать сообщения от\из системы в нашу
3. Пока сложно сказать, не понятно применение, какие у тебя идеи?
---
## 4. Tasks-service
- Какие основные статусы задачи? (created, assigned, in_progress, completed, cancelled, rejected?)
- Нужна ли поддержка подзадач и зависимостей между задачами?
- Нужно ли версионирование задач (история изменений)?
**Ответ:**
1. Пока да, но вероятно пользователь захочет созхдавать "свои" статусы. Поэтому вероятно стоит заложится что в MVP делаем стандартынй набор, а потом чтобы смогли поддержать гибкость
2. Между задачами точно появятся зависимости. На счет подзадач не уверен, пока откажемся от них
3. История изменений нужна
---
## 5. Безопасность и разграничение доступа
- Будет ли мультитенантность (несколько отелей в одной инсталляции)?
- Какие основные роли предполагаются? (admin, manager, department_head, worker?)
- Нужна ли изоляция данных между отелями?
**Ответ:**
1. Да отелей у компании может быть несколько
2. админ, техподдержка, и по ролям дальше, все виды сотрудников. скорее всего тоже надо делать настраиваемым, чтобы можно было для отелей настроить свои роли, вдруг там уникальные какието названия
3. Скорее всего нет, так как тот же маркетинг один на всю сетку отелей. Мы все же говорим что отелей несколько, но владелец один. Тоесть между отелями разных владельцев связей не будет!
---
## 6. Масштабирование
- Сколько отелей планируется поддерживать в одной инсталляции?
- Какой объем сообщений в день ожидается (примерно)?
- Kafka будет в одном экземпляре или нужен кластер?
**Ответ:**
1. Может быть разное, от 1 до 50 (а может и больше)
2. Пока не известно, будем проводить НТ после разработки MVP
3. Пока предполагаем одну, кажется должно хватить. Разграничивать сообщения топиками.
---
## 7. Дополнительные сервисы
- **File Storage Service** - где хранить фото/файлы? (S3-compatible, локально, CDN?)
- **Notification Service** - нужны ли уведомления? (email, push, SMS?)
- **Audit Service** - нужен ли отдельный сервис для аудита действий?
- **Scheduler Service** - для периодических задач нужен отдельный сервис?
**Ответ:**
1. s3
2. уведомления нужны: email, push, sms, telegram, max и любые другие месенджеры
3. да аудит нужен
4. scheduler тоже нужен
---
## 8. Технологический стек
- Python сервисы - какой фреймворк? (FastAPI, Django, Flask?)
- Нужна ли REST API Gateway перед всеми сервисами?
- GraphQL рассматривается или только REST?
- Контейнеризация (Docker) + оркестрация (Kubernetes, Docker Compose)?
**Ответ:**
1. Пока не понятно. Это уже детали
2. Rest api нужно для веба. Но еще для веба нужно получать информацию и от сервера. Поэтому вероятно лучше сразу смотреть в grpc + может rest оставить для каких то отдельных запросов
3. Да, скорее всего кластер кубер. Сервисы по контейнерам с возможностью масштабирования подов
---
## 9. Дополнительные вопросы от вас
Есть ли у вас дополнительные требования или ограничения, которые нужно учесть при проектировании?
**Ответ:**
1. Пока наверно все рассказал

View File

@ -1,27 +0,0 @@
[Назад](/README.md)
## Вопросы:
1. Список всех потребителей приложения Task Manager. Кто потенциально может им начать пользоваться?
##### Текущий список:
- Хаускипинг (горничные)
- Техническая (инженерная) служба
- ИТ служба
- Ресторанная служба
- Ресепшен
- Хаусмены
- Служба консьержа
- Прачечная
2. Какие проблемы для каждого подразделения закрывает Task Manager?
##### Общие:
- Передача задач от подразделения к подразделению
##### Хаускипинг (горничные)
- ???
##### ???
- ???

View File

@ -1,12 +0,0 @@
[Назад](/README.md)
# Список проблем
1. Отсутствие единой системы для передачи задач между подразделениями
2. Невозможность документировать выполнение задач с помощью фото/видео
3. Сложность ввода информации в процессе работы
4. Отсутствие автоматизации повторяющихся задач
5. Отсутствие учета расходных материалов
6. Неэффективное управление задачами для мероприятий
7. Отсутствие контроля процесса уборки номеров
8. Перегрузка ресепшена запросами от гостей

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 105 KiB