docs: Add Mission Service architecture documentation in English and Vietnamese.
This commit is contained in:
477
services/mission-service-net/docs/en/ARCHITECTURE.md
Normal file
477
services/mission-service-net/docs/en/ARCHITECTURE.md
Normal file
@@ -0,0 +1,477 @@
|
||||
# Mission Service Architecture
|
||||
|
||||
Technical architecture documentation for the Mission Service.
|
||||
|
||||
## High-Level Architecture
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
graph TD
|
||||
subgraph API["🌐 API Layer"]
|
||||
Controllers[Controllers]
|
||||
Commands[Commands]
|
||||
Queries[Queries]
|
||||
end
|
||||
|
||||
subgraph Domain["⚙️ Domain Layer"]
|
||||
Mission[Mission Aggregate]
|
||||
Task[Task Aggregate]
|
||||
CheckIn[CheckIn Aggregate]
|
||||
end
|
||||
|
||||
subgraph Infra["💾 Infrastructure Layer"]
|
||||
EF[EF Core]
|
||||
Redis[(Redis Cache)]
|
||||
RabbitMQ[RabbitMQ]
|
||||
end
|
||||
|
||||
subgraph Data["🗄️ Data Storage"]
|
||||
DB[(PostgreSQL)]
|
||||
end
|
||||
|
||||
API --> Domain
|
||||
Domain --> Infra
|
||||
EF --> DB
|
||||
|
||||
style API fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:3px
|
||||
style Domain fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:2px
|
||||
style Infra fill:#34495E,color:#ECF0F1,stroke:#2C3E50,stroke-width:2px
|
||||
style Data fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### CQRS Pattern
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
flowchart LR
|
||||
subgraph Write["📝 Commands"]
|
||||
C1[StartTaskCommand]
|
||||
C2[CompleteTaskCommand]
|
||||
C3[ClaimRewardCommand]
|
||||
C4[CheckInCommand]
|
||||
C5[CreateMissionCommand]
|
||||
end
|
||||
|
||||
subgraph Read["📖 Queries"]
|
||||
Q1[GetMissionsQuery]
|
||||
Q2[GetUserTasksQuery]
|
||||
Q3[GetCheckInStatusQuery]
|
||||
Q4[GetAnalyticsQuery]
|
||||
end
|
||||
|
||||
style Write fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
|
||||
style Read fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px
|
||||
```
|
||||
|
||||
## Domain Model
|
||||
|
||||
### Mission Aggregate
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
classDiagram
|
||||
class Mission {
|
||||
+Guid Id
|
||||
+string Code
|
||||
+LocalizedString Title
|
||||
+LocalizedString Description
|
||||
+MissionType Type
|
||||
+MissionCategory Category
|
||||
+MissionReward Reward
|
||||
+FrequencyType Frequency
|
||||
+int MaxCompletions
|
||||
+DateTime StartDate
|
||||
+DateTime EndDate
|
||||
+MissionStatus Status
|
||||
+int Priority
|
||||
+Activate()
|
||||
+Pause()
|
||||
+ValidateCompletion()
|
||||
}
|
||||
|
||||
class MissionRule {
|
||||
+Guid Id
|
||||
+string RuleType
|
||||
+string Operator
|
||||
+string Value
|
||||
+string Metadata
|
||||
}
|
||||
|
||||
class MissionReward {
|
||||
+decimal Points
|
||||
+decimal MiningBoost
|
||||
+int ExperiencePoints
|
||||
+string BadgeId
|
||||
}
|
||||
|
||||
Mission "1" --> "*" MissionRule : has
|
||||
Mission --> MissionReward : uses
|
||||
```
|
||||
|
||||
### Task Aggregate
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
classDiagram
|
||||
class UserTask {
|
||||
+Guid Id
|
||||
+Guid UserId
|
||||
+Guid MissionId
|
||||
+TaskStatus Status
|
||||
+TaskProgress Progress
|
||||
+TaskEvidence Evidence
|
||||
+VerificationResult Verification
|
||||
+bool RewardClaimed
|
||||
+DateTime StartedAt
|
||||
+DateTime CompletedAt
|
||||
+Start()
|
||||
+UpdateProgress()
|
||||
+SubmitEvidence()
|
||||
+Complete()
|
||||
+ClaimReward()
|
||||
}
|
||||
|
||||
class TaskProgress {
|
||||
+int CurrentValue
|
||||
+int TargetValue
|
||||
+decimal PercentComplete
|
||||
+DateTime LastUpdated
|
||||
}
|
||||
|
||||
class TaskEvidence {
|
||||
+string EvidenceType
|
||||
+string Data
|
||||
+string ScreenshotUrl
|
||||
+DateTime CapturedAt
|
||||
}
|
||||
|
||||
class VerificationResult {
|
||||
+bool IsValid
|
||||
+string FailureReason
|
||||
+VerificationMethod Method
|
||||
+Guid VerifiedBy
|
||||
+DateTime VerifiedAt
|
||||
}
|
||||
|
||||
UserTask --> TaskProgress : tracks
|
||||
UserTask --> TaskEvidence : has
|
||||
UserTask --> VerificationResult : verified by
|
||||
```
|
||||
|
||||
### CheckIn Aggregate
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
classDiagram
|
||||
class UserCheckIn {
|
||||
+Guid Id
|
||||
+Guid UserId
|
||||
+int CurrentStreak
|
||||
+int LongestStreak
|
||||
+int TotalCheckIns
|
||||
+DateOnly LastCheckInDate
|
||||
+CheckIn()
|
||||
+CanCheckInToday()
|
||||
+GetStreakBonus()
|
||||
+GetMonthlyReward()
|
||||
}
|
||||
|
||||
class CheckInDay {
|
||||
+Guid Id
|
||||
+DateOnly Date
|
||||
+decimal PointsEarned
|
||||
+bool IsMilestone
|
||||
}
|
||||
|
||||
class StreakBonus {
|
||||
+int MinDays
|
||||
+int MaxDays
|
||||
+decimal BonusPercent
|
||||
+decimal MilestoneBonus
|
||||
}
|
||||
|
||||
UserCheckIn "1" --> "*" CheckInDay : records
|
||||
UserCheckIn --> StreakBonus : applies
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
### ER Diagram
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
erDiagram
|
||||
Mission ||--o{ MissionRule : has
|
||||
Mission ||--o{ UserTask : generates
|
||||
UserTask }o--|| User : belongs
|
||||
User ||--o| UserCheckIn : has
|
||||
UserCheckIn ||--o{ CheckInDay : records
|
||||
|
||||
Mission {
|
||||
uuid Id PK
|
||||
string Code UK
|
||||
json Title
|
||||
json Description
|
||||
string Type
|
||||
string Category
|
||||
json Reward
|
||||
string Frequency
|
||||
int MaxCompletions
|
||||
datetime StartDate
|
||||
datetime EndDate
|
||||
string Status
|
||||
int Priority
|
||||
datetime CreatedAt
|
||||
}
|
||||
|
||||
MissionRule {
|
||||
uuid Id PK
|
||||
uuid MissionId FK
|
||||
string RuleType
|
||||
string Operator
|
||||
string Value
|
||||
json Metadata
|
||||
}
|
||||
|
||||
UserTask {
|
||||
uuid Id PK
|
||||
uuid UserId FK
|
||||
uuid MissionId FK
|
||||
string Status
|
||||
json Progress
|
||||
json Evidence
|
||||
json Verification
|
||||
bool RewardClaimed
|
||||
datetime StartedAt
|
||||
datetime CompletedAt
|
||||
}
|
||||
|
||||
UserCheckIn {
|
||||
uuid Id PK
|
||||
uuid UserId FK
|
||||
int CurrentStreak
|
||||
int LongestStreak
|
||||
int TotalCheckIns
|
||||
date LastCheckInDate
|
||||
}
|
||||
|
||||
CheckInDay {
|
||||
uuid Id PK
|
||||
uuid UserCheckInId FK
|
||||
date Date
|
||||
decimal PointsEarned
|
||||
bool IsMilestone
|
||||
}
|
||||
```
|
||||
|
||||
## Reward Flow with Wallet Service
|
||||
|
||||
### Claim Reward Sequence
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
sequenceDiagram
|
||||
participant Client as 📱 Client
|
||||
participant API as 🌐 Mission API
|
||||
participant Task as ⚙️ TaskAggregate
|
||||
participant DB as 💾 PostgreSQL
|
||||
participant MQ as 📨 RabbitMQ
|
||||
participant Wallet as 💰 Wallet Service
|
||||
|
||||
Client->>API: POST /api/v1/tasks/{id}/claim
|
||||
API->>Task: ClaimRewardCommand
|
||||
Task->>Task: ValidateTaskCompleted()
|
||||
Task->>Task: ValidateNotAlreadyClaimed()
|
||||
|
||||
alt Task Valid
|
||||
Task->>Task: MarkRewardClaimed()
|
||||
Task->>DB: SaveChanges()
|
||||
Task->>MQ: Publish MissionCompletedEvent
|
||||
MQ->>Wallet: Consume MissionCompletedEvent
|
||||
Wallet->>Wallet: GrantPoints(userId, amount)
|
||||
API-->>Client: 200 OK { points_earned }
|
||||
else Already Claimed
|
||||
API-->>Client: 400 Bad Request
|
||||
end
|
||||
```
|
||||
|
||||
### CheckIn Reward Flow
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
sequenceDiagram
|
||||
participant Client as 📱 Client
|
||||
participant API as 🌐 Mission API
|
||||
participant CheckIn as 📅 CheckInAggregate
|
||||
participant DB as 💾 PostgreSQL
|
||||
participant MQ as 📨 RabbitMQ
|
||||
participant Wallet as 💰 Wallet Service
|
||||
|
||||
Client->>API: POST /api/v1/checkin
|
||||
API->>CheckIn: CheckInCommand
|
||||
CheckIn->>CheckIn: CanCheckInToday()
|
||||
|
||||
alt Can Check In
|
||||
CheckIn->>CheckIn: IncrementStreak()
|
||||
CheckIn->>CheckIn: CalculateStreakBonus()
|
||||
CheckIn->>CheckIn: CheckMilestone()
|
||||
CheckIn->>DB: SaveCheckInDay()
|
||||
CheckIn->>MQ: Publish CheckInCompletedEvent
|
||||
MQ->>Wallet: Consume CheckInCompletedEvent
|
||||
Wallet->>Wallet: GrantPoints(userId, dailyPoints + bonus)
|
||||
API-->>Client: 200 OK { streak, points, milestone }
|
||||
else Already Checked In
|
||||
API-->>Client: 400 Already checked in today
|
||||
end
|
||||
```
|
||||
|
||||
## Integration Events
|
||||
|
||||
### Events Published
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
flowchart LR
|
||||
subgraph Mission["📋 Mission Service"]
|
||||
M[Events Publisher]
|
||||
end
|
||||
|
||||
subgraph Events["📨 Integration Events"]
|
||||
E1[MissionCompletedEvent]
|
||||
E2[CheckInCompletedEvent]
|
||||
E3[TaskStartedEvent]
|
||||
E4[RewardClaimedEvent]
|
||||
end
|
||||
|
||||
subgraph Consumers["📥 Consumers"]
|
||||
Wallet[Wallet Service]
|
||||
Mining[Mining Service]
|
||||
end
|
||||
|
||||
M --> E1 --> Wallet
|
||||
M --> E2 --> Wallet
|
||||
M --> E3
|
||||
M --> E4 --> Wallet
|
||||
E1 -.-> Mining
|
||||
|
||||
style Events fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
|
||||
style Wallet fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
|
||||
```
|
||||
|
||||
### Event Payloads
|
||||
|
||||
```csharp
|
||||
public record MissionCompletedEvent(
|
||||
Guid TaskId,
|
||||
Guid UserId,
|
||||
Guid MissionId,
|
||||
string MissionType,
|
||||
decimal PointsEarned,
|
||||
DateTime CompletedAt
|
||||
);
|
||||
|
||||
public record CheckInCompletedEvent(
|
||||
Guid UserId,
|
||||
int StreakDays,
|
||||
decimal BasePoints,
|
||||
decimal StreakBonus,
|
||||
decimal MilestoneBonus,
|
||||
decimal TotalPoints,
|
||||
DateTime CheckedInAt
|
||||
);
|
||||
|
||||
public record RewardClaimedEvent(
|
||||
Guid TaskId,
|
||||
Guid UserId,
|
||||
decimal PointsAmount,
|
||||
string RewardType,
|
||||
DateTime ClaimedAt
|
||||
);
|
||||
```
|
||||
|
||||
## Service Dependencies
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
graph TD
|
||||
subgraph External["🔐 Authentication"]
|
||||
IAM[IAM Service]
|
||||
end
|
||||
|
||||
subgraph Core["📋 Mission Service"]
|
||||
Mission[Mission Service]
|
||||
end
|
||||
|
||||
subgraph Integration["🔗 Integrations"]
|
||||
Wallet[Wallet Service]
|
||||
Mining[Mining Service]
|
||||
Social[Social Service]
|
||||
Storage[Storage Service]
|
||||
end
|
||||
|
||||
IAM -->|JWT Validation| Mission
|
||||
Mission -->|Grant Points| Wallet
|
||||
Mission <-->|Referral Sync| Mining
|
||||
Mission <-->|Friend Data| Social
|
||||
Mission <-->|Media Upload| Storage
|
||||
|
||||
style External fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
|
||||
style Core fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:3px
|
||||
style Integration fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px
|
||||
```
|
||||
|
||||
## Caching Strategy
|
||||
|
||||
| Key Pattern | TTL | Purpose |
|
||||
|-------------|-----|---------|
|
||||
| `mission:{id}` | 1h | Cache mission details |
|
||||
| `missions:active` | 5m | Active missions list |
|
||||
| `user:{id}:tasks` | 10m | User's active tasks |
|
||||
| `user:{id}:checkin` | 24h | Check-in status |
|
||||
| `config:missions` | 30m | Mission configurations |
|
||||
|
||||
## Security
|
||||
|
||||
### Anti-Fraud Flow
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
flowchart TD
|
||||
Request([🚀 Request]) --> RateLimit{⏱️ Rate Limit}
|
||||
RateLimit -->|Exceeded| Block[❌ Block]
|
||||
RateLimit -->|OK| Cooldown{⏳ Cooldown}
|
||||
Cooldown -->|Too Fast| Wait[⏸️ Wait]
|
||||
Cooldown -->|OK| Device{🔍 Device Check}
|
||||
Device -->|New/Suspicious| Flag[⚠️ Flag for Review]
|
||||
Device -->|OK| Evidence{📝 Evidence Valid?}
|
||||
Evidence -->|No| Reject[❌ Reject]
|
||||
Evidence -->|Yes| Approve[✅ Approve & Reward]
|
||||
|
||||
style Block fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
|
||||
style Reject fill:#E74C3C,color:#ECF0F1,stroke:#C0392B,stroke-width:2px
|
||||
style Approve fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
| Endpoint | Check |
|
||||
|----------|-------|
|
||||
| `/health/live` | ✅ Service running |
|
||||
| `/health/ready` | ✅ DB + Redis + RabbitMQ connected |
|
||||
| `/health` | ✅ Full status |
|
||||
|
||||
## Observability
|
||||
|
||||
### Metrics
|
||||
- Tasks started per minute
|
||||
- Tasks completed per minute
|
||||
- Check-in rate
|
||||
- Reward claims
|
||||
- Verification success rate
|
||||
|
||||
### Logging
|
||||
- Structured JSON logs (Serilog)
|
||||
- Correlation IDs for tracing
|
||||
- Sensitive data masking
|
||||
477
services/mission-service-net/docs/vi/ARCHITECTURE.md
Normal file
477
services/mission-service-net/docs/vi/ARCHITECTURE.md
Normal file
@@ -0,0 +1,477 @@
|
||||
# Kiến Trúc Mission Service
|
||||
|
||||
Tài liệu kiến trúc kỹ thuật cho Mission Service.
|
||||
|
||||
## Kiến Trúc Tổng Quan
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
graph TD
|
||||
subgraph API["🌐 Tầng API"]
|
||||
Controllers[Controllers]
|
||||
Commands[Commands]
|
||||
Queries[Queries]
|
||||
end
|
||||
|
||||
subgraph Domain["⚙️ Tầng Domain"]
|
||||
Mission[Mission Aggregate]
|
||||
Task[Task Aggregate]
|
||||
CheckIn[CheckIn Aggregate]
|
||||
end
|
||||
|
||||
subgraph Infra["💾 Tầng Infrastructure"]
|
||||
EF[EF Core]
|
||||
Redis[(Redis Cache)]
|
||||
RabbitMQ[RabbitMQ]
|
||||
end
|
||||
|
||||
subgraph Data["🗄️ Lưu Trữ Dữ Liệu"]
|
||||
DB[(PostgreSQL)]
|
||||
end
|
||||
|
||||
API --> Domain
|
||||
Domain --> Infra
|
||||
EF --> DB
|
||||
|
||||
style API fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:3px
|
||||
style Domain fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:2px
|
||||
style Infra fill:#34495E,color:#ECF0F1,stroke:#2C3E50,stroke-width:2px
|
||||
style Data fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
|
||||
```
|
||||
|
||||
## Các Mẫu Kiến Trúc
|
||||
|
||||
### CQRS Pattern
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
flowchart LR
|
||||
subgraph Write["📝 Commands (Ghi)"]
|
||||
C1[StartTaskCommand]
|
||||
C2[CompleteTaskCommand]
|
||||
C3[ClaimRewardCommand]
|
||||
C4[CheckInCommand]
|
||||
C5[CreateMissionCommand]
|
||||
end
|
||||
|
||||
subgraph Read["📖 Queries (Đọc)"]
|
||||
Q1[GetMissionsQuery]
|
||||
Q2[GetUserTasksQuery]
|
||||
Q3[GetCheckInStatusQuery]
|
||||
Q4[GetAnalyticsQuery]
|
||||
end
|
||||
|
||||
style Write fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
|
||||
style Read fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px
|
||||
```
|
||||
|
||||
## Domain Model
|
||||
|
||||
### Mission Aggregate
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
classDiagram
|
||||
class Mission {
|
||||
+Guid Id
|
||||
+string Code
|
||||
+LocalizedString Title
|
||||
+LocalizedString Description
|
||||
+MissionType Type
|
||||
+MissionCategory Category
|
||||
+MissionReward Reward
|
||||
+FrequencyType Frequency
|
||||
+int MaxCompletions
|
||||
+DateTime StartDate
|
||||
+DateTime EndDate
|
||||
+MissionStatus Status
|
||||
+int Priority
|
||||
+Activate()
|
||||
+Pause()
|
||||
+ValidateCompletion()
|
||||
}
|
||||
|
||||
class MissionRule {
|
||||
+Guid Id
|
||||
+string RuleType
|
||||
+string Operator
|
||||
+string Value
|
||||
+string Metadata
|
||||
}
|
||||
|
||||
class MissionReward {
|
||||
+decimal Points
|
||||
+decimal MiningBoost
|
||||
+int ExperiencePoints
|
||||
+string BadgeId
|
||||
}
|
||||
|
||||
Mission "1" --> "*" MissionRule : chứa
|
||||
Mission --> MissionReward : sử dụng
|
||||
```
|
||||
|
||||
### Task Aggregate
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
classDiagram
|
||||
class UserTask {
|
||||
+Guid Id
|
||||
+Guid UserId
|
||||
+Guid MissionId
|
||||
+TaskStatus Status
|
||||
+TaskProgress Progress
|
||||
+TaskEvidence Evidence
|
||||
+VerificationResult Verification
|
||||
+bool RewardClaimed
|
||||
+DateTime StartedAt
|
||||
+DateTime CompletedAt
|
||||
+Start()
|
||||
+UpdateProgress()
|
||||
+SubmitEvidence()
|
||||
+Complete()
|
||||
+ClaimReward()
|
||||
}
|
||||
|
||||
class TaskProgress {
|
||||
+int CurrentValue
|
||||
+int TargetValue
|
||||
+decimal PercentComplete
|
||||
+DateTime LastUpdated
|
||||
}
|
||||
|
||||
class TaskEvidence {
|
||||
+string EvidenceType
|
||||
+string Data
|
||||
+string ScreenshotUrl
|
||||
+DateTime CapturedAt
|
||||
}
|
||||
|
||||
class VerificationResult {
|
||||
+bool IsValid
|
||||
+string FailureReason
|
||||
+VerificationMethod Method
|
||||
+Guid VerifiedBy
|
||||
+DateTime VerifiedAt
|
||||
}
|
||||
|
||||
UserTask --> TaskProgress : theo dõi
|
||||
UserTask --> TaskEvidence : có
|
||||
UserTask --> VerificationResult : xác thực bởi
|
||||
```
|
||||
|
||||
### CheckIn Aggregate
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
classDiagram
|
||||
class UserCheckIn {
|
||||
+Guid Id
|
||||
+Guid UserId
|
||||
+int CurrentStreak
|
||||
+int LongestStreak
|
||||
+int TotalCheckIns
|
||||
+DateOnly LastCheckInDate
|
||||
+CheckIn()
|
||||
+CanCheckInToday()
|
||||
+GetStreakBonus()
|
||||
+GetMonthlyReward()
|
||||
}
|
||||
|
||||
class CheckInDay {
|
||||
+Guid Id
|
||||
+DateOnly Date
|
||||
+decimal PointsEarned
|
||||
+bool IsMilestone
|
||||
}
|
||||
|
||||
class StreakBonus {
|
||||
+int MinDays
|
||||
+int MaxDays
|
||||
+decimal BonusPercent
|
||||
+decimal MilestoneBonus
|
||||
}
|
||||
|
||||
UserCheckIn "1" --> "*" CheckInDay : ghi nhận
|
||||
UserCheckIn --> StreakBonus : áp dụng
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
### ER Diagram
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
erDiagram
|
||||
Mission ||--o{ MissionRule : có
|
||||
Mission ||--o{ UserTask : tạo ra
|
||||
UserTask }o--|| User : thuộc về
|
||||
User ||--o| UserCheckIn : có
|
||||
UserCheckIn ||--o{ CheckInDay : ghi nhận
|
||||
|
||||
Mission {
|
||||
uuid Id PK
|
||||
string Code UK
|
||||
json Title
|
||||
json Description
|
||||
string Type
|
||||
string Category
|
||||
json Reward
|
||||
string Frequency
|
||||
int MaxCompletions
|
||||
datetime StartDate
|
||||
datetime EndDate
|
||||
string Status
|
||||
int Priority
|
||||
datetime CreatedAt
|
||||
}
|
||||
|
||||
MissionRule {
|
||||
uuid Id PK
|
||||
uuid MissionId FK
|
||||
string RuleType
|
||||
string Operator
|
||||
string Value
|
||||
json Metadata
|
||||
}
|
||||
|
||||
UserTask {
|
||||
uuid Id PK
|
||||
uuid UserId FK
|
||||
uuid MissionId FK
|
||||
string Status
|
||||
json Progress
|
||||
json Evidence
|
||||
json Verification
|
||||
bool RewardClaimed
|
||||
datetime StartedAt
|
||||
datetime CompletedAt
|
||||
}
|
||||
|
||||
UserCheckIn {
|
||||
uuid Id PK
|
||||
uuid UserId FK
|
||||
int CurrentStreak
|
||||
int LongestStreak
|
||||
int TotalCheckIns
|
||||
date LastCheckInDate
|
||||
}
|
||||
|
||||
CheckInDay {
|
||||
uuid Id PK
|
||||
uuid UserCheckInId FK
|
||||
date Date
|
||||
decimal PointsEarned
|
||||
bool IsMilestone
|
||||
}
|
||||
```
|
||||
|
||||
## Luồng Nhận Thưởng với Wallet Service
|
||||
|
||||
### Luồng Nhận Thưởng Task
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
sequenceDiagram
|
||||
participant Client as 📱 Client
|
||||
participant API as 🌐 Mission API
|
||||
participant Task as ⚙️ TaskAggregate
|
||||
participant DB as 💾 PostgreSQL
|
||||
participant MQ as 📨 RabbitMQ
|
||||
participant Wallet as 💰 Wallet Service
|
||||
|
||||
Client->>API: POST /api/v1/tasks/{id}/claim
|
||||
API->>Task: ClaimRewardCommand
|
||||
Task->>Task: ValidateTaskCompleted()
|
||||
Task->>Task: ValidateNotAlreadyClaimed()
|
||||
|
||||
alt Task Hợp Lệ
|
||||
Task->>Task: MarkRewardClaimed()
|
||||
Task->>DB: SaveChanges()
|
||||
Task->>MQ: Publish MissionCompletedEvent
|
||||
MQ->>Wallet: Consume MissionCompletedEvent
|
||||
Wallet->>Wallet: GrantPoints(userId, amount)
|
||||
API-->>Client: 200 OK { points_earned }
|
||||
else Đã Nhận Thưởng
|
||||
API-->>Client: 400 Bad Request
|
||||
end
|
||||
```
|
||||
|
||||
### Luồng Thưởng Check-in
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
sequenceDiagram
|
||||
participant Client as 📱 Client
|
||||
participant API as 🌐 Mission API
|
||||
participant CheckIn as 📅 CheckInAggregate
|
||||
participant DB as 💾 PostgreSQL
|
||||
participant MQ as 📨 RabbitMQ
|
||||
participant Wallet as 💰 Wallet Service
|
||||
|
||||
Client->>API: POST /api/v1/checkin
|
||||
API->>CheckIn: CheckInCommand
|
||||
CheckIn->>CheckIn: CanCheckInToday()
|
||||
|
||||
alt Có Thể Check-in
|
||||
CheckIn->>CheckIn: IncrementStreak()
|
||||
CheckIn->>CheckIn: CalculateStreakBonus()
|
||||
CheckIn->>CheckIn: CheckMilestone()
|
||||
CheckIn->>DB: SaveCheckInDay()
|
||||
CheckIn->>MQ: Publish CheckInCompletedEvent
|
||||
MQ->>Wallet: Consume CheckInCompletedEvent
|
||||
Wallet->>Wallet: GrantPoints(userId, dailyPoints + bonus)
|
||||
API-->>Client: 200 OK { streak, points, milestone }
|
||||
else Đã Check-in Hôm Nay
|
||||
API-->>Client: 400 Đã điểm danh hôm nay
|
||||
end
|
||||
```
|
||||
|
||||
## Integration Events
|
||||
|
||||
### Các Event Được Xuất Bản
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
flowchart LR
|
||||
subgraph Mission["📋 Mission Service"]
|
||||
M[Events Publisher]
|
||||
end
|
||||
|
||||
subgraph Events["📨 Integration Events"]
|
||||
E1[MissionCompletedEvent]
|
||||
E2[CheckInCompletedEvent]
|
||||
E3[TaskStartedEvent]
|
||||
E4[RewardClaimedEvent]
|
||||
end
|
||||
|
||||
subgraph Consumers["📥 Người Tiêu Thụ"]
|
||||
Wallet[Wallet Service]
|
||||
Mining[Mining Service]
|
||||
end
|
||||
|
||||
M --> E1 --> Wallet
|
||||
M --> E2 --> Wallet
|
||||
M --> E3
|
||||
M --> E4 --> Wallet
|
||||
E1 -.-> Mining
|
||||
|
||||
style Events fill:#E67E22,color:#ECF0F1,stroke:#D35400,stroke-width:2px
|
||||
style Wallet fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
|
||||
```
|
||||
|
||||
### Event Payloads
|
||||
|
||||
```csharp
|
||||
public record MissionCompletedEvent(
|
||||
Guid TaskId,
|
||||
Guid UserId,
|
||||
Guid MissionId,
|
||||
string MissionType,
|
||||
decimal PointsEarned, // Điểm kiếm được
|
||||
DateTime CompletedAt
|
||||
);
|
||||
|
||||
public record CheckInCompletedEvent(
|
||||
Guid UserId,
|
||||
int StreakDays, // Số ngày streak
|
||||
decimal BasePoints, // Điểm cơ bản
|
||||
decimal StreakBonus, // Thưởng streak
|
||||
decimal MilestoneBonus, // Thưởng mốc
|
||||
decimal TotalPoints, // Tổng điểm
|
||||
DateTime CheckedInAt
|
||||
);
|
||||
|
||||
public record RewardClaimedEvent(
|
||||
Guid TaskId,
|
||||
Guid UserId,
|
||||
decimal PointsAmount,
|
||||
string RewardType,
|
||||
DateTime ClaimedAt
|
||||
);
|
||||
```
|
||||
|
||||
## Phụ Thuộc Dịch Vụ
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
graph TD
|
||||
subgraph External["🔐 Xác Thực"]
|
||||
IAM[IAM Service]
|
||||
end
|
||||
|
||||
subgraph Core["📋 Mission Service"]
|
||||
Mission[Mission Service]
|
||||
end
|
||||
|
||||
subgraph Integration["🔗 Tích Hợp"]
|
||||
Wallet[Wallet Service]
|
||||
Mining[Mining Service]
|
||||
Social[Social Service]
|
||||
Storage[Storage Service]
|
||||
end
|
||||
|
||||
IAM -->|Xác thực JWT| Mission
|
||||
Mission -->|Cấp Điểm| Wallet
|
||||
Mission <-->|Đồng bộ Referral| Mining
|
||||
Mission <-->|Dữ liệu Bạn bè| Social
|
||||
Mission <-->|Upload Media| Storage
|
||||
|
||||
style External fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
|
||||
style Core fill:#8E44AD,color:#ECF0F1,stroke:#7D3C98,stroke-width:3px
|
||||
style Integration fill:#3498DB,color:#ECF0F1,stroke:#2980B9,stroke-width:2px
|
||||
```
|
||||
|
||||
## Chiến Lược Cache
|
||||
|
||||
| Key Pattern | TTL | Mục Đích |
|
||||
|-------------|-----|----------|
|
||||
| `mission:{id}` | 1h | Cache chi tiết mission |
|
||||
| `missions:active` | 5m | Danh sách missions hoạt động |
|
||||
| `user:{id}:tasks` | 10m | Tasks đang làm của user |
|
||||
| `user:{id}:checkin` | 24h | Trạng thái check-in |
|
||||
| `config:missions` | 30m | Cấu hình mission |
|
||||
|
||||
## Bảo Mật
|
||||
|
||||
### Luồng Chống Gian Lận
|
||||
|
||||
```mermaid
|
||||
%%{init: {'theme':'dark'}}%%
|
||||
flowchart TD
|
||||
Request([🚀 Yêu Cầu]) --> RateLimit{⏱️ Rate Limit}
|
||||
RateLimit -->|Vượt Quá| Block[❌ Chặn]
|
||||
RateLimit -->|OK| Cooldown{⏳ Cooldown}
|
||||
Cooldown -->|Quá Nhanh| Wait[⏸️ Chờ]
|
||||
Cooldown -->|OK| Device{🔍 Kiểm Tra Thiết Bị}
|
||||
Device -->|Mới/Đáng Ngờ| Flag[⚠️ Đánh Dấu Xem Xét]
|
||||
Device -->|OK| Evidence{📝 Bằng Chứng Hợp Lệ?}
|
||||
Evidence -->|Không| Reject[❌ Từ Chối]
|
||||
Evidence -->|Có| Approve[✅ Duyệt & Thưởng]
|
||||
|
||||
style Block fill:#C0392B,color:#ECF0F1,stroke:#A93226,stroke-width:2px
|
||||
style Reject fill:#E74C3C,color:#ECF0F1,stroke:#C0392B,stroke-width:2px
|
||||
style Approve fill:#27AE60,color:#ECF0F1,stroke:#229954,stroke-width:2px
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
| Endpoint | Kiểm Tra |
|
||||
|----------|----------|
|
||||
| `/health/live` | ✅ Service đang chạy |
|
||||
| `/health/ready` | ✅ DB + Redis + RabbitMQ đã kết nối |
|
||||
| `/health` | ✅ Trạng thái đầy đủ |
|
||||
|
||||
## Observability
|
||||
|
||||
### Metrics
|
||||
- Số tasks bắt đầu mỗi phút
|
||||
- Số tasks hoàn thành mỗi phút
|
||||
- Tỷ lệ check-in
|
||||
- Số lần nhận thưởng
|
||||
- Tỷ lệ xác thực thành công
|
||||
|
||||
### Logging
|
||||
- Logs JSON có cấu trúc (Serilog)
|
||||
- Correlation IDs để tracing
|
||||
- Ẩn dữ liệu nhạy cảm
|
||||
Reference in New Issue
Block a user