diff --git a/LctMonolith/Controllers/AuthController.cs b/LctMonolith/Controllers/AuthController.cs
index b9acfef..13c9f92 100644
--- a/LctMonolith/Controllers/AuthController.cs
+++ b/LctMonolith/Controllers/AuthController.cs
@@ -1,10 +1,13 @@
using System.Security.Claims;
-using LctMonolith.Domain.Entities;
+using LctMonolith.Models;
using LctMonolith.Services;
+using LctMonolith.Services.Contracts;
using LctMonolith.Services.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Identity.Data;
using Microsoft.AspNetCore.Mvc;
+using RefreshRequest = LctMonolith.Services.Models.RefreshRequest;
namespace LctMonolith.Controllers;
diff --git a/LctMonolith/Controllers/GamificationController.cs b/LctMonolith/Controllers/GamificationController.cs
index 4908305..55c9ec9 100644
--- a/LctMonolith/Controllers/GamificationController.cs
+++ b/LctMonolith/Controllers/GamificationController.cs
@@ -1,5 +1,6 @@
using System.Security.Claims;
using LctMonolith.Services;
+using LctMonolith.Services.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
diff --git a/LctMonolith/Controllers/InventoryController.cs b/LctMonolith/Controllers/InventoryController.cs
index eb025bc..5c88260 100644
--- a/LctMonolith/Controllers/InventoryController.cs
+++ b/LctMonolith/Controllers/InventoryController.cs
@@ -1,5 +1,6 @@
using System.Security.Claims;
using LctMonolith.Services;
+using LctMonolith.Services.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
diff --git a/LctMonolith/Controllers/MissionsController.cs b/LctMonolith/Controllers/MissionsController.cs
index bc315db..6de58ad 100644
--- a/LctMonolith/Controllers/MissionsController.cs
+++ b/LctMonolith/Controllers/MissionsController.cs
@@ -1,6 +1,7 @@
using System.Security.Claims;
-using LctMonolith.Domain.Entities;
+using LctMonolith.Models;
using LctMonolith.Services;
+using LctMonolith.Services.Contracts;
using LctMonolith.Services.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -50,4 +51,3 @@ public class MissionsController : ControllerBase
return Ok(new { result.MissionId, result.Status, result.UpdatedAt });
}
}
-
diff --git a/LctMonolith/Controllers/NotificationsController.cs b/LctMonolith/Controllers/NotificationsController.cs
index f07d889..193ec56 100644
--- a/LctMonolith/Controllers/NotificationsController.cs
+++ b/LctMonolith/Controllers/NotificationsController.cs
@@ -1,5 +1,6 @@
using System.Security.Claims;
using LctMonolith.Services;
+using LctMonolith.Services.Contracts;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
diff --git a/LctMonolith/Controllers/StoreController.cs b/LctMonolith/Controllers/StoreController.cs
index 77c05fb..d955aaf 100644
--- a/LctMonolith/Controllers/StoreController.cs
+++ b/LctMonolith/Controllers/StoreController.cs
@@ -1,5 +1,6 @@
using System.Security.Claims;
using LctMonolith.Services;
+using LctMonolith.Services.Contracts;
using LctMonolith.Services.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
diff --git a/LctMonolith/Infrastructure/Data/AppDbContext.cs b/LctMonolith/Database/Data/AppDbContext.cs
similarity index 97%
rename from LctMonolith/Infrastructure/Data/AppDbContext.cs
rename to LctMonolith/Database/Data/AppDbContext.cs
index b206f3f..2fb14dd 100644
--- a/LctMonolith/Infrastructure/Data/AppDbContext.cs
+++ b/LctMonolith/Database/Data/AppDbContext.cs
@@ -1,15 +1,18 @@
-using LctMonolith.Domain.Entities;
+using System.Diagnostics;
+using LctMonolith.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
+using EventLog = LctMonolith.Models.EventLog;
-namespace LctMonolith.Infrastructure.Data;
+namespace LctMonolith.Database.Data;
///
/// Main EF Core database context for gamification module (PostgreSQL provider expected).
///
public class AppDbContext : IdentityDbContext, Guid>
{
+
public AppDbContext(DbContextOptions options) : base(options) { }
public DbSet Ranks => Set();
diff --git a/LctMonolith/Infrastructure/Data/DbSeeder.cs b/LctMonolith/Database/Data/DbSeeder.cs
similarity index 94%
rename from LctMonolith/Infrastructure/Data/DbSeeder.cs
rename to LctMonolith/Database/Data/DbSeeder.cs
index 805d018..a608d21 100644
--- a/LctMonolith/Infrastructure/Data/DbSeeder.cs
+++ b/LctMonolith/Database/Data/DbSeeder.cs
@@ -1,9 +1,8 @@
-using LctMonolith.Domain.Entities;
+using LctMonolith.Models;
using Microsoft.EntityFrameworkCore;
using Serilog;
-using System.Collections.Generic;
-namespace LctMonolith.Infrastructure.Data;
+namespace LctMonolith.Database.Data;
///
/// Development database seeder for initial ranks, competencies, sample store items.
diff --git a/LctMonolith/Database/Models/EventType.cs b/LctMonolith/Database/Models/EventType.cs
new file mode 100644
index 0000000..713cb6c
--- /dev/null
+++ b/LctMonolith/Database/Models/EventType.cs
@@ -0,0 +1,19 @@
+namespace LctMonolith.Models;
+
+public enum EventType
+{
+ SkillProgress = 1,
+ MissionStatusChanged = 2,
+ RankChanged = 3,
+ ItemPurchased = 4,
+ ArtifactObtained = 5,
+ RewardGranted = 6,
+ ProfileChanged = 7,
+ AuthCredentialsChanged = 8,
+ ItemReturned = 9,
+ ItemSold = 10
+}
+
+#if false
+// Moved to Models/EventType.cs
+#endif
diff --git a/LctMonolith/Infrastructure/Repositories/GenericRepository.cs b/LctMonolith/Database/Repositories/GenericRepository.cs
similarity index 95%
rename from LctMonolith/Infrastructure/Repositories/GenericRepository.cs
rename to LctMonolith/Database/Repositories/GenericRepository.cs
index 0f3796a..168d593 100644
--- a/LctMonolith/Infrastructure/Repositories/GenericRepository.cs
+++ b/LctMonolith/Database/Repositories/GenericRepository.cs
@@ -1,8 +1,8 @@
using System.Linq.Expressions;
+using LctMonolith.Database.Data;
using Microsoft.EntityFrameworkCore;
-using LctMonolith.Infrastructure.Data;
-namespace LctMonolith.Infrastructure.Repositories;
+namespace LctMonolith.Database.Repositories;
///
/// Generic repository implementation for common CRUD and query composition.
diff --git a/LctMonolith/Infrastructure/Repositories/IGenericRepository.cs b/LctMonolith/Database/Repositories/IGenericRepository.cs
similarity index 94%
rename from LctMonolith/Infrastructure/Repositories/IGenericRepository.cs
rename to LctMonolith/Database/Repositories/IGenericRepository.cs
index b856629..1d75e37 100644
--- a/LctMonolith/Infrastructure/Repositories/IGenericRepository.cs
+++ b/LctMonolith/Database/Repositories/IGenericRepository.cs
@@ -1,6 +1,6 @@
using System.Linq.Expressions;
-namespace LctMonolith.Infrastructure.Repositories;
+namespace LctMonolith.Database.Repositories;
///
/// Generic repository abstraction for aggregate root / entity access. Read operations return IQueryable for composition.
diff --git a/LctMonolith/Infrastructure/UnitOfWork/IUnitOfWork.cs b/LctMonolith/Database/UnitOfWork/IUnitOfWork.cs
similarity index 88%
rename from LctMonolith/Infrastructure/UnitOfWork/IUnitOfWork.cs
rename to LctMonolith/Database/UnitOfWork/IUnitOfWork.cs
index 7cae5de..9a5ac5b 100644
--- a/LctMonolith/Infrastructure/UnitOfWork/IUnitOfWork.cs
+++ b/LctMonolith/Database/UnitOfWork/IUnitOfWork.cs
@@ -1,7 +1,9 @@
-using LctMonolith.Domain.Entities;
-using LctMonolith.Infrastructure.Repositories;
+using System.Diagnostics;
+using LctMonolith.Database.Repositories;
+using LctMonolith.Models;
+using EventLog = LctMonolith.Models.EventLog;
-namespace LctMonolith.Infrastructure.UnitOfWork;
+namespace LctMonolith.Database.UnitOfWork;
///
/// Unit of Work aggregates repositories and transaction boundary.
diff --git a/LctMonolith/Infrastructure/UnitOfWork/UnitOfWork.cs b/LctMonolith/Database/UnitOfWork/UnitOfWork.cs
similarity index 96%
rename from LctMonolith/Infrastructure/UnitOfWork/UnitOfWork.cs
rename to LctMonolith/Database/UnitOfWork/UnitOfWork.cs
index 9d94bd9..bf688be 100644
--- a/LctMonolith/Infrastructure/UnitOfWork/UnitOfWork.cs
+++ b/LctMonolith/Database/UnitOfWork/UnitOfWork.cs
@@ -1,9 +1,11 @@
-using LctMonolith.Domain.Entities;
-using LctMonolith.Infrastructure.Data;
-using LctMonolith.Infrastructure.Repositories;
-using Microsoft.EntityFrameworkCore.Storage;
-namespace LctMonolith.Infrastructure.UnitOfWork;
+using LctMonolith.Database.Data;
+using LctMonolith.Database.Repositories;
+using LctMonolith.Models;
+using Microsoft.EntityFrameworkCore.Storage;
+using EventLog = LctMonolith.Models.EventLog;
+
+namespace LctMonolith.Database.UnitOfWork;
///
/// Unit of Work implementation encapsulating repositories and DB transaction scope.
diff --git a/LctMonolith/Domain/Entities/ArtifactsAndStore.cs b/LctMonolith/Domain/Entities/ArtifactsAndStore.cs
deleted file mode 100644
index 1a072c8..0000000
--- a/LctMonolith/Domain/Entities/ArtifactsAndStore.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-namespace LctMonolith.Domain.Entities;
-
-/// Artifact definition (unique reward objects).
-public class Artifact
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public string Name { get; set; } = null!;
- public string? Description { get; set; }
- public string? ImageUrl { get; set; }
- public ArtifactRarity Rarity { get; set; }
-
- public ICollection Users { get; set; } = new List();
- public ICollection MissionRewards { get; set; } = new List();
-}
-
-/// Mapping artifact to user ownership.
-public class UserArtifact
-{
- public Guid UserId { get; set; }
- public AppUser User { get; set; } = null!;
- public Guid ArtifactId { get; set; }
- public Artifact Artifact { get; set; } = null!;
- public DateTime ObtainedAt { get; set; } = DateTime.UtcNow;
-}
-
-/// Reward mapping: mission grants artifact(s).
-public class MissionArtifactReward
-{
- public Guid MissionId { get; set; }
- public Mission Mission { get; set; } = null!;
- public Guid ArtifactId { get; set; }
- public Artifact Artifact { get; set; } = null!;
-}
-
-/// Item in store that can be purchased with mana.
-public class StoreItem
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public string Name { get; set; } = null!;
- public string? Description { get; set; }
- public int Price { get; set; }
- public bool IsActive { get; set; } = true;
- public int? Stock { get; set; }
-
- public ICollection UserInventory { get; set; } = new List();
-}
-
-/// User owned store item record.
-public class UserInventoryItem
-{
- public Guid UserId { get; set; }
- public AppUser User { get; set; } = null!;
- public Guid StoreItemId { get; set; }
- public StoreItem StoreItem { get; set; } = null!;
- public int Quantity { get; set; } = 1;
- public DateTime AcquiredAt { get; set; } = DateTime.UtcNow;
- public bool IsReturned { get; set; }
-}
-
-/// Transaction record for purchases/returns/sales.
-public class Transaction
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public Guid UserId { get; set; }
- public AppUser User { get; set; } = null!;
- public TransactionType Type { get; set; }
- public Guid? StoreItemId { get; set; }
- public StoreItem? StoreItem { get; set; }
- public int ManaAmount { get; set; }
- public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
-}
-
-/// System event log for auditing user actions and progression.
-public class EventLog
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public EventType Type { get; set; }
- public Guid UserId { get; set; }
- public AppUser User { get; set; } = null!;
- public string? Data { get; set; }
- public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
-}
-
-/// Refresh token for JWT auth.
-public class RefreshToken
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public string Token { get; set; } = null!;
- public DateTime ExpiresAt { get; set; }
- public bool IsRevoked { get; set; }
- public Guid UserId { get; set; }
- public AppUser User { get; set; } = null!;
- public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
-}
-
diff --git a/LctMonolith/Domain/Entities/Competency.cs b/LctMonolith/Domain/Entities/Competency.cs
deleted file mode 100644
index b9e99c9..0000000
--- a/LctMonolith/Domain/Entities/Competency.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-namespace LctMonolith.Domain.Entities;
-
-///
-/// Competency (skill) that can be progressed by completing missions.
-///
-public class Competency
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public string Name { get; set; } = null!;
- public string? Description { get; set; }
-
- public ICollection UserCompetencies { get; set; } = new List();
- public ICollection MissionRewards { get; set; } = new List();
- public ICollection RankRequirements { get; set; } = new List();
-}
-
-/// Per-user competency level.
-public class UserCompetency
-{
- public Guid UserId { get; set; }
- public AppUser User { get; set; } = null!;
- public Guid CompetencyId { get; set; }
- public Competency Competency { get; set; } = null!;
- /// Current level (integer simple scale).
- public int Level { get; set; }
- /// Optional numeric progress inside level (e.g., partial points).
- public int ProgressPoints { get; set; }
-}
-
-/// Reward mapping: mission increases competency level points.
-public class MissionCompetencyReward
-{
- public Guid MissionId { get; set; }
- public Mission Mission { get; set; } = null!;
- public Guid CompetencyId { get; set; }
- public Competency Competency { get; set; } = null!;
- /// Increment value in levels (could be 0 or 1) or points depending on design.
- public int LevelDelta { get; set; }
- public int ProgressPointsDelta { get; set; }
-}
-
diff --git a/LctMonolith/Domain/Entities/Enums.cs b/LctMonolith/Domain/Entities/Enums.cs
deleted file mode 100644
index 9f71201..0000000
--- a/LctMonolith/Domain/Entities/Enums.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-namespace LctMonolith.Domain.Entities;
-
-/// Mission category taxonomy.
-public enum MissionCategory
-{
- Quest = 0,
- Recruiting = 1,
- Lecture = 2,
- Simulator = 3
-}
-
-/// Status of a mission for a specific user.
-public enum MissionStatus
-{
- Locked = 0,
- Available = 1,
- InProgress = 2,
- Submitted = 3,
- Completed = 4,
- Rejected = 5
-}
-
-/// Rarity level of an artifact.
-public enum ArtifactRarity
-{
- Common = 0,
- Rare = 1,
- Epic = 2,
- Legendary = 3
-}
-
-/// Type of transactional operation in store.
-public enum TransactionType
-{
- Purchase = 0,
- Return = 1,
- Sale = 2
-}
-
-/// Auditable event types enumerated in requirements.
-public enum EventType
-{
- SkillProgress = 1,
- MissionStatusChanged = 2,
- RankChanged = 3,
- ItemPurchased = 4,
- ArtifactObtained = 5,
- RewardGranted = 6,
- ProfileChanged = 7,
- AuthCredentialsChanged = 8,
- ItemReturned = 9,
- ItemSold = 10
-}
-
diff --git a/LctMonolith/Domain/Entities/Mission.cs b/LctMonolith/Domain/Entities/Mission.cs
deleted file mode 100644
index cc32f87..0000000
--- a/LctMonolith/Domain/Entities/Mission.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-namespace LctMonolith.Domain.Entities;
-
-///
-/// Mission (task) definition configured by HR.
-///
-public class Mission
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- public string Title { get; set; } = null!;
- public string? Description { get; set; }
- /// Optional branch (path) name for grouping / visualization.
- public string? Branch { get; set; }
- public MissionCategory Category { get; set; }
- /// Minimum rank required to access the mission (nullable = available from start).
- public Guid? MinRankId { get; set; }
- public Rank? MinRank { get; set; }
- /// Experience reward on completion.
- public int ExperienceReward { get; set; }
- /// Mana reward on completion.
- public int ManaReward { get; set; }
-
- public bool IsActive { get; set; } = true;
-
- public ICollection CompetencyRewards { get; set; } = new List();
- public ICollection ArtifactRewards { get; set; } = new List();
- public ICollection UserMissions { get; set; } = new List();
- public ICollection RanksRequiring { get; set; } = new List();
-}
-
-/// Per-user mission status and progression.
-public class UserMission
-{
- public Guid UserId { get; set; }
- public AppUser User { get; set; } = null!;
- public Guid MissionId { get; set; }
- public Mission Mission { get; set; } = null!;
-
- public MissionStatus Status { get; set; } = MissionStatus.Available;
- /// Date/time of last status change.
- public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
- /// Optional submission payload (e.g., link, text, attachments pointer).
- public string? SubmissionData { get; set; }
-}
-
diff --git a/LctMonolith/Domain/Entities/Rank.cs b/LctMonolith/Domain/Entities/Rank.cs
deleted file mode 100644
index a41ac8b..0000000
--- a/LctMonolith/Domain/Entities/Rank.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace LctMonolith.Domain.Entities;
-
-///
-/// Linear rank in progression ladder. User must meet XP, key mission and competency requirements.
-///
-public class Rank
-{
- public Guid Id { get; set; } = Guid.NewGuid();
- /// Display name (e.g., "Искатель", "Пилот-кандидат").
- public string Name { get; set; } = null!;
- /// Ordering position. Lower value = earlier rank.
- public int Order { get; set; }
- /// Required cumulative experience to attain this rank.
- public int RequiredExperience { get; set; }
-
- public ICollection RequiredMissions { get; set; } = new List();
- public ICollection RequiredCompetencies { get; set; } = new List();
- public ICollection Users { get; set; } = new List();
-}
-
-/// Mapping of rank to required mission.
-public class RankRequiredMission
-{
- public Guid RankId { get; set; }
- public Rank Rank { get; set; } = null!;
- public Guid MissionId { get; set; }
- public Mission Mission { get; set; } = null!;
-}
-
-/// Mapping of rank to required competency minimum level.
-public class RankRequiredCompetency
-{
- public Guid RankId { get; set; }
- public Rank Rank { get; set; } = null!;
- public Guid CompetencyId { get; set; }
- public Competency Competency { get; set; } = null!;
- /// Minimum level required for the competency.
- public int MinLevel { get; set; }
-}
-
diff --git a/LctMonolith/Domain/Entities/AppUser.cs b/LctMonolith/Models/AppUser.cs
similarity index 68%
rename from LctMonolith/Domain/Entities/AppUser.cs
rename to LctMonolith/Models/AppUser.cs
index 63d091f..e7db1ce 100644
--- a/LctMonolith/Domain/Entities/AppUser.cs
+++ b/LctMonolith/Models/AppUser.cs
@@ -1,32 +1,19 @@
+using System.Diagnostics;
using Microsoft.AspNetCore.Identity;
-namespace LctMonolith.Domain.Entities;
+namespace LctMonolith.Models;
-///
-/// Application user (candidate or employee) participating in gamification.
-/// Extends IdentityUser with Guid primary key.
-///
public class AppUser : IdentityUser
{
- /// User given (first) name.
public string? FirstName { get; set; }
- /// User family (last) name.
public string? LastName { get; set; }
- /// Date of birth.
public DateOnly? BirthDate { get; set; }
-
- /// Current accumulated experience points.
public int Experience { get; set; }
- /// Current mana (in-game currency).
public int Mana { get; set; }
-
- /// Current rank reference.
public Guid? RankId { get; set; }
public Rank? Rank { get; set; }
-
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
-
public ICollection Competencies { get; set; } = new List();
public ICollection Missions { get; set; } = new List();
public ICollection Inventory { get; set; } = new List();
diff --git a/LctMonolith/Models/Artifact.cs b/LctMonolith/Models/Artifact.cs
new file mode 100644
index 0000000..72b26a5
--- /dev/null
+++ b/LctMonolith/Models/Artifact.cs
@@ -0,0 +1,13 @@
+namespace LctMonolith.Models;
+
+public class Artifact
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public string Name { get; set; } = null!;
+ public string? Description { get; set; }
+ public string? ImageUrl { get; set; }
+ public ArtifactRarity Rarity { get; set; }
+ public ICollection Users { get; set; } = new List();
+ public ICollection MissionRewards { get; set; } = new List();
+}
+
diff --git a/LctMonolith/Models/ArtifactRarity.cs b/LctMonolith/Models/ArtifactRarity.cs
new file mode 100644
index 0000000..909cb1c
--- /dev/null
+++ b/LctMonolith/Models/ArtifactRarity.cs
@@ -0,0 +1,3 @@
+namespace LctMonolith.Models;
+
+public enum ArtifactRarity { Common = 0, Rare = 1, Epic = 2, Legendary = 3 }
diff --git a/LctMonolith/Models/Competency.cs b/LctMonolith/Models/Competency.cs
new file mode 100644
index 0000000..55d7b8d
--- /dev/null
+++ b/LctMonolith/Models/Competency.cs
@@ -0,0 +1,11 @@
+namespace LctMonolith.Models;
+
+public class Competency
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public string Name { get; set; } = null!;
+ public string? Description { get; set; }
+ public ICollection UserCompetencies { get; set; } = new List();
+ public ICollection MissionRewards { get; set; } = new List();
+ public ICollection RankRequirements { get; set; } = new List();
+}
diff --git a/LctMonolith/Models/EventLog.cs b/LctMonolith/Models/EventLog.cs
new file mode 100644
index 0000000..457f998
--- /dev/null
+++ b/LctMonolith/Models/EventLog.cs
@@ -0,0 +1,11 @@
+namespace LctMonolith.Models;
+
+public class EventLog
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public EventType Type { get; set; }
+ public Guid UserId { get; set; }
+ public AppUser User { get; set; } = null!;
+ public string? Data { get; set; }
+ public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+}
diff --git a/LctMonolith/Models/Mission.cs b/LctMonolith/Models/Mission.cs
new file mode 100644
index 0000000..85706f4
--- /dev/null
+++ b/LctMonolith/Models/Mission.cs
@@ -0,0 +1,19 @@
+namespace LctMonolith.Models;
+
+public class Mission
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public string Title { get; set; } = null!;
+ public string? Description { get; set; }
+ public string? Branch { get; set; }
+ public MissionCategory Category { get; set; }
+ public Guid? MinRankId { get; set; }
+ public Rank? MinRank { get; set; }
+ public int ExperienceReward { get; set; }
+ public int ManaReward { get; set; }
+ public bool IsActive { get; set; } = true;
+ public ICollection CompetencyRewards { get; set; } = new List();
+ public ICollection ArtifactRewards { get; set; } = new List();
+ public ICollection UserMissions { get; set; } = new List();
+ public ICollection RanksRequiring { get; set; } = new List();
+}
diff --git a/LctMonolith/Models/MissionArtifactReward.cs b/LctMonolith/Models/MissionArtifactReward.cs
new file mode 100644
index 0000000..5ea6201
--- /dev/null
+++ b/LctMonolith/Models/MissionArtifactReward.cs
@@ -0,0 +1,9 @@
+namespace LctMonolith.Models;
+
+public class MissionArtifactReward
+{
+ public Guid MissionId { get; set; }
+ public Mission Mission { get; set; } = null!;
+ public Guid ArtifactId { get; set; }
+ public Artifact Artifact { get; set; } = null!;
+}
diff --git a/LctMonolith/Models/MissionCategory.cs b/LctMonolith/Models/MissionCategory.cs
new file mode 100644
index 0000000..db47ad3
--- /dev/null
+++ b/LctMonolith/Models/MissionCategory.cs
@@ -0,0 +1,3 @@
+namespace LctMonolith.Models;
+
+public enum MissionCategory { Quest = 0, Recruiting = 1, Lecture = 2, Simulator = 3 }
diff --git a/LctMonolith/Models/MissionCompetencyReward.cs b/LctMonolith/Models/MissionCompetencyReward.cs
new file mode 100644
index 0000000..c00fafa
--- /dev/null
+++ b/LctMonolith/Models/MissionCompetencyReward.cs
@@ -0,0 +1,11 @@
+namespace LctMonolith.Models;
+
+public class MissionCompetencyReward
+{
+ public Guid MissionId { get; set; }
+ public Mission Mission { get; set; } = null!;
+ public Guid CompetencyId { get; set; }
+ public Competency Competency { get; set; } = null!;
+ public int LevelDelta { get; set; }
+ public int ProgressPointsDelta { get; set; }
+}
diff --git a/LctMonolith/Models/MissionStatus.cs b/LctMonolith/Models/MissionStatus.cs
new file mode 100644
index 0000000..4290f20
--- /dev/null
+++ b/LctMonolith/Models/MissionStatus.cs
@@ -0,0 +1,11 @@
+namespace LctMonolith.Models;
+
+public enum MissionStatus
+{
+ Locked = 0,
+ Available = 1,
+ InProgress = 2,
+ Submitted = 3,
+ Completed = 4,
+ Rejected = 5
+}
diff --git a/LctMonolith/Domain/Entities/Notification.cs b/LctMonolith/Models/Notification.cs
similarity index 72%
rename from LctMonolith/Domain/Entities/Notification.cs
rename to LctMonolith/Models/Notification.cs
index e51107e..7797119 100644
--- a/LctMonolith/Domain/Entities/Notification.cs
+++ b/LctMonolith/Models/Notification.cs
@@ -1,12 +1,10 @@
-namespace LctMonolith.Domain.Entities;
+namespace LctMonolith.Models;
-/// User notification (in-app).
public class Notification
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid UserId { get; set; }
public AppUser User { get; set; } = null!;
- /// Short classification tag (e.g., rank, mission, store).
public string Type { get; set; } = null!;
public string Title { get; set; } = null!;
public string Message { get; set; } = null!;
diff --git a/LctMonolith/Models/Rank.cs b/LctMonolith/Models/Rank.cs
new file mode 100644
index 0000000..2afc367
--- /dev/null
+++ b/LctMonolith/Models/Rank.cs
@@ -0,0 +1,12 @@
+namespace LctMonolith.Models;
+
+public class Rank
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public string Name { get; set; } = null!;
+ public int Order { get; set; }
+ public int RequiredExperience { get; set; }
+ public ICollection RequiredMissions { get; set; } = new List();
+ public ICollection RequiredCompetencies { get; set; } = new List();
+ public ICollection Users { get; set; } = new List();
+}
diff --git a/LctMonolith/Models/RankRequiredCompetency.cs b/LctMonolith/Models/RankRequiredCompetency.cs
new file mode 100644
index 0000000..5df49f7
--- /dev/null
+++ b/LctMonolith/Models/RankRequiredCompetency.cs
@@ -0,0 +1,10 @@
+namespace LctMonolith.Models;
+
+public class RankRequiredCompetency
+{
+ public Guid RankId { get; set; }
+ public Rank Rank { get; set; } = null!;
+ public Guid CompetencyId { get; set; }
+ public Competency Competency { get; set; } = null!;
+ public int MinLevel { get; set; }
+}
diff --git a/LctMonolith/Models/RankRequiredMission.cs b/LctMonolith/Models/RankRequiredMission.cs
new file mode 100644
index 0000000..226c9ab
--- /dev/null
+++ b/LctMonolith/Models/RankRequiredMission.cs
@@ -0,0 +1,9 @@
+namespace LctMonolith.Models;
+
+public class RankRequiredMission
+{
+ public Guid RankId { get; set; }
+ public Rank Rank { get; set; } = null!;
+ public Guid MissionId { get; set; }
+ public Mission Mission { get; set; } = null!;
+}
diff --git a/LctMonolith/Models/RefreshToken.cs b/LctMonolith/Models/RefreshToken.cs
new file mode 100644
index 0000000..559b008
--- /dev/null
+++ b/LctMonolith/Models/RefreshToken.cs
@@ -0,0 +1,12 @@
+namespace LctMonolith.Models;
+
+public class RefreshToken
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public string Token { get; set; } = null!;
+ public DateTime ExpiresAt { get; set; }
+ public bool IsRevoked { get; set; }
+ public Guid UserId { get; set; }
+ public AppUser User { get; set; } = null!;
+ public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+}
diff --git a/LctMonolith/Models/StoreItem.cs b/LctMonolith/Models/StoreItem.cs
new file mode 100644
index 0000000..ad609a0
--- /dev/null
+++ b/LctMonolith/Models/StoreItem.cs
@@ -0,0 +1,12 @@
+namespace LctMonolith.Models;
+
+public class StoreItem
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public string Name { get; set; } = null!;
+ public string? Description { get; set; }
+ public int Price { get; set; }
+ public bool IsActive { get; set; } = true;
+ public int? Stock { get; set; }
+ public ICollection UserInventory { get; set; } = new List();
+}
diff --git a/LctMonolith/Models/Transaction.cs b/LctMonolith/Models/Transaction.cs
new file mode 100644
index 0000000..ba0cbd0
--- /dev/null
+++ b/LctMonolith/Models/Transaction.cs
@@ -0,0 +1,13 @@
+namespace LctMonolith.Models;
+
+public class Transaction
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+ public Guid UserId { get; set; }
+ public AppUser User { get; set; } = null!;
+ public TransactionType Type { get; set; }
+ public Guid? StoreItemId { get; set; }
+ public StoreItem? StoreItem { get; set; }
+ public int ManaAmount { get; set; }
+ public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
+}
diff --git a/LctMonolith/Models/TransactionType.cs b/LctMonolith/Models/TransactionType.cs
new file mode 100644
index 0000000..9eff065
--- /dev/null
+++ b/LctMonolith/Models/TransactionType.cs
@@ -0,0 +1,3 @@
+namespace LctMonolith.Models;
+
+public enum TransactionType { Purchase = 0, Return = 1, Sale = 2 }
diff --git a/LctMonolith/Models/UserArtifact.cs b/LctMonolith/Models/UserArtifact.cs
new file mode 100644
index 0000000..84f5517
--- /dev/null
+++ b/LctMonolith/Models/UserArtifact.cs
@@ -0,0 +1,10 @@
+namespace LctMonolith.Models;
+
+public class UserArtifact
+{
+ public Guid UserId { get; set; }
+ public AppUser User { get; set; } = null!;
+ public Guid ArtifactId { get; set; }
+ public Artifact Artifact { get; set; } = null!;
+ public DateTime ObtainedAt { get; set; } = DateTime.UtcNow;
+}
diff --git a/LctMonolith/Models/UserCompetency.cs b/LctMonolith/Models/UserCompetency.cs
new file mode 100644
index 0000000..ec81c16
--- /dev/null
+++ b/LctMonolith/Models/UserCompetency.cs
@@ -0,0 +1,11 @@
+namespace LctMonolith.Models;
+
+public class UserCompetency
+{
+ public Guid UserId { get; set; }
+ public AppUser User { get; set; } = null!;
+ public Guid CompetencyId { get; set; }
+ public Competency Competency { get; set; } = null!;
+ public int Level { get; set; }
+ public int ProgressPoints { get; set; }
+}
diff --git a/LctMonolith/Models/UserInventoryItem.cs b/LctMonolith/Models/UserInventoryItem.cs
new file mode 100644
index 0000000..154ebf2
--- /dev/null
+++ b/LctMonolith/Models/UserInventoryItem.cs
@@ -0,0 +1,12 @@
+namespace LctMonolith.Models;
+
+public class UserInventoryItem
+{
+ public Guid UserId { get; set; }
+ public AppUser User { get; set; } = null!;
+ public Guid StoreItemId { get; set; }
+ public StoreItem StoreItem { get; set; } = null!;
+ public int Quantity { get; set; } = 1;
+ public DateTime AcquiredAt { get; set; } = DateTime.UtcNow;
+ public bool IsReturned { get; set; }
+}
diff --git a/LctMonolith/Models/UserMission.cs b/LctMonolith/Models/UserMission.cs
new file mode 100644
index 0000000..eca94b9
--- /dev/null
+++ b/LctMonolith/Models/UserMission.cs
@@ -0,0 +1,12 @@
+namespace LctMonolith.Models;
+
+public class UserMission
+{
+ public Guid UserId { get; set; }
+ public AppUser User { get; set; } = null!;
+ public Guid MissionId { get; set; }
+ public Mission Mission { get; set; } = null!;
+ public MissionStatus Status { get; set; } = MissionStatus.Available;
+ public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
+ public string? SubmissionData { get; set; }
+}
diff --git a/LctMonolith/Program.cs b/LctMonolith/Program.cs
index 9358148..6182081 100644
--- a/LctMonolith/Program.cs
+++ b/LctMonolith/Program.cs
@@ -4,13 +4,14 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
-using LctMonolith.Infrastructure.Data;
-using LctMonolith.Domain.Entities;
+using LctMonolith.Models; // replaced Domain.Entities
using Microsoft.AspNetCore.Identity;
-using LctMonolith.Infrastructure.UnitOfWork;
using LctMonolith.Application.Middleware;
using LctMonolith.Services;
-using LctMonolith.Application.Options; // Added for JwtOptions
+using LctMonolith.Application.Options;
+using LctMonolith.Database.Data;
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Services.Contracts; // Added for JwtOptions
var builder = WebApplication.CreateBuilder(args);
diff --git a/LctMonolith/Services/AnalyticsService.cs b/LctMonolith/Services/AnalyticsService.cs
index ef83780..89523c7 100644
--- a/LctMonolith/Services/AnalyticsService.cs
+++ b/LctMonolith/Services/AnalyticsService.cs
@@ -1,4 +1,5 @@
-using LctMonolith.Infrastructure.UnitOfWork;
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Models;
using LctMonolith.Services.Models;
using Microsoft.EntityFrameworkCore;
@@ -16,7 +17,7 @@ public class AnalyticsService : IAnalyticsService
{
var totalUsers = await _uow.Users.Query().CountAsync(ct);
var totalMissions = await _uow.Missions.Query().CountAsync(ct);
- var completedMissions = await _uow.UserMissions.Query(um => um.Status == Domain.Entities.MissionStatus.Completed).CountAsync(ct);
+ var completedMissions = await _uow.UserMissions.Query(um => um.Status == MissionStatus.Completed).CountAsync(ct);
var totalArtifacts = await _uow.Artifacts.Query().CountAsync(ct);
var totalStoreItems = await _uow.StoreItems.Query().CountAsync(ct);
var totalExperience = await _uow.Users.Query().SumAsync(u => (long)u.Experience, ct);
@@ -31,4 +32,3 @@ public class AnalyticsService : IAnalyticsService
};
}
}
-
diff --git a/LctMonolith/Services/Contracts/IAnalyticsService.cs b/LctMonolith/Services/Contracts/IAnalyticsService.cs
new file mode 100644
index 0000000..6276fd4
--- /dev/null
+++ b/LctMonolith/Services/Contracts/IAnalyticsService.cs
@@ -0,0 +1,10 @@
+using LctMonolith.Services.Models;
+
+using LctMonolith.Services.Models;
+
+namespace LctMonolith.Services;
+
+public interface IAnalyticsService
+{
+ Task GetSummaryAsync(CancellationToken ct = default);
+}
diff --git a/LctMonolith/Services/Contracts/IGamificationService.cs b/LctMonolith/Services/Contracts/IGamificationService.cs
new file mode 100644
index 0000000..a87940c
--- /dev/null
+++ b/LctMonolith/Services/Contracts/IGamificationService.cs
@@ -0,0 +1,15 @@
+using LctMonolith.Models;
+using LctMonolith.Services.Models;
+
+namespace LctMonolith.Services.Contracts;
+
+/// Gamification progression logic (progress, rewards, rank evaluation).
+public interface IGamificationService
+{
+ /// Get current user progression snapshot (xp, mana, next rank requirements).
+ Task GetProgressAsync(Guid userId, CancellationToken ct = default);
+ /// Apply mission completion rewards (xp, mana, skills, artifacts) to user.
+ Task ApplyMissionCompletionAsync(Guid userId, Mission mission, CancellationToken ct = default);
+ /// Re-evaluate and apply rank upgrade if requirements are met.
+ Task EvaluateRankUpgradeAsync(Guid userId, CancellationToken ct = default);
+}
diff --git a/LctMonolith/Services/Contracts/IInventoryService.cs b/LctMonolith/Services/Contracts/IInventoryService.cs
new file mode 100644
index 0000000..4723570
--- /dev/null
+++ b/LctMonolith/Services/Contracts/IInventoryService.cs
@@ -0,0 +1,9 @@
+using LctMonolith.Models;
+
+namespace LctMonolith.Services.Contracts;
+
+public interface IInventoryService
+{
+ Task> GetStoreInventoryAsync(Guid userId, CancellationToken ct = default);
+ Task> GetArtifactsAsync(Guid userId, CancellationToken ct = default);
+}
diff --git a/LctMonolith/Services/Contracts/IMissionService.cs b/LctMonolith/Services/Contracts/IMissionService.cs
new file mode 100644
index 0000000..a0df91e
--- /dev/null
+++ b/LctMonolith/Services/Contracts/IMissionService.cs
@@ -0,0 +1,11 @@
+using LctMonolith.Models;
+using LctMonolith.Services.Models;
+
+namespace LctMonolith.Services.Contracts;
+
+public interface IMissionService
+{
+ Task CreateMissionAsync(CreateMissionModel model, CancellationToken ct = default);
+ Task> GetAvailableMissionsAsync(Guid userId, CancellationToken ct = default);
+ Task UpdateStatusAsync(Guid userId, Guid missionId, MissionStatus status, string? submissionData, CancellationToken ct = default);
+}
diff --git a/LctMonolith/Services/Contracts/INotificationService.cs b/LctMonolith/Services/Contracts/INotificationService.cs
new file mode 100644
index 0000000..e1193aa
--- /dev/null
+++ b/LctMonolith/Services/Contracts/INotificationService.cs
@@ -0,0 +1,12 @@
+using LctMonolith.Models;
+
+namespace LctMonolith.Services.Contracts;
+
+public interface INotificationService
+{
+ Task CreateAsync(Guid userId, string type, string title, string message, CancellationToken ct = default);
+ Task> GetUnreadAsync(Guid userId, CancellationToken ct = default);
+ Task> GetAllAsync(Guid userId, int take = 100, CancellationToken ct = default);
+ Task MarkReadAsync(Guid userId, Guid notificationId, CancellationToken ct = default);
+ Task MarkAllReadAsync(Guid userId, CancellationToken ct = default);
+}
diff --git a/LctMonolith/Services/Contracts/IStoreService.cs b/LctMonolith/Services/Contracts/IStoreService.cs
new file mode 100644
index 0000000..5cb7004
--- /dev/null
+++ b/LctMonolith/Services/Contracts/IStoreService.cs
@@ -0,0 +1,9 @@
+using LctMonolith.Models;
+
+namespace LctMonolith.Services.Contracts;
+
+public interface IStoreService
+{
+ Task> GetActiveItemsAsync(CancellationToken ct = default);
+ Task PurchaseAsync(Guid userId, Guid itemId, int quantity, CancellationToken ct = default);
+}
diff --git a/LctMonolith/Services/Contracts/ITokenService.cs b/LctMonolith/Services/Contracts/ITokenService.cs
new file mode 100644
index 0000000..583e234
--- /dev/null
+++ b/LctMonolith/Services/Contracts/ITokenService.cs
@@ -0,0 +1,11 @@
+using LctMonolith.Models;
+using LctMonolith.Services.Models;
+
+namespace LctMonolith.Services.Contracts;
+
+public interface ITokenService
+{
+ Task IssueAsync(AppUser user, CancellationToken ct = default);
+ Task RefreshAsync(string refreshToken, CancellationToken ct = default);
+ Task RevokeAsync(string refreshToken, CancellationToken ct = default);
+}
diff --git a/LctMonolith/Services/GamificationService.cs b/LctMonolith/Services/GamificationService.cs
index 6e93f02..63b6c53 100644
--- a/LctMonolith/Services/GamificationService.cs
+++ b/LctMonolith/Services/GamificationService.cs
@@ -1,6 +1,7 @@
using System.Linq;
-using LctMonolith.Domain.Entities;
-using LctMonolith.Infrastructure.UnitOfWork;
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Models;
+using LctMonolith.Services.Contracts;
using LctMonolith.Services.Models;
using Microsoft.EntityFrameworkCore;
using Serilog;
diff --git a/LctMonolith/Services/IWeatherService.cs b/LctMonolith/Services/IWeatherService.cs
deleted file mode 100644
index 0ebafcc..0000000
--- a/LctMonolith/Services/IWeatherService.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-// Removed legacy Weather service placeholder.
-// Intentionally left blank.
-
diff --git a/LctMonolith/Services/Interfaces.cs b/LctMonolith/Services/Interfaces.cs
deleted file mode 100644
index b071c4e..0000000
--- a/LctMonolith/Services/Interfaces.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using LctMonolith.Domain.Entities;
-using LctMonolith.Services.Models;
-
-namespace LctMonolith.Services;
-
-/// Service for issuing JWT and refresh tokens.
-public interface ITokenService
-{
- Task IssueAsync(AppUser user, CancellationToken ct = default);
- Task RefreshAsync(string refreshToken, CancellationToken ct = default);
- Task RevokeAsync(string refreshToken, CancellationToken ct = default);
-}
-
-/// Gamification progression logic (awards, rank upgrade).
-public interface IGamificationService
-{
- Task GetProgressAsync(Guid userId, CancellationToken ct = default);
- Task ApplyMissionCompletionAsync(Guid userId, Mission mission, CancellationToken ct = default);
- Task EvaluateRankUpgradeAsync(Guid userId, CancellationToken ct = default);
-}
-
-/// Mission management and user mission state transitions.
-public interface IMissionService
-{
- Task CreateMissionAsync(CreateMissionModel model, CancellationToken ct = default);
- Task> GetAvailableMissionsAsync(Guid userId, CancellationToken ct = default);
- Task UpdateStatusAsync(Guid userId, Guid missionId, MissionStatus status, string? submissionData, CancellationToken ct = default);
-}
-
-/// Store and inventory operations.
-public interface IStoreService
-{
- Task> GetActiveItemsAsync(CancellationToken ct = default);
- Task PurchaseAsync(Guid userId, Guid itemId, int quantity, CancellationToken ct = default);
-}
-
-/// User notifications (in-app) management.
-public interface INotificationService
-{
- Task CreateAsync(Guid userId, string type, string title, string message, CancellationToken ct = default);
- Task> GetUnreadAsync(Guid userId, CancellationToken ct = default);
- Task> GetAllAsync(Guid userId, int take = 100, CancellationToken ct = default);
- Task MarkReadAsync(Guid userId, Guid notificationId, CancellationToken ct = default);
- Task MarkAllReadAsync(Guid userId, CancellationToken ct = default);
-}
-
-/// Inventory querying (owned artifacts, store items).
-public interface IInventoryService
-{
- Task> GetStoreInventoryAsync(Guid userId, CancellationToken ct = default);
- Task> GetArtifactsAsync(Guid userId, CancellationToken ct = default);
-}
-
-/// Basic analytics / aggregated metrics.
-public interface IAnalyticsService
-{
- Task GetSummaryAsync(CancellationToken ct = default);
-}
diff --git a/LctMonolith/Services/InventoryService.cs b/LctMonolith/Services/InventoryService.cs
index d2c87b4..32f740c 100644
--- a/LctMonolith/Services/InventoryService.cs
+++ b/LctMonolith/Services/InventoryService.cs
@@ -1,5 +1,6 @@
-using LctMonolith.Domain.Entities;
-using LctMonolith.Infrastructure.UnitOfWork;
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Models;
+using LctMonolith.Services.Contracts;
using Microsoft.EntityFrameworkCore;
namespace LctMonolith.Services;
@@ -32,4 +33,3 @@ public class InventoryService : IInventoryService
.ToListAsync(ct);
}
}
-
diff --git a/LctMonolith/Services/MissionService.cs b/LctMonolith/Services/MissionService.cs
index a6e252f..b44f733 100644
--- a/LctMonolith/Services/MissionService.cs
+++ b/LctMonolith/Services/MissionService.cs
@@ -1,10 +1,11 @@
-using LctMonolith.Domain.Entities;
-using LctMonolith.Infrastructure.UnitOfWork;
+using LctMonolith.Models;
using LctMonolith.Services.Models;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System.Text.Json;
using System.Linq;
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Services.Contracts;
namespace LctMonolith.Services;
diff --git a/LctMonolith/Services/Models/AnalyticsSummary.cs b/LctMonolith/Services/Models/AnalyticsSummary.cs
new file mode 100644
index 0000000..937a567
--- /dev/null
+++ b/LctMonolith/Services/Models/AnalyticsSummary.cs
@@ -0,0 +1,12 @@
+namespace LctMonolith.Services.Models;
+
+public class AnalyticsSummary
+{
+ public int TotalUsers { get; set; }
+ public int TotalMissions { get; set; }
+ public int CompletedMissions { get; set; }
+ public int TotalArtifacts { get; set; }
+ public int TotalStoreItems { get; set; }
+ public long TotalExperience { get; set; }
+ public DateTime GeneratedAtUtc { get; set; } = DateTime.UtcNow;
+}
diff --git a/LctMonolith/Services/Models/AuthRequest.cs b/LctMonolith/Services/Models/AuthRequest.cs
new file mode 100644
index 0000000..cab5737
--- /dev/null
+++ b/LctMonolith/Services/Models/AuthRequest.cs
@@ -0,0 +1,9 @@
+namespace LctMonolith.Services.Models;
+
+public class AuthRequest
+{
+ public string Email { get; set; } = null!;
+ public string Password { get; set; } = null!;
+ public string? FirstName { get; set; }
+ public string? LastName { get; set; }
+}
diff --git a/LctMonolith/Services/Models/CompetencyRewardModel.cs b/LctMonolith/Services/Models/CompetencyRewardModel.cs
new file mode 100644
index 0000000..ce9cd05
--- /dev/null
+++ b/LctMonolith/Services/Models/CompetencyRewardModel.cs
@@ -0,0 +1,8 @@
+namespace LctMonolith.Services.Models;
+
+public class CompetencyRewardModel
+{
+ public Guid CompetencyId { get; set; }
+ public int LevelDelta { get; set; }
+ public int ProgressPointsDelta { get; set; }
+}
diff --git a/LctMonolith/Services/Models/CreateMissionModel.cs b/LctMonolith/Services/Models/CreateMissionModel.cs
new file mode 100644
index 0000000..3197210
--- /dev/null
+++ b/LctMonolith/Services/Models/CreateMissionModel.cs
@@ -0,0 +1,16 @@
+using LctMonolith.Models;
+
+namespace LctMonolith.Services.Models;
+
+public class CreateMissionModel
+{
+ public string Title { get; set; } = null!;
+ public string? Description { get; set; }
+ public string? Branch { get; set; }
+ public MissionCategory Category { get; set; }
+ public Guid? MinRankId { get; set; }
+ public int ExperienceReward { get; set; }
+ public int ManaReward { get; set; }
+ public List CompetencyRewards { get; set; } = new();
+ public List ArtifactRewardIds { get; set; } = new();
+}
diff --git a/LctMonolith/Services/Models/Models.cs b/LctMonolith/Services/Models/Models.cs
deleted file mode 100644
index 8bc0a00..0000000
--- a/LctMonolith/Services/Models/Models.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using LctMonolith.Domain.Entities;
-
-namespace LctMonolith.Services.Models;
-
-/// Returned access+refresh token pair with expiry metadata.
-public record TokenPair(string AccessToken, DateTime AccessTokenExpiresAt, string RefreshToken, DateTime RefreshTokenExpiresAt);
-
-/// Mission creation request model.
-public class CreateMissionModel
-{
- public string Title { get; set; } = null!;
- public string? Description { get; set; }
- public string? Branch { get; set; }
- public MissionCategory Category { get; set; }
- public Guid? MinRankId { get; set; }
- public int ExperienceReward { get; set; }
- public int ManaReward { get; set; }
- public List CompetencyRewards { get; set; } = new();
- public List ArtifactRewardIds { get; set; } = new();
-}
-
-/// Competency reward definition for mission creation.
-public class CompetencyRewardModel
-{
- public Guid CompetencyId { get; set; }
- public int LevelDelta { get; set; }
- public int ProgressPointsDelta { get; set; }
-}
-
-/// Progress snapshot for UI: rank, xp, remaining requirements.
-public class ProgressSnapshot
-{
- public int Experience { get; set; }
- public int Mana { get; set; }
- public Guid? CurrentRankId { get; set; }
- public string? CurrentRankName { get; set; }
- public Guid? NextRankId { get; set; }
- public string? NextRankName { get; set; }
- public int? RequiredExperienceForNextRank { get; set; }
- public int? ExperienceRemaining => RequiredExperienceForNextRank.HasValue ? Math.Max(0, RequiredExperienceForNextRank.Value - Experience) : null;
- public List OutstandingMissionIds { get; set; } = new();
- public List OutstandingCompetencies { get; set; } = new();
-}
-
-/// Competency requirement still unmet for next rank.
-public class OutstandingCompetency
-{
- public Guid CompetencyId { get; set; }
- public string? CompetencyName { get; set; }
- public int RequiredLevel { get; set; }
- public int CurrentLevel { get; set; }
-}
-
-/// Request to update mission status with optional submission data.
-public class UpdateMissionStatusRequest
-{
- public MissionStatus Status { get; set; }
- public string? SubmissionData { get; set; }
-}
-
-/// Store purchase request.
-public class PurchaseRequest
-{
- public Guid ItemId { get; set; }
- public int Quantity { get; set; } = 1;
-}
-
-/// Authentication request (login/register) simple mock.
-public class AuthRequest
-{
- public string Email { get; set; } = null!;
- public string Password { get; set; } = null!;
- public string? FirstName { get; set; }
- public string? LastName { get; set; }
-}
-
-/// Refresh token request.
-public class RefreshRequest
-{
- public string RefreshToken { get; set; } = null!;
-}
-
-/// Revoke refresh token request.
-public class RevokeRequest
-{
- public string RefreshToken { get; set; } = null!;
-}
-
-/// Analytics summary for admin dashboard.
-public class AnalyticsSummary
-{
- public int TotalUsers { get; set; }
- public int TotalMissions { get; set; }
- public int CompletedMissions { get; set; }
- public int TotalArtifacts { get; set; }
- public int TotalStoreItems { get; set; }
- public long TotalExperience { get; set; }
- public DateTime GeneratedAtUtc { get; set; } = DateTime.UtcNow;
-}
diff --git a/LctMonolith/Services/Models/ProgressSnapshot.cs b/LctMonolith/Services/Models/ProgressSnapshot.cs
new file mode 100644
index 0000000..7fe960e
--- /dev/null
+++ b/LctMonolith/Services/Models/ProgressSnapshot.cs
@@ -0,0 +1,23 @@
+namespace LctMonolith.Services.Models;
+
+public class ProgressSnapshot
+{
+ public int Experience { get; set; }
+ public int Mana { get; set; }
+ public Guid? CurrentRankId { get; set; }
+ public string? CurrentRankName { get; set; }
+ public Guid? NextRankId { get; set; }
+ public string? NextRankName { get; set; }
+ public int? RequiredExperienceForNextRank { get; set; }
+ public int? ExperienceRemaining => RequiredExperienceForNextRank.HasValue ? Math.Max(0, RequiredExperienceForNextRank.Value - Experience) : null;
+ public List OutstandingMissionIds { get; set; } = new();
+ public List OutstandingCompetencies { get; set; } = new();
+}
+
+public class OutstandingCompetency
+{
+ public Guid CompetencyId { get; set; }
+ public string? CompetencyName { get; set; }
+ public int RequiredLevel { get; set; }
+ public int CurrentLevel { get; set; }
+}
diff --git a/LctMonolith/Services/Models/PurchaseRequest.cs b/LctMonolith/Services/Models/PurchaseRequest.cs
new file mode 100644
index 0000000..61ceb7e
--- /dev/null
+++ b/LctMonolith/Services/Models/PurchaseRequest.cs
@@ -0,0 +1,7 @@
+namespace LctMonolith.Services.Models;
+
+public class PurchaseRequest
+{
+ public Guid ItemId { get; set; }
+ public int Quantity { get; set; } = 1;
+}
diff --git a/LctMonolith/Services/Models/RefreshRequest.cs b/LctMonolith/Services/Models/RefreshRequest.cs
new file mode 100644
index 0000000..dd1fb95
--- /dev/null
+++ b/LctMonolith/Services/Models/RefreshRequest.cs
@@ -0,0 +1,6 @@
+namespace LctMonolith.Services.Models;
+
+public class RefreshRequest
+{
+ public string RefreshToken { get; set; } = null!;
+}
diff --git a/LctMonolith/Services/Models/RevokeRequest.cs b/LctMonolith/Services/Models/RevokeRequest.cs
new file mode 100644
index 0000000..0b0aa49
--- /dev/null
+++ b/LctMonolith/Services/Models/RevokeRequest.cs
@@ -0,0 +1,6 @@
+namespace LctMonolith.Services.Models;
+
+public class RevokeRequest
+{
+ public string RefreshToken { get; set; } = null!;
+}
diff --git a/LctMonolith/Services/Models/TokenPair.cs b/LctMonolith/Services/Models/TokenPair.cs
new file mode 100644
index 0000000..5b00a6f
--- /dev/null
+++ b/LctMonolith/Services/Models/TokenPair.cs
@@ -0,0 +1,3 @@
+namespace LctMonolith.Services.Models;
+
+public record TokenPair(string AccessToken, DateTime AccessTokenExpiresAt, string RefreshToken, DateTime RefreshTokenExpiresAt);
diff --git a/LctMonolith/Services/Models/UpdateMissionStatusRequest.cs b/LctMonolith/Services/Models/UpdateMissionStatusRequest.cs
new file mode 100644
index 0000000..591eb52
--- /dev/null
+++ b/LctMonolith/Services/Models/UpdateMissionStatusRequest.cs
@@ -0,0 +1,9 @@
+using LctMonolith.Models;
+
+namespace LctMonolith.Services.Models;
+
+public class UpdateMissionStatusRequest
+{
+ public MissionStatus Status { get; set; }
+ public string? SubmissionData { get; set; }
+}
diff --git a/LctMonolith/Services/NotificationService.cs b/LctMonolith/Services/NotificationService.cs
index 8b13789..6587e58 100644
--- a/LctMonolith/Services/NotificationService.cs
+++ b/LctMonolith/Services/NotificationService.cs
@@ -1 +1,79 @@
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Models;
+using LctMonolith.Services.Contracts;
+using Microsoft.EntityFrameworkCore;
+namespace LctMonolith.Services;
+
+///
+/// In-app user notifications CRUD / read-state operations.
+///
+public class NotificationService : INotificationService
+{
+ private readonly IUnitOfWork _uow;
+
+ public NotificationService(IUnitOfWork uow)
+ {
+ _uow = uow;
+ }
+
+ public async Task CreateAsync(Guid userId, string type, string title, string message, CancellationToken ct = default)
+ {
+ if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Required", nameof(type));
+ if (string.IsNullOrWhiteSpace(title)) throw new ArgumentException("Required", nameof(title));
+ if (string.IsNullOrWhiteSpace(message)) throw new ArgumentException("Required", nameof(message));
+
+ var n = new Notification
+ {
+ UserId = userId,
+ Type = type.Trim(),
+ Title = title.Trim(),
+ Message = message.Trim(),
+ CreatedAt = DateTime.UtcNow,
+ IsRead = false
+ };
+ await _uow.Notifications.AddAsync(n, ct);
+ await _uow.SaveChangesAsync(ct);
+ return n;
+ }
+
+ public async Task> GetUnreadAsync(Guid userId, CancellationToken ct = default)
+ {
+ var query = _uow.Notifications.Query(n => n.UserId == userId && !n.IsRead, q => q.OrderByDescending(x => x.CreatedAt));
+ return await query.Take(100).ToListAsync(ct);
+ }
+
+ public async Task> GetAllAsync(Guid userId, int take = 100, CancellationToken ct = default)
+ {
+ if (take <= 0) take = 1;
+ if (take > 500) take = 500;
+ var query = _uow.Notifications.Query(n => n.UserId == userId, q => q.OrderByDescending(x => x.CreatedAt));
+ return await query.Take(take).ToListAsync(ct);
+ }
+
+ public async Task MarkReadAsync(Guid userId, Guid notificationId, CancellationToken ct = default)
+ {
+ var notif = await _uow.Notifications.Query(n => n.Id == notificationId && n.UserId == userId).FirstOrDefaultAsync(ct)
+ ?? throw new KeyNotFoundException("Notification not found");
+ if (!notif.IsRead)
+ {
+ notif.IsRead = true;
+ notif.ReadAt = DateTime.UtcNow;
+ await _uow.SaveChangesAsync(ct);
+ }
+ }
+
+ public async Task MarkAllReadAsync(Guid userId, CancellationToken ct = default)
+ {
+ var unread = await _uow.Notifications.Query(n => n.UserId == userId && !n.IsRead).ToListAsync(ct);
+ if (unread.Count == 0) return 0;
+ var now = DateTime.UtcNow;
+ foreach (var n in unread)
+ {
+ n.IsRead = true;
+ n.ReadAt = now;
+ }
+ await _uow.SaveChangesAsync(ct);
+ return unread.Count;
+ }
+}
diff --git a/LctMonolith/Services/StoreService.cs b/LctMonolith/Services/StoreService.cs
index e25ed6f..c530ee3 100644
--- a/LctMonolith/Services/StoreService.cs
+++ b/LctMonolith/Services/StoreService.cs
@@ -1,8 +1,9 @@
-using LctMonolith.Domain.Entities;
-using LctMonolith.Infrastructure.UnitOfWork;
+using LctMonolith.Models;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System.Text.Json;
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Services.Contracts;
namespace LctMonolith.Services;
@@ -72,4 +73,3 @@ public class StoreService : IStoreService
return inv;
}
}
-
diff --git a/LctMonolith/Services/TokenService.cs b/LctMonolith/Services/TokenService.cs
index 717ab83..34e9217 100644
--- a/LctMonolith/Services/TokenService.cs
+++ b/LctMonolith/Services/TokenService.cs
@@ -3,8 +3,9 @@ using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using LctMonolith.Application.Options;
-using LctMonolith.Domain.Entities;
-using LctMonolith.Infrastructure.UnitOfWork;
+using LctMonolith.Database.UnitOfWork;
+using LctMonolith.Models;
+using LctMonolith.Services.Contracts;
using LctMonolith.Services.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;