Реализация реферальной системы и улучшение системы авторизации

- Добавлена полная реферальная система с GraphQL резолверами и UI компонентами
- Улучшена система регистрации с поддержкой ВКонтакте и реферальных ссылок
- Обновлена схема Prisma для поддержки реферальной системы
- Добавлены новые файлы документации правил системы
- Улучшена система партнерства и контрагентов
- Обновлены компоненты авторизации для поддержки новых функций
- Удален устаревший server.log

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-11 09:47:00 +03:00
parent af16402f22
commit 8f7ec70fe6
28 changed files with 5827 additions and 4313 deletions

View File

@ -2,7 +2,6 @@ generator client {
provider = "prisma-client-js"
}
// Конфигурация для автоматического seeding
generator seed {
provider = "prisma-client-js"
output = "./generated/client"
@ -31,7 +30,7 @@ model User {
model Admin {
id String @id @default(cuid())
username String @unique
password String // Хеш пароля
password String
email String? @unique
isActive Boolean @default(true)
lastLogin DateTime?
@ -89,6 +88,9 @@ model Organization {
revenue BigInt?
taxSystem String?
dadataData Json?
referralCode String? @unique
referredById String?
referralPoints Int @default(0)
apiKeys ApiKey[]
carts Cart?
counterpartyOf Counterparty[] @relation("CounterpartyOf")
@ -96,25 +98,31 @@ model Organization {
receivedRequests CounterpartyRequest[] @relation("ReceivedRequests")
sentRequests CounterpartyRequest[] @relation("SentRequests")
employees Employee[]
externalAds ExternalAd[] @relation("ExternalAds")
favorites Favorites[]
logistics Logistics[]
receivedMessages Message[] @relation("ReceivedMessages")
sentMessages Message[] @relation("SentMessages")
referredBy Organization? @relation("ReferralRelation", fields: [referredById], references: [id])
referrals Organization[] @relation("ReferralRelation")
products Product[]
referralTransactions ReferralTransaction[] @relation("ReferralTransactions")
referrerTransactions ReferralTransaction[] @relation("ReferrerTransactions")
sellerStatsCaches SellerStatsCache[] @relation("SellerStatsCaches")
services Service[]
supplies Supply[]
users User[]
logistics Logistics[]
supplyOrders SupplyOrder[]
partnerSupplyOrders SupplyOrder[] @relation("SupplyOrderPartner")
sellerSupplies Supply[] @relation("SellerSupplies")
fulfillmentSupplyOrders SupplyOrder[] @relation("SupplyOrderFulfillmentCenter")
logisticsSupplyOrders SupplyOrder[] @relation("SupplyOrderLogistics")
wildberriesSupplies WildberriesSupply[]
supplyOrders SupplyOrder[]
partnerSupplyOrders SupplyOrder[] @relation("SupplyOrderPartner")
supplySuppliers SupplySupplier[] @relation("SupplySuppliers")
externalAds ExternalAd[] @relation("ExternalAds")
users User[]
wbWarehouseCaches WBWarehouseCache[] @relation("WBWarehouseCaches")
sellerStatsCaches SellerStatsCache[] @relation("SellerStatsCaches")
sellerSupplies Supply[] @relation("SellerSupplies")
wildberriesSupplies WildberriesSupply[]
@@index([referralCode])
@@index([referredById])
@@map("organizations")
}
@ -149,14 +157,18 @@ model CounterpartyRequest {
}
model Counterparty {
id String @id @default(cuid())
createdAt DateTime @default(now())
organizationId String
counterpartyId String
counterparty Organization @relation("CounterpartyOf", fields: [counterpartyId], references: [id])
organization Organization @relation("OrganizationCounterparties", fields: [organizationId], references: [id])
id String @id @default(cuid())
createdAt DateTime @default(now())
organizationId String
counterpartyId String
type CounterpartyType @default(MANUAL)
triggeredBy String?
triggerEntityId String?
counterparty Organization @relation("CounterpartyOf", fields: [counterpartyId], references: [id])
organization Organization @relation("OrganizationCounterparties", fields: [organizationId], references: [id])
@@unique([organizationId, counterpartyId])
@@index([type])
@@map("counterparties")
}
@ -203,26 +215,26 @@ model Supply {
id String @id @default(cuid())
name String
description String?
price Decimal @db.Decimal(10, 2) // Цена закупки у поставщика (не меняется)
pricePerUnit Decimal? @db.Decimal(10, 2) // Цена продажи селлерам (устанавливается фулфилментом)
price Decimal @db.Decimal(10, 2)
pricePerUnit Decimal? @db.Decimal(10, 2)
quantity Int @default(0)
unit String @default("шт")
category String @default("Расходники")
status String @default("planned") // planned, in-transit, delivered, in-stock
status String @default("planned")
date DateTime @default(now())
supplier String @default("Не указан")
minStock Int @default(0)
currentStock Int @default(0)
usedStock Int @default(0) // Количество использованных расходников
usedStock Int @default(0)
imageUrl String?
type SupplyType @default(FULFILLMENT_CONSUMABLES) // Тип расходников
sellerOwnerId String? // ID селлера-владельца (для расходников селлеров)
sellerOwner Organization? @relation("SellerSupplies", fields: [sellerOwnerId], references: [id], onDelete: SetNull)
shopLocation String? // Местоположение в магазине фулфилмента
type SupplyType @default(FULFILLMENT_CONSUMABLES)
sellerOwnerId String?
shopLocation String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
sellerOwner Organization? @relation("SellerSupplies", fields: [sellerOwnerId], references: [id])
@@map("supplies")
}
@ -266,9 +278,9 @@ model Product {
organizationId String
cartItems CartItem[]
favorites Favorites[]
supplyOrderItems SupplyOrderItem[]
category Category? @relation(fields: [categoryId], references: [id])
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
supplyOrderItems SupplyOrderItem[]
@@unique([organizationId, article])
@@map("products")
@ -401,6 +413,156 @@ model WildberriesSupplyCard {
@@map("wildberries_supply_cards")
}
model Logistics {
id String @id @default(cuid())
fromLocation String
toLocation String
priceUnder1m3 Float
priceOver1m3 Float
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
@@map("logistics")
}
model SupplyOrder {
id String @id @default(cuid())
partnerId String
deliveryDate DateTime
status SupplyOrderStatus @default(PENDING)
totalAmount Decimal @db.Decimal(12, 2)
totalItems Int
fulfillmentCenterId String?
logisticsPartnerId String?
consumableType String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizationId String
items SupplyOrderItem[]
fulfillmentCenter Organization? @relation("SupplyOrderFulfillmentCenter", fields: [fulfillmentCenterId], references: [id])
logisticsPartner Organization? @relation("SupplyOrderLogistics", fields: [logisticsPartnerId], references: [id])
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
partner Organization @relation("SupplyOrderPartner", fields: [partnerId], references: [id])
@@map("supply_orders")
}
model SupplyOrderItem {
id String @id @default(cuid())
supplyOrderId String
productId String
quantity Int
price Decimal @db.Decimal(12, 2)
totalPrice Decimal @db.Decimal(12, 2)
services String[] @default([])
fulfillmentConsumables String[] @default([])
sellerConsumables String[] @default([])
marketplaceCardId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
product Product @relation(fields: [productId], references: [id])
supplyOrder SupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade)
@@unique([supplyOrderId, productId])
@@map("supply_order_items")
}
model SupplySupplier {
id String @id @default(cuid())
name String
contactName String
phone String
market String?
address String?
place String?
telegram String?
organizationId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("SupplySuppliers", fields: [organizationId], references: [id], onDelete: Cascade)
@@map("supply_suppliers")
}
model ExternalAd {
id String @id @default(cuid())
name String
url String
cost Decimal @db.Decimal(12, 2)
date DateTime
nmId String
clicks Int @default(0)
organizationId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("ExternalAds", fields: [organizationId], references: [id], onDelete: Cascade)
@@index([organizationId, date])
@@map("external_ads")
}
model WBWarehouseCache {
id String @id @default(cuid())
organizationId String
cacheDate DateTime
data Json
totalProducts Int @default(0)
totalStocks Int @default(0)
totalReserved Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("WBWarehouseCaches", fields: [organizationId], references: [id], onDelete: Cascade)
@@unique([organizationId, cacheDate])
@@index([organizationId, cacheDate])
@@map("wb_warehouse_caches")
}
model SellerStatsCache {
id String @id @default(cuid())
organizationId String
cacheDate DateTime
period String
dateFrom DateTime?
dateTo DateTime?
productsData Json?
productsTotalSales Decimal? @db.Decimal(15, 2)
productsTotalOrders Int?
productsCount Int?
advertisingData Json?
advertisingTotalCost Decimal? @db.Decimal(15, 2)
advertisingTotalViews Int?
advertisingTotalClicks Int?
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("SellerStatsCaches", fields: [organizationId], references: [id], onDelete: Cascade)
@@unique([organizationId, cacheDate, period, dateFrom, dateTo])
@@index([organizationId, cacheDate])
@@index([expiresAt])
@@map("seller_stats_caches")
}
model ReferralTransaction {
id String @id @default(cuid())
referrerId String
referralId String
points Int
type ReferralTransactionType
description String?
createdAt DateTime @default(now())
referral Organization @relation("ReferralTransactions", fields: [referralId], references: [id])
referrer Organization @relation("ReferrerTransactions", fields: [referrerId], references: [id])
@@index([referrerId, createdAt])
@@index([referralId])
@@map("referral_transactions")
}
enum OrganizationType {
FULFILLMENT
SELLER
@ -467,147 +629,20 @@ enum ProductType {
}
enum SupplyType {
FULFILLMENT_CONSUMABLES // Расходники фулфилмента (купленные фулфилментом для себя)
SELLER_CONSUMABLES // Расходники селлеров (принятые от селлеров для хранения)
FULFILLMENT_CONSUMABLES
SELLER_CONSUMABLES
}
model Logistics {
id String @id @default(cuid())
fromLocation String
toLocation String
priceUnder1m3 Float
priceOver1m3 Float
description String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizationId String
organization Organization @relation(fields: [organizationId], references: [id])
@@map("logistics")
enum CounterpartyType {
MANUAL
REFERRAL
AUTO_BUSINESS
AUTO
}
model SupplyOrder {
id String @id @default(cuid())
partnerId String
deliveryDate DateTime
status SupplyOrderStatus @default(PENDING)
totalAmount Decimal @db.Decimal(12, 2)
totalItems Int
fulfillmentCenterId String?
logisticsPartnerId String? // Опциональная логистика - может назначить фулфилмент
consumableType String? // Классификация расходников: FULFILLMENT_CONSUMABLES, SELLER_CONSUMABLES
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizationId String
items SupplyOrderItem[]
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
partner Organization @relation("SupplyOrderPartner", fields: [partnerId], references: [id])
fulfillmentCenter Organization? @relation("SupplyOrderFulfillmentCenter", fields: [fulfillmentCenterId], references: [id])
logisticsPartner Organization? @relation("SupplyOrderLogistics", fields: [logisticsPartnerId], references: [id])
@@map("supply_orders")
}
model SupplyOrderItem {
id String @id @default(cuid())
supplyOrderId String
productId String
quantity Int
price Decimal @db.Decimal(12, 2)
totalPrice Decimal @db.Decimal(12, 2)
// Поля для рецептуры продукта
services String[] @default([]) // ID услуг
fulfillmentConsumables String[] @default([]) // ID расходников фулфилмента
sellerConsumables String[] @default([]) // ID расходников селлера
marketplaceCardId String? // ID карточки маркетплейса
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
supplyOrder SupplyOrder @relation(fields: [supplyOrderId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id])
@@unique([supplyOrderId, productId])
@@map("supply_order_items")
}
model SupplySupplier {
id String @id @default(cuid())
name String
contactName String
phone String
market String?
address String?
place String?
telegram String?
organizationId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("SupplySuppliers", fields: [organizationId], references: [id], onDelete: Cascade)
@@map("supply_suppliers")
}
model ExternalAd {
id String @id @default(cuid())
name String // Название рекламы
url String // URL рекламы
cost Decimal @db.Decimal(12, 2) // Стоимость
date DateTime // Дата рекламы
nmId String // ID товара Wildberries
clicks Int @default(0) // Количество кликов
organizationId String // ID организации
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("ExternalAds", fields: [organizationId], references: [id], onDelete: Cascade)
@@index([organizationId, date])
@@map("external_ads")
}
model WBWarehouseCache {
id String @id @default(cuid())
organizationId String // ID организации
cacheDate DateTime // Дата кеширования (только дата, без времени)
data Json // Кешированные данные склада WB
totalProducts Int @default(0) // Общее количество товаров
totalStocks Int @default(0) // Общее количество остатков
totalReserved Int @default(0) // Общее количество в резерве
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("WBWarehouseCaches", fields: [organizationId], references: [id], onDelete: Cascade)
@@unique([organizationId, cacheDate])
@@index([organizationId, cacheDate])
@@map("wb_warehouse_caches")
}
model SellerStatsCache {
id String @id @default(cuid())
organizationId String // ID организации
cacheDate DateTime // Дата кеширования (только дата, без времени)
period String // Период статистики (week, month, quarter, custom)
dateFrom DateTime? // Дата начала периода (для custom)
dateTo DateTime? // Дата окончания периода (для custom)
// Данные товаров
productsData Json? // Кешированные данные товаров
productsTotalSales Decimal? @db.Decimal(15, 2) // Общая сумма продаж товаров
productsTotalOrders Int? // Общее количество заказов товаров
productsCount Int? // Количество товаров
// Данные рекламы
advertisingData Json? // Кешированные данные рекламы
advertisingTotalCost Decimal? @db.Decimal(15, 2) // Общие расходы на рекламу
advertisingTotalViews Int? // Общие показы рекламы
advertisingTotalClicks Int? // Общие клики рекламы
// Метаданные
expiresAt DateTime // Время истечения кеша (24 часа)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organization Organization @relation("SellerStatsCaches", fields: [organizationId], references: [id], onDelete: Cascade)
@@unique([organizationId, cacheDate, period, dateFrom, dateTo])
@@index([organizationId, cacheDate])
@@index([expiresAt])
@@map("seller_stats_caches")
enum ReferralTransactionType {
REGISTRATION
AUTO_PARTNERSHIP
FIRST_ORDER
MONTHLY_BONUS
}