✨ Características: - 45 ejercicios universitarios (Basic → Advanced) - Renderizado LaTeX profesional - IA generativa (Z.ai/DashScope) - Docker 9 servicios - Tests 123/123 pasando - Seguridad enterprise (JWT, XSS, Rate limiting) 🐳 Infraestructura: - Next.js 14 + Node.js 20 - PostgreSQL 15 + Redis 7 - Docker Compose completo - Nginx + SSL ready 📚 Documentación: - 5 informes técnicos completos - README profesional - Scripts de deployment automatizados Estado: Producción lista ✅
646 lines
13 KiB
Markdown
646 lines
13 KiB
Markdown
# Data Model Documentation
|
|
|
|
## Overview
|
|
|
|
Complete Prisma schema with 13 entities, 10 enums, and comprehensive relationships for the Math Learning Platform.
|
|
|
|
---
|
|
|
|
## Entities
|
|
|
|
### 1. User (users)
|
|
|
|
User accounts with authentication and profile information.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `email` (String, @unique)
|
|
- `username` (String, @unique)
|
|
- `passwordHash` (String)
|
|
- `telegramChatId` (String?, @unique, hidden from users)
|
|
- `isActive` (Boolean, @default(true))
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
- `lastLoginAt` (DateTime?)
|
|
|
|
**Relationships:**
|
|
- Has many ExerciseAttempt
|
|
- Has many Progress
|
|
- Has many UserAchievement
|
|
- Has many Ranking
|
|
- Has many Notification
|
|
|
|
**Indexes:**
|
|
- email
|
|
- username
|
|
- isActive
|
|
|
|
---
|
|
|
|
### 2. Module (modules)
|
|
|
|
Pedagogical modules containing educational content.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `name` (String)
|
|
- `description` (String)
|
|
- `type` (ModuleType, enum)
|
|
- `order` (Int)
|
|
- `isPublished` (Boolean, @default(false))
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
- `introduction` (String?, Text, LaTeX supported)
|
|
- `examples` (Json?, array of examples with formulas)
|
|
- `exercises` (Json?, exercise sections)
|
|
- `answers` (Json?, answer keys)
|
|
- `estimatedHours` (Int?, @default(0))
|
|
- `difficultyLevel` (ExerciseDifficulty, @default(INTERMEDIATE))
|
|
- `totalExercises` (Int, @default(0))
|
|
|
|
**Relationships:**
|
|
- Has many Topic
|
|
- Has many Exercise
|
|
- Has many Progress
|
|
- Has many Ranking
|
|
|
|
**Indexes:**
|
|
- type+order (unique)
|
|
- type
|
|
- order
|
|
- isPublished
|
|
|
|
**Modules:**
|
|
1. FUNDAMENTOS - Vectores and Matrices
|
|
2. SISTEMAS_ESPACIOS - Sistemas and Espacios Vectoriales
|
|
3. APLICACIONES - Programación Lineal
|
|
|
|
---
|
|
|
|
### 3. Topic (topics)
|
|
|
|
Mathematical topics within modules.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `moduleId` (String, foreign key)
|
|
- `name` (String)
|
|
- `type` (TopicType, enum)
|
|
- `order` (Int)
|
|
- `description` (String?)
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
- `theoryContent` (Json?, theory with formulas)
|
|
- `formulas` (Json?, LaTeX formulas)
|
|
- `keyPoints` (Json?, key learning points)
|
|
- `commonMistakes` (Json?, common errors)
|
|
|
|
**Relationships:**
|
|
- Belongs to Module (onDelete: Cascade)
|
|
- Has many Exercise
|
|
|
|
**Indexes:**
|
|
- moduleId+order (unique)
|
|
- moduleId
|
|
- type
|
|
|
|
**Topics:**
|
|
1. VECTORES
|
|
2. MATRICES
|
|
3. SISTEMAS
|
|
4. ESPACIOS_VECTORIALES
|
|
5. PROGRAMACION_LINEAL
|
|
|
|
---
|
|
|
|
### 4. Exercise (exercises)
|
|
|
|
Mathematical exercises with LaTeX formulas.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `moduleId` (String, foreign key)
|
|
- `topicId` (String?, foreign key)
|
|
- `type` (ExerciseType, enum)
|
|
- `difficulty` (ExerciseDifficulty, enum)
|
|
- `order` (Int)
|
|
- `statement` (String, Text, LaTeX supported)
|
|
- `correctAnswer` (String, Text, LaTeX supported)
|
|
- `solutionSteps` (Json?, step-by-step solutions)
|
|
- `formulas` (Json?, related formulas)
|
|
- `hints` (Json?, hints with costs)
|
|
- `isAIGenerated` (Boolean, @default(false))
|
|
- `isPublished` (Boolean, @default(false))
|
|
- `points` (Int, @default(10))
|
|
- `timeLimitSeconds` (Int?)
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
- `multipleChoiceOptions` (Json?, for multiple choice)
|
|
- `proofRequirements` (Json?, for proof exercises)
|
|
- `calculationSteps` (Json?, for calculation exercises)
|
|
|
|
**Relationships:**
|
|
- Belongs to Module (onDelete: Cascade)
|
|
- Belongs to Topic (onDelete: SetNull)
|
|
- Has many ExerciseAttempt
|
|
|
|
**Indexes:**
|
|
- moduleId+order (unique)
|
|
- moduleId
|
|
- topicId
|
|
- type
|
|
- difficulty
|
|
- isPublished
|
|
- isAIGenerated
|
|
|
|
---
|
|
|
|
### 5. ExerciseAttempt (exercise_attempts)
|
|
|
|
User attempts at exercises.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `userId` (String, foreign key)
|
|
- `exerciseId` (String, foreign key)
|
|
- `userAnswer` (String, Text)
|
|
- `status` (AttemptStatus, enum)
|
|
- `pointsEarned` (Int, @default(0))
|
|
- `timeSpentSeconds` (Int)
|
|
- `hintsUsed` (Int, @default(0))
|
|
- `feedback` (String?, Text)
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `attemptNumber` (Int)
|
|
- `isPerfect` (Boolean, @default(false))
|
|
- `skipped` (Boolean, @default(false))
|
|
|
|
**Relationships:**
|
|
- Belongs to User (onDelete: Cascade)
|
|
- Belongs to Exercise (onDelete: Cascade)
|
|
|
|
**Indexes:**
|
|
- userId+exerciseId+attemptNumber (unique)
|
|
- userId
|
|
- exerciseId
|
|
- status
|
|
- createdAt
|
|
- userId+exerciseId
|
|
|
|
---
|
|
|
|
### 6. Progress (progress)
|
|
|
|
User progress tracking per module.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `userId` (String, foreign key)
|
|
- `moduleId` (String, foreign key)
|
|
- `exercisesCompleted` (Int, @default(0))
|
|
- `totalExercises` (Int, @default(0))
|
|
- `points` (Int, @default(0))
|
|
- `percentage` (Float, @default(0))
|
|
- `isStarted` (Boolean, @default(false))
|
|
- `isCompleted` (Boolean, @default(false))
|
|
- `startedAt` (DateTime?)
|
|
- `completedAt` (DateTime?)
|
|
- `lastAccessedAt` (DateTime?)
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
- `averageScore` (Float?)
|
|
- `totalTimeSpent` (Int, @default(0), seconds)
|
|
- `perfectExercises` (Int, @default(0))
|
|
- `attemptsCount` (Int, @default(0))
|
|
|
|
**Relationships:**
|
|
- Belongs to User (onDelete: Cascade)
|
|
- Belongs to Module (onDelete: Cascade)
|
|
|
|
**Indexes:**
|
|
- userId+moduleId (unique)
|
|
- userId
|
|
- moduleId
|
|
- isCompleted
|
|
- points
|
|
|
|
---
|
|
|
|
### 7. Achievement (achievements)
|
|
|
|
Gamification badges and achievements.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `code` (String, @unique)
|
|
- `name` (String)
|
|
- `description` (String)
|
|
- `category` (AchievementCategory, enum)
|
|
- `rarity` (AchievementRarity, enum)
|
|
- `icon` (String, emoji or icon name)
|
|
- `requirementType` (RequirementType, enum)
|
|
- `requirementValue` (Int)
|
|
- `points` (Int, @default(0))
|
|
- `metadata` (Json?, {color, animation, tooltip, unlockMessage})
|
|
- `isActive` (Boolean, @default(true))
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
|
|
**Relationships:**
|
|
- Has many UserAchievement
|
|
|
|
**Indexes:**
|
|
- category
|
|
- rarity
|
|
- isActive
|
|
- code
|
|
|
|
**Achievements (18 total):**
|
|
- Exercises: FIRST_EXERCISE, TEN_EXERCISES, HUNDRED_EXERCISES, FIVE_PERFECT
|
|
- Modules: FIRST_MODULE, ALL_MODULES, PERFECT_MODULE
|
|
- Streaks: THREE_DAY_STREAK, WEEK_STREAK, MONTH_STREAK
|
|
- Ranking: TOP_10, PODIUM, CHAMPION
|
|
- Special: EARLY_BIRD, NIGHT_OWL, AUTODIDACT
|
|
|
|
---
|
|
|
|
### 8. UserAchievement (user_achievements)
|
|
|
|
Unlocked achievements for users.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `userId` (String, foreign key)
|
|
- `achievementId` (String, foreign key)
|
|
- `progress` (Int, @default(0))
|
|
- `unlockedAt` (DateTime?)
|
|
- `metadata` (Json?, {unlockedDetails, relatedStats})
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
|
|
**Relationships:**
|
|
- Belongs to User (onDelete: Cascade)
|
|
- Belongs to Achievement (onDelete: Cascade)
|
|
|
|
**Indexes:**
|
|
- userId+achievementId (unique)
|
|
- userId
|
|
- achievementId
|
|
- unlockedAt
|
|
|
|
---
|
|
|
|
### 9. Ranking (rankings)
|
|
|
|
Leaderboard positions (global and per-module).
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `userId` (String, foreign key)
|
|
- `moduleId` (String?, foreign key, null=global)
|
|
- `position` (Int)
|
|
- `points` (Int, @default(0))
|
|
- `exercisesCompleted` (Int, @default(0))
|
|
- `streak` (Int, @default(0))
|
|
- `lastUpdated` (DateTime, @default(now))
|
|
- `perfectExercises` (Int, @default(0))
|
|
- `averageScore` (Float?)
|
|
- `totalAttempts` (Int, @default(0))
|
|
- `achievementsUnlocked` (Int, @default(0))
|
|
|
|
**Relationships:**
|
|
- Belongs to User (onDelete: Cascade)
|
|
- Belongs to Module (onDelete: Cascade)
|
|
|
|
**Indexes:**
|
|
- userId+moduleId (unique)
|
|
- moduleId
|
|
- position
|
|
- points
|
|
- streak
|
|
|
|
---
|
|
|
|
### 10. Notification (notifications)
|
|
|
|
Telegram notifications (backend only).
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `type` (NotificationType, enum)
|
|
- `title` (String)
|
|
- `message` (String, Text)
|
|
- `telegramChatId` (String, foreign key to User.telegramChatId)
|
|
- `status` (NotificationStatus, enum, @default(PENDING))
|
|
- `priority` (Int, @default(0))
|
|
- `metadata` (Json?, {userId, relatedData, actionUrl})
|
|
- `attempts` (Int, @default(0))
|
|
- `lastAttemptAt` (DateTime?)
|
|
- `sentAt` (DateTime?)
|
|
- `errorMessage` (String?, Text)
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
|
|
**Relationships:**
|
|
- Belongs to User (via telegramChatId, onDelete: Cascade)
|
|
|
|
**Indexes:**
|
|
- status
|
|
- type
|
|
- telegramChatId
|
|
- createdAt
|
|
- priority
|
|
|
|
---
|
|
|
|
### 11. ProcessedPdf (processed_pdfs)
|
|
|
|
PDF processing metadata.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `fileName` (String, @unique)
|
|
- `originalPath` (String)
|
|
- `type` (PdfType, enum)
|
|
- `topicType` (TopicType?, enum)
|
|
- `isProcessed` (Boolean, @default(false))
|
|
- `processingStartedAt` (DateTime?)
|
|
- `processingCompletedAt` (DateTime?)
|
|
- `errorMessage` (String?, Text)
|
|
- `extractedText` (Json?, {pages: [...]})
|
|
- `exercisesDetected` (Json?, array of exercises)
|
|
- `formulasExtracted` (Json?, array of formulas)
|
|
- `metadata` (Json?, {author, pages, isbn, year, edition})
|
|
- `totalPages` (Int?)
|
|
- `processingVersion` (String?)
|
|
- `checksum` (String?)
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
|
|
**Indexes:**
|
|
- type
|
|
- topicType
|
|
- isProcessed
|
|
- fileName
|
|
|
|
---
|
|
|
|
### 12. SystemConfig (system_config)
|
|
|
|
System configuration key-value storage.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `key` (String, @unique)
|
|
- `value` (String, Text)
|
|
- `description` (String?)
|
|
- `category` (String?)
|
|
- `isPublic` (Boolean, @default(false))
|
|
- `createdAt` (DateTime, @default(now))
|
|
- `updatedAt` (DateTime, @updatedAt)
|
|
|
|
**Indexes:**
|
|
- category
|
|
- key
|
|
|
|
---
|
|
|
|
### 13. AuditLog (audit_logs)
|
|
|
|
Audit trail for system events.
|
|
|
|
**Fields:**
|
|
- `id` (String, @id, @default(cuid))
|
|
- `userId` (String?)
|
|
- `action` (String)
|
|
- `entityType` (String)
|
|
- `entityId` (String?)
|
|
- `metadata` (Json?)
|
|
- `ipAddress` (String?)
|
|
- `userAgent` (String?)
|
|
- `createdAt` (DateTime, @default(now))
|
|
|
|
**Indexes:**
|
|
- userId
|
|
- action
|
|
- entityType
|
|
- createdAt
|
|
|
|
---
|
|
|
|
## Enums
|
|
|
|
### ModuleType
|
|
- FUNDAMENTOS
|
|
- SISTEMAS_ESPACIOS
|
|
- APLICACIONES
|
|
|
|
### TopicType
|
|
- VECTORES
|
|
- MATRICES
|
|
- SISTEMAS
|
|
- ESPACIOS_VECTORIALES
|
|
- PROGRAMACION_LINEAL
|
|
|
|
### ExerciseType
|
|
- MULTIPLE_CHOICE
|
|
- OPEN_RESPONSE
|
|
- CALCULATION
|
|
- PROOF
|
|
- TRUE_FALSE
|
|
|
|
### ExerciseDifficulty
|
|
- BASIC
|
|
- INTERMEDIATE
|
|
- ADVANCED
|
|
- EXPERT
|
|
|
|
### AttemptStatus
|
|
- CORRECT
|
|
- INCORRECT
|
|
- PARTIAL
|
|
- PENDING
|
|
|
|
### AchievementCategory
|
|
- EXERCISES
|
|
- MODULES
|
|
- STREAKS
|
|
- RANKING
|
|
- SPECIAL
|
|
|
|
### AchievementRarity
|
|
- COMMON
|
|
- RARE
|
|
- EPIC
|
|
- LEGENDARY
|
|
|
|
### RequirementType
|
|
- EXERCISES_COMPLETED
|
|
- MODULES_COMPLETED
|
|
- PERFECT_SCORES
|
|
- STREAK_DAYS
|
|
- RANKING_POSITION
|
|
- EXERCISES_WITHOUT_HINTS
|
|
- EARLY_BIRD
|
|
- NIGHT_OWL
|
|
- PERFECT_MODULE
|
|
|
|
### NotificationType
|
|
- NEW_USER
|
|
- EXERCISE_COMPLETED
|
|
- MODULE_COMPLETED
|
|
- ACHIEVEMENT_UNLOCKED
|
|
- SYSTEM_ERROR
|
|
- DAILY_SUMMARY
|
|
- RANKING_CHANGED
|
|
|
|
### NotificationStatus
|
|
- PENDING
|
|
- SENT
|
|
- FAILED
|
|
|
|
### PdfType
|
|
- TEXTBOOK
|
|
- PRACTICE
|
|
- PRACTICE_ANSWERS
|
|
- EXAM
|
|
- ADDITIONAL_MATERIAL
|
|
|
|
---
|
|
|
|
## Key Relationships
|
|
|
|
### User Progress Flow
|
|
1. User → ExerciseAttempt (many)
|
|
2. User → Progress (one per module)
|
|
3. User → UserAchievement (many)
|
|
4. User → Ranking (one global + one per module)
|
|
|
|
### Module Content Flow
|
|
1. Module → Topic (many)
|
|
2. Module → Exercise (many)
|
|
3. Topic → Exercise (many)
|
|
4. Exercise → ExerciseAttempt (many)
|
|
|
|
### Gamification Flow
|
|
1. Achievement → UserAchievement (many)
|
|
2. ExerciseAttempt → triggers Achievement check
|
|
3. Progress → triggers Ranking update
|
|
|
|
### Notification Flow
|
|
1. ExerciseAttempt → triggers Notification
|
|
2. Progress → triggers Notification
|
|
3. UserAchievement → triggers Notification
|
|
|
|
---
|
|
|
|
## JSON Fields Structure
|
|
|
|
### Module.examples
|
|
```json
|
|
[
|
|
{
|
|
"title": "string",
|
|
"content": "string",
|
|
"latexFormula": "string",
|
|
"explanation": "string",
|
|
"difficulty": "BASIC|INTERMEDIATE|ADVANCED|EXPERT"
|
|
}
|
|
]
|
|
```
|
|
|
|
### Exercise.solutionSteps
|
|
```json
|
|
[
|
|
{
|
|
"step": 1,
|
|
"explanation": "string",
|
|
"latexFormula": "string"
|
|
}
|
|
]
|
|
```
|
|
|
|
### Exercise.hints
|
|
```json
|
|
[
|
|
{
|
|
"hint": "string",
|
|
"cost": 2
|
|
}
|
|
]
|
|
```
|
|
|
|
### ProcessedPdf.extractedText
|
|
```json
|
|
{
|
|
"pages": [
|
|
{
|
|
"page": 1,
|
|
"text": "string",
|
|
"tables": [...],
|
|
"images": [...]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Optimization
|
|
|
|
### Indexes
|
|
All foreign keys and frequently queried fields are indexed.
|
|
|
|
### Cascade Deletes
|
|
- User deletion → all related data deleted
|
|
- Module deletion → topics, exercises, progress deleted
|
|
- Exercise deletion → attempts deleted
|
|
|
|
### JSON Fields
|
|
Used for flexible content storage (formulas, examples, solutions).
|
|
|
|
---
|
|
|
|
## Data Integrity
|
|
|
|
### Unique Constraints
|
|
- User email, username
|
|
- User telegramChatId
|
|
- Achievement code
|
|
- ProcessedPdf fileName
|
|
- Module type+order
|
|
- Topic moduleId+order
|
|
- Exercise moduleId+order
|
|
- UserProgress userId+moduleId
|
|
- UserAchievement userId+achievementId
|
|
- Ranking userId+moduleId
|
|
|
|
### Required Fields
|
|
All critical fields have @default or are required.
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
### Potential Additions
|
|
1. Exercise comments/discussions
|
|
2. User social features (follow, share)
|
|
3. Course enrollments
|
|
4. Certificate generation
|
|
5. Analytics dashboard
|
|
6. Content versioning
|
|
7. Multi-language support
|
|
|
|
### Scalability Considerations
|
|
1. Partitioning for large tables (ExerciseAttempt)
|
|
2. Archival strategy for old data
|
|
3. Caching layer for frequently accessed data
|
|
4. Read replicas for reporting queries
|
|
|
|
---
|
|
|
|
**Schema Version**: 1.0.0
|
|
**Last Updated**: 2026-03-23
|
|
**Total Entities**: 13
|
|
**Total Enums**: 10
|
|
**Total Relationships**: 20+
|