feat: add tenant admin engagement endpoints

This commit is contained in:
xiong
2026-07-26 19:15:39 +08:00
parent dca6e8c66d
commit 28befc57be
6 changed files with 930 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ using Tiku.Api.Contracts;
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;
@@ -299,6 +300,90 @@ public sealed class TenantAdminDirectEndpointTests
Assert.NotEmpty(auditJson.RootElement.GetProperty("items").EnumerateArray());
}
[Fact]
public async Task Tenant_admin_can_manage_badges_notifications_and_feedbacks()
{
await using var factory = new ApiTestFactory();
var seed = await SeedAdminAsync(factory);
var studentId = Guid.NewGuid();
var feedbackId = Guid.NewGuid();
await factory.SeedAsync(
new User { Id = studentId, Phone = "13900000005", Name = "反馈学生" },
new TenantMembership { TenantId = seed.TenantId, UserId = studentId, Role = TenantRole.Student, Status = MembershipStatus.Active },
new StudentProfile { TenantId = seed.TenantId, UserId = studentId },
new Report
{
Id = feedbackId,
TenantId = seed.TenantId,
UserId = studentId,
Type = ReportType.Suggestion,
Title = "希望增加解析",
Description = "视频解析再详细一点",
Status = ReportStatus.Pending
});
using var client = factory.CreateClient();
await LoginAsync(client, seed);
var badgeResponse = await client.PutAsJsonAsync(
"/api/tenant-admin/badges",
new UpsertTenantAdminBadgeDto
{
Name = "反馈达人",
Category = "feedback",
UnlockType = "manual",
Order = 1
});
var badgeJson = await ReadJsonAsync(badgeResponse);
var badgeId = badgeJson.RootElement.GetProperty("item").GetProperty("id").GetGuid();
var grantResponse = await client.PostAsJsonAsync(
"/api/tenant-admin/badge-grants",
new GrantTenantAdminBadgeDto
{
UserId = studentId,
BadgeId = badgeId,
Note = "感谢反馈"
});
var notificationResponse = await client.PutAsJsonAsync(
"/api/tenant-admin/notifications",
new UpsertTenantAdminNotificationDto
{
UserId = studentId,
NotificationType = "admin_message",
Severity = "info",
Title = "学习提醒",
Message = "记得复习错题",
DedupeKey = "daily-review"
});
var notificationsListResponse = await client.GetAsync($"/api/tenant-admin/notifications?userId={studentId}");
var feedbackUpdateResponse = await client.PostAsJsonAsync(
"/api/tenant-admin/feedbacks/status",
new UpdateTenantAdminFeedbackDto
{
FeedbackId = feedbackId,
Status = "resolved",
Priority = "high",
Resolution = "已安排补充解析",
Note = "后台处理完成"
});
var feedbackListResponse = await client.GetAsync("/api/tenant-admin/feedbacks?status=resolved");
var notificationsJson = await ReadJsonAsync(notificationsListResponse);
var feedbackJson = await ReadJsonAsync(feedbackListResponse);
Assert.Equal(HttpStatusCode.OK, badgeResponse.StatusCode);
Assert.Equal(HttpStatusCode.OK, grantResponse.StatusCode);
Assert.Equal(HttpStatusCode.OK, notificationResponse.StatusCode);
Assert.Equal(HttpStatusCode.OK, feedbackUpdateResponse.StatusCode);
Assert.True(notificationsJson.RootElement.GetProperty("items").GetArrayLength() >= 2);
Assert.Single(feedbackJson.RootElement.GetProperty("items").EnumerateArray());
using var scope = factory.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
Assert.True(await dbContext.UserBadges.AnyAsync(item => item.TenantId == seed.TenantId && item.UserId == studentId && item.BadgeId == badgeId));
Assert.True(await dbContext.UserNotifications.AnyAsync(item => item.TenantId == seed.TenantId && item.UserId == studentId && item.NotificationType == "badge_granted"));
Assert.True(await dbContext.ReportStatusEvents.AnyAsync(item => item.TenantId == seed.TenantId && item.ReportId == feedbackId && item.ToStatus == ReportStatus.Resolved));
}
private static async Task<(Guid TenantId, Guid UserId, string Phone)> SeedAdminAsync(
ApiTestFactory factory,
TenantRole role = TenantRole.TenantAdmin)