프리즈마 스키마

// This is your Prisma schema file,
// learn more about it in the docs: <https://pris.ly/d/prisma-schema>

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: <https://pris.ly/cli/accelerate-init>

generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "debian-openssl-3.0.x"]
}

datasource db {
  provider          = "postgresql"
  url               = env("DATABASE_URL")
  shadowDatabaseUrl = env("SHADOW_DATABASE_URL") // Add this line
}

enum PointType {
  DRAW // 뽑기(양수)
  PURCHASE // 구매(음수)
  SALE // 판매(양수)
}

enum ExchangeStatus {
  REQUESTED // 교환 요청됨
  ACCEPTED // 교환 수락됨
  REJECTED // 교환 거절됨
  CANCELLED // 교환 취소됨
  COMPLETED // 교환 완료됨
}

// UserCard의 상태를 나타내는 Enum
// FOR_SALE, FOR_SALE_AND_TRADE는 Shop의 listingType으로 이전됨
enum CardStatus {
  IDLE // 기본 상태 (소장 중)
  LISTED // 판매 게시글에 현재 등록된 상태
  SOLD // 판매 또는 교환이 완료된 상태
}

// Shop 게시글의 판매 유형을 나타내는 Enum (새로 추가)
enum ShopListingType {
  FOR_SALE // 판매만 원하는 상태
  FOR_SALE_AND_TRADE // 판매 및 교환을 원하는 상태
}

enum CardGrade {
  COMMON
  RARE
  SUPER_RARE
  LEGENDARY
}

enum CardGenre {
  TRAVEL // 여행
  LANDSCAPE // 풍경, 자연
  PORTRAIT // 인물로 kpop 인물
  OBJECT // 사물
}

enum NotificationType {
  EXCHANGE_PROPOSED // 교환 제안 알림
  EXCHANGE_ACCEPTED // 교환 승인 알림
  EXCHANGE_DECLINED // 교환 거절 알림
  PURCHASE_COMPLETED // 구매 완료 알림
  SELL_COMPLETED // 판매 완료 알림
  SOLD_OUT // 품절 알림 (Shop 게시글의 remainingQuantity가 0이 되었을 때)
}

model User {
  id                Int            @id @default(autoincrement())
  email             String         @unique // 이메일 (유일해야 함)
  encryptedPassword String? // 암호화된 비밀번호
  nickname          String         @unique // 닉네임 (유일해야 함)
  refreshToken      String? // 리프레시 토큰 (선택 사항)
  createdAt         DateTime       @default(now()) // 레코드 생성 시간
  updatedAt         DateTime       @updatedAt // 레코드 업데이트 시간
  userCard          UserCard[] // 유저가 소유한 포토카드 목록 (UserCard 모델과 1:N 관계)
  notification      Notification[] // 유저가 받은 알림 목록 (Notification 모델과 1:N 관계)
  pointHistory      PointHistory[] // 유저의 포인트 적립/사용 기록 (PointHistory 모델과 1:N 관계)
  point             Point? // 유저의 현재 포인트 잔액 정보 (Point 모델과 1:1 관계)
  shopListings      Shop[]         @relation("UserShopListings") // 유저가 등록한 판매 게시글 목록 (새로 추가된 관계)
  provider          String         @default("local")
  providerId        String?        @unique
  PhotoCard         PhotoCard[]
}

model PhotoCard {
  id              Int        @id @default(autoincrement())
  name            String
  description     String?
  imageUrl        String
  grade           CardGrade
  genre           CardGenre
  price           Int // 이 필드는 기본 가격 정보로 사용하거나, Shop에서 개별 가격을 정하므로 제거/수정 고려 가능
  initialQuantity Int // PhotoCard 자체의 최초 발행 수량
  createdAt       DateTime   @default(now())
  updatedAt       DateTime   @updatedAt
  creatorId       Int // 최초 발행자 id
  creator         User       @relation(fields: [creatorId], references: [id])
  userCard        UserCard[] // 이 종류의 포토카드를 소유한 UserCard 목록
  shopListings    Shop[] // 이 종류의 포토카드가 등록된 판매 게시글 목록
}

// 유저가 소유한 포토카드 정보를 관리하는 모델
model UserCard {
  id        Int        @id @default(autoincrement())
  status    CardStatus @default(IDLE) // 포토카드 상태 (IDLE, LISTED, SOLD 등)
  createdAt DateTime   @default(now())
  updatedAt DateTime   @updatedAt

  user        User      @relation(fields: [userId], references: [id])
  userId      Int
  photoCard   PhotoCard @relation(fields: [photoCardId], references: [id])
  photoCardId Int

  // 이 UserCard가 어떤 판매 게시글에 속해 있는지
  // UserCard는 동시에 하나의 활성 판매 게시글에만 속할 수 있음
  shopListing   Shop? @relation("ShopListedCards", fields: [shopListingId], references: [id])
  shopListingId Int? // 이 UserCard를 포함하는 Shop의 ID (선택 사항)

  requestExchanges Exchange[] @relation("RequestCard")
  targetExchanges  Exchange[] @relation("TargetCard")
}

// 포토카드 판매 게시글 정보를 관리하는 모델
// 하나의 Shop 게시글이 여러 장의 동일한 PhotoCard 종류의 UserCard를 묶어서 판매 가능
model Shop {
  id                Int             @id @default(autoincrement())
  price             Int // 판매 가격 (이 게시글에 포함된 모든 카드에 동일 적용)
  initialQuantity   Int // 이 판매 게시글에 처음 등록된 카드의 수량 (예: 3장)
  remainingQuantity Int // 판매되고 남은 카드의 수량
  listingType       ShopListingType // 판매 유형 (FOR_SALE, FOR_SALE_AND_TRADE)

  // 교환 관련 정보 (listingType이 FOR_SALE_AND_TRADE일 때 유효)
  exchangeGrade       CardGrade? // 교환 시 원하는 포토카드 등급 (선택 사항)
  exchangeGenre       CardGenre? // 교환 시 원하는 포토카드 장르 (선택 사항)
  exchangeDescription String? // 교환 관련 추가 설명 (선택 사항)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // 어떤 종류의 PhotoCard를 판매하는 게시글인지
  photoCard   PhotoCard @relation(fields: [photoCardId], references: [id])
  photoCardId Int

  // 이 판매 게시글을 등록한 판매자
  seller   User @relation("UserShopListings", fields: [sellerId], references: [id])
  sellerId Int

  // 이 판매 게시글에 현재 포함된 UserCard 목록
  // UserCard의 shopListingId를 통해 연결됨
  listedItems UserCard[] @relation("ShopListedCards")
}

// 유저의 포인트 적립/사용 기록을 관리하는 모델(필수는 아님)
model PointHistory {
  id        Int       @id @default(autoincrement())
  points    Int // 포인트 변동량 (양수: 적립, 음수: 사용)
  pointType PointType // 포인트 변동 유형
  createdAt DateTime  @default(now()) // 레코드 생성 시간
  updatedAt DateTime  @updatedAt // 레코드 업데이트 시간
  user      User      @relation(fields: [userId], references: [id]) // 포인트 기록과 연결된 유저 (User 모델 참조)
  userId    Int
}

model Point {
  id         Int      @id @default(autoincrement())
  balance    Int      @default(0) // 현재 포인트 잔액 (기본값: 0)
  lastDrawAt DateTime @default("2000-01-01T00:00:00Z")
  createdAt  DateTime @default(now()) // 레코드 생성 시간
  updatedAt  DateTime @updatedAt // 레코드 업데이트 시간
  user       User     @relation(fields: [userId], references: [id]) // 포인트 정보와 연결된 유저 (User 모델 참조)
  userId     Int      @unique
}

model Notification {
  id               Int              @id @default(autoincrement())
  content          String // 알림 내용
  notificationType NotificationType // 알림 유형
  isRead           Boolean          @default(false) // 알림 읽음 여부 (기본값: 안 읽음)
  createdAt        DateTime         @default(now()) // 레코드 생성 시간
  updatedAt        DateTime         @updatedAt // 레코드 업데이트 시간
  user             User             @relation(fields: [userId], references: [id]) // 알림을 받는 유저 (User 모델 참조)
  userId           Int
  relatedShopId    Int
}

// 유저 간의 포토카드 교환 요청 정보를 관리하는 모델
model Exchange {
  id            Int            @id @default(autoincrement())
  status        ExchangeStatus // 교환 상태 (REQUESTED, ACCEPTED, REJECTED, CANCELLED, COMPLETED 중 하나)
  description   String? // 교환 관련 설명 (선택 사항)
  createdAt     DateTime       @default(now()) // 레코드 생성 시간
  updatedAt     DateTime       @updatedAt // 레코드 업데이트 시간
  requestCard   UserCard       @relation("RequestCard", fields: [requestCardId], references: [id]) // 교환을 요청하는 유저의 카드 (UserCard 모델 참조)
  requestCardId Int
  targetCard    UserCard       @relation("TargetCard", fields: [targetCardId], references: [id]) // 교환 대상 카드 (UserCard 모델 참조)
  targetCardId  Int
}

스키마를 바탕으로 제작한 다이어그램 ERD

https://www.dbdiagram.io

image.png