统一通知中心

This commit is contained in:
2026-07-25 15:50:00 +08:00 Unverified
parent 13b61f191a
commit 2c6103d3ae
8 changed files with 552 additions and 144 deletions
@@ -0,0 +1,161 @@
using System.Security.Claims;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Jiaowu.Api.Controllers;
[ApiController]
[Authorize]
[Route("api/notifications")]
public sealed class NotificationsController(
AppDbContext db,
ICurrentUserDataScope currentUserDataScope) : ControllerBase
{
[HttpGet]
public async Task<ActionResult> GetNotifications(
bool? unreadOnly,
int page = 1,
int pageSize = 20,
CancellationToken cancellationToken = default)
{
var userId = currentUserDataScope.Current.UserId;
var source = db.Notifications.AsNoTracking()
.Where(x => x.UserId == userId);
if (unreadOnly == true)
source = source.Where(x => !x.IsRead);
var total = await source.CountAsync(cancellationToken);
var items = await source
.OrderByDescending(x => x.CreatedAt)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(x => new
{
x.Id, x.Title, x.Content, x.IsRead, x.LinkUrl, x.CreatedAt
})
.ToListAsync(cancellationToken);
var unreadCount = await db.Notifications
.CountAsync(x => x.UserId == userId && !x.IsRead, cancellationToken);
return Ok(new { Items = items, Total = total, UnreadCount = unreadCount });
}
[HttpGet("unread-count")]
public async Task<ActionResult> GetUnreadCount(CancellationToken cancellationToken)
{
var userId = currentUserDataScope.Current.UserId;
var count = await db.Notifications
.CountAsync(x => x.UserId == userId && !x.IsRead, cancellationToken);
return Ok(new { Count = count });
}
[HttpPost("{id:guid}/read")]
public async Task<ActionResult> MarkRead(Guid id, CancellationToken cancellationToken)
{
var userId = currentUserDataScope.Current.UserId;
var n = await db.Notifications
.FirstOrDefaultAsync(x => x.Id == id && x.UserId == userId, cancellationToken);
if (n is null) return NotFound();
n.IsRead = true;
await db.SaveChangesAsync(cancellationToken);
return NoContent();
}
[HttpPost("read-all")]
public async Task<ActionResult> MarkAllRead(CancellationToken cancellationToken)
{
var userId = currentUserDataScope.Current.UserId;
await db.Notifications
.Where(x => x.UserId == userId && !x.IsRead)
.ExecuteUpdateAsync(s => s.SetProperty(x => x.IsRead, true),
cancellationToken);
return NoContent();
}
}
/// <summary>
/// Centralized helper to send notifications across the app.
/// </summary>
public static class NotificationService
{
public static async Task SendAsync(
AppDbContext db,
Guid userId,
string title,
string content,
string? linkUrl = null,
CancellationToken cancellationToken = default)
{
db.Notifications.Add(new Notification
{
UserId = userId,
Title = title,
Content = content,
LinkUrl = linkUrl
});
await db.SaveChangesAsync(cancellationToken);
}
public static async Task SendToRoleAsync(
AppDbContext db,
string roleName,
string title,
string content,
Guid? collegeId = null,
string? linkUrl = null,
CancellationToken cancellationToken = default)
{
var query = db.Users
.Join(db.UserRoles, u => u.Id, ur => ur.UserId, (u, ur) => new { u.Id, ur.RoleId })
.Join(db.Roles, x => x.RoleId, r => r.Id, (x, r) => new { x.Id, RoleName = r.Name! });
var userIds = query.Where(x => x.RoleName == roleName);
if (collegeId.HasValue && roleName == SystemRoles.CollegeAdmin)
{
userIds = userIds.Where(x =>
db.Teachers.Any(t =>
t.UserId == x.Id && t.CollegeId == collegeId.Value));
}
var ids = await userIds.Select(x => x.Id).Distinct().ToListAsync(cancellationToken);
foreach (var id in ids)
{
db.Notifications.Add(new Notification
{
UserId = id,
Title = title,
Content = content,
LinkUrl = linkUrl
});
}
await db.SaveChangesAsync(cancellationToken);
}
public static async Task SendToUserIdsAsync(
AppDbContext db,
IEnumerable<Guid> userIds,
string title,
string content,
string? linkUrl = null,
CancellationToken cancellationToken = default)
{
foreach (var userId in userIds.Distinct())
{
db.Notifications.Add(new Notification
{
UserId = userId,
Title = title,
Content = content,
LinkUrl = linkUrl
});
}
await db.SaveChangesAsync(cancellationToken);
}
}