feat: Fixed errors
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using LctMonolith.Domain.Entities;
|
using LctMonolith.Models;
|
||||||
using LctMonolith.Services;
|
using LctMonolith.Services;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using LctMonolith.Services.Models;
|
using LctMonolith.Services.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Identity.Data;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using RefreshRequest = LctMonolith.Services.Models.RefreshRequest;
|
||||||
|
|
||||||
namespace LctMonolith.Controllers;
|
namespace LctMonolith.Controllers;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using LctMonolith.Services;
|
using LctMonolith.Services;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using LctMonolith.Services;
|
using LctMonolith.Services;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using LctMonolith.Domain.Entities;
|
using LctMonolith.Models;
|
||||||
using LctMonolith.Services;
|
using LctMonolith.Services;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using LctMonolith.Services.Models;
|
using LctMonolith.Services.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -50,4 +51,3 @@ public class MissionsController : ControllerBase
|
|||||||
return Ok(new { result.MissionId, result.Status, result.UpdatedAt });
|
return Ok(new { result.MissionId, result.Status, result.UpdatedAt });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using LctMonolith.Services;
|
using LctMonolith.Services;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using LctMonolith.Services;
|
using LctMonolith.Services;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using LctMonolith.Services.Models;
|
using LctMonolith.Services.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
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;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using EventLog = LctMonolith.Models.EventLog;
|
||||||
|
|
||||||
namespace LctMonolith.Infrastructure.Data;
|
namespace LctMonolith.Database.Data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main EF Core database context for gamification module (PostgreSQL provider expected).
|
/// Main EF Core database context for gamification module (PostgreSQL provider expected).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AppDbContext : IdentityDbContext<AppUser, IdentityRole<Guid>, Guid>
|
public class AppDbContext : IdentityDbContext<AppUser, IdentityRole<Guid>, Guid>
|
||||||
{
|
{
|
||||||
|
|
||||||
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
|
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
|
||||||
|
|
||||||
public DbSet<Rank> Ranks => Set<Rank>();
|
public DbSet<Rank> Ranks => Set<Rank>();
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
using LctMonolith.Domain.Entities;
|
using LctMonolith.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace LctMonolith.Infrastructure.Data;
|
namespace LctMonolith.Database.Data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Development database seeder for initial ranks, competencies, sample store items.
|
/// 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 System.Linq.Expressions;
|
||||||
|
using LctMonolith.Database.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using LctMonolith.Infrastructure.Data;
|
|
||||||
|
|
||||||
namespace LctMonolith.Infrastructure.Repositories;
|
namespace LctMonolith.Database.Repositories;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generic repository implementation for common CRUD and query composition.
|
/// Generic repository implementation for common CRUD and query composition.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
|
||||||
namespace LctMonolith.Infrastructure.Repositories;
|
namespace LctMonolith.Database.Repositories;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generic repository abstraction for aggregate root / entity access. Read operations return IQueryable for composition.
|
/// Generic repository abstraction for aggregate root / entity access. Read operations return IQueryable for composition.
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
using LctMonolith.Domain.Entities;
|
using System.Diagnostics;
|
||||||
using LctMonolith.Infrastructure.Repositories;
|
using LctMonolith.Database.Repositories;
|
||||||
|
using LctMonolith.Models;
|
||||||
|
using EventLog = LctMonolith.Models.EventLog;
|
||||||
|
|
||||||
namespace LctMonolith.Infrastructure.UnitOfWork;
|
namespace LctMonolith.Database.UnitOfWork;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unit of Work aggregates repositories and transaction boundary.
|
/// 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>
|
/// <summary>
|
||||||
/// Unit of Work implementation encapsulating repositories and DB transaction scope.
|
/// 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;
|
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>
|
public class AppUser : IdentityUser<Guid>
|
||||||
{
|
{
|
||||||
/// <summary>User given (first) name.</summary>
|
|
||||||
public string? FirstName { get; set; }
|
public string? FirstName { get; set; }
|
||||||
/// <summary>User family (last) name.</summary>
|
|
||||||
public string? LastName { get; set; }
|
public string? LastName { get; set; }
|
||||||
/// <summary>Date of birth.</summary>
|
|
||||||
public DateOnly? BirthDate { get; set; }
|
public DateOnly? BirthDate { get; set; }
|
||||||
|
|
||||||
/// <summary>Current accumulated experience points.</summary>
|
|
||||||
public int Experience { get; set; }
|
public int Experience { get; set; }
|
||||||
/// <summary>Current mana (in-game currency).</summary>
|
|
||||||
public int Mana { get; set; }
|
public int Mana { get; set; }
|
||||||
|
|
||||||
/// <summary>Current rank reference.</summary>
|
|
||||||
public Guid? RankId { get; set; }
|
public Guid? RankId { get; set; }
|
||||||
public Rank? Rank { get; set; }
|
public Rank? Rank { get; set; }
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
public ICollection<UserCompetency> Competencies { get; set; } = new List<UserCompetency>();
|
public ICollection<UserCompetency> Competencies { get; set; } = new List<UserCompetency>();
|
||||||
public ICollection<UserMission> Missions { get; set; } = new List<UserMission>();
|
public ICollection<UserMission> Missions { get; set; } = new List<UserMission>();
|
||||||
public ICollection<UserInventoryItem> Inventory { get; set; } = new List<UserInventoryItem>();
|
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 class Notification
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
public Guid UserId { get; set; }
|
public Guid UserId { get; set; }
|
||||||
public AppUser User { get; set; } = null!;
|
public AppUser User { get; set; } = null!;
|
||||||
/// <summary>Short classification tag (e.g., rank, mission, store).</summary>
|
|
||||||
public string Type { get; set; } = null!;
|
public string Type { get; set; } = null!;
|
||||||
public string Title { get; set; } = null!;
|
public string Title { get; set; } = null!;
|
||||||
public string Message { 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.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LctMonolith.Infrastructure.Data;
|
using LctMonolith.Models; // replaced Domain.Entities
|
||||||
using LctMonolith.Domain.Entities;
|
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using LctMonolith.Infrastructure.UnitOfWork;
|
|
||||||
using LctMonolith.Application.Middleware;
|
using LctMonolith.Application.Middleware;
|
||||||
using LctMonolith.Services;
|
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);
|
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 LctMonolith.Services.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ public class AnalyticsService : IAnalyticsService
|
|||||||
{
|
{
|
||||||
var totalUsers = await _uow.Users.Query().CountAsync(ct);
|
var totalUsers = await _uow.Users.Query().CountAsync(ct);
|
||||||
var totalMissions = await _uow.Missions.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 totalArtifacts = await _uow.Artifacts.Query().CountAsync(ct);
|
||||||
var totalStoreItems = await _uow.StoreItems.Query().CountAsync(ct);
|
var totalStoreItems = await _uow.StoreItems.Query().CountAsync(ct);
|
||||||
var totalExperience = await _uow.Users.Query().SumAsync(u => (long)u.Experience, 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 System.Linq;
|
||||||
using LctMonolith.Domain.Entities;
|
using LctMonolith.Database.UnitOfWork;
|
||||||
using LctMonolith.Infrastructure.UnitOfWork;
|
using LctMonolith.Models;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using LctMonolith.Services.Models;
|
using LctMonolith.Services.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Serilog;
|
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.Database.UnitOfWork;
|
||||||
using LctMonolith.Infrastructure.UnitOfWork;
|
using LctMonolith.Models;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LctMonolith.Services;
|
namespace LctMonolith.Services;
|
||||||
@@ -32,4 +33,3 @@ public class InventoryService : IInventoryService
|
|||||||
.ToListAsync(ct);
|
.ToListAsync(ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using LctMonolith.Domain.Entities;
|
using LctMonolith.Models;
|
||||||
using LctMonolith.Infrastructure.UnitOfWork;
|
|
||||||
using LctMonolith.Services.Models;
|
using LctMonolith.Services.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using LctMonolith.Database.UnitOfWork;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
|
|
||||||
namespace LctMonolith.Services;
|
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.Models;
|
||||||
using LctMonolith.Infrastructure.UnitOfWork;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using LctMonolith.Database.UnitOfWork;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
|
|
||||||
namespace LctMonolith.Services;
|
namespace LctMonolith.Services;
|
||||||
|
|
||||||
@@ -72,4 +73,3 @@ public class StoreService : IStoreService
|
|||||||
return inv;
|
return inv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ using System.Security.Claims;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using LctMonolith.Application.Options;
|
using LctMonolith.Application.Options;
|
||||||
using LctMonolith.Domain.Entities;
|
using LctMonolith.Database.UnitOfWork;
|
||||||
using LctMonolith.Infrastructure.UnitOfWork;
|
using LctMonolith.Models;
|
||||||
|
using LctMonolith.Services.Contracts;
|
||||||
using LctMonolith.Services.Models;
|
using LctMonolith.Services.Models;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|||||||
Reference in New Issue
Block a user