// InkReach Product Center Prisma Schema // Generated based on docs/dev/database-table-design.md // All TIMESTAMPTZ columns use DateTime @db.Timestamptz(6) // All BIGINT columns use BigInt // snake_case table & column names via @@map / @map generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ---------- Origin Goods ---------- model OriginGood { id BigInt @id @default(autoincrement()) @map("origin_good_id") sdsGoodId String @unique @map("sds_good_id") // Cached SDS product metadata (filled during sync) sdsCategoryId String? @map("sds_category_id") goodName String? @map("good_name") goodImage String? @map("good_image") goodPrice Decimal? @map("good_price") @db.Decimal(12, 2) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) goods Good[] @@index([sdsCategoryId]) @@map("origin_goods") } // ---------- Countries ---------- model Country { id BigInt @id @default(autoincrement()) @map("country_id") countryName String @unique @map("country_name") countryIcon String? @map("country_icon") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) goods Good[] positions Position[] @@map("countries") } // ---------- Categories (self-referential tree) ---------- model Category { id BigInt @id @default(autoincrement()) @map("category_id") parentCategoryId BigInt? @map("parent_category_id") categoryName String @map("category_name") categoryIcon String? @map("category_icon") sdsCategoryId String? @unique @map("sds_category_id") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) parent Category? @relation("CategoryToCategory", fields: [parentCategoryId], references: [id], onDelete: Restrict, onUpdate: NoAction) children Category[] @relation("CategoryToCategory") goods Good[] positions Position[] @@index([parentCategoryId]) @@map("categories") } // ---------- Tags ---------- model Tag { id BigInt @id @default(autoincrement()) @map("tag_id") tagName String @unique @map("tag_name") tagColor String? @map("tag_color") timing String? createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) goods Good[] goodTags GoodTag[] @@map("tags") } // ---------- Positions ---------- model Position { id BigInt @id @default(autoincrement()) @map("position_id") indexVal Int @map("index_val") countryId BigInt? @map("country_id") categoryId BigInt? @map("category_id") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) country Country? @relation(fields: [countryId], references: [id], onDelete: Cascade, onUpdate: NoAction) category Category? @relation(fields: [categoryId], references: [id], onDelete: Cascade, onUpdate: NoAction) goods Good[] @@index([countryId]) @@index([categoryId]) @@index([countryId, categoryId]) @@map("positions") } // ---------- Goods ---------- model Good { id BigInt @id @default(autoincrement()) @map("good_id") originGoodId BigInt @map("origin_good_id") countryId BigInt @map("country_id") categoryId BigInt @map("category_id") tagId BigInt? @map("tag_id") positionId BigInt? @map("position_id") goodName String @map("good_name") goodImage String? @map("good_image") goodPriority Int @default(0) @map("good_priority") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) originGood OriginGood @relation(fields: [originGoodId], references: [id], onDelete: Restrict, onUpdate: NoAction) country Country @relation(fields: [countryId], references: [id], onDelete: Restrict, onUpdate: NoAction) category Category @relation(fields: [categoryId], references: [id], onDelete: Restrict, onUpdate: NoAction) tag Tag? @relation(fields: [tagId], references: [id], onDelete: SetNull, onUpdate: NoAction) position Position? @relation(fields: [positionId], references: [id], onDelete: SetNull, onUpdate: NoAction) goodTags GoodTag[] @@index([originGoodId]) @@index([countryId]) @@index([categoryId]) @@index([tagId]) @@index([positionId]) @@index([goodPriority(sort: Desc)]) @@index([countryId, goodPriority(sort: Desc)]) @@index([categoryId, countryId]) @@map("goods") } // ---------- Good-Tag Junction (M:N) ---------- model GoodTag { goodId BigInt @map("good_id") tagId BigInt @map("tag_id") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) good Good @relation(fields: [goodId], references: [id], onDelete: Cascade, onUpdate: NoAction) tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade, onUpdate: NoAction) @@id([goodId, tagId]) @@index([tagId]) @@map("good_tags") } // ---------- Users (admin authentication) ---------- model User { id BigInt @id @default(autoincrement()) username String @unique passwordHash String @map("password_hash") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) @@map("users") } // ---------- Sync Logs ---------- enum SyncType { CATEGORIES PRODUCTS } enum SyncStatus { RUNNING SUCCESS FAILED } model SyncLog { id BigInt @id @default(autoincrement()) type SyncType status SyncStatus message String? startedAt DateTime @default(now()) @map("started_at") @db.Timestamptz(6) finishedAt DateTime? @map("finished_at") @db.Timestamptz(6) @@index([type, startedAt]) @@map("sync_logs") }