From 8c0809d620a8e8b79caf71b02490e1fad08d7755 Mon Sep 17 00:00:00 2001 From: ereshk1gal Date: Tue, 30 Sep 2025 02:16:37 +0300 Subject: [PATCH] feat: Fixed errors --- LctMonolith/Controllers/AuthController.cs | 5 +- .../Controllers/GamificationController.cs | 1 + .../Controllers/InventoryController.cs | 1 + LctMonolith/Controllers/MissionsController.cs | 4 +- .../Controllers/NotificationsController.cs | 1 + LctMonolith/Controllers/StoreController.cs | 1 + .../Data/AppDbContext.cs | 7 +- .../Data/DbSeeder.cs | 5 +- LctMonolith/Database/Models/EventType.cs | 19 ++++ .../Repositories/GenericRepository.cs | 4 +- .../Repositories/IGenericRepository.cs | 2 +- .../UnitOfWork/IUnitOfWork.cs | 8 +- .../UnitOfWork/UnitOfWork.cs | 12 ++- .../Domain/Entities/ArtifactsAndStore.cs | 95 ------------------ LctMonolith/Domain/Entities/Competency.cs | 41 -------- LctMonolith/Domain/Entities/Enums.cs | 54 ---------- LctMonolith/Domain/Entities/Mission.cs | 44 --------- LctMonolith/Domain/Entities/Rank.cs | 40 -------- .../{Domain/Entities => Models}/AppUser.cs | 17 +--- LctMonolith/Models/Artifact.cs | 13 +++ LctMonolith/Models/ArtifactRarity.cs | 3 + LctMonolith/Models/Competency.cs | 11 +++ LctMonolith/Models/EventLog.cs | 11 +++ LctMonolith/Models/Mission.cs | 19 ++++ LctMonolith/Models/MissionArtifactReward.cs | 9 ++ LctMonolith/Models/MissionCategory.cs | 3 + LctMonolith/Models/MissionCompetencyReward.cs | 11 +++ LctMonolith/Models/MissionStatus.cs | 11 +++ .../Entities => Models}/Notification.cs | 4 +- LctMonolith/Models/Rank.cs | 12 +++ LctMonolith/Models/RankRequiredCompetency.cs | 10 ++ LctMonolith/Models/RankRequiredMission.cs | 9 ++ LctMonolith/Models/RefreshToken.cs | 12 +++ LctMonolith/Models/StoreItem.cs | 12 +++ LctMonolith/Models/Transaction.cs | 13 +++ LctMonolith/Models/TransactionType.cs | 3 + LctMonolith/Models/UserArtifact.cs | 10 ++ LctMonolith/Models/UserCompetency.cs | 11 +++ LctMonolith/Models/UserInventoryItem.cs | 12 +++ LctMonolith/Models/UserMission.cs | 12 +++ LctMonolith/Program.cs | 9 +- LctMonolith/Services/AnalyticsService.cs | 6 +- .../Services/Contracts/IAnalyticsService.cs | 10 ++ .../Contracts/IGamificationService.cs | 15 +++ .../Services/Contracts/IInventoryService.cs | 9 ++ .../Services/Contracts/IMissionService.cs | 11 +++ .../Contracts/INotificationService.cs | 12 +++ .../Services/Contracts/IStoreService.cs | 9 ++ .../Services/Contracts/ITokenService.cs | 11 +++ LctMonolith/Services/GamificationService.cs | 5 +- LctMonolith/Services/IWeatherService.cs | 3 - LctMonolith/Services/Interfaces.cs | 58 ----------- LctMonolith/Services/InventoryService.cs | 6 +- LctMonolith/Services/MissionService.cs | 5 +- .../Services/Models/AnalyticsSummary.cs | 12 +++ LctMonolith/Services/Models/AuthRequest.cs | 9 ++ .../Services/Models/CompetencyRewardModel.cs | 8 ++ .../Services/Models/CreateMissionModel.cs | 16 +++ LctMonolith/Services/Models/Models.cs | 99 ------------------- .../Services/Models/ProgressSnapshot.cs | 23 +++++ .../Services/Models/PurchaseRequest.cs | 7 ++ LctMonolith/Services/Models/RefreshRequest.cs | 6 ++ LctMonolith/Services/Models/RevokeRequest.cs | 6 ++ LctMonolith/Services/Models/TokenPair.cs | 3 + .../Models/UpdateMissionStatusRequest.cs | 9 ++ LctMonolith/Services/NotificationService.cs | 78 +++++++++++++++ LctMonolith/Services/StoreService.cs | 6 +- LctMonolith/Services/TokenService.cs | 5 +- 68 files changed, 538 insertions(+), 490 deletions(-) rename LctMonolith/{Infrastructure => Database}/Data/AppDbContext.cs (97%) rename LctMonolith/{Infrastructure => Database}/Data/DbSeeder.cs (94%) create mode 100644 LctMonolith/Database/Models/EventType.cs rename LctMonolith/{Infrastructure => Database}/Repositories/GenericRepository.cs (95%) rename LctMonolith/{Infrastructure => Database}/Repositories/IGenericRepository.cs (94%) rename LctMonolith/{Infrastructure => Database}/UnitOfWork/IUnitOfWork.cs (88%) rename LctMonolith/{Infrastructure => Database}/UnitOfWork/UnitOfWork.cs (96%) delete mode 100644 LctMonolith/Domain/Entities/ArtifactsAndStore.cs delete mode 100644 LctMonolith/Domain/Entities/Competency.cs delete mode 100644 LctMonolith/Domain/Entities/Enums.cs delete mode 100644 LctMonolith/Domain/Entities/Mission.cs delete mode 100644 LctMonolith/Domain/Entities/Rank.cs rename LctMonolith/{Domain/Entities => Models}/AppUser.cs (68%) create mode 100644 LctMonolith/Models/Artifact.cs create mode 100644 LctMonolith/Models/ArtifactRarity.cs create mode 100644 LctMonolith/Models/Competency.cs create mode 100644 LctMonolith/Models/EventLog.cs create mode 100644 LctMonolith/Models/Mission.cs create mode 100644 LctMonolith/Models/MissionArtifactReward.cs create mode 100644 LctMonolith/Models/MissionCategory.cs create mode 100644 LctMonolith/Models/MissionCompetencyReward.cs create mode 100644 LctMonolith/Models/MissionStatus.cs rename LctMonolith/{Domain/Entities => Models}/Notification.cs (72%) create mode 100644 LctMonolith/Models/Rank.cs create mode 100644 LctMonolith/Models/RankRequiredCompetency.cs create mode 100644 LctMonolith/Models/RankRequiredMission.cs create mode 100644 LctMonolith/Models/RefreshToken.cs create mode 100644 LctMonolith/Models/StoreItem.cs create mode 100644 LctMonolith/Models/Transaction.cs create mode 100644 LctMonolith/Models/TransactionType.cs create mode 100644 LctMonolith/Models/UserArtifact.cs create mode 100644 LctMonolith/Models/UserCompetency.cs create mode 100644 LctMonolith/Models/UserInventoryItem.cs create mode 100644 LctMonolith/Models/UserMission.cs create mode 100644 LctMonolith/Services/Contracts/IAnalyticsService.cs create mode 100644 LctMonolith/Services/Contracts/IGamificationService.cs create mode 100644 LctMonolith/Services/Contracts/IInventoryService.cs create mode 100644 LctMonolith/Services/Contracts/IMissionService.cs create mode 100644 LctMonolith/Services/Contracts/INotificationService.cs create mode 100644 LctMonolith/Services/Contracts/IStoreService.cs create mode 100644 LctMonolith/Services/Contracts/ITokenService.cs delete mode 100644 LctMonolith/Services/IWeatherService.cs delete mode 100644 LctMonolith/Services/Interfaces.cs create mode 100644 LctMonolith/Services/Models/AnalyticsSummary.cs create mode 100644 LctMonolith/Services/Models/AuthRequest.cs create mode 100644 LctMonolith/Services/Models/CompetencyRewardModel.cs create mode 100644 LctMonolith/Services/Models/CreateMissionModel.cs delete mode 100644 LctMonolith/Services/Models/Models.cs create mode 100644 LctMonolith/Services/Models/ProgressSnapshot.cs create mode 100644 LctMonolith/Services/Models/PurchaseRequest.cs create mode 100644 LctMonolith/Services/Models/RefreshRequest.cs create mode 100644 LctMonolith/Services/Models/RevokeRequest.cs create mode 100644 LctMonolith/Services/Models/TokenPair.cs create mode 100644 LctMonolith/Services/Models/UpdateMissionStatusRequest.cs 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;