feat: Fixed errors
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using LctMonolith.Services;
|
||||
using LctMonolith.Services.Contracts;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using LctMonolith.Services;
|
||||
using LctMonolith.Services.Contracts;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Security.Claims;
|
||||
using LctMonolith.Services;
|
||||
using LctMonolith.Services.Contracts;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Main EF Core database context for gamification module (PostgreSQL provider expected).
|
||||
/// </summary>
|
||||
public class AppDbContext : IdentityDbContext<AppUser, IdentityRole<Guid>, Guid>
|
||||
{
|
||||
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
|
||||
|
||||
public DbSet<Rank> Ranks => Set<Rank>();
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Development database seeder for initial ranks, competencies, sample store items.
|
||||
19
LctMonolith/Database/Models/EventType.cs
Normal file
19
LctMonolith/Database/Models/EventType.cs
Normal file
@@ -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
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Generic repository implementation for common CRUD and query composition.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace LctMonolith.Infrastructure.Repositories;
|
||||
namespace LctMonolith.Database.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Generic repository abstraction for aggregate root / entity access. Read operations return IQueryable for composition.
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Unit of Work aggregates repositories and transaction boundary.
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Unit of Work implementation encapsulating repositories and DB transaction scope.
|
||||
@@ -1,95 +0,0 @@
|
||||
namespace LctMonolith.Domain.Entities;
|
||||
|
||||
/// <summary>Artifact definition (unique reward objects).</summary>
|
||||
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<UserArtifact> Users { get; set; } = new List<UserArtifact>();
|
||||
public ICollection<MissionArtifactReward> MissionRewards { get; set; } = new List<MissionArtifactReward>();
|
||||
}
|
||||
|
||||
/// <summary>Mapping artifact to user ownership.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Reward mapping: mission grants artifact(s).</summary>
|
||||
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!;
|
||||
}
|
||||
|
||||
/// <summary>Item in store that can be purchased with mana.</summary>
|
||||
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<UserInventoryItem> UserInventory { get; set; } = new List<UserInventoryItem>();
|
||||
}
|
||||
|
||||
/// <summary>User owned store item record.</summary>
|
||||
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; }
|
||||
}
|
||||
|
||||
/// <summary>Transaction record for purchases/returns/sales.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>System event log for auditing user actions and progression.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>Refresh token for JWT auth.</summary>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
namespace LctMonolith.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Competency (skill) that can be progressed by completing missions.
|
||||
/// </summary>
|
||||
public class Competency
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public string Name { get; set; } = null!;
|
||||
public string? Description { get; set; }
|
||||
|
||||
public ICollection<UserCompetency> UserCompetencies { get; set; } = new List<UserCompetency>();
|
||||
public ICollection<MissionCompetencyReward> MissionRewards { get; set; } = new List<MissionCompetencyReward>();
|
||||
public ICollection<RankRequiredCompetency> RankRequirements { get; set; } = new List<RankRequiredCompetency>();
|
||||
}
|
||||
|
||||
/// <summary>Per-user competency level.</summary>
|
||||
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!;
|
||||
/// <summary>Current level (integer simple scale).</summary>
|
||||
public int Level { get; set; }
|
||||
/// <summary>Optional numeric progress inside level (e.g., partial points).</summary>
|
||||
public int ProgressPoints { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Reward mapping: mission increases competency level points.</summary>
|
||||
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!;
|
||||
/// <summary>Increment value in levels (could be 0 or 1) or points depending on design.</summary>
|
||||
public int LevelDelta { get; set; }
|
||||
public int ProgressPointsDelta { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
namespace LctMonolith.Domain.Entities;
|
||||
|
||||
/// <summary>Mission category taxonomy.</summary>
|
||||
public enum MissionCategory
|
||||
{
|
||||
Quest = 0,
|
||||
Recruiting = 1,
|
||||
Lecture = 2,
|
||||
Simulator = 3
|
||||
}
|
||||
|
||||
/// <summary>Status of a mission for a specific user.</summary>
|
||||
public enum MissionStatus
|
||||
{
|
||||
Locked = 0,
|
||||
Available = 1,
|
||||
InProgress = 2,
|
||||
Submitted = 3,
|
||||
Completed = 4,
|
||||
Rejected = 5
|
||||
}
|
||||
|
||||
/// <summary>Rarity level of an artifact.</summary>
|
||||
public enum ArtifactRarity
|
||||
{
|
||||
Common = 0,
|
||||
Rare = 1,
|
||||
Epic = 2,
|
||||
Legendary = 3
|
||||
}
|
||||
|
||||
/// <summary>Type of transactional operation in store.</summary>
|
||||
public enum TransactionType
|
||||
{
|
||||
Purchase = 0,
|
||||
Return = 1,
|
||||
Sale = 2
|
||||
}
|
||||
|
||||
/// <summary>Auditable event types enumerated in requirements.</summary>
|
||||
public enum EventType
|
||||
{
|
||||
SkillProgress = 1,
|
||||
MissionStatusChanged = 2,
|
||||
RankChanged = 3,
|
||||
ItemPurchased = 4,
|
||||
ArtifactObtained = 5,
|
||||
RewardGranted = 6,
|
||||
ProfileChanged = 7,
|
||||
AuthCredentialsChanged = 8,
|
||||
ItemReturned = 9,
|
||||
ItemSold = 10
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
namespace LctMonolith.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Mission (task) definition configured by HR.
|
||||
/// </summary>
|
||||
public class Mission
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public string Title { get; set; } = null!;
|
||||
public string? Description { get; set; }
|
||||
/// <summary>Optional branch (path) name for grouping / visualization.</summary>
|
||||
public string? Branch { get; set; }
|
||||
public MissionCategory Category { get; set; }
|
||||
/// <summary>Minimum rank required to access the mission (nullable = available from start).</summary>
|
||||
public Guid? MinRankId { get; set; }
|
||||
public Rank? MinRank { get; set; }
|
||||
/// <summary>Experience reward on completion.</summary>
|
||||
public int ExperienceReward { get; set; }
|
||||
/// <summary>Mana reward on completion.</summary>
|
||||
public int ManaReward { get; set; }
|
||||
|
||||
public bool IsActive { get; set; } = true;
|
||||
|
||||
public ICollection<MissionCompetencyReward> CompetencyRewards { get; set; } = new List<MissionCompetencyReward>();
|
||||
public ICollection<MissionArtifactReward> ArtifactRewards { get; set; } = new List<MissionArtifactReward>();
|
||||
public ICollection<UserMission> UserMissions { get; set; } = new List<UserMission>();
|
||||
public ICollection<RankRequiredMission> RanksRequiring { get; set; } = new List<RankRequiredMission>();
|
||||
}
|
||||
|
||||
/// <summary>Per-user mission status and progression.</summary>
|
||||
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;
|
||||
/// <summary>Date/time of last status change.</summary>
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
/// <summary>Optional submission payload (e.g., link, text, attachments pointer).</summary>
|
||||
public string? SubmissionData { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
namespace LctMonolith.Domain.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// Linear rank in progression ladder. User must meet XP, key mission and competency requirements.
|
||||
/// </summary>
|
||||
public class Rank
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
/// <summary>Display name (e.g., "Искатель", "Пилот-кандидат").</summary>
|
||||
public string Name { get; set; } = null!;
|
||||
/// <summary>Ordering position. Lower value = earlier rank.</summary>
|
||||
public int Order { get; set; }
|
||||
/// <summary>Required cumulative experience to attain this rank.</summary>
|
||||
public int RequiredExperience { get; set; }
|
||||
|
||||
public ICollection<RankRequiredMission> RequiredMissions { get; set; } = new List<RankRequiredMission>();
|
||||
public ICollection<RankRequiredCompetency> RequiredCompetencies { get; set; } = new List<RankRequiredCompetency>();
|
||||
public ICollection<AppUser> Users { get; set; } = new List<AppUser>();
|
||||
}
|
||||
|
||||
/// <summary>Mapping of rank to required mission.</summary>
|
||||
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!;
|
||||
}
|
||||
|
||||
/// <summary>Mapping of rank to required competency minimum level.</summary>
|
||||
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!;
|
||||
/// <summary>Minimum level required for the competency.</summary>
|
||||
public int MinLevel { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,32 +1,19 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace LctMonolith.Domain.Entities;
|
||||
namespace LctMonolith.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Application user (candidate or employee) participating in gamification.
|
||||
/// Extends IdentityUser with Guid primary key.
|
||||
/// </summary>
|
||||
public class AppUser : IdentityUser<Guid>
|
||||
{
|
||||
/// <summary>User given (first) name.</summary>
|
||||
public string? FirstName { get; set; }
|
||||
/// <summary>User family (last) name.</summary>
|
||||
public string? LastName { get; set; }
|
||||
/// <summary>Date of birth.</summary>
|
||||
public DateOnly? BirthDate { get; set; }
|
||||
|
||||
/// <summary>Current accumulated experience points.</summary>
|
||||
public int Experience { get; set; }
|
||||
/// <summary>Current mana (in-game currency).</summary>
|
||||
public int Mana { get; set; }
|
||||
|
||||
/// <summary>Current rank reference.</summary>
|
||||
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<UserCompetency> Competencies { get; set; } = new List<UserCompetency>();
|
||||
public ICollection<UserMission> Missions { get; set; } = new List<UserMission>();
|
||||
public ICollection<UserInventoryItem> Inventory { get; set; } = new List<UserInventoryItem>();
|
||||
13
LctMonolith/Models/Artifact.cs
Normal file
13
LctMonolith/Models/Artifact.cs
Normal file
@@ -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<UserArtifact> Users { get; set; } = new List<UserArtifact>();
|
||||
public ICollection<MissionArtifactReward> MissionRewards { get; set; } = new List<MissionArtifactReward>();
|
||||
}
|
||||
|
||||
3
LctMonolith/Models/ArtifactRarity.cs
Normal file
3
LctMonolith/Models/ArtifactRarity.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace LctMonolith.Models;
|
||||
|
||||
public enum ArtifactRarity { Common = 0, Rare = 1, Epic = 2, Legendary = 3 }
|
||||
11
LctMonolith/Models/Competency.cs
Normal file
11
LctMonolith/Models/Competency.cs
Normal file
@@ -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<UserCompetency> UserCompetencies { get; set; } = new List<UserCompetency>();
|
||||
public ICollection<MissionCompetencyReward> MissionRewards { get; set; } = new List<MissionCompetencyReward>();
|
||||
public ICollection<RankRequiredCompetency> RankRequirements { get; set; } = new List<RankRequiredCompetency>();
|
||||
}
|
||||
11
LctMonolith/Models/EventLog.cs
Normal file
11
LctMonolith/Models/EventLog.cs
Normal file
@@ -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;
|
||||
}
|
||||
19
LctMonolith/Models/Mission.cs
Normal file
19
LctMonolith/Models/Mission.cs
Normal file
@@ -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<MissionCompetencyReward> CompetencyRewards { get; set; } = new List<MissionCompetencyReward>();
|
||||
public ICollection<MissionArtifactReward> ArtifactRewards { get; set; } = new List<MissionArtifactReward>();
|
||||
public ICollection<UserMission> UserMissions { get; set; } = new List<UserMission>();
|
||||
public ICollection<RankRequiredMission> RanksRequiring { get; set; } = new List<RankRequiredMission>();
|
||||
}
|
||||
9
LctMonolith/Models/MissionArtifactReward.cs
Normal file
9
LctMonolith/Models/MissionArtifactReward.cs
Normal file
@@ -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!;
|
||||
}
|
||||
3
LctMonolith/Models/MissionCategory.cs
Normal file
3
LctMonolith/Models/MissionCategory.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace LctMonolith.Models;
|
||||
|
||||
public enum MissionCategory { Quest = 0, Recruiting = 1, Lecture = 2, Simulator = 3 }
|
||||
11
LctMonolith/Models/MissionCompetencyReward.cs
Normal file
11
LctMonolith/Models/MissionCompetencyReward.cs
Normal file
@@ -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; }
|
||||
}
|
||||
11
LctMonolith/Models/MissionStatus.cs
Normal file
11
LctMonolith/Models/MissionStatus.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace LctMonolith.Models;
|
||||
|
||||
public enum MissionStatus
|
||||
{
|
||||
Locked = 0,
|
||||
Available = 1,
|
||||
InProgress = 2,
|
||||
Submitted = 3,
|
||||
Completed = 4,
|
||||
Rejected = 5
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
namespace LctMonolith.Domain.Entities;
|
||||
namespace LctMonolith.Models;
|
||||
|
||||
/// <summary>User notification (in-app).</summary>
|
||||
public class Notification
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
public Guid UserId { get; set; }
|
||||
public AppUser User { get; set; } = null!;
|
||||
/// <summary>Short classification tag (e.g., rank, mission, store).</summary>
|
||||
public string Type { get; set; } = null!;
|
||||
public string Title { get; set; } = null!;
|
||||
public string Message { get; set; } = null!;
|
||||
12
LctMonolith/Models/Rank.cs
Normal file
12
LctMonolith/Models/Rank.cs
Normal file
@@ -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<RankRequiredMission> RequiredMissions { get; set; } = new List<RankRequiredMission>();
|
||||
public ICollection<RankRequiredCompetency> RequiredCompetencies { get; set; } = new List<RankRequiredCompetency>();
|
||||
public ICollection<AppUser> Users { get; set; } = new List<AppUser>();
|
||||
}
|
||||
10
LctMonolith/Models/RankRequiredCompetency.cs
Normal file
10
LctMonolith/Models/RankRequiredCompetency.cs
Normal file
@@ -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; }
|
||||
}
|
||||
9
LctMonolith/Models/RankRequiredMission.cs
Normal file
9
LctMonolith/Models/RankRequiredMission.cs
Normal file
@@ -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!;
|
||||
}
|
||||
12
LctMonolith/Models/RefreshToken.cs
Normal file
12
LctMonolith/Models/RefreshToken.cs
Normal file
@@ -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;
|
||||
}
|
||||
12
LctMonolith/Models/StoreItem.cs
Normal file
12
LctMonolith/Models/StoreItem.cs
Normal file
@@ -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<UserInventoryItem> UserInventory { get; set; } = new List<UserInventoryItem>();
|
||||
}
|
||||
13
LctMonolith/Models/Transaction.cs
Normal file
13
LctMonolith/Models/Transaction.cs
Normal file
@@ -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;
|
||||
}
|
||||
3
LctMonolith/Models/TransactionType.cs
Normal file
3
LctMonolith/Models/TransactionType.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace LctMonolith.Models;
|
||||
|
||||
public enum TransactionType { Purchase = 0, Return = 1, Sale = 2 }
|
||||
10
LctMonolith/Models/UserArtifact.cs
Normal file
10
LctMonolith/Models/UserArtifact.cs
Normal file
@@ -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;
|
||||
}
|
||||
11
LctMonolith/Models/UserCompetency.cs
Normal file
11
LctMonolith/Models/UserCompetency.cs
Normal file
@@ -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; }
|
||||
}
|
||||
12
LctMonolith/Models/UserInventoryItem.cs
Normal file
12
LctMonolith/Models/UserInventoryItem.cs
Normal file
@@ -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; }
|
||||
}
|
||||
12
LctMonolith/Models/UserMission.cs
Normal file
12
LctMonolith/Models/UserMission.cs
Normal file
@@ -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; }
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
LctMonolith/Services/Contracts/IAnalyticsService.cs
Normal file
10
LctMonolith/Services/Contracts/IAnalyticsService.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using LctMonolith.Services.Models;
|
||||
|
||||
using LctMonolith.Services.Models;
|
||||
|
||||
namespace LctMonolith.Services;
|
||||
|
||||
public interface IAnalyticsService
|
||||
{
|
||||
Task<AnalyticsSummary> GetSummaryAsync(CancellationToken ct = default);
|
||||
}
|
||||
15
LctMonolith/Services/Contracts/IGamificationService.cs
Normal file
15
LctMonolith/Services/Contracts/IGamificationService.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using LctMonolith.Models;
|
||||
using LctMonolith.Services.Models;
|
||||
|
||||
namespace LctMonolith.Services.Contracts;
|
||||
|
||||
/// <summary>Gamification progression logic (progress, rewards, rank evaluation).</summary>
|
||||
public interface IGamificationService
|
||||
{
|
||||
/// <summary>Get current user progression snapshot (xp, mana, next rank requirements).</summary>
|
||||
Task<ProgressSnapshot> GetProgressAsync(Guid userId, CancellationToken ct = default);
|
||||
/// <summary>Apply mission completion rewards (xp, mana, skills, artifacts) to user.</summary>
|
||||
Task ApplyMissionCompletionAsync(Guid userId, Mission mission, CancellationToken ct = default);
|
||||
/// <summary>Re-evaluate and apply rank upgrade if requirements are met.</summary>
|
||||
Task EvaluateRankUpgradeAsync(Guid userId, CancellationToken ct = default);
|
||||
}
|
||||
9
LctMonolith/Services/Contracts/IInventoryService.cs
Normal file
9
LctMonolith/Services/Contracts/IInventoryService.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using LctMonolith.Models;
|
||||
|
||||
namespace LctMonolith.Services.Contracts;
|
||||
|
||||
public interface IInventoryService
|
||||
{
|
||||
Task<IEnumerable<UserInventoryItem>> GetStoreInventoryAsync(Guid userId, CancellationToken ct = default);
|
||||
Task<IEnumerable<UserArtifact>> GetArtifactsAsync(Guid userId, CancellationToken ct = default);
|
||||
}
|
||||
11
LctMonolith/Services/Contracts/IMissionService.cs
Normal file
11
LctMonolith/Services/Contracts/IMissionService.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using LctMonolith.Models;
|
||||
using LctMonolith.Services.Models;
|
||||
|
||||
namespace LctMonolith.Services.Contracts;
|
||||
|
||||
public interface IMissionService
|
||||
{
|
||||
Task<Mission> CreateMissionAsync(CreateMissionModel model, CancellationToken ct = default);
|
||||
Task<IEnumerable<Mission>> GetAvailableMissionsAsync(Guid userId, CancellationToken ct = default);
|
||||
Task<UserMission> UpdateStatusAsync(Guid userId, Guid missionId, MissionStatus status, string? submissionData, CancellationToken ct = default);
|
||||
}
|
||||
12
LctMonolith/Services/Contracts/INotificationService.cs
Normal file
12
LctMonolith/Services/Contracts/INotificationService.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using LctMonolith.Models;
|
||||
|
||||
namespace LctMonolith.Services.Contracts;
|
||||
|
||||
public interface INotificationService
|
||||
{
|
||||
Task<Notification> CreateAsync(Guid userId, string type, string title, string message, CancellationToken ct = default);
|
||||
Task<IEnumerable<Notification>> GetUnreadAsync(Guid userId, CancellationToken ct = default);
|
||||
Task<IEnumerable<Notification>> GetAllAsync(Guid userId, int take = 100, CancellationToken ct = default);
|
||||
Task MarkReadAsync(Guid userId, Guid notificationId, CancellationToken ct = default);
|
||||
Task<int> MarkAllReadAsync(Guid userId, CancellationToken ct = default);
|
||||
}
|
||||
9
LctMonolith/Services/Contracts/IStoreService.cs
Normal file
9
LctMonolith/Services/Contracts/IStoreService.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using LctMonolith.Models;
|
||||
|
||||
namespace LctMonolith.Services.Contracts;
|
||||
|
||||
public interface IStoreService
|
||||
{
|
||||
Task<IEnumerable<StoreItem>> GetActiveItemsAsync(CancellationToken ct = default);
|
||||
Task<UserInventoryItem> PurchaseAsync(Guid userId, Guid itemId, int quantity, CancellationToken ct = default);
|
||||
}
|
||||
11
LctMonolith/Services/Contracts/ITokenService.cs
Normal file
11
LctMonolith/Services/Contracts/ITokenService.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using LctMonolith.Models;
|
||||
using LctMonolith.Services.Models;
|
||||
|
||||
namespace LctMonolith.Services.Contracts;
|
||||
|
||||
public interface ITokenService
|
||||
{
|
||||
Task<TokenPair> IssueAsync(AppUser user, CancellationToken ct = default);
|
||||
Task<TokenPair> RefreshAsync(string refreshToken, CancellationToken ct = default);
|
||||
Task RevokeAsync(string refreshToken, CancellationToken ct = default);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// Removed legacy Weather service placeholder.
|
||||
// Intentionally left blank.
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
using LctMonolith.Domain.Entities;
|
||||
using LctMonolith.Services.Models;
|
||||
|
||||
namespace LctMonolith.Services;
|
||||
|
||||
/// <summary>Service for issuing JWT and refresh tokens.</summary>
|
||||
public interface ITokenService
|
||||
{
|
||||
Task<TokenPair> IssueAsync(AppUser user, CancellationToken ct = default);
|
||||
Task<TokenPair> RefreshAsync(string refreshToken, CancellationToken ct = default);
|
||||
Task RevokeAsync(string refreshToken, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>Gamification progression logic (awards, rank upgrade).</summary>
|
||||
public interface IGamificationService
|
||||
{
|
||||
Task<ProgressSnapshot> GetProgressAsync(Guid userId, CancellationToken ct = default);
|
||||
Task ApplyMissionCompletionAsync(Guid userId, Mission mission, CancellationToken ct = default);
|
||||
Task EvaluateRankUpgradeAsync(Guid userId, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>Mission management and user mission state transitions.</summary>
|
||||
public interface IMissionService
|
||||
{
|
||||
Task<Mission> CreateMissionAsync(CreateMissionModel model, CancellationToken ct = default);
|
||||
Task<IEnumerable<Mission>> GetAvailableMissionsAsync(Guid userId, CancellationToken ct = default);
|
||||
Task<UserMission> UpdateStatusAsync(Guid userId, Guid missionId, MissionStatus status, string? submissionData, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>Store and inventory operations.</summary>
|
||||
public interface IStoreService
|
||||
{
|
||||
Task<IEnumerable<StoreItem>> GetActiveItemsAsync(CancellationToken ct = default);
|
||||
Task<UserInventoryItem> PurchaseAsync(Guid userId, Guid itemId, int quantity, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>User notifications (in-app) management.</summary>
|
||||
public interface INotificationService
|
||||
{
|
||||
Task<Notification> CreateAsync(Guid userId, string type, string title, string message, CancellationToken ct = default);
|
||||
Task<IEnumerable<Notification>> GetUnreadAsync(Guid userId, CancellationToken ct = default);
|
||||
Task<IEnumerable<Notification>> GetAllAsync(Guid userId, int take = 100, CancellationToken ct = default);
|
||||
Task MarkReadAsync(Guid userId, Guid notificationId, CancellationToken ct = default);
|
||||
Task<int> MarkAllReadAsync(Guid userId, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>Inventory querying (owned artifacts, store items).</summary>
|
||||
public interface IInventoryService
|
||||
{
|
||||
Task<IEnumerable<UserInventoryItem>> GetStoreInventoryAsync(Guid userId, CancellationToken ct = default);
|
||||
Task<IEnumerable<UserArtifact>> GetArtifactsAsync(Guid userId, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>Basic analytics / aggregated metrics.</summary>
|
||||
public interface IAnalyticsService
|
||||
{
|
||||
Task<AnalyticsSummary> GetSummaryAsync(CancellationToken ct = default);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
12
LctMonolith/Services/Models/AnalyticsSummary.cs
Normal file
12
LctMonolith/Services/Models/AnalyticsSummary.cs
Normal file
@@ -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;
|
||||
}
|
||||
9
LctMonolith/Services/Models/AuthRequest.cs
Normal file
9
LctMonolith/Services/Models/AuthRequest.cs
Normal file
@@ -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; }
|
||||
}
|
||||
8
LctMonolith/Services/Models/CompetencyRewardModel.cs
Normal file
8
LctMonolith/Services/Models/CompetencyRewardModel.cs
Normal file
@@ -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; }
|
||||
}
|
||||
16
LctMonolith/Services/Models/CreateMissionModel.cs
Normal file
16
LctMonolith/Services/Models/CreateMissionModel.cs
Normal file
@@ -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<CompetencyRewardModel> CompetencyRewards { get; set; } = new();
|
||||
public List<Guid> ArtifactRewardIds { get; set; } = new();
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
using LctMonolith.Domain.Entities;
|
||||
|
||||
namespace LctMonolith.Services.Models;
|
||||
|
||||
/// <summary>Returned access+refresh token pair with expiry metadata.</summary>
|
||||
public record TokenPair(string AccessToken, DateTime AccessTokenExpiresAt, string RefreshToken, DateTime RefreshTokenExpiresAt);
|
||||
|
||||
/// <summary>Mission creation request model.</summary>
|
||||
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<CompetencyRewardModel> CompetencyRewards { get; set; } = new();
|
||||
public List<Guid> ArtifactRewardIds { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>Competency reward definition for mission creation.</summary>
|
||||
public class CompetencyRewardModel
|
||||
{
|
||||
public Guid CompetencyId { get; set; }
|
||||
public int LevelDelta { get; set; }
|
||||
public int ProgressPointsDelta { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Progress snapshot for UI: rank, xp, remaining requirements.</summary>
|
||||
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<Guid> OutstandingMissionIds { get; set; } = new();
|
||||
public List<OutstandingCompetency> OutstandingCompetencies { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>Competency requirement still unmet for next rank.</summary>
|
||||
public class OutstandingCompetency
|
||||
{
|
||||
public Guid CompetencyId { get; set; }
|
||||
public string? CompetencyName { get; set; }
|
||||
public int RequiredLevel { get; set; }
|
||||
public int CurrentLevel { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Request to update mission status with optional submission data.</summary>
|
||||
public class UpdateMissionStatusRequest
|
||||
{
|
||||
public MissionStatus Status { get; set; }
|
||||
public string? SubmissionData { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Store purchase request.</summary>
|
||||
public class PurchaseRequest
|
||||
{
|
||||
public Guid ItemId { get; set; }
|
||||
public int Quantity { get; set; } = 1;
|
||||
}
|
||||
|
||||
/// <summary>Authentication request (login/register) simple mock.</summary>
|
||||
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; }
|
||||
}
|
||||
|
||||
/// <summary>Refresh token request.</summary>
|
||||
public class RefreshRequest
|
||||
{
|
||||
public string RefreshToken { get; set; } = null!;
|
||||
}
|
||||
|
||||
/// <summary>Revoke refresh token request.</summary>
|
||||
public class RevokeRequest
|
||||
{
|
||||
public string RefreshToken { get; set; } = null!;
|
||||
}
|
||||
|
||||
/// <summary>Analytics summary for admin dashboard.</summary>
|
||||
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;
|
||||
}
|
||||
23
LctMonolith/Services/Models/ProgressSnapshot.cs
Normal file
23
LctMonolith/Services/Models/ProgressSnapshot.cs
Normal file
@@ -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<Guid> OutstandingMissionIds { get; set; } = new();
|
||||
public List<OutstandingCompetency> 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; }
|
||||
}
|
||||
7
LctMonolith/Services/Models/PurchaseRequest.cs
Normal file
7
LctMonolith/Services/Models/PurchaseRequest.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace LctMonolith.Services.Models;
|
||||
|
||||
public class PurchaseRequest
|
||||
{
|
||||
public Guid ItemId { get; set; }
|
||||
public int Quantity { get; set; } = 1;
|
||||
}
|
||||
6
LctMonolith/Services/Models/RefreshRequest.cs
Normal file
6
LctMonolith/Services/Models/RefreshRequest.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace LctMonolith.Services.Models;
|
||||
|
||||
public class RefreshRequest
|
||||
{
|
||||
public string RefreshToken { get; set; } = null!;
|
||||
}
|
||||
6
LctMonolith/Services/Models/RevokeRequest.cs
Normal file
6
LctMonolith/Services/Models/RevokeRequest.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace LctMonolith.Services.Models;
|
||||
|
||||
public class RevokeRequest
|
||||
{
|
||||
public string RefreshToken { get; set; } = null!;
|
||||
}
|
||||
3
LctMonolith/Services/Models/TokenPair.cs
Normal file
3
LctMonolith/Services/Models/TokenPair.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace LctMonolith.Services.Models;
|
||||
|
||||
public record TokenPair(string AccessToken, DateTime AccessTokenExpiresAt, string RefreshToken, DateTime RefreshTokenExpiresAt);
|
||||
@@ -0,0 +1,9 @@
|
||||
using LctMonolith.Models;
|
||||
|
||||
namespace LctMonolith.Services.Models;
|
||||
|
||||
public class UpdateMissionStatusRequest
|
||||
{
|
||||
public MissionStatus Status { get; set; }
|
||||
public string? SubmissionData { get; set; }
|
||||
}
|
||||
@@ -1 +1,79 @@
|
||||
using LctMonolith.Database.UnitOfWork;
|
||||
using LctMonolith.Models;
|
||||
using LctMonolith.Services.Contracts;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace LctMonolith.Services;
|
||||
|
||||
/// <summary>
|
||||
/// In-app user notifications CRUD / read-state operations.
|
||||
/// </summary>
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
private readonly IUnitOfWork _uow;
|
||||
|
||||
public NotificationService(IUnitOfWork uow)
|
||||
{
|
||||
_uow = uow;
|
||||
}
|
||||
|
||||
public async Task<Notification> 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<IEnumerable<Notification>> 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<IEnumerable<Notification>> 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<int> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user