All checks were successful
continuous-integration/drone/push Build is passing
262 lines
11 KiB
C#
262 lines
11 KiB
C#
using CampusErrand.Data;
|
|
using CampusErrand.Models;
|
|
using CampusErrand.Models.Dtos;
|
|
using CampusErrand.Helpers;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace CampusErrand.Endpoints;
|
|
|
|
public static class MessageEndpoints
|
|
{
|
|
public static void MapMessageEndpoints(this WebApplication app)
|
|
{
|
|
// 获取未读消息数
|
|
app.MapGet("/api/messages/unread-count", async (HttpContext httpContext, AppDbContext db) =>
|
|
{
|
|
var userIdClaim = httpContext.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
|
if (userIdClaim == null) return Results.Unauthorized();
|
|
var userId = int.Parse(userIdClaim.Value);
|
|
|
|
// 获取该用户可见的系统消息(按目标类型过滤)
|
|
var user = await db.Users.FindAsync(userId);
|
|
var visibleSystemMessages = await BusinessHelpers.GetVisibleSystemMessages(db, userId, user);
|
|
var visibleSystemMessageIds = visibleSystemMessages.Select(m => m.Id).ToHashSet();
|
|
|
|
// 已读的系统消息 ID
|
|
var readSystemIds = await db.MessageReads
|
|
.Where(r => r.UserId == userId && r.MessageType == MessageType.System)
|
|
.Select(r => r.MessageId)
|
|
.ToListAsync();
|
|
|
|
var systemUnread = visibleSystemMessageIds.Count(id => !readSystemIds.Contains(id));
|
|
|
|
// 订单通知:用户相关的订单中状态变更过的订单数
|
|
var orderNotifications = await db.Orders
|
|
.Where(o => (o.OwnerId == userId || o.RunnerId == userId) && o.Status != OrderStatus.Pending)
|
|
.Select(o => o.Id)
|
|
.ToListAsync();
|
|
|
|
var readOrderNotificationIds = await db.MessageReads
|
|
.Where(r => r.UserId == userId && r.MessageType == MessageType.OrderNotification)
|
|
.Select(r => r.MessageId)
|
|
.ToListAsync();
|
|
|
|
var orderNotificationUnread = orderNotifications.Count(id => !readOrderNotificationIds.Contains(id));
|
|
|
|
return Results.Ok(new UnreadCountResponse
|
|
{
|
|
SystemUnread = systemUnread,
|
|
OrderNotificationUnread = orderNotificationUnread,
|
|
TotalUnread = systemUnread + orderNotificationUnread
|
|
});
|
|
}).RequireAuthorization();
|
|
|
|
// 获取系统消息列表
|
|
app.MapGet("/api/messages/system", async (HttpContext httpContext, AppDbContext db) =>
|
|
{
|
|
var userIdClaim = httpContext.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
|
if (userIdClaim == null) return Results.Unauthorized();
|
|
var userId = int.Parse(userIdClaim.Value);
|
|
|
|
var user = await db.Users.FindAsync(userId);
|
|
var messages = await BusinessHelpers.GetVisibleSystemMessages(db, userId, user);
|
|
|
|
// 获取已读记录
|
|
var readIds = await db.MessageReads
|
|
.Where(r => r.UserId == userId && r.MessageType == MessageType.System)
|
|
.Select(r => r.MessageId)
|
|
.ToListAsync();
|
|
|
|
var result = messages
|
|
.OrderByDescending(m => m.CreatedAt)
|
|
.Select(m => new SystemMessageResponse
|
|
{
|
|
Id = m.Id,
|
|
Title = m.Title,
|
|
ContentPreview = m.Content.Length > 100 ? m.Content[..100] + "…" : m.Content,
|
|
ThumbnailUrl = m.ThumbnailUrl,
|
|
CreatedAt = m.CreatedAt,
|
|
IsRead = readIds.Contains(m.Id)
|
|
})
|
|
.ToList();
|
|
|
|
// 标记所有为已读
|
|
foreach (var msg in messages.Where(m => !readIds.Contains(m.Id)))
|
|
{
|
|
db.MessageReads.Add(new MessageRead
|
|
{
|
|
UserId = userId,
|
|
MessageType = MessageType.System,
|
|
MessageId = msg.Id,
|
|
ReadAt = DateTime.UtcNow
|
|
});
|
|
}
|
|
await db.SaveChangesAsync();
|
|
|
|
return Results.Ok(result);
|
|
}).RequireAuthorization();
|
|
|
|
// 获取系统消息详情
|
|
app.MapGet("/api/messages/system/{id}", async (int id, HttpContext httpContext, AppDbContext db) =>
|
|
{
|
|
var userIdClaim = httpContext.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
|
if (userIdClaim == null) return Results.Unauthorized();
|
|
|
|
var message = await db.SystemMessages.FindAsync(id);
|
|
if (message == null) return Results.NotFound(new { code = 404, message = "消息不存在" });
|
|
|
|
return Results.Ok(new SystemMessageDetailResponse
|
|
{
|
|
Id = message.Id,
|
|
Title = message.Title,
|
|
Content = message.Content,
|
|
ThumbnailUrl = message.ThumbnailUrl,
|
|
CreatedAt = message.CreatedAt
|
|
});
|
|
}).RequireAuthorization();
|
|
|
|
// 获取订单通知列表
|
|
app.MapGet("/api/messages/order-notifications", async (string? category, HttpContext httpContext, AppDbContext db) =>
|
|
{
|
|
var userIdClaim = httpContext.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier);
|
|
if (userIdClaim == null) return Results.Unauthorized();
|
|
var userId = int.Parse(userIdClaim.Value);
|
|
|
|
// 查询用户相关的订单(状态已变更的)
|
|
var query = db.Orders
|
|
.Where(o => (o.OwnerId == userId || o.RunnerId == userId) && o.Status != OrderStatus.Pending);
|
|
|
|
// 按分类筛选
|
|
if (!string.IsNullOrEmpty(category))
|
|
{
|
|
query = category.ToLower() switch
|
|
{
|
|
"accepted" => query.Where(o => o.Status == OrderStatus.InProgress),
|
|
"completed" => query.Where(o => o.Status == OrderStatus.Completed),
|
|
"cancelled" => query.Where(o => o.Status == OrderStatus.Cancelled),
|
|
_ => query
|
|
};
|
|
}
|
|
|
|
var orders = await query
|
|
.OrderByDescending(o => o.AcceptedAt ?? o.CreatedAt)
|
|
.Select(o => new OrderNotificationResponse
|
|
{
|
|
Id = o.Id,
|
|
OrderNo = o.OrderNo,
|
|
OrderType = o.OrderType.ToString(),
|
|
Title = o.Status == OrderStatus.InProgress
|
|
? (o.OwnerId == userId ? "订单已被接取" : "您已接取订单")
|
|
: o.Status == OrderStatus.Completed
|
|
? (o.RunnerId == userId ? "单主已确认完成" : "订单已完成")
|
|
: o.Status == OrderStatus.Cancelled ? "订单已取消"
|
|
: o.Status == OrderStatus.WaitConfirm
|
|
? (o.OwnerId == userId ? "跑腿已提交完成,请确认" : "等待单主确认完成")
|
|
: o.Status == OrderStatus.Appealing ? "订单申诉中"
|
|
: "订单状态变更",
|
|
ItemName = o.ItemName,
|
|
Status = o.Status.ToString(),
|
|
CreatedAt = o.AcceptedAt ?? o.CreatedAt
|
|
})
|
|
.ToListAsync();
|
|
|
|
// 标记订单通知为已读
|
|
var orderIds = orders.Select(o => o.Id).ToList();
|
|
var readIds = await db.MessageReads
|
|
.Where(r => r.UserId == userId && r.MessageType == MessageType.OrderNotification && orderIds.Contains(r.MessageId))
|
|
.Select(r => r.MessageId)
|
|
.ToListAsync();
|
|
|
|
foreach (var orderId in orderIds.Where(id => !readIds.Contains(id)))
|
|
{
|
|
db.MessageReads.Add(new MessageRead
|
|
{
|
|
UserId = userId,
|
|
MessageType = MessageType.OrderNotification,
|
|
MessageId = orderId,
|
|
ReadAt = DateTime.UtcNow
|
|
});
|
|
}
|
|
await db.SaveChangesAsync();
|
|
|
|
return Results.Ok(orders);
|
|
}).RequireAuthorization();
|
|
|
|
// 管理端获取已发送通知列表
|
|
app.MapGet("/api/admin/notifications", async (AppDbContext db) =>
|
|
{
|
|
var list = await db.SystemMessages
|
|
.OrderByDescending(m => m.CreatedAt)
|
|
.Select(m => new
|
|
{
|
|
m.Id,
|
|
m.Title,
|
|
ContentPreview = m.Content.Length > 80 ? m.Content.Substring(0, 80) + "…" : m.Content,
|
|
m.ThumbnailUrl,
|
|
TargetType = m.TargetType.ToString(),
|
|
m.TargetUserIds,
|
|
m.CreatedAt
|
|
})
|
|
.ToListAsync();
|
|
|
|
return Results.Ok(list);
|
|
}).RequireAuthorization("AdminOnly");
|
|
|
|
// 管理端发布系统通知
|
|
app.MapPost("/api/admin/notifications", async (CreateNotificationRequest request, AppDbContext db) =>
|
|
{
|
|
// 校验
|
|
var errors = new List<object>();
|
|
if (string.IsNullOrWhiteSpace(request.Title))
|
|
errors.Add(new { field = "title", message = "标题不能为空" });
|
|
if (string.IsNullOrWhiteSpace(request.Content))
|
|
errors.Add(new { field = "content", message = "正文不能为空" });
|
|
if (errors.Count > 0)
|
|
return Results.BadRequest(new { code = 400, message = "校验失败", errors });
|
|
|
|
if (!Enum.TryParse<MessageTargetType>(request.TargetType, true, out var targetType) || !Enum.IsDefined(targetType))
|
|
return Results.BadRequest(new { code = 400, message = "目标类型不合法" });
|
|
|
|
var message = new SystemMessage
|
|
{
|
|
Title = request.Title,
|
|
Content = request.Content,
|
|
ThumbnailUrl = request.ThumbnailUrl,
|
|
TargetType = targetType,
|
|
TargetUserIds = request.TargetUserIds != null
|
|
? System.Text.Json.JsonSerializer.Serialize(request.TargetUserIds)
|
|
: null,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
db.SystemMessages.Add(message);
|
|
await db.SaveChangesAsync();
|
|
|
|
return Results.Created($"/api/admin/notifications/{message.Id}", new SystemMessageDetailResponse
|
|
{
|
|
Id = message.Id,
|
|
Title = message.Title,
|
|
Content = message.Content,
|
|
ThumbnailUrl = message.ThumbnailUrl,
|
|
CreatedAt = message.CreatedAt
|
|
});
|
|
}).RequireAuthorization("AdminOnly");
|
|
|
|
// 管理端删除通知
|
|
app.MapDelete("/api/admin/notifications/{id}", async (int id, AppDbContext db) =>
|
|
{
|
|
var msg = await db.SystemMessages.FindAsync(id);
|
|
if (msg == null)
|
|
return Results.NotFound(new { code = 404, message = "通知不存在" });
|
|
|
|
// 删除关联的已读记录
|
|
db.MessageReads.RemoveRange(
|
|
db.MessageReads.Where(r => r.MessageType == MessageType.System && r.MessageId == id));
|
|
db.SystemMessages.Remove(msg);
|
|
await db.SaveChangesAsync();
|
|
|
|
return Results.Ok(new { message = "已删除" });
|
|
}).RequireAuthorization("AdminOnly");
|
|
}
|
|
}
|