Files
SubTracker/apps/api/prisma/schema.prisma
SmileQWQ 264db1a0a6 feat: refactor subscriptions to tags and add Wallos import pipeline
- replace legacy category-based subscription flow with tag-based models, routes, types and UI management

- rebuild Prisma schema around Tag and SubscriptionTag, remove legacy category relation fields, and reseed defaults

- add unified Wallos import inspect/commit flow for JSON, SQLite and ZIP packages with ZIP logo ingestion and DB-version compatibility

- add auto-renew execution in scheduler plus payment record drawer entry in subscription actions

- rename user-facing renewal wording to 续订 in key views and align subscription form with tag selection and auto-renew

- improve logo handling with local library support, remote import persistence, and ZIP asset matching fallback
2026-04-15 11:42:11 +08:00

144 lines
3.9 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
enum SubscriptionStatus {
active
paused
cancelled
expired
}
enum BillingIntervalUnit {
day
week
month
quarter
year
}
enum WebhookDeliveryStatus {
pending
success
failed
}
model Tag {
id String @id @default(cuid())
name String @unique
color String @default("#3b82f6")
icon String @default("apps-outline")
sortOrder Int @default(0)
subscriptionTags SubscriptionTag[]
}
model Subscription {
id String @id @default(cuid())
name String
description String @default("")
websiteUrl String?
logoUrl String?
logoSource String?
logoFetchedAt DateTime?
status SubscriptionStatus @default(active)
amount Float
currency String
billingIntervalCount Int @default(1)
billingIntervalUnit BillingIntervalUnit
autoRenew Boolean @default(false)
startDate DateTime
nextRenewalDate DateTime
notifyDaysBefore Int @default(3)
webhookEnabled Boolean @default(true)
notes String @default("")
tags SubscriptionTag[]
paymentRecords PaymentRecord[]
deliveries WebhookDelivery[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status, nextRenewalDate])
@@index([autoRenew, nextRenewalDate])
}
model SubscriptionTag {
subscriptionId String
tagId String
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@id([subscriptionId, tagId])
@@index([tagId])
}
model PaymentRecord {
id String @id @default(cuid())
subscriptionId String
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
amount Float
currency String
baseCurrency String
convertedAmount Float
exchangeRate Float
paidAt DateTime
periodStart DateTime
periodEnd DateTime
createdAt DateTime @default(now())
@@index([subscriptionId, paidAt])
}
model ExchangeRateSnapshot {
id String @id @default(cuid())
baseCurrency String @unique
ratesJson Json
provider String
fetchedAt DateTime
isStale Boolean @default(false)
updatedAt DateTime @updatedAt
}
model WebhookEndpoint {
id String @id @default(cuid())
name String
url String
secret String
enabled Boolean @default(true)
eventsJson Json
deliveries WebhookDelivery[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model WebhookDelivery {
id String @id @default(cuid())
endpointId String
endpoint WebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
subscriptionId String?
subscription Subscription? @relation(fields: [subscriptionId], references: [id], onDelete: SetNull)
eventType String
resourceKey String
periodKey String
payloadJson Json
status WebhookDeliveryStatus @default(pending)
responseCode Int?
responseBody String?
attemptCount Int @default(0)
lastAttemptAt DateTime?
createdAt DateTime @default(now())
@@unique([endpointId, eventType, resourceKey, periodKey])
@@index([status, createdAt])
}
model Setting {
key String @id
valueJson Json
updatedAt DateTime @updatedAt
}