Wave 1 — 6 parallel agents fixing P0 issues from code audit: Auth (18 services secured): - Added JWT Bearer auth + [Authorize] to all unprotected controllers - Webhook endpoints (Facebook/WhatsApp/Zalo/X) stay [AllowAnonymous] - Health checks remain public for Docker/K8s probes - Services: catalog, order, booking, fnb-engine, inventory, social, ads-manager, ads-serving, ads-billing, ads-tracking, ads-analytics, mkt-facebook, mkt-whatsapp, mkt-x, mkt-zalo, promotion Template artifacts (4 services): - mission-service: myservice_db → mission_service - mkt-facebook: Dockerfile MyService.API → FacebookService.API - mkt-whatsapp: MyServiceContext.cs → WhatsAppServiceContext.cs - promotion: UserSecretsId fixed Critical handler bugs (7 fixes): - ads-tracking: TrackPixelEventHandler now persists to DB - ads-tracking: RecordConversion endpoint exposed via controller - booking: UpdateResource now applies Name + Capacity changes - ads-manager: ListPendingAds uses correct enum (pending_review) - mining: BanMiner calls Ban() not Suspend() - mining: ResetMinerStreak now actually resets streak - mkt-x: 8 missing repository DI registrations added Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
24 KiB
MissionService - Service Documentation
1. Overview
Purpose: Gamification microservice that manages missions (tasks users complete for rewards), daily check-in streaks with tiered bonus points, and reward tracking. Supports mission types including video watching, link clicking, content uploading, friend invitations, daily check-ins, and social actions.
Port: 5000 (development, via launchSettings.json), 8080 (Docker/production)
Database: PostgreSQL (mission_service default in appsettings.json, configurable via ConnectionStrings:DefaultConnection or DATABASE_URL)
Framework: .NET 10.0, C# 14, Clean Architecture + CQRS
Solution file: MissionService.slnx
Migration: 20260117134348_InitialCreate — auto-applied on startup via dbContext.Database.MigrateAsync()
2. API Endpoints
MissionsController — api/v1/missions (Authorize)
| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /api/v1/missions |
JWT required | Get all available missions for the authenticated user. Returns MissionsListResult. |
| GET | /api/v1/missions/category/{category} |
AllowAnonymous | Get missions filtered by category name (e.g., "Daily", "Weekly"). Returns MissionsListResult. |
| GET | /api/v1/missions/{id:guid} |
AllowAnonymous | Get mission details by ID. Returns MissionDetailsResult or 404. Optionally includes user progress if authenticated. |
| POST | /api/v1/missions/{id:guid}/start |
JWT required | Start a mission task for the user. Returns StartTaskResult. |
| PUT | /api/v1/missions/tasks/{taskId:guid}/progress |
JWT required | Update task progress. Body: UpdateProgressRequest(CurrentValue, Evidence?). Returns UpdateProgressResult. |
| POST | /api/v1/missions/tasks/{taskId:guid}/claim |
JWT required | Claim reward for a completed task. Returns ClaimRewardResult. |
CheckInsController — api/v1/checkins (Authorize)
| Method | Route | Auth | Description |
|---|---|---|---|
| POST | /api/v1/checkins |
JWT required | Perform daily check-in. Returns CheckInResult with streak info and points earned. |
| GET | /api/v1/checkins/status |
JWT required | Get current check-in status (streak, total, can check-in today, next reward preview). |
| GET | /api/v1/checkins/history?year=&month= |
JWT required | Get check-in history for a specific month. Defaults to current month. |
| GET | /api/v1/checkins/leaderboard?count=10 |
AllowAnonymous | Get check-in streak leaderboard (max 100 entries). |
| GET | /api/v1/checkins/config |
AllowAnonymous | Get streak bonus tier configuration for client display. |
AdminController — api/v1/admin (Authorize Roles="Admin")
| Method | Route | Description |
|---|---|---|
| GET | /api/v1/admin/checkins/users/{userId:guid} |
Get a user's check-in profile details. |
| POST | /api/v1/admin/checkins/users/{userId:guid}/reset-streak |
Reset a user's current streak to 0. |
| GET | /api/v1/admin/checkins/top-streaks?count=20 |
Get top streak users (max 100). |
| GET | /api/v1/admin/tasks/pending-verification |
Get all tasks pending admin verification. |
| POST | /api/v1/admin/tasks/{taskId:guid}/approve |
Approve a pending verification task. |
| POST | /api/v1/admin/tasks/{taskId:guid}/reject |
Reject a pending verification task. Body: RejectTaskRequest(Reason). |
| GET | /api/v1/admin/tasks/users/{userId:guid} |
Get all tasks for a specific user. |
AdminMissionsController — api/v1/admin/missions (Authorize Roles="Admin")
| Method | Route | Description |
|---|---|---|
| GET | /api/v1/admin/missions |
Get all active missions (admin view with full details). |
| POST | /api/v1/admin/missions |
Create a new mission. Body: CreateMissionRequest. Returns 201. |
| GET | /api/v1/admin/missions/{id:guid} |
Get mission by ID (admin view). |
| POST | /api/v1/admin/missions/{id:guid}/activate |
Activate a draft or paused mission. |
| POST | /api/v1/admin/missions/{id:guid}/pause |
Pause an active mission. |
| POST | /api/v1/admin/missions/{id:guid}/archive |
Archive a mission (any status). |
Health Endpoints
| Route | Description |
|---|---|
/health |
Full health check (includes PostgreSQL). |
/health/live |
Liveness probe (app is running). |
/health/ready |
Readiness probe (includes PostgreSQL). |
3. Commands
PerformCheckInCommand
- File:
Application/Commands/CheckInCommands.cs - Parameters:
Guid UserId - Result:
CheckInResult(Success, CurrentStreak, DailyPoints, StreakBonus, MilestoneBonus, TotalPoints, IsMilestone, Message) - Behavior: Gets or creates user's check-in profile, validates not already checked in today, calculates streak continuation or reset, applies tiered bonus config, saves CheckInDay record.
StartMissionTaskCommand
- File:
Application/Commands/TaskCommands.cs - Parameters:
Guid UserId, Guid MissionId - Result:
StartTaskResult(Success, TaskId?, Message) - Behavior: Validates mission exists and is available, checks no existing active task for this user+mission, checks max completions not reached, creates new
UserTask.
UpdateTaskProgressCommand
- File:
Application/Commands/TaskCommands.cs - Parameters:
Guid UserId, Guid TaskId, int CurrentValue, TaskEvidenceDto? Evidence - Result:
UpdateProgressResult(Success, CurrentValue, TargetValue, PercentComplete, IsComplete, Message) - Behavior: Validates task ownership and InProgress status, updates progress value, auto-submits evidence if provided and complete, auto-completes if no verification needed.
ClaimTaskRewardCommand
- File:
Application/Commands/TaskCommands.cs - Parameters:
Guid UserId, Guid TaskId - Result:
ClaimRewardResult(Success, PointsEarned?, Message) - Behavior: Validates task ownership, completed status, and not already claimed. Looks up mission reward points, marks task as claimed.
4. Queries
GetAvailableMissionsQuery
- Parameters:
Guid UserId - Returns:
MissionsListResult— list of active missions with optional user task progress per mission.
GetMissionDetailsQuery
- Parameters:
Guid MissionId, Guid? UserId - Returns:
MissionDetailsResult?— full mission details (bilingual titles, reward, rules, frequency) with optional user progress. Returns null if not found.
GetMissionsByCategoryQuery
- Parameters:
string Category(display name, e.g., "Daily") - Returns:
MissionsListResult— active missions filtered by category. No user progress included.
GetUserMissionProgressQuery
- Parameters:
Guid UserId - Returns:
UserMissionProgressResult— aggregated stats (total, completed, in-progress counts, total points, active/completed mission lists). - Note: Query record is defined but no handler implementation exists in the codebase.
GetCheckInStatusQuery
- Parameters:
Guid UserId - Returns:
CheckInStatusResult(CurrentStreak, LongestStreak, TotalCheckIns, LastCheckInDate, CanCheckInToday, NextReward)— includes a preview of the next reward milestone.
GetCheckInHistoryQuery
- Parameters:
Guid UserId, int Year, int Month - Returns:
CheckInHistoryResult(Year, Month, Days[])— list ofCheckInDayDto(Date, PointsEarned, IsMilestone, StreakOnDay)for the requested month.
GetCheckInLeaderboardQuery
- Parameters:
int Count(default 10) - Returns:
CheckInLeaderboardResult(Entries[])— ranked list by longest streak, then total check-ins.
5. Domain Model
Aggregates
Mission (Aggregate Root)
- File:
Domain/AggregatesModel/MissionAggregate/Mission.cs - Properties: Code (unique), TitleEn, TitleVi, DescriptionEn, DescriptionVi, Type (MissionType), Category (MissionCategory), Reward (MissionReward), Frequency (FrequencyType), MaxCompletions, StartDate, EndDate, Status (MissionStatus), Priority, Rules (collection of MissionRule)
- Behavior methods:
Activate()(Draft/Paused -> Active),Pause()(Active -> Paused),Archive()(any -> Archived),AddRule(MissionRule),IsAvailable(),UpdateDetails(...) - State machine: Draft -> Active <-> Paused -> Archived; any -> Archived
UserCheckIn (Aggregate Root)
- File:
Domain/AggregatesModel/CheckInAggregate/UserCheckIn.cs - Properties: UserId (unique), CurrentStreak, LongestStreak, TotalCheckIns, LastCheckInDate (DateOnly?), CheckInDays (collection of CheckInDay)
- Behavior methods:
CanCheckInToday(),CheckIn(StreakBonusConfig)— calculates streak continuation/reset and returns points,GetMonthlyCheckIns(year, month),ResetStreak()(admin)
UserTask (Aggregate Root)
- File:
Domain/AggregatesModel/TaskAggregate/UserTask.cs - Properties: UserId, MissionId, Status (TaskStatus), Progress (TaskProgress), Evidence (TaskEvidence?), Verification (VerificationResult?), RewardClaimed, StartedAt, CompletedAt, ClaimedAt
- Behavior methods:
UpdateProgress(int),SubmitEvidence(TaskEvidence),Complete(VerificationResult),AutoComplete(),ClaimReward(),Cancel() - State machine: InProgress -> PendingVerification -> Completed/Rejected; InProgress -> Completed (auto); any (unclaimed) -> Cancelled
UserReward (Aggregate Root)
- File:
Domain/AggregatesModel/RewardAggregate/UserReward.cs - Properties: UserId, SourceId (TaskId or CheckInId), Type (RewardType enum), Status (RewardStatus enum), Amount (RewardAmount), EarnedAt, ClaimedAt, ExpiresAt
- Factory methods:
ForMission(...),ForCheckIn(...),ForMilestone(...),ForReferral(...) - Behavior methods:
Claim(),Expire(),Cancel(string reason)
Entities (non-root)
MissionRule
- Properties: RuleType, Operator, Value, Metadata (jsonb)
- Factory methods:
MinDuration(seconds),MinWatchPercent(percent),SpecificUrl(url) - Owned by: Mission (cascade delete)
CheckInDay
- Properties: Date (DateOnly), PointsEarned, IsMilestone, StreakOnDay
- Owned by: UserCheckIn (cascade delete)
Value Objects
MissionReward
- Properties: Points (decimal), MiningBoostPercent (decimal), ExperiencePoints (int), BadgeId (string?)
- Factory:
Create(points, miningBoost, xp, badgeId),PointsOnly(points)
TaskProgress
- Properties: CurrentValue (int), TargetValue (int), LastUpdated (DateTime)
- Computed: PercentComplete, IsComplete
- Methods:
WithValue(int),Increment(int)
TaskEvidence
- Properties: Type (EvidenceType enum), Data, ScreenshotUrl, VideoUrl, CapturedAt
- Factory methods:
WatchDuration(seconds),Click(url),Upload(fileUrl, isImage),SocialProof(screenshotUrl),InviteCode(code)
VerificationResult
- Properties: IsValid, FailureReason, Method (VerificationMethod enum), VerifiedBy (Guid?), VerifiedAt
- Factory methods:
AutoApproved(),AiApproved(),ManualApproved(adminId),Rejected(reason, method, adminId?)
RewardAmount
- Properties: Points (decimal), BonusPoints (decimal), Currency (string, default "MP")
- Computed: TotalPoints
- Methods:
WithBonus(additionalBonus)
StreakBonusConfig
- Properties: BasePoints (decimal), Tiers (list of StreakTier)
- Default tiers: Days 1-6: 2 MP/day; Day 7: 3 MP + 20 MP milestone; Days 8-13: 3 MP; Day 14: 4 MP + 35 MP milestone; Days 15-20: 4 MP; Day 21: 5 MP + 50 MP milestone; Days 22-29: 5 MP; Day 30+: 10 MP + 100 MP milestone
- Method:
CalculateReward(streakDay)returns (DailyPoints, StreakBonus, MilestoneBonus, IsMilestone)
Enumerations (Type-safe enum pattern)
MissionType
| Id | Name | Description |
|---|---|---|
| 1 | Video | Watch video to earn rewards |
| 2 | Click | Click on links/ads |
| 3 | Upload | Upload user-generated content |
| 4 | Invite | Invite friends |
| 5 | CheckIn | Daily check-in |
| 6 | Social | Social actions (like, share, subscribe) |
MissionCategory
| Id | Name |
|---|---|
| 1 | Daily |
| 2 | Weekly |
| 3 | Special |
| 4 | Onboarding |
| 5 | Event |
MissionStatus
| Id | Name |
|---|---|
| 1 | Draft |
| 2 | Active |
| 3 | Paused |
| 4 | Expired |
| 5 | Archived |
FrequencyType
| Id | Name | Description |
|---|---|---|
| 1 | Once | Complete once total |
| 2 | Daily | Once per day |
| 3 | Weekly | Once per week |
| 4 | Unlimited | Unlimited times |
TaskStatus
| Id | Name |
|---|---|
| 1 | Pending |
| 2 | InProgress |
| 3 | PendingVerification |
| 4 | Completed |
| 5 | Rejected |
| 6 | Cancelled |
| 7 | Expired |
Enums (C# enum)
EvidenceType
WatchDuration = 1, ClickData = 2, UploadedContent = 3, SocialProof = 4, InviteCode = 5
VerificationMethod
Automatic = 1, AI = 2, Manual = 3
RewardType
MissionComplete = 1, CheckInDaily = 2, CheckInMilestone = 3, ReferralBonus = 4, SocialAction = 5
RewardStatus
Pending = 1, Claimed = 2, Expired = 3, Cancelled = 4
6. Database Schema
Database: PostgreSQL. All column names use snake_case. Migration: 20260117134348_InitialCreate.
Table: missions
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK, not generated |
| code | varchar(100) | NOT NULL, UNIQUE INDEX |
| title_en | varchar(200) | NOT NULL |
| title_vi | varchar(200) | NOT NULL |
| description_en | varchar(2000) | nullable |
| description_vi | varchar(2000) | nullable |
| type_id | integer | NOT NULL (FK concept to mission_types) |
| category_id | integer | NOT NULL (FK concept to mission_categories) |
| reward_points | numeric(18,2) | NOT NULL (owned: MissionReward) |
| reward_mining_boost | numeric(5,2) | NOT NULL (owned: MissionReward) |
| reward_xp | integer | NOT NULL (owned: MissionReward) |
| reward_badge_id | varchar(50) | nullable (owned: MissionReward) |
| frequency_id | integer | NOT NULL (FK concept to frequency_types) |
| max_completions | integer | NOT NULL |
| start_date | timestamp with time zone | NOT NULL |
| end_date | timestamp with time zone | nullable |
| status_id | integer | NOT NULL (FK concept to mission_statuses) |
| priority | integer | NOT NULL |
Indexes: IX_missions_code (unique on code)
Table: mission_rules
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK, not generated |
| mission_id | uuid | NOT NULL, FK -> missions(id) CASCADE |
| rule_type | varchar(50) | NOT NULL |
| operator | varchar(20) | NOT NULL |
| value | varchar(500) | NOT NULL |
| metadata | jsonb | nullable |
Indexes: IX_mission_rules_mission_id
Table: user_tasks
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK, not generated |
| user_id | uuid | NOT NULL |
| mission_id | uuid | NOT NULL |
| status_id | integer | NOT NULL |
| progress_current | integer | NOT NULL (owned: TaskProgress) |
| progress_target | integer | NOT NULL (owned: TaskProgress) |
| progress_updated_at | timestamp with time zone | NOT NULL (owned: TaskProgress) |
| evidence_type | integer | nullable (owned: TaskEvidence) |
| evidence_data | varchar(4000) | nullable (owned: TaskEvidence) |
| evidence_screenshot_url | varchar(500) | nullable (owned: TaskEvidence) |
| evidence_video_url | varchar(500) | nullable (owned: TaskEvidence) |
| evidence_captured_at | timestamp with time zone | nullable (owned: TaskEvidence) |
| verification_valid | boolean | nullable (owned: VerificationResult) |
| verification_failure_reason | varchar(500) | nullable (owned: VerificationResult) |
| verification_method | integer | nullable (owned: VerificationResult) |
| verification_by | uuid | nullable (owned: VerificationResult) |
| verification_at | timestamp with time zone | nullable (owned: VerificationResult) |
| reward_claimed | boolean | NOT NULL |
| started_at | timestamp with time zone | NOT NULL |
| completed_at | timestamp with time zone | nullable |
| claimed_at | timestamp with time zone | nullable |
Indexes: IX_user_tasks_user_id, IX_user_tasks_mission_id, IX_user_tasks_user_id_mission_id (composite)
Table: user_checkins
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK, not generated |
| user_id | uuid | NOT NULL, UNIQUE INDEX |
| current_streak | integer | NOT NULL |
| longest_streak | integer | NOT NULL |
| total_checkins | integer | NOT NULL |
| last_checkin_date | date | nullable |
Indexes: IX_user_checkins_user_id (unique)
Table: checkin_days
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK, not generated |
| user_checkin_id | uuid | NOT NULL, FK -> user_checkins(id) CASCADE |
| date | date | NOT NULL |
| points_earned | numeric(18,2) | NOT NULL |
| is_milestone | boolean | NOT NULL |
| streak_on_day | integer | NOT NULL |
Indexes: IX_checkin_days_user_checkin_id_date (unique composite)
Table: user_rewards
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK, not generated |
| user_id | uuid | NOT NULL |
| source_id | uuid | NOT NULL, UNIQUE INDEX |
| type | integer | NOT NULL (RewardType enum) |
| status | integer | NOT NULL (RewardStatus enum) |
| points | numeric(18,2) | NOT NULL (owned: RewardAmount) |
| bonus_points | numeric(18,2) | NOT NULL (owned: RewardAmount) |
| currency | varchar(10) | NOT NULL, default "MP" (owned: RewardAmount) |
| earned_at | timestamp with time zone | NOT NULL |
| claimed_at | timestamp with time zone | nullable |
| expires_at | timestamp with time zone | nullable |
Indexes: IX_user_rewards_user_id, IX_user_rewards_source_id (unique), IX_user_rewards_status_expires_at (composite)
Lookup Tables (seeded)
| Table | Columns | Seed Data |
|---|---|---|
mission_types |
id (int PK), name (varchar 50) | 1=Video, 2=Click, 3=Upload, 4=Invite, 5=CheckIn, 6=Social |
mission_categories |
id (int PK), name (varchar 50) | 1=Daily, 2=Weekly, 3=Special, 4=Onboarding, 5=Event |
mission_statuses |
id (int PK), name (varchar 50) | 1=Draft, 2=Active, 3=Paused, 4=Expired, 5=Archived |
frequency_types |
id (int PK), name (varchar 50) | 1=Once, 2=Daily, 3=Weekly, 4=Unlimited |
task_statuses |
id (int PK), name (varchar 50) | 1=Pending, 2=InProgress, 3=PendingVerification, 4=Completed, 5=Rejected, 6=Cancelled, 7=Expired |
7. Integration Events
No integration events are currently implemented. The domain supports domain events via the Entity.AddDomainEvent() / MissionDbContext.DispatchDomainEventsAsync() infrastructure, but no domain event records or handlers exist in the codebase. No RabbitMQ integration event publishers or consumers are present.
The Domain/Events/ directory referenced in the template pattern does not exist in this service.
8. Dependencies
NuGet Packages
API Layer (MissionService.API.csproj):
| Package | Version |
|---|---|
| MediatR | 12.4.1 |
| FluentValidation | 11.11.0 |
| FluentValidation.DependencyInjectionExtensions | 11.11.0 |
| Microsoft.EntityFrameworkCore.Design | 10.0.2 |
| Swashbuckle.AspNetCore | 7.2.0 |
| Asp.Versioning.Mvc | 8.1.0 |
| Asp.Versioning.Mvc.ApiExplorer | 8.1.0 |
| AspNetCore.HealthChecks.NpgSql | 8.0.2 |
| AspNetCore.HealthChecks.Redis | 8.0.1 |
| Hellang.Middleware.ProblemDetails | 6.5.1 |
| Serilog.AspNetCore | 8.0.3 |
| Serilog.Sinks.Console | 6.0.0 |
| Serilog.Sinks.Seq | 8.0.0 |
| Microsoft.AspNetCore.Authentication.JwtBearer | 9.0.0 |
Domain Layer (MissionService.Domain.csproj):
| Package | Version |
|---|---|
| MediatR.Contracts | 2.0.1 |
Infrastructure Layer (MissionService.Infrastructure.csproj):
| Package | Version |
|---|---|
| Microsoft.EntityFrameworkCore | 10.0.0 |
| Npgsql.EntityFrameworkCore.PostgreSQL | 10.0.0 |
| Microsoft.EntityFrameworkCore.Tools | 10.0.0 |
| MediatR | 12.4.1 |
| Dapper | 2.1.35 |
| Microsoft.Extensions.Http.Polly | 9.0.0 |
| Polly | 8.5.0 |
| StackExchange.Redis | 2.8.16 |
Shared (Directory.Build.props):
| Package | Version |
|---|---|
| Microsoft.SourceLink.GitHub | 8.0.0 |
External Service Dependencies
- PostgreSQL: Primary database (connection via Npgsql)
- IAM Service: JWT token validation (Authority:
http://iam-service-net:8080default) - Redis: Package referenced but not actively used in application code (health check configured)
9. Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
ASPNETCORE_ENVIRONMENT |
Production (Docker) / Development (launch) |
Runtime environment |
ASPNETCORE_URLS |
http://+:8080 (Docker) / http://localhost:5000 (launch) |
Listening URLs |
DATABASE_URL |
— | Fallback connection string if ConnectionStrings:DefaultConnection not set |
appsettings.json Keys
| Key | Default Value | Description |
|---|---|---|
ConnectionStrings:DefaultConnection |
Host=localhost;Port=5432;Database=mission_service;Username=postgres;Password=postgres |
PostgreSQL connection string |
Redis:ConnectionString |
localhost:6379 |
Redis connection string |
Jwt:Secret |
your-super-secret-key-min-32-characters |
JWT signing key |
Jwt:Issuer |
goodgo-platform |
JWT issuer |
Jwt:Audience |
goodgo-services |
JWT audience |
Jwt:AccessTokenExpiryMinutes |
15 |
Access token lifetime |
Jwt:RefreshTokenExpiryDays |
7 |
Refresh token lifetime |
Jwt:Authority |
http://iam-service-net:8080 |
OIDC authority for token validation |
MediatR Pipeline (execution order)
LoggingBehavior— Logs request name and elapsed time (Stopwatch)ValidatorBehavior— Runs FluentValidation validators, throwsValidationExceptionon failureTransactionBehavior— Wraps commands in a database transaction (skips queries ending in "Query"), usesExecutionStrategyfor retry
Docker
- Multi-stage build:
sdk:10.0(build) ->aspnet:10.0(runtime) - Non-root user:
dotnetuser(UID/GID 1001) - Health check:
curl -f http://localhost:8080/health/live(30s interval, 3 retries, 10s start period) - Exposed port: 8080
Build Properties
- Target:
net10.0, C#14.0 - Nullable reference types: enabled
- Implicit usings: enabled
- Treat warnings as errors: true
- XML documentation generation: enabled
10. Tests
Unit Tests (tests/MissionService.UnitTests/)
Application/Commands/PerformCheckInCommandHandlerTests.cs— Tests for the check-in command handlerDomain/MissionAggregateTests.cs— Tests for Mission entity behaviorDomain/UserCheckInAggregateTests.cs— Tests for UserCheckIn streak logic
Functional Tests (tests/MissionService.FunctionalTests/)
CustomWebApplicationFactory.cs— Test server factory (swaps DbContext)Controllers/MissionsControllerTests.cs— API endpoint integration tests
11. Notable Gaps / Observations
- GetUserMissionProgressQuery: Query record is defined but has no handler implementation.
- No FluentValidation validators exist: The
ValidatorBehavioris registered but noAbstractValidator<T>classes are present in the codebase. - No domain events defined: The infrastructure for dispatching domain events exists in
MissionDbContext, but no domain event records (INotification) are defined or raised by any entity. - No integration events: No RabbitMQ publishers/consumers. Reward claiming does not notify external services (e.g., wallet service).
- UserReward aggregate: Has a full repository and EF configuration but is not used by any command handler — rewards are tracked via
UserTask.RewardClaimedflag only. - Idempotency:
IRequestManager/RequestManagerare registered but not used by any handler or behavior. - Redis: Package referenced and health check configured, but no caching logic exists in the codebase.
- Database name: Default in appsettings is
mission_service(renamed from template default). - JWT audience validation: Disabled (
ValidateAudience = false,ValidateIssuer = false) — relies on IAM service for token signing.