feat: добавил личные уведомления для пользователей
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 9s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 26s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 19s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 8s

Реализовал хранение, получение и отметку прочитанными пользовательских уведомлений. Обновил фронтенд для отображения и управления уведомлениями в профиле студента.
This commit is contained in:
2026-05-12 23:54:55 +03:00
parent feff77b232
commit b0a4a6d259
19 changed files with 401 additions and 10 deletions
@@ -5,6 +5,7 @@ using System.Globalization;
using UniVerse.Application.DTOs.Achievements;
using UniVerse.Application.DTOs.Common;
using UniVerse.Application.DTOs.Gamification;
using UniVerse.Application.DTOs.Notifications;
using UniVerse.Application.Interfaces;
using UniVerse.Application.Mappings;
using UniVerse.Domain.Entities;
@@ -17,11 +18,16 @@ public class GamificationService : IGamificationService
{
private readonly AppDbContext _db;
private readonly IConfiguration _config;
private readonly INotificationService _notifications;
private readonly ILogger<GamificationService> _logger;
public GamificationService(AppDbContext db, IConfiguration config, ILogger<GamificationService> logger)
public GamificationService(
AppDbContext db,
IConfiguration config,
INotificationService notifications,
ILogger<GamificationService> logger)
{
_db = db; _config = config; _logger = logger;
_db = db; _config = config; _notifications = notifications; _logger = logger;
}
public async Task AwardCoinsAsync(int userId, int amount, CoinTransactionType type,
@@ -91,10 +97,43 @@ public class GamificationService : IGamificationService
achievementId: achievement.Id, description: $"Achievement: {achievement.Name}");
earnedCoins += achievement.CoinReward;
}
await TryNotifyAchievementAsync(user, achievement);
}
await _db.SaveChangesAsync();
}
private async Task TryNotifyAchievementAsync(User user, Achievement achievement)
{
try
{
var title = $"Новое достижение: {achievement.Name}";
var rewardText = achievement.CoinReward > 0
? $" Награда: {achievement.CoinReward} монет."
: string.Empty;
var body = $"{achievement.Description ?? "Вы выполнили условие достижения."}{rewardText}";
await _notifications.CreateUserNotificationAsync(user.Id, "achievement", title, body);
await _notifications.SendAsync(new NotificationMessage(
NotificationChannels.Email,
user.Email,
title,
$"Здравствуйте, {user.DisplayName ?? user.Email}!\n\nПоздравляем: вы получили достижение «{achievement.Name}» в UniVerse.\n\n{body}",
user.DisplayName,
new Dictionary<string, string>
{
["event"] = "achievement_earned",
["achievement_id"] = achievement.Id.ToString(),
["achievement_name"] = achievement.Name
}));
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to send achievement notification {AchievementId} to user {UserId}", achievement.Id, user.Id);
}
}
private static bool TryParseCondition(string? condition, out string type, out int value)
{
type = string.Empty;