# Fleet BLE Monitor Backend Contract Draft Черновик backend-модели, GraphQL-like API и use-cases для `fleet-ble-monitor`. Документ написан в формате, близком к GraphQL Docs: у сущности сразу перечислены поля, рядом указаны связанные enum/input/page-типы. Это не финальная схема, а база для обсуждения со старшим. ## Общие скаляры ```graphql scalar DateTime scalar Date ``` `DateTime` — ISO-строка даты и времени. `Date` — календарная дата в формате `YYYY-MM-DD`. ## User Пользователь системы. Используется для авторизации, прав доступа и как получатель отчетов. ```graphql type User { id: ID! lastName: String! firstName: String! middleName: String email: String! login: String! role: UserRole! createdAt: DateTime! updatedAt: DateTime } ``` ### UserRole Роль пользователя. ```graphql enum UserRole { ADMIN USER } ``` `ADMIN` — администратор. `USER` — пользователь. ### UsersPage Страница пользователей для таблицы. ```graphql type UsersPage { totalPages: Int! totalElements: Int! page: [User!]! } ``` ### UserSortField Поле сортировки пользователей. ```graphql enum UserSortField { ID NAME DATE } ``` ### SortDirection Общее направление сортировки. ```graphql enum SortDirection { ASC DESC } ``` ### LoginPayload ```graphql input LoginPayload { loginOrEmail: String! password: String! } ``` ### CreateUserPayload ```graphql input CreateUserPayload { lastName: String! firstName: String! middleName: String email: String! login: String! password: String! role: UserRole! } ``` ### UpdateUserPayload ```graphql input UpdateUserPayload { lastName: String firstName: String middleName: String email: String login: String password: String role: UserRole } ``` ## Machine АТТ / единица техники. Основная бизнес-сущность мониторинга. ```graphql type Machine { id: ID! name: String! licensePlate: String! type: MachineType! department: Department! description: String beacon: Beacon lastKnownState: MachineStateCode! lastDataReceivedAt: DateTime createdAt: DateTime! updatedAt: DateTime! } ``` ### MachineType Справочник типов АТТ. ```graphql type MachineType { id: ID! name: String! description: String createdAt: DateTime! updatedAt: DateTime! } ``` Примеры: каток, самосвал, погрузчик, экскаватор. ### Department Справочник подразделений, участков или объектов. ```graphql type Department { id: ID! name: String! description: String createdAt: DateTime! updatedAt: DateTime! } ``` ### MachineStateCode Последнее известное базовое состояние АТТ. ```graphql enum MachineStateCode { IDLE ENGINE_ON MOVING NO_DATA } ``` `IDLE` — стоит. `ENGINE_ON` — заведена. `MOVING` — в движении. `NO_DATA` — нет данных. ### MachinesPage ```graphql type MachinesPage { totalPages: Int! totalElements: Int! page: [Machine!]! } ``` ### MachineSortField ```graphql enum MachineSortField { ID NAME DATE } ``` ### CreateMachinePayload ```graphql input CreateMachinePayload { name: String! licensePlate: String! typeId: ID! departmentId: ID! description: String } ``` ### UpdateMachinePayload ```graphql input UpdateMachinePayload { name: String licensePlate: String typeId: ID departmentId: ID description: String } ``` ## Beacon BLE-маяк. Хранит идентификатор, MAC-адрес, статус привязки, последнюю связь. ```graphql type Beacon { id: ID! identifier: String! macAddress: String! status: BeaconStatus! boundMachine: Machine lastSeenAt: DateTime createdAt: DateTime! updatedAt: DateTime! } ``` ### BeaconStatus ```graphql enum BeaconStatus { FREE BOUND } ``` `FREE` — маяк свободен. `BOUND` — маяк привязан к АТТ. ### BeaconTelemetryPacket Сырое сообщение от маяка. ```graphql type BeaconTelemetryPacket { id: ID! beacon: Beacon! machine: Machine mac: String! vibration: Float! duration: Int! timeMillis: Long! receivedAt: DateTime! } ``` ### BeaconTelemetryPayload Payload входящего сообщения от маяка. ```graphql input BeaconTelemetryPayload { mac: String! vibration: Float! duration: Int! timeMillis: Long! } ``` `duration` — длительность замера/пакета в секундах. `timeMillis` — время события на стороне маяка или шлюза. ## MachineStateDefinition Справочник состояний АТТ без привязки к конкретным значениям акселерометра. ```graphql type MachineStateDefinition { id: ID! name: String! icon: String! color: String! description: String isSystem: Boolean! createdAt: DateTime! updatedAt: DateTime! } ``` Примеры: - `Стоит` - `Заведена` - `В движении` - `Работа с нагрузкой` - `Нет данных` `Нет данных` — системное состояние, его нельзя удалять и нельзя использовать в ручном правиле калибровки. ### CreateMachineStateDefinitionPayload ```graphql input CreateMachineStateDefinitionPayload { name: String! icon: String! color: String! description: String } ``` ### UpdateMachineStateDefinitionPayload ```graphql input UpdateMachineStateDefinitionPayload { name: String icon: String color: String description: String } ``` ## MachineCalibrationRule Правило калибровки для конкретной АТТ. Связывает диапазон значений вибрации с состоянием из справочника. ```graphql type MachineCalibrationRule { id: ID! machine: Machine! stateDefinition: MachineStateDefinition! vibrationMin: Float! vibrationMax: Float! createdAt: DateTime! updatedAt: DateTime! } ``` ### CreateMachineCalibrationRulePayload ```graphql input CreateMachineCalibrationRulePayload { machineId: ID! stateDefinitionId: ID! vibrationMin: Float! vibrationMax: Float! } ``` ### UpdateMachineCalibrationRulePayload ```graphql input UpdateMachineCalibrationRulePayload { stateDefinitionId: ID vibrationMin: Float vibrationMax: Float } ``` ## MachineStateInterval Распознанный интервал состояния АТТ. ```graphql type MachineStateInterval { id: ID! machine: Machine! stateDefinition: MachineStateDefinition stateCode: MachineStateCode startedAt: DateTime! endedAt: DateTime durationSeconds: Int! vibrationMin: Float vibrationMax: Float vibrationAvg: Float sampleCount: Int! isNoData: Boolean! } ``` Если `stateDefinition` равен `null`, участок считается нераспознанным. Если `isNoData = true`, участок относится к системному состоянию `Нет данных`. ### MachineStateHistoryPage ```graphql type MachineStateHistoryPage { totalPages: Int! totalElements: Int! page: [MachineStateInterval!]! } ``` ## MachineUsageSummary Краткая статистика по АТТ за период. ```graphql type MachineUsageSummary { machine: Machine! periodStart: DateTime! periodEnd: DateTime! movingSeconds: Int! engineOnSeconds: Int! idleSeconds: Int! noDataSeconds: Int! stateChangesCount: Int! utilizationPercent: Int! } ``` ## FleetReportSchedule Настройка ежедневной рассылки отчета по парку АТТ. ```graphql type FleetReportSchedule { id: ID! name: String! recipients: [User!]! reportFormat: ReportFormat! sendTime: String! period: ReportPeriod! timezone: String! enabled: Boolean! createdAt: DateTime! updatedAt: DateTime! } ``` ### ReportFormat Формат отчета. ```graphql enum ReportFormat { PDF XLSX } ``` ### ReportPeriod Период отчета. ```graphql enum ReportPeriod { PREVIOUS_DAY } ``` `PREVIOUS_DAY` — предыдущий календарный день относительно timezone расписания. ### CreateFleetReportSchedulePayload ```graphql input CreateFleetReportSchedulePayload { recipientUserIds: [ID!]! reportFormat: ReportFormat! sendTime: String! } ``` ### UpdateFleetReportSchedulePayload ```graphql input UpdateFleetReportSchedulePayload { recipientUserIds: [ID!] reportFormat: ReportFormat sendTime: String enabled: Boolean } ``` ## FleetReportDelivery История конкретной попытки отправки отчета. ```graphql type FleetReportDelivery { id: ID! schedule: FleetReportSchedule reportDate: Date! reportFormat: ReportFormat! recipients: [User!]! status: ReportDeliveryStatus! fileName: String errorMessage: String createdAt: DateTime! sentAt: DateTime } ``` ### ReportDeliveryStatus ```graphql enum ReportDeliveryStatus { CREATED GENERATING SENT FAILED } ``` `CREATED` — задача создана. `GENERATING` — отчет формируется. `SENT` — отчет отправлен. `FAILED` — ошибка генерации или отправки. ## FleetDailyReport Доменная модель дневного отчета по парку АТТ. ```graphql type FleetDailyReport { date: Date! generatedAt: DateTime! summary: FleetDailyReportSummary! machines: [FleetDailyReportMachineRow!]! history: [FleetDailyReportHistoryRow!]! } ``` ### FleetDailyReportSummary ```graphql type FleetDailyReportSummary { totalMachines: Int! boundMachines: Int! unboundMachines: Int! machinesWithMovement: Int! machinesWithoutData: Int! movingSeconds: Int! engineOnSeconds: Int! idleSeconds: Int! noDataSeconds: Int! stateChangesCount: Int! } ``` ### FleetDailyReportMachineRow ```graphql type FleetDailyReportMachineRow { machine: Machine! movingSeconds: Int! engineOnSeconds: Int! idleSeconds: Int! noDataSeconds: Int! stateChangesCount: Int! utilizationPercent: Int! } ``` ### FleetDailyReportHistoryRow ```graphql type FleetDailyReportHistoryRow { machine: Machine! startedAt: DateTime! endedAt: DateTime! stateDefinition: MachineStateDefinition stateCode: MachineStateCode durationSeconds: Int! } ``` ### GenerateFleetDailyReportPayload ```graphql input GenerateFleetDailyReportPayload { date: Date! format: ReportFormat! } ``` ### ReportFile Ссылка на готовый файл отчета. ```graphql type ReportFile { fileName: String! mimeType: String! downloadUrl: String! expiresAt: DateTime } ``` Физическая отдача файла может быть не через GraphQL, а через REST endpoint или object storage URL. GraphQL в таком случае возвращает `downloadUrl`. ## Query ```graphql type Query { currentUser: User getUsers( page: Int! pageSize: Int query: String roles: [UserRole!] sortDirection: SortDirection! sortField: UserSortField! ): UsersPage! getUser(id: ID!): User getMachines( page: Int! pageSize: Int query: String sortDirection: SortDirection! sortField: MachineSortField! ): MachinesPage! getMachine(id: ID!): Machine getMachineTypes: [MachineType!]! getDepartments: [Department!]! getBeacons: [Beacon!]! getFreeBeacons: [Beacon!]! getBeaconTelemetry( beaconId: ID machineId: ID startDate: DateTime! endDate: DateTime! ): [BeaconTelemetryPacket!]! getMachineStateDefinitions: [MachineStateDefinition!]! getMachineCalibrationRules(machineId: ID!): [MachineCalibrationRule!]! getMachineStateHistory( machineId: ID! startDate: DateTime! endDate: DateTime! page: Int pageSize: Int ): MachineStateHistoryPage! getMachineUsageSummary( machineId: ID! startDate: DateTime! endDate: DateTime! ): MachineUsageSummary! getUnrecognizedTelemetryIntervals( machineId: ID! startDate: DateTime! endDate: DateTime! ): [MachineStateInterval!]! getFleetReportSchedules: [FleetReportSchedule!]! getFleetReportDeliveries( scheduleId: ID dateFrom: Date dateTo: Date ): [FleetReportDelivery!]! getFleetDailyReport(date: Date!): FleetDailyReport! createFleetDailyReportDownloadUrl( payload: GenerateFleetDailyReportPayload! ): ReportFile! } ``` ## Mutation ```graphql type Mutation { login(payload: LoginPayload!): User! logout: Boolean! createUser(payload: CreateUserPayload!): User! updateUser( id: ID! payload: UpdateUserPayload! ): User! deleteUser(id: ID!): Boolean! createMachine(payload: CreateMachinePayload!): Machine! updateMachine( id: ID! payload: UpdateMachinePayload! ): Machine! deleteMachine(id: ID!): Boolean! bindBeaconToMachine( machineId: ID! beaconId: ID! ): Machine! unbindBeaconFromMachine(machineId: ID!): Machine! registerBeaconTelemetryPacket( payload: BeaconTelemetryPayload! ): Boolean! createMachineType(name: String!): MachineType! updateMachineType( id: ID! name: String! ): MachineType! deleteMachineType(id: ID!): Boolean! createDepartment(name: String!): Department! updateDepartment( id: ID! name: String! ): Department! deleteDepartment(id: ID!): Boolean! createMachineStateDefinition( payload: CreateMachineStateDefinitionPayload! ): MachineStateDefinition! updateMachineStateDefinition( id: ID! payload: UpdateMachineStateDefinitionPayload! ): MachineStateDefinition! deleteMachineStateDefinition(id: ID!): Boolean! createMachineCalibrationRule( payload: CreateMachineCalibrationRulePayload! ): MachineCalibrationRule! updateMachineCalibrationRule( id: ID! payload: UpdateMachineCalibrationRulePayload! ): MachineCalibrationRule! deleteMachineCalibrationRule(id: ID!): Boolean! recognizeMachineStates( machineId: ID! startDate: DateTime! endDate: DateTime! ): [MachineStateInterval!]! createFleetReportSchedule( payload: CreateFleetReportSchedulePayload! ): FleetReportSchedule! updateFleetReportSchedule( id: ID! payload: UpdateFleetReportSchedulePayload! ): FleetReportSchedule! deleteFleetReportSchedule(id: ID!): Boolean! sendFleetDailyReport( scheduleId: ID! reportDate: Date! ): FleetReportDelivery! } ``` ## Use-Cases ### Пользователи `LoginUserUseCase` — авторизует пользователя по логину/email и паролю. `GetCurrentUserUseCase` — возвращает текущего авторизованного пользователя. `GetUsersUseCase` — возвращает список пользователей с поиском, сортировкой и пагинацией. `CreateUserUseCase` — создает пользователя. `UpdateUserUseCase` — редактирует данные пользователя. `DeleteUserUseCase` — удаляет пользователя или блокирует, если будет выбран soft-delete подход. ### АТТ `GetMachinesUseCase` — возвращает список АТТ с поиском, сортировкой и пагинацией. `GetMachineByIdUseCase` — возвращает карточку конкретной АТТ. `CreateMachineUseCase` — создает новую АТТ. `UpdateMachineUseCase` — редактирует данные АТТ. `DeleteMachineUseCase` — удаляет АТТ или переводит в архив. `BindBeaconToMachineUseCase` — привязывает свободный маяк к АТТ. `UnbindBeaconFromMachineUseCase` — отвязывает маяк от АТТ. ### Маяки и телеметрия `RegisterBeaconTelemetryPacketUseCase` — принимает сырое сообщение от маяка и сохраняет его. `ResolveBeaconByMacUseCase` — находит маяк по MAC-адресу из входящего пакета. `AttachTelemetryPacketToMachineUseCase` — определяет, к какой АТТ относится пакет, если маяк привязан. `GetBeaconTelemetryUseCase` — возвращает сырые показания маяка за период. `GetFreeBeaconsUseCase` — возвращает список свободных маяков. ### Калибровка `GetMachineStateDefinitionsUseCase` — возвращает справочник состояний АТТ. `CreateMachineStateDefinitionUseCase` — создает новое состояние в справочнике. `UpdateMachineStateDefinitionUseCase` — редактирует состояние справочника. `DeleteMachineStateDefinitionUseCase` — удаляет состояние, если оно не используется правилами. `GetMachineCalibrationRulesUseCase` — возвращает правила калибровки конкретной АТТ. `CreateMachineCalibrationRuleUseCase` — создает правило калибровки для АТТ. `UpdateMachineCalibrationRuleUseCase` — редактирует правило калибровки. `DeleteMachineCalibrationRuleUseCase` — удаляет правило калибровки. `ValidateMachineCalibrationRuleUseCase` — проверяет правило на конфликты с уже существующими диапазонами. `RecognizeMachineStatesUseCase` — по сырым данным акселерометра и правилам калибровки строит интервалы состояний. `GetUnrecognizedTelemetryIntervalsUseCase` — возвращает участки телеметрии, которые не попали ни под одно правило. ### История и аналитика `GetMachineStateHistoryUseCase` — возвращает историю состояний АТТ за период. `GetMachineUsageSummaryUseCase` — собирает краткую статистику по АТТ за период. `GetMachineDailyUsageStatsUseCase` — возвращает дневную статистику по АТТ для графиков. `AggregateMachineStateIntervalsUseCase` — агрегирует интервалы состояний из телеметрии. ### Отчеты `GetFleetReportSchedulesUseCase` — возвращает настройки рассылок отчетов. `CreateFleetReportScheduleUseCase` — создает настройку ежедневной рассылки отчета. `UpdateFleetReportScheduleUseCase` — редактирует настройку рассылки. `DeleteFleetReportScheduleUseCase` — удаляет настройку рассылки. `GenerateFleetDailyReportUseCase` — собирает дневной отчет по парку АТТ за выбранную дату. `ExportFleetDailyReportToPdfUseCase` — формирует PDF-файл дневного отчета. `ExportFleetDailyReportToXlsxUseCase` — формирует Excel-файл дневного отчета. `DownloadFleetDailyReportUseCase` — отдает файл отчета пользователю по запросу из интерфейса. `RunScheduledFleetReportsUseCase` — периодически запускает активные расписания рассылок. `SendFleetDailyReportUseCase` — генерирует отчет и отправляет его выбранным пользователям. `CreateFleetReportDeliveryUseCase` — создает запись о попытке отправки отчета. `MarkFleetReportDeliveryAsSentUseCase` — помечает отправку как успешную. `MarkFleetReportDeliveryAsFailedUseCase` — сохраняет ошибку отправки. ## Доменные сервисы `MachineStateRecognitionService` — определяет состояние АТТ по телеметрии и правилам калибровки. `MachineAnalyticsService` — считает агрегаты: время в движении, заведена, стояла, нет данных, смены состояний. `FleetDailyReportBuilder` — собирает доменную модель дневного отчета. `FleetReportExporter` — общий интерфейс для экспорта отчета в файл. `PdfFleetReportExporter` — экспортирует отчет в PDF. `XlsxFleetReportExporter` — экспортирует отчет в Excel. `MailService` — отправляет письма с вложениями. `ReportScheduler` — запускает ежедневные рассылки по расписанию.