docs: создать правила для синхронизации данных, layout и статистических компонентов

- DATA_SYNCHRONIZATION_RULES.md - правила синхронизации между компонентами
- GRAPHQL_CACHE_RULES.md - настройки кеширования и fetchPolicy
- CSS_LAYOUT_SCROLL_RULES.md - решение проблем с overflow и scroll
- STATISTICAL_COMPONENTS_RULES.md - правила Master-Detail архитектуры

Документация основана на исправлениях в кабинете фулфилмента

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-27 12:29:00 +03:00
parent 2790fa9b98
commit 121a4dece1
4 changed files with 1763 additions and 0 deletions

View File

@ -0,0 +1,286 @@
# 🔄 ПРАВИЛА СИНХРОНИЗАЦИИ ДАННЫХ МЕЖДУ КОМПОНЕНТАМИ
> **Цель:** Обеспечить консистентность данных между различными компонентами системы SFERA
## 📋 **ОСНОВНЫЕ ПРИНЦИПЫ**
### 1. **ЕДИНЫЙ ИСТОЧНИК ИСТИНЫ (Single Source of Truth)**
-**Один резолвер = одна таблица БД** для одного типа данных
-**V2 система** - приоритет над legacy кодом
-**Никогда не дублируйте** одни данные в разных резолверах
### 2. **КОНСИСТЕНТНАЯ CACHE POLICY**
```typescript
// ✅ ПРАВИЛЬНО - одинаковая policy для связанных данных
const query1 = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const query2 = useQuery(QUERY_B, { fetchPolicy: 'cache-and-network' })
// ❌ НЕПРАВИЛЬНО - разные policies создают рассинхронизацию
const query1 = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const query2 = useQuery(QUERY_B, {}) // default cache-first policy
```
### 3. **ОБЯЗАТЕЛЬНЫЕ FETCH POLICIES**
#### **Для связанных компонентов:**
```typescript
fetchPolicy: 'cache-and-network', // Всегда актуальные данные
pollInterval: 30000, // Автообновление каждые 30 сек
```
#### **Для dashboard/статистики:**
```typescript
fetchPolicy: 'cache-and-network', // Синхронизация с основными данными
pollInterval: 30000, // Регулярное обновление
errorPolicy: 'all', // Показывать частичные данные при ошибках
```
---
## 🏢 **ПРАВИЛА ДЛЯ ФУЛФИЛМЕНТА**
### **ТАБЛИЦЫ V2 СИСТЕМЫ**
- **`fulfillmentConsumableInventory`** - складские остатки расходников
- **`fulfillmentConsumableSupplyOrder`** - поставки расходников
- **`fulfillmentInventoryV2`** - общий инвентарь фулфилмента
### **СВЯЗАННЫЕ КОМПОНЕНТЫ**
```typescript
// Все эти компоненты должны использовать одинаковые данные:
1. Главный dashboard склада (/fulfillment-warehouse)
- Карточка "РАСХОДНИКИ ФУЛФИЛМЕНТА"
2. Подраздел расходников (/fulfillment-warehouse/supplies)
- Карточка "ОСТАТОК"
- Главная таблица поставок
3. Раздел услуг (/services)
- Вкладка "Расходники"
4. История поставок
- Раскрывающиеся детали каждого товара
```
### **СИНХРОНИЗИРОВАННЫЕ ПОЛЯ**
```typescript
interface SynchronizedData {
currentStock: number // Текущий остаток (одинаковый везде)
totalReceived: number // Общее количество поступлений
productId: string // ID для группировки истории поставок
fulfillmentCenterId: string // ID фулфилмент-центра
}
```
---
## 📊 **ПРАВИЛА ДЛЯ СТАТИСТИЧЕСКИХ КОМПОНЕНТОВ**
### **1. АРХИТЕКТУРА MASTER-DETAIL**
```typescript
// MASTER компонент (главный раздел)
const masterStats = useQuery(GET_WAREHOUSE_STATS, {
fetchPolicy: 'cache-and-network',
pollInterval: 30000,
})
// DETAIL компонент (подраздел)
const detailStats = useQuery(GET_DETAILED_SUPPLIES, {
fetchPolicy: 'cache-and-network', // ← ОБЯЗАТЕЛЬНО то же самое!
pollInterval: 30000, // ← ОБЯЗАТЕЛЬНО то же самое!
})
// Вычисление должно давать одинаковые результаты
const masterValue = masterStats.data?.fulfillmentSupplies?.current
const detailValue = detailStats.data?.supplies?.reduce((sum, s) => sum + s.currentStock, 0)
// masterValue === detailValue ← ОБЯЗАТЕЛЬНО!
```
### **2. ПРАВИЛА АГРЕГАЦИИ**
```typescript
// ✅ ПРАВИЛЬНО - агрегация из одного источника данных
const totalStock = inventoryItems.reduce((sum, item) => sum + item.currentStock, 0)
// ❌ НЕПРАВИЛЬНО - агрегация из разных источников
const totalStock1 = supplies.reduce((sum, s) => sum + s.currentStock, 0) // источник A
const totalStock2 = inventory.reduce((sum, i) => sum + i.remainingStock, 0) // источник B
```
---
## 🔗 **ПРАВИЛА СВЯЗЫВАНИЯ ДАННЫХ**
### **ОБЯЗАТЕЛЬНЫЕ ПОЛЯ ДЛЯ СВЯЗИ**
```typescript
// В GraphQL схемах ОБЯЗАТЕЛЬНО указывать связующие поля:
type Supply {
id: ID!
productId: ID! // ← Для фильтрации истории поставок
fulfillmentCenterId: ID // ← Для привязки к фулфилменту
}
type SupplyOrder {
id: ID!
fulfillmentCenterId: ID! // ← Для фильтрации по фулфилменту
items: [SupplyOrderItem!]!
}
type SupplyOrderItem {
productId: ID! // ← Для связи с inventory
requestedQuantity: Int!
receivedQuantity: Int! // ← Для подсчёта остатков
}
```
### **ФИЛЬТРАЦИЯ ПО СВЯЗАННЫМ ДАННЫМ**
```typescript
// ✅ ПРАВИЛЬНО - фильтрация по ID
const getSupplyHistory = (supply: Supply) => {
return allDeliveries.filter((delivery) => delivery.items?.some((item) => item.productId === supply.productId))
}
// ❌ НЕПРАВИЛЬНО - фильтрация по названию (может быть дубли)
const getSupplyHistory = (supply: Supply) => {
return allDeliveries.filter((delivery) => delivery.name === supply.name && delivery.category === supply.category)
}
```
---
## ⚡ **ПРАВИЛА REAL-TIME ОБНОВЛЕНИЙ**
### **АВТОМАТИЧЕСКАЯ СИНХРОНИЗАЦИЯ**
```typescript
// Компоненты должны обновляться автоматически при изменениях
// 1. При приёме поставки на склад
prisma.fulfillmentConsumableInventory.update({
where: { id: itemId },
data: { currentStock: newStock },
})
// → Автоматически обновятся: статистика, таблица, услуги
// 2. При отгрузке товаров
prisma.fulfillmentConsumableInventory.update({
where: { id: itemId },
data: { currentStock: { decrement: shippedQuantity } },
})
// → Автоматически обновятся все связанные компоненты
```
### **POLLING ИНТЕРВАЛЫ**
```typescript
pollInterval: 30000 // 30 сек - для статистики и dashboard
pollInterval: 60000 // 1 мин - для списков и таблиц
pollInterval: 120000 // 2 мин - для отчётов и архивных данных
```
---
## 🚨 **АНТИ-ПАТТЕРНЫ И ТИПИЧНЫЕ ОШИБКИ**
### **❌ НИКОГДА НЕ ДЕЛАЙТЕ:**
#### **1. Разные источники для одних данных**
```typescript
// ❌ ПЛОХО
const stats = useQuery(GET_OLD_SUPPLIES) // legacy таблица
const details = useQuery(GET_NEW_SUPPLIES) // V2 таблица
```
#### **2. Разные cache policies для связанных данных**
```typescript
// ❌ ПЛОХО
const master = useQuery(QUERY_A, { fetchPolicy: 'cache-and-network' })
const detail = useQuery(QUERY_B, {}) // default cache-first
```
#### **3. Ручная синхронизация через состояние**
```typescript
// ❌ ПЛОХО
const [manualSync, setManualSync] = useState(0)
useEffect(() => {
// Попытка ручной синхронизации через state
}, [manualSync])
```
#### **4. Группировка по нестабильным полям**
```typescript
// ❌ ПЛОХО - названия могут изменяться
supplies.filter((s) => s.name === targetName)
// ✅ ХОРОШО - ID стабильны
supplies.filter((s) => s.productId === targetProductId)
```
---
## 🎯 **ЧЕКЛИСТ СИНХРОНИЗАЦИИ**
### **При создании связанных компонентов:**
- [ ] Используют одну таблицу БД как источник данных
- [ ] Одинаковые `fetchPolicy` настройки
- [ ] Одинаковые `pollInterval` значения
- [ ] Связь через стабильные ID поля
- [ ] Одинаковая логика агрегации/фильтрации
- [ ] Обработка ошибок и loading состояний
### **При тестировании синхронизации:**
- [ ] Изменение данных обновляет все связанные компоненты
- [ ] Значения в master и detail компонентах совпадают
- [ ] Нет задержек между обновлениями (< 30 сек)
- [ ] Ошибки в одном компоненте не ломают другие
---
## 📈 **МОНИТОРИНГ СИНХРОНИЗАЦИИ**
### **DEBUG ЛОГИ**
```typescript
// Добавляйте логи для контроля синхронизации
console.warn('SYNC CHECK:', {
masterValue: masterData?.totalStock,
detailValue: detailData?.reduce(sum, 0),
timestamp: new Date().toISOString(),
source: 'fulfillmentInventorySync',
})
```
### **УВЕДОМЛЕНИЯ О РАССИНХРОНИЗАЦИИ**
```typescript
// Проверка консистентности данных
if (Math.abs(masterValue - detailValue) > 0) {
console.error('🚨 DATA SYNCHRONIZATION ERROR', {
master: masterValue,
detail: detailValue,
component: 'FulfillmentStats',
})
// Отправка уведомления разработчикам
toast.error('Обнаружена рассинхронизация данных')
}
```
**Следование этим правилам обеспечит надёжную синхронизацию данных между всеми компонентами системы!** 🚀