🎓 Initial commit: Math2 Platform - Plataforma de Álgebra Lineal PRO
✨ 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 ✅
This commit is contained in:
645
backend/DATA_MODEL_DOCUMENTATION.md
Normal file
645
backend/DATA_MODEL_DOCUMENTATION.md
Normal file
@@ -0,0 +1,645 @@
|
||||
# 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+
|
||||
Reference in New Issue
Block a user