feat: Phase 1 - Implementation of Data Security Infrastructure
Implemented comprehensive data security infrastructure for SFERA platform: ## Security Classes Created: - `SupplyDataFilter`: Role-based data filtering for supply orders - `ParticipantIsolation`: Data isolation between competing organizations - `RecipeAccessControl`: Protection of production recipes and trade secrets - `CommercialDataAudit`: Audit logging and suspicious activity detection - `SecurityLogger`: Centralized security event logging system ## Infrastructure Components: - Feature flags system for gradual security rollout - Database migrations for audit logging (AuditLog, SecurityAlert models) - Secure resolver wrapper for automatic GraphQL security - TypeScript interfaces and type safety throughout ## Security Features: - Role-based access control (SELLER, WHOLESALE, FULFILLMENT, LOGIST) - Commercial data protection between competitors - Production recipe confidentiality - Audit trail for all data access - Real-time security monitoring and alerts - Rate limiting and suspicious activity detection ## Implementation Notes: - All console logging replaced with centralized security logger - Comprehensive TypeScript typing with no explicit 'any' types - Modular architecture following SFERA coding standards - Feature flag controlled rollout for safe deployment This completes Phase 1 of the security implementation plan. Next phases will integrate these classes into existing GraphQL resolvers. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -54,20 +54,20 @@ graph TD
|
||||
**GraphQL мутация подтверждения поставщиком:**
|
||||
|
||||
```graphql
|
||||
# Поставщик может указать детали упаковки при подтверждении
|
||||
# Поставщик указывает детали упаковки при одобрении (опционально)
|
||||
mutation SupplierApproveOrderWithPackaging($id: ID!, $packagesCount: Int, $volume: Float) {
|
||||
supplierApproveOrderWithPackaging(
|
||||
id: $id
|
||||
packagesCount: $packagesCount # Количество грузовых мест
|
||||
volume: $volume # Объём в м³ (влияет на логистические тарифы)
|
||||
packagesCount: $packagesCount # Опционально: количество грузовых мест
|
||||
volume: $volume # Опционально: объём в м³ для расчета логистических тарифов
|
||||
) {
|
||||
success
|
||||
message
|
||||
order {
|
||||
id
|
||||
status
|
||||
packagesCount
|
||||
volume
|
||||
packagesCount # null если не указано
|
||||
volume # null если не указано
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -247,6 +247,211 @@ createSupplyOrder(input: {
|
||||
|
||||
**Обработка входящих заказов:**
|
||||
|
||||
```typescript
|
||||
// Поставщик получает заказы где он является поставщиком
|
||||
const supplierOrders = await prisma.supplyOrder.findMany({
|
||||
where: {
|
||||
partnerId: currentUser.organization.id, // Мы - поставщик
|
||||
status: 'PENDING', // Ожидает подтверждения
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
**Действия поставщика:**
|
||||
|
||||
```graphql
|
||||
# Одобрение заказа
|
||||
mutation SupplierApproveOrder($orderId: ID!) {
|
||||
supplierApproveOrder(id: $orderId) {
|
||||
success
|
||||
order {
|
||||
id
|
||||
status
|
||||
} # PENDING → SUPPLIER_APPROVED
|
||||
}
|
||||
}
|
||||
|
||||
# Отклонение заказа
|
||||
mutation SupplierRejectOrder($orderId: ID!, $reason: String) {
|
||||
supplierRejectOrder(id: $orderId, reason: $reason) {
|
||||
success
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
# Отгрузка товара (после подтверждения логистики)
|
||||
mutation SupplierShipOrder($orderId: ID!) {
|
||||
supplierShipOrder(id: $orderId) {
|
||||
success
|
||||
order {
|
||||
id
|
||||
status
|
||||
} # LOGISTICS_CONFIRMED → SHIPPED
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Компоненты поставщика:**
|
||||
|
||||
```typescript
|
||||
// Техническая реализация кабинета поставщика
|
||||
src/components/supplier-orders/
|
||||
├── supplier-orders-dashboard.tsx # Главный dashboard
|
||||
├── supplier-order-card.tsx # Карточка заказа
|
||||
├── supplier-orders-tabs.tsx # Табы по статусам
|
||||
├── supplier-orders-search.tsx # Поиск и фильтры
|
||||
└── supplier-order-stats.tsx # Статистика заказов
|
||||
```
|
||||
|
||||
**Возможности:**
|
||||
|
||||
- ✅ Просматривать входящие заказы (PENDING)
|
||||
- ✅ Одобрять заказы (PENDING → SUPPLIER_APPROVED)
|
||||
- ✅ Отклонять заказы (PENDING → CANCELLED)
|
||||
- ✅ Отгружать товары (LOGISTICS_CONFIRMED → SHIPPED)
|
||||
- ❌ Изменять детали заказа после создания
|
||||
- ❌ Видеть заказы других поставщиков
|
||||
|
||||
## 🚨 КРИТИЧЕСКИЕ ПРОБЛЕМЫ WORKFLOW
|
||||
|
||||
### ВЫЯВЛЕННЫЕ ПРОБЛЕМЫ В ЦЕПОЧКЕ ПОСТАВОК:
|
||||
|
||||
#### ❌ **ПРОБЛЕМА 1: Неправильное отображение статусов у поставщика**
|
||||
|
||||
```typescript
|
||||
// ПРОБЛЕМА: Поставщик видит "ожидает подтверждения" вместо только кнопок
|
||||
// РЕШЕНИЕ: Показывать только кнопки действий, скрывать статусы
|
||||
|
||||
// Текущий код (неправильно):
|
||||
<StatusBadge status={order.status} />
|
||||
<ActionButtons />
|
||||
|
||||
// Правильный код:
|
||||
{user?.organization?.type === 'WHOLESALE' ? (
|
||||
<ActionButtons only /> // Только кнопки, без статуса
|
||||
) : (
|
||||
<StatusBadge status={order.status} />
|
||||
)}
|
||||
```
|
||||
|
||||
#### ❌ **ПРОБЛЕМА 2: Отсутствие полей ввода у поставщика**
|
||||
|
||||
```typescript
|
||||
// ПРОБЛЕМА: Поставщик не может указать важные данные при одобрении
|
||||
interface SupplierPackagingFields {
|
||||
packagesCount?: number // ОПЦИОНАЛЬНО: Количество грузовых мест
|
||||
volume?: number // ОПЦИОНАЛЬНО: Объем груза для логистических расчетов
|
||||
readyDate?: DateTime // ОПЦИОНАЛЬНО: Дата готовности к отгрузке
|
||||
notes?: string // ОПЦИОНАЛЬНО: Комментарии для логистики
|
||||
}
|
||||
|
||||
// ТРЕБОВАНИЯ:
|
||||
// ✅ Поля НЕ обязательные - заказ можно одобрить без них
|
||||
// ✅ Показываются сразу при одобрении для удобства заполнения
|
||||
// ✅ Используются логистикой для расчета тарифов и планирования
|
||||
// ✅ Отображаются на 1-м уровне визуализации поставки
|
||||
|
||||
// РЕШЕНИЕ: Расширить мутацию supplierApproveOrder
|
||||
mutation SupplierApproveOrder($input: SupplierApprovalInput!) {
|
||||
supplierApproveOrder(input: $input) {
|
||||
success
|
||||
order {
|
||||
id, status, packagesCount, volume, readyDate, notes
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### ❌ **ПРОБЛЕМА 3: Конфликт статусов в приемке фулфилмента**
|
||||
|
||||
```typescript
|
||||
// КРИТИЧЕСКАЯ ОШИБКА: Резолвер ожидает SHIPPED, но получает SUPPLIER_APPROVED
|
||||
if (supplyOrder.status !== 'SHIPPED') {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Заказ должен быть в статусе SHIPPED для приемки', // ❌ БЛОКИРУЕТ ПРОЦЕСС
|
||||
}
|
||||
}
|
||||
|
||||
// РЕШЕНИЕ: Исправить проверку статуса
|
||||
if (!['SUPPLIER_APPROVED', 'LOGISTICS_CONFIRMED'].includes(supplyOrder.status)) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Заказ должен быть одобрен поставщиком для приемки',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### ❌ **ПРОБЛЕМА 4: Отсутствие уведомлений поставщика**
|
||||
|
||||
```typescript
|
||||
// ПРОБЛЕМА: Поставщик не знает о новых заказах в реальном времени
|
||||
// РЕШЕНИЕ: Добавить систему уведомлений
|
||||
|
||||
interface SupplierNotifications {
|
||||
newOrder: 'Новый заказ от {sellerName} на сумму {amount}'
|
||||
orderCancelled: 'Заказ #{orderNumber} отменен заказчиком'
|
||||
logistics: 'Логистика подтверждена для заказа #{orderNumber}'
|
||||
}
|
||||
```
|
||||
|
||||
### ПЛАН ИСПРАВЛЕНИЯ WORKFLOW:
|
||||
|
||||
```typescript
|
||||
interface WorkflowFixes {
|
||||
// Фаза 1: UI поставщика
|
||||
supplierInterface: {
|
||||
hideStatuses: 'Показывать только кнопки действий'
|
||||
addFields: 'Поля для packagesCount, volume, readyDate'
|
||||
realtime: 'Уведомления о новых заказах'
|
||||
}
|
||||
|
||||
// Фаза 2: Backend логика
|
||||
backendLogic: {
|
||||
expandMutation: 'Расширить supplierApproveOrder с дополнительными полями'
|
||||
fixStatusCheck: 'Исправить проверку статусов в fulfillmentReceiveOrder'
|
||||
notifications: 'Система реалтайм уведомлений'
|
||||
}
|
||||
|
||||
// Фаза 3: Интеграция
|
||||
integration: {
|
||||
validation: 'Валидация минимальных количеств заказа'
|
||||
inventory: 'Проверка доступности товаров у поставщика'
|
||||
logistics: 'Автоматическое назначение логистики'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ТРЕБОВАНИЯ К РЕАЛИЗАЦИИ:
|
||||
|
||||
```typescript
|
||||
// 1. Исправленная фильтрация заказов для поставщика
|
||||
const fixedSupplierFilter = `
|
||||
if (currentUser.organization.type === 'WHOLESALE') {
|
||||
whereClause = {
|
||||
partnerId: currentUser.organization.id, // Мы - поставщик
|
||||
}
|
||||
} else {
|
||||
whereClause = {
|
||||
organizationId: currentUser.organization.id, // Мы - заказчик
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// 2. Правильная обработка статусов
|
||||
const correctStatusHandling = `
|
||||
// Поставщик видит только кнопки, без статусов
|
||||
{userRole === 'WHOLESALE' && status === 'PENDING' && (
|
||||
<ApproveRejectButtons orderId={order.id} />
|
||||
)}
|
||||
|
||||
// Остальные видят статусы
|
||||
{userRole !== 'WHOLESALE' && (
|
||||
<StatusBadge status={status} />
|
||||
)}
|
||||
`
|
||||
```
|
||||
|
||||
```typescript
|
||||
// Из кода resolvers.ts:
|
||||
const incomingSupplierOrders = await prisma.supplyOrder.count({
|
||||
|
Reference in New Issue
Block a user