Assist_Design/apps/bff/prisma/schema.prisma
barsa 2b183272cf Implement Notifications Feature and Enhance BFF Modules
- Introduced a new Notification model in the Prisma schema to manage in-app notifications for users.
- Integrated the NotificationsModule into the BFF application, allowing for the handling of notifications related to user actions and events.
- Updated the CatalogCdcSubscriber to create notifications for account eligibility and verification status changes, improving user engagement.
- Enhanced the CheckoutRegistrationService to create opportunities for SIM orders, integrating with the new notifications system.
- Refactored various modules to include the NotificationsModule, ensuring seamless interaction and notification handling across the application.
- Updated the frontend to display notification alerts in the AppShell header, enhancing user experience and accessibility.
2025-12-23 11:36:44 +09:00

280 lines
9.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// =============================================================================
// Prisma Schema - Customer Portal BFF
// =============================================================================
// IMPORTANT: When building Docker images, the Prisma client must be regenerated
// from the production directory structure. See prisma/README.md for details.
// =============================================================================
generator client {
provider = "prisma-client-js"
// Only include engines we actually need:
// - native: for local development
// - linux-musl-openssl-3.0.x: for Alpine production
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
// Prisma 7+: URL is configured via prisma.config.ts for migrations
// and via --url flag for studio. Runtime uses the adapter in PrismaClient.
}
model User {
id String @id @default(uuid())
email String @unique
passwordHash String? @map("password_hash")
role UserRole @default(USER)
// Authentication state only - profile data comes from WHMCS
mfaSecret String? @map("mfa_secret")
emailVerified Boolean @default(false) @map("email_verified")
failedLoginAttempts Int @default(0) @map("failed_login_attempts")
lockedUntil DateTime? @map("locked_until")
lastLoginAt DateTime? @map("last_login_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
auditLogs AuditLog[]
idMapping IdMapping?
residenceCardSubmission ResidenceCardSubmission?
notifications Notification[]
@@map("users")
}
model IdMapping {
userId String @id @map("user_id")
whmcsClientId Int @unique @map("whmcs_client_id")
sfAccountId String? @map("sf_account_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("id_mappings")
}
model AuditLog {
id String @id @default(uuid())
userId String? @map("user_id")
action AuditAction
resource String?
details Json?
ipAddress String? @map("ip_address")
userAgent String? @map("user_agent")
success Boolean @default(true)
error String?
createdAt DateTime @default(now()) @map("created_at")
user User? @relation(fields: [userId], references: [id])
@@index([userId, action])
@@index([action, createdAt])
@@index([createdAt])
@@map("audit_logs")
}
enum UserRole {
USER
ADMIN
}
enum AuditAction {
LOGIN_SUCCESS
LOGIN_FAILED
LOGOUT
SIGNUP
PASSWORD_RESET
PASSWORD_CHANGE
ACCOUNT_LOCKED
ACCOUNT_UNLOCKED
PROFILE_UPDATE
MFA_ENABLED
MFA_DISABLED
API_ACCESS
SYSTEM_MAINTENANCE
}
enum ResidenceCardStatus {
PENDING
VERIFIED
REJECTED
}
model ResidenceCardSubmission {
id String @id @default(uuid())
userId String @unique @map("user_id")
status ResidenceCardStatus @default(PENDING)
filename String
mimeType String @map("mime_type")
sizeBytes Int @map("size_bytes")
content Bytes @db.ByteA
submittedAt DateTime @default(now()) @map("submitted_at")
reviewedAt DateTime? @map("reviewed_at")
reviewerNotes String? @map("reviewer_notes")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("residence_card_submissions")
}
// Per-SIM daily usage snapshot used to build full-month charts
model SimUsageDaily {
id Int @id @default(autoincrement())
account String
date DateTime @db.Date
usageMb Float
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([account, date])
@@index([account, date])
@@map("sim_usage_daily")
}
model SimVoiceOptions {
account String @id
voiceMailEnabled Boolean @default(false) @map("voice_mail_enabled")
callWaitingEnabled Boolean @default(false) @map("call_waiting_enabled")
internationalRoamingEnabled Boolean @default(false) @map("international_roaming_enabled")
networkType String @default("4G") @map("network_type")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("sim_voice_options")
}
// Call history from SFTP monthly imports (domestic calls)
model SimCallHistoryDomestic {
id String @id @default(uuid())
account String // Customer phone number (e.g., "08077052946")
callDate DateTime @db.Date @map("call_date") // Date the call was made
callTime String @map("call_time") // Start time of the call (HHMMSS)
calledTo String @map("called_to") // Phone number called
location String? // Location info
durationSec Int @map("duration_sec") // Duration in seconds (320 = 32.0 sec)
chargeYen Int @map("charge_yen") // Call charge in JPY
month String // YYYY-MM format for filtering
createdAt DateTime @default(now()) @map("created_at")
@@unique([account, callDate, callTime, calledTo])
@@index([account, month])
@@index([account, callDate])
@@map("sim_call_history_domestic")
}
// Call history from SFTP monthly imports (international calls)
model SimCallHistoryInternational {
id String @id @default(uuid())
account String // Customer phone number
callDate DateTime @db.Date @map("call_date") // Date the call was made
startTime String @map("start_time") // Start time of the call
stopTime String? @map("stop_time") // Stop time (if available)
country String? // Country/location for international calls
calledTo String @map("called_to") // Phone number called
durationSec Int @map("duration_sec") // Duration in seconds
chargeYen Int @map("charge_yen") // Call charge in JPY
month String // YYYY-MM format for filtering
createdAt DateTime @default(now()) @map("created_at")
@@unique([account, callDate, startTime, calledTo])
@@index([account, month])
@@index([account, callDate])
@@map("sim_call_history_international")
}
// SMS history from SFTP monthly imports
model SimSmsHistory {
id String @id @default(uuid())
account String // Customer phone number
smsDate DateTime @db.Date @map("sms_date") // Date the SMS was sent
smsTime String @map("sms_time") // Time the SMS was sent
sentTo String @map("sent_to") // Phone number SMS was sent to
smsType SmsType @default(DOMESTIC) @map("sms_type") // SMS or 国際SMS
month String // YYYY-MM format for filtering
createdAt DateTime @default(now()) @map("created_at")
@@unique([account, smsDate, smsTime, sentTo])
@@index([account, month])
@@index([account, smsDate])
@@map("sim_sms_history")
}
enum SmsType {
DOMESTIC //
INTERNATIONAL // 国際SMS
}
// Track which months have been imported
model SimHistoryImport {
id String @id @default(uuid())
month String @unique // YYYY-MM format
talkFile String? @map("talk_file") // Filename imported
smsFile String? @map("sms_file") // Filename imported
talkRecords Int @default(0) @map("talk_records") // Records imported
smsRecords Int @default(0) @map("sms_records") // Records imported
importedAt DateTime @default(now()) @map("imported_at")
status String @default("completed") // completed, failed, partial
@@map("sim_history_imports")
}
// =============================================================================
// Notifications - In-app notifications synced with Salesforce email triggers
// =============================================================================
model Notification {
id String @id @default(uuid())
userId String @map("user_id")
// Notification content
type NotificationType
title String
message String?
// Action (optional CTA button)
actionUrl String? @map("action_url")
actionLabel String? @map("action_label")
// Source tracking for deduplication
source NotificationSource @default(SALESFORCE)
sourceId String? @map("source_id") // SF Account ID, Order ID, etc.
// Status
read Boolean @default(false)
readAt DateTime? @map("read_at")
dismissed Boolean @default(false)
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
expiresAt DateTime @map("expires_at") // 30 days from creation
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, read, dismissed])
@@index([userId, createdAt])
@@index([expiresAt])
@@map("notifications")
}
enum NotificationType {
ELIGIBILITY_ELIGIBLE
ELIGIBILITY_INELIGIBLE
VERIFICATION_VERIFIED
VERIFICATION_REJECTED
ORDER_APPROVED
ORDER_ACTIVATED
ORDER_FAILED
CANCELLATION_SCHEDULED
CANCELLATION_COMPLETE
PAYMENT_METHOD_EXPIRING
INVOICE_DUE
SYSTEM_ANNOUNCEMENT
}
enum NotificationSource {
SALESFORCE
WHMCS
PORTAL
SYSTEM
}