feat: core domain models (habits, village, quests)

This commit is contained in:
Alexander Andreev 2026-01-02 14:57:37 +03:00
commit 0f85059e92
3 changed files with 128 additions and 0 deletions

View File

@ -0,0 +1,9 @@
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"email" TEXT NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

View File

@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "sqlite"

116
prisma/schema.prisma Normal file
View File

@ -0,0 +1,116 @@
// prisma/schema.prisma
// Enums
// VillageObjectType: Defines the possible types of objects that can be placed in the village grid.
enum VillageObjectType {
HOUSE
FIELD
ROAD
FENCE
OBSTACLE
}
// CropType: Defines the types of crops that can be planted.
enum CropType {
BLUEBERRIES
CORN
}
// User: Represents a registered user and stores their core profile,
// settings, and in-game resources like coins and experience points.
model User {
id Int @id @default(autoincrement())
email String @unique
password String
nickname String?
avatar String? @default("/avatars/default.png")
coins Int @default(0)
exp Int @default(0)
// User settings
soundOn Boolean @default(true)
confettiOn Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
habits Habit[]
dailyVisits DailyVisit[]
village Village?
}
// Habit: A recurring task a user wants to build. Each habit belongs to a
// single user. The `daysOfWeek` field stores which days the habit is active.
model Habit {
id Int @id @default(autoincrement())
name String
// `daysOfWeek` is a JSON array of numbers [0, 1, ..., 6], where 0 is Sunday.
daysOfWeek Json
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
completions HabitCompletion[]
}
// HabitCompletion: Records a single completion of a habit on a specific date.
// This creates a history of the user's progress for each habit.
model HabitCompletion {
id Int @id @default(autoincrement())
date DateTime @db.Date // Store only the date part
// Relations
habit Habit @relation(fields: [habitId], references: [id], onDelete: Cascade)
habitId Int
@@unique([habitId, date]) // A habit can only be completed once per day
}
// DailyVisit: Tracks the user's daily visit for the "I visited the site today"
// quest and for calculating 5-day streaks.
model DailyVisit {
id Int @id @default(autoincrement())
date DateTime @db.Date // Store only the date part
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int
@@unique([userId, date]) // A user can only have one recorded visit per day
}
// Village: The user's personal village, which acts as a container for all
// village objects. Each user has exactly one village.
model Village {
id Int @id @default(autoincrement())
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int @unique // Each user has only one village
objects VillageObject[]
}
// VillageObject: An object (e.g., house, field, obstacle) placed on the
// village grid. It stores the object's type, its coordinates, and optionally
// details if it's an obstacle or a planted crop.
model VillageObject {
id Int @id @default(autoincrement())
type VillageObjectType
x Int
y Int
obstacleMetadata String? // Stores metadata for obstacles (e.g., "rock", "bush").
// Crop details (only if type is FIELD)
cropType CropType?
plantedAt DateTime?
// Relations
village Village @relation(fields: [villageId], references: [id], onDelete: Cascade)
villageId Int
@@unique([villageId, x, y]) // Ensure only one object per grid cell per village
}