프리즈마 스키마
// 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