FlowAPI / FlowAPI.Application /Services /AchievementService.cs
danylokhodus's picture
init
b9c7f0e
Raw
History Blame Contribute Delete
8.02 kB
using FlowAPI.Application.DTOs;
using FlowAPI.Application.Interfaces;
using FlowAPI.Domain.Entities;
using FlowAPI.Domain.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FlowAPI.Application.Services
{
public class AchievementService : IAchievementService
{
private readonly IAchievementRepository _achievementRepo;
private readonly IUserAchievementRepository _userAchievementRepo;
private readonly IUserRepository _userRepo;
public AchievementService(IAchievementRepository achievementRepo, IUserAchievementRepository userAchievementRepo, IUserRepository userRepo)
{
_achievementRepo = achievementRepo;
_userAchievementRepo = userAchievementRepo;
_userRepo = userRepo;
}
public async Task<IEnumerable<UserAchievementDto>> GetUserAchievementsAsync(Guid userId)
{
var allAchievements = await _achievementRepo.GetAllAsync();
var userAchievements = await _userAchievementRepo.GetByUserIdAsync(userId);
var user = await _userRepo.GetByIdAsync(userId);
var isAvatarUploaded = user != null && !string.IsNullOrEmpty(user.AvatarUrl);
var result = new List<UserAchievementDto>();
foreach (var achievement in allAchievements)
{
var userRecord = userAchievements.FirstOrDefault(ua => ua.AchievementId == achievement.Id);
// Retroactively unlock Self Portrait if user already has an avatar set but no record or locked record in DB
if (achievement.Title == "Self Portrait" && isAvatarUploaded)
{
if (userRecord == null)
{
var newUa = new UserAchievement
{
UserId = userId,
AchievementId = achievement.Id,
CurrentValue = 1,
IsUnlocked = true,
UnlockedAt = DateTime.UtcNow
};
userRecord = await _userAchievementRepo.CreateAsync(newUa);
}
else if (!userRecord.IsUnlocked)
{
userRecord.CurrentValue = 1;
userRecord.IsUnlocked = true;
userRecord.UnlockedAt = DateTime.UtcNow;
userRecord = await _userAchievementRepo.UpdateAsync(userRecord);
}
}
result.Add(new UserAchievementDto
{
Id = userRecord?.Id ?? Guid.Empty,
AchievementId = achievement.Id,
CurrentValue = userRecord?.CurrentValue ?? 0,
IsUnlocked = userRecord?.IsUnlocked ?? false,
UnlockedAt = userRecord?.UnlockedAt,
Achievement = new AchievementDto
{
Id = achievement.Id,
Title = achievement.Title,
Description = achievement.Description,
IconUrl = achievement.IconUrl,
Category = achievement.Category,
Type = achievement.Type,
TargetValue = achievement.TargetValue,
RewardPoints = achievement.RewardPoints
}
});
}
return result;
}
public async Task ProcessEventAsync(Guid userId, string eventName, int amount = 1)
{
// 1. Find achievements triggered by this event
var triggeredAchievements = await _achievementRepo.GetByTriggerEventAsync(eventName);
if (!triggeredAchievements.Any()) return;
// 2. Fetch user's current progress for these achievements
var achievementIds = triggeredAchievements.Select(a => a.Id).ToList();
var userAchievements = await _userAchievementRepo.GetByUserAndAchievementIdsAsync(userId, achievementIds);
foreach (var achievement in triggeredAchievements)
{
var ua = userAchievements.FirstOrDefault(u => u.AchievementId == achievement.Id);
// If user doesn't have a tracking record yet, create it
if (ua == null)
{
ua = new UserAchievement
{
UserId = userId,
AchievementId = achievement.Id,
CurrentValue = 0,
IsUnlocked = false
};
ua = await _userAchievementRepo.CreateAsync(ua);
}
// Skip if already unlocked
if (ua.IsUnlocked) continue;
// Update progress
if (achievement.Type == AchievementType.Accumulative || achievement.Type == AchievementType.SingleAction)
{
ua.CurrentValue += amount;
}
// Check for unlock
if (ua.CurrentValue >= achievement.TargetValue)
{
ua.CurrentValue = achievement.TargetValue;
ua.IsUnlocked = true;
ua.UnlockedAt = DateTime.UtcNow;
}
await _userAchievementRepo.UpdateAsync(ua);
}
}
public async Task SeedAchievementsAsync()
{
var seedData = new List<Achievement>
{
new Achievement { Title = "Blank Canvas", Description = "Create your first project (Graph).", Category = "Projects", Type = AchievementType.SingleAction, TargetValue = 1, RewardPoints = 10, TriggerEventName = "GraphCreated" },
new Achievement { Title = "Connecting the Dots", Description = "Create 10 connections (Edges) between tasks.", Category = "Tasks", Type = AchievementType.Accumulative, TargetValue = 10, RewardPoints = 50, TriggerEventName = "EdgeCreated" },
new Achievement { Title = "Sketch Master", Description = "Create 100 task nodes overall.", Category = "Tasks", Type = AchievementType.Accumulative, TargetValue = 100, RewardPoints = 100, TriggerEventName = "NodeCreated" },
new Achievement { Title = "Daily Doodler", Description = "Track habits 7 times.", Category = "Habits", Type = AchievementType.Accumulative, TargetValue = 7, RewardPoints = 70, TriggerEventName = "HabitTracked" },
new Achievement { Title = "Decision Maker", Description = "Complete 50 tasks.", Category = "Tasks", Type = AchievementType.Accumulative, TargetValue = 50, RewardPoints = 50, TriggerEventName = "TaskCompleted" },
new Achievement { Title = "Clear Desk", Description = "Delete 5 projects.", Category = "System", Type = AchievementType.Accumulative, TargetValue = 5, RewardPoints = 20, TriggerEventName = "GraphDeleted" },
new Achievement { Title = "First Milestone", Description = "Complete your first task.", Category = "Tasks", Type = AchievementType.SingleAction, TargetValue = 1, RewardPoints = 15, TriggerEventName = "TaskCompleted" },
new Achievement { Title = "Self Portrait", Description = "Upload your profile avatar.", Category = "System", Type = AchievementType.SingleAction, TargetValue = 1, RewardPoints = 15, TriggerEventName = "AvatarUploaded" }
};
var existing = await _achievementRepo.GetByTitlesAsync(seedData.Select(s => s.Title));
var existingTitles = existing.Select(e => e.Title).ToList();
var toAdd = seedData.Where(s => !existingTitles.Contains(s.Title)).ToList();
if (toAdd.Any())
{
await _achievementRepo.AddRangeAsync(toAdd);
}
}
}
}