All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 22s
Docker Build & Deploy / Deploy to Production (push) Successful in 7s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
- 创建 LogCleanupJob 替代 LogCleanupService (BackgroundService) - 在 Expand.cs 中注册 LogCleanupJob (每天凌晨2点执行, 保留30天日志) - 从 Program.cs 移除 LogCleanupService 的 HostedService 注册 - 删除 Service/LogCleanupService.cs - 删除 Service/PeriodicBillBackgroundService.cs (已无用的重复服务) 所有后台任务现在统一通过 Quartz.NET 管理, 支持运行时控制
95 lines
3.2 KiB
C#
95 lines
3.2 KiB
C#
using WebPush;
|
|
using PushSubscription = Entity.PushSubscription;
|
|
|
|
namespace Service.Message;
|
|
|
|
public interface INotificationService
|
|
{
|
|
Task<string> GetVapidPublicKeyAsync();
|
|
Task SubscribeAsync(PushSubscription subscription);
|
|
Task SendNotificationAsync(string message, string? url = null);
|
|
}
|
|
|
|
public class NotificationService(
|
|
IPushSubscriptionRepository subscriptionRepo,
|
|
IConfiguration configuration,
|
|
ILogger<NotificationService> logger) : INotificationService
|
|
{
|
|
private NotificationSettings GetSettings()
|
|
{
|
|
var settings = configuration.GetSection("NotificationSettings").Get<NotificationSettings>();
|
|
if (settings == null)
|
|
{
|
|
// Fallback or throw. For now, let's return empty to avoid crashing if not configured,
|
|
// but logging error is better.
|
|
logger.LogWarning("NotificationSettings not configured");
|
|
return new NotificationSettings();
|
|
}
|
|
return settings;
|
|
}
|
|
|
|
public Task<string> GetVapidPublicKeyAsync()
|
|
{
|
|
return Task.FromResult(GetSettings().PublicKey);
|
|
}
|
|
|
|
public async Task SubscribeAsync(PushSubscription subscription)
|
|
{
|
|
var existing = await subscriptionRepo.GetByEndpointAsync(subscription.Endpoint);
|
|
if (existing != null)
|
|
{
|
|
existing.P256DH = subscription.P256DH;
|
|
existing.Auth = subscription.Auth;
|
|
existing.UpdateTime = DateTime.Now;
|
|
await subscriptionRepo.UpdateAsync(existing);
|
|
}
|
|
else
|
|
{
|
|
await subscriptionRepo.AddAsync(subscription);
|
|
}
|
|
}
|
|
|
|
public async Task SendNotificationAsync(string message, string? url = null)
|
|
{
|
|
var settings = GetSettings();
|
|
if (string.IsNullOrEmpty(settings.PublicKey) || string.IsNullOrEmpty(settings.PrivateKey))
|
|
{
|
|
logger.LogWarning("VAPID keys not configured, skipping notification");
|
|
return;
|
|
}
|
|
|
|
var vapidDetails = new VapidDetails(settings.Subject, settings.PublicKey, settings.PrivateKey);
|
|
var webPushClient = new WebPushClient();
|
|
|
|
var subscriptions = await subscriptionRepo.GetAllAsync();
|
|
var payload = JsonSerializer.Serialize(new
|
|
{
|
|
title = "System Notification",
|
|
body = message,
|
|
url = url ?? "/",
|
|
icon = "/pwa-192x192.png"
|
|
});
|
|
|
|
foreach (var sub in subscriptions)
|
|
{
|
|
try
|
|
{
|
|
var pushSubscription = new WebPush.PushSubscription(sub.Endpoint, sub.P256DH, sub.Auth);
|
|
await webPushClient.SendNotificationAsync(pushSubscription, payload, vapidDetails);
|
|
}
|
|
catch (WebPushException ex)
|
|
{
|
|
if (ex.StatusCode == HttpStatusCode.Gone || ex.StatusCode == HttpStatusCode.NotFound)
|
|
{
|
|
await subscriptionRepo.DeleteAsync(sub.Id);
|
|
}
|
|
logger.LogError(ex, "Error sending push notification to {Endpoint}", sub.Endpoint);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Error sending push notification to {Endpoint}", sub.Endpoint);
|
|
}
|
|
}
|
|
}
|
|
}
|