80 lines
4.4 KiB
Markdown
80 lines
4.4 KiB
Markdown
# Фронтенд-контракты и Модели данных Krona Control Center
|
||
|
||
## 1. Концепция Minor Currency Units (Минорные единицы валюты)
|
||
|
||
Для исключения ошибок округления чисел с плавающей точкой (`0.1 + 0.2 !== 0.3`) все финансовые поля в Control Center хранятся исключительно в минорных единицах (копейки):
|
||
|
||
- **Хранение**: целочисленные значения `number` (`priceMonthlyMinor`, `mrrMinor`, `amountMinor`).
|
||
- **Пример**: `149 000 руб.` представляется как `14900000` (копеек).
|
||
- **Форматирование**: компонент `<MoneyDisplay amountMinor={14900000} currency="RUB" />` производит безопасное форматирование с помощью `Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB' })`, деля число на 100 исключительно на этапе рендеринга строки.
|
||
|
||
---
|
||
|
||
## 2. Неизменяемый криптографический аудит (SHA-256 Hash Chaining)
|
||
|
||
Каждое операторское действие формирует блок в локальной цепочке аудита. Порядок связывания:
|
||
|
||
```typescript
|
||
export interface OperatorAuditEvent {
|
||
id: string;
|
||
timestamp: string;
|
||
operatorRole: OperatorRole;
|
||
operatorName: string;
|
||
actionType: OperatorActionType;
|
||
entityType: 'organization' | 'installation' | 'node' | 'module' | 'announcement' | 'incident' | 'system';
|
||
entityId: string;
|
||
reason: string;
|
||
impactSummary: string;
|
||
diffBeforeAfter?: {
|
||
before: Record<string, unknown>;
|
||
after: Record<string, unknown>;
|
||
};
|
||
clientIp?: string;
|
||
previousHash: string; // Ссылка на хеш предыдущей записи
|
||
eventHash: string; // SHA-256(previousHash + timestamp + operatorRole + actionType + entityId + reason)
|
||
}
|
||
```
|
||
|
||
### Алгоритм вычисления хеша:
|
||
```typescript
|
||
export async function computeEventHash(
|
||
previousHash: string,
|
||
timestamp: string,
|
||
operatorRole: string,
|
||
actionType: string,
|
||
entityId: string,
|
||
reason: string
|
||
): Promise<string> {
|
||
const payload = `${previousHash}|${timestamp}|${operatorRole}|${actionType}|${entityId}|${reason}`;
|
||
const msgUint8 = new TextEncoder().encode(payload);
|
||
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
|
||
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
||
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Схема постоянного хранилища IndexedDB
|
||
|
||
База данных `krona_control_center_db` (версия 1) содержит следующие хранилища объектов (Object Stores):
|
||
|
||
1. `organizations`: карточки тенантов платформы (`PlatformOrganization`).
|
||
2. `module_instances`: установленные модули и версии (`ModuleInstance`).
|
||
3. `installations`: физические устройства, кассы, терминалы (`InstallationDevice`).
|
||
4. `nodes`: серверные узлы кластера (`ClusterNode`).
|
||
5. `announcements`: сервисные сообщения и баннеры (`SystemAnnouncement`).
|
||
6. `incidents`: инциденты платформы (`PlatformIncident`).
|
||
7. `audit_log`: цепочка блоков аудита с индексом по `timestamp`.
|
||
8. `settings`: глобальные флаги (maintenance mode, circuit breaker test flags).
|
||
|
||
---
|
||
|
||
## 4. Контракт устойчивости Server Monitor Outage (Circuit Breaker)
|
||
|
||
При отказе внешнего сервиса мониторинга кластера:
|
||
1. `useKccStore.serverMonitorOutage = true`.
|
||
2. Интерфейс переходит в режим graceful degradation:
|
||
- Отображается аварийный баннер `ServerMonitorOutageBanner` с индикатором «Используются локальные кэшированные метрики».
|
||
- Метрики узлов замораживаются на последнем известном валидном снимке из IndexedDB.
|
||
- Запрещается автоматическая эвакуация нод без явного ручного подтверждения `force_offline_drain`.
|