feat: add student engagement endpoints

This commit is contained in:
xiong
2026-07-26 18:39:55 +08:00
parent 387b85342a
commit 89e1f2a4df
5 changed files with 547 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ using Tiku.Domain.Catalog;
using Tiku.Domain.Common;
using Tiku.Domain.Identity;
using Tiku.Domain.Learning;
using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Auth;
using Tiku.Infrastructure.Persistence;
@@ -107,6 +108,83 @@ public sealed class ProfileEndpointTests
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}
[Fact]
public async Task Student_can_manage_notifications_badges_and_feedbacks()
{
await using var factory = new ApiTestFactory();
var seed = await SeedStudentAsync(factory);
var notificationId = Guid.NewGuid();
var badgeId = Guid.NewGuid();
await factory.SeedAsync(
new StudentProfile
{
TenantId = seed.TenantId,
UserId = seed.UserId
},
new UserNotification
{
Id = notificationId,
TenantId = seed.TenantId,
UserId = seed.UserId,
NotificationType = "system",
Title = "欢迎",
Message = "欢迎回来",
Status = NotificationStatus.Unread
},
new Badge
{
Id = badgeId,
TenantId = seed.TenantId,
Name = "首练",
Category = "learning",
IsActive = true
},
new UserBadge
{
TenantId = seed.TenantId,
UserId = seed.UserId,
BadgeId = badgeId,
GrantedAt = DateTimeOffset.UtcNow
});
using var client = factory.CreateClient();
await LoginAsync(client, seed);
var notificationsResponse = await client.GetAsync("/api/profile/notifications");
var notificationsJson = await ReadJsonAsync(notificationsResponse);
var statusResponse = await client.PostAsJsonAsync(
"/api/profile/notifications/status",
new NotificationStatusDto { NotificationIds = [notificationId], Status = "read" });
var badgesResponse = await client.GetAsync("/api/profile/badges?includeLocked=true");
var badgesJson = await ReadJsonAsync(badgesResponse);
var feedbackResponse = await client.PostAsJsonAsync(
"/api/profile/feedbacks",
new SubmitFeedbackDto
{
Type = "suggestion",
Title = "建议",
Description = "希望增加练习提醒",
Priority = "normal"
});
var feedbackJson = await ReadJsonAsync(feedbackResponse);
var feedbacksResponse = await client.GetAsync("/api/profile/feedbacks?type=suggestion");
var feedbacksJson = await ReadJsonAsync(feedbacksResponse);
Assert.Equal(HttpStatusCode.OK, notificationsResponse.StatusCode);
Assert.Equal("unread", notificationsJson.RootElement.GetProperty("items").EnumerateArray().Single().GetProperty("status").GetString());
Assert.Equal(HttpStatusCode.OK, statusResponse.StatusCode);
Assert.Equal(HttpStatusCode.OK, badgesResponse.StatusCode);
Assert.True(badgesJson.RootElement.GetProperty("items").EnumerateArray().Single().GetProperty("isUnlocked").GetBoolean());
Assert.Equal(HttpStatusCode.OK, feedbackResponse.StatusCode);
Assert.Equal("suggestion", feedbackJson.RootElement.GetProperty("type").GetString());
Assert.Equal(HttpStatusCode.OK, feedbacksResponse.StatusCode);
Assert.Single(feedbacksJson.RootElement.EnumerateArray());
using var scope = factory.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
Assert.Equal(NotificationStatus.Read, dbContext.UserNotifications.Single(item => item.Id == notificationId).Status);
Assert.Single(dbContext.ReportStatusEvents);
}
private static async Task<(Guid TenantId, Guid UserId, string Phone)> SeedStudentAsync(ApiTestFactory factory)
{
var tenantId = Guid.NewGuid();