Files
tiku-backend.net/Tiku.Infrastructure/TenantAdmin/Dashboard/TenantAdminDirectService.Dashboard.cs
xiong c497a3ca8d
Some checks failed
ci / release-gate (push) Has been cancelled
清理代码
2026-08-03 12:31:39 +08:00

101 lines
5.1 KiB
C#

using Microsoft.EntityFrameworkCore;
using Tiku.Application.TenantAdmin;
using Tiku.Domain.Operations;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Security;
using CommerceRefundStatus = Tiku.Domain.Commerce.CommerceRefundStatus;
using OrderStatus = Tiku.Domain.Commerce.OrderStatus;
using ReconciliationIssueStatus = Tiku.Domain.Commerce.ReconciliationIssueStatus;
namespace Tiku.Infrastructure.TenantAdmin;
public sealed partial class TenantAdminDirectService
{
public async Task<TenantAdminOverviewItem> GetOverviewAsync(
TenantAdminActor actor,
CancellationToken cancellationToken = default)
{
var scope = await RequireDataScopeAsync(actor, cancellationToken);
var regionIds = scope.RegionIds.ToArray();
var classIds = scope.ClassIds.ToArray();
var now = DateTimeOffset.UtcNow;
var today = new DateTimeOffset(now.UtcDateTime.Date, TimeSpan.Zero);
var scopedClasses = dbContext.TenantClasses.AsNoTracking()
.Where(item => item.TenantId == actor.TenantId)
.ApplyDataScope(
scope,
item => item.CreatedBy == actor.UserId,
item => classIds.Contains(item.Id) ||
(item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value)));
var scopedStudents = dbContext.StudentProfiles.AsNoTracking()
.Where(item => item.TenantId == actor.TenantId)
.ApplyDataScope(
scope,
item => item.UserId == actor.UserId,
item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
var scopedOrders = dbContext.Orders.AsNoTracking()
.Where(item => item.TenantId == actor.TenantId)
.ApplyDataScope(
scope,
item => item.UserId == actor.UserId,
item => item.RegionId.HasValue && regionIds.Contains(item.RegionId.Value));
var studentCount = await scopedStudents.CountAsync(cancellationToken);
var classCount =
await scopedClasses.CountAsync(item => item.Status == TenantRecordStatus.Active, cancellationToken);
var staffCount = await dbContext.TenantMemberships.AsNoTracking()
.CountAsync(item =>
item.TenantId == actor.TenantId &&
item.Status == MembershipStatus.Active &&
item.Role != TenantRole.Student,
cancellationToken);
var activePracticeCount = await dbContext.PracticeSessions.AsNoTracking()
.CountAsync(item =>
item.TenantId == actor.TenantId &&
item.FinishedAt == null &&
(!item.ExpiresAt.HasValue || item.ExpiresAt > now),
cancellationToken);
var todayPracticeCount = await dbContext.PracticeSessions.AsNoTracking()
.CountAsync(item => item.TenantId == actor.TenantId && item.StartedAt >= today, cancellationToken);
var pendingFollowupCount = await dbContext.TenantStudentFollowups.AsNoTracking()
.CountAsync(item =>
item.TenantId == actor.TenantId &&
(item.Status == StudentFollowupStatus.Open || item.Status == StudentFollowupStatus.InProgress),
cancellationToken);
var unreadNotificationCount = await dbContext.UserNotifications.AsNoTracking()
.CountAsync(item => item.TenantId == actor.TenantId && item.Status == NotificationStatus.Unread,
cancellationToken);
var paidOrderCount = await scopedOrders.CountAsync(item => item.Status == OrderStatus.Paid, cancellationToken);
var revenueCents = await scopedOrders
.Where(item => item.Status == OrderStatus.Paid || item.Status == OrderStatus.PartiallyRefunded ||
item.Status == OrderStatus.Refunded)
.SumAsync(item => item.AmountCents - item.RefundedAmountCents, cancellationToken);
var pendingRefundCount = await dbContext.CommerceRefundRequests.AsNoTracking()
.CountAsync(item =>
item.TenantId == actor.TenantId &&
(item.Status == CommerceRefundStatus.Requested ||
item.Status == CommerceRefundStatus.Approved ||
item.Status == CommerceRefundStatus.Processing),
cancellationToken);
var openReconciliationIssueCount = await dbContext.CommerceReconciliationIssues.AsNoTracking()
.CountAsync(item =>
item.TenantId == actor.TenantId &&
item.Status != ReconciliationIssueStatus.Resolved &&
item.Status != ReconciliationIssueStatus.Ignored,
cancellationToken);
return new TenantAdminOverviewItem(
studentCount,
classCount,
staffCount,
activePracticeCount,
todayPracticeCount,
pendingFollowupCount,
unreadNotificationCount,
paidOrderCount,
revenueCents,
pendingRefundCount,
openReconciliationIssueCount,
now);
}
}