636 lines
16 KiB
C#
636 lines
16 KiB
C#
using System.Text.Json;
|
|
using Tiku.Domain.Common;
|
|
using Tiku.Domain.Learning;
|
|
using Tiku.Domain.Operations;
|
|
using Tiku.Domain.Tenancy;
|
|
|
|
namespace Tiku.Application.TenantAdmin;
|
|
|
|
public sealed record TenantAdminActor(Guid TenantId, Guid UserId)
|
|
{
|
|
public static TenantAdminActor FromResolvedIdentity(Guid? tenantId, Guid? userId)
|
|
{
|
|
if (tenantId is null || userId is null)
|
|
{
|
|
throw new InvalidOperationException("Tenant admin actor was not resolved.");
|
|
}
|
|
|
|
return new(tenantId.Value, userId.Value);
|
|
}
|
|
}
|
|
|
|
public sealed record TenantAdminClassFilter(
|
|
Guid? RegionId = null,
|
|
string? Status = null,
|
|
string? Keyword = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminClassMemberFilter(
|
|
Guid ClassId,
|
|
string? MemberType = null,
|
|
string? Status = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminStudentFilter(
|
|
Guid? RegionId = null,
|
|
Guid? ClassId = null,
|
|
string? Status = null,
|
|
string? Keyword = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminStudentActivityFilter(
|
|
Guid? StudentUserId = null,
|
|
string? Status = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminMemberFilter(
|
|
string? Role = null,
|
|
string? Status = null,
|
|
string? Keyword = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminAuditLogFilter(
|
|
string? Action = null,
|
|
string? TargetType = null,
|
|
Guid? ActorUserId = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminBadgeFilter(
|
|
string? Category = null,
|
|
bool IncludeInactive = false,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminBadgeGrantFilter(
|
|
Guid? UserId = null,
|
|
Guid? BadgeId = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminNotificationFilter(
|
|
Guid? UserId = null,
|
|
string? Status = null,
|
|
string? NotificationType = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminFeedbackFilter(
|
|
Guid? UserId = null,
|
|
string? Status = null,
|
|
string? Type = null,
|
|
string? Keyword = null,
|
|
int? Limit = null);
|
|
|
|
public sealed record TenantAdminOverviewItem(
|
|
int StudentCount,
|
|
int ClassCount,
|
|
int StaffCount,
|
|
int ActivePracticeCount,
|
|
int TodayPracticeCount,
|
|
int PendingFollowupCount,
|
|
int UnreadNotificationCount,
|
|
int PaidOrderCount,
|
|
int RevenueCents,
|
|
int PendingRefundCount,
|
|
int OpenReconciliationIssueCount,
|
|
DateTimeOffset GeneratedAt);
|
|
|
|
public sealed record UpsertTenantAdminClassCommand(
|
|
Guid? Id,
|
|
Guid? RegionId,
|
|
string? LegacyId,
|
|
string? Code,
|
|
string Name,
|
|
string? Description,
|
|
string? Status,
|
|
int? Order,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record UserLookupCommand(
|
|
Guid? UserId,
|
|
string? Username,
|
|
string? Email,
|
|
string? Phone,
|
|
string? Name,
|
|
string? AvatarUrl);
|
|
|
|
public sealed record UpsertTenantAdminClassMemberCommand(
|
|
Guid ClassId,
|
|
UserLookupCommand User,
|
|
string? MemberType,
|
|
string? Status,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record UpsertTenantAdminStudentCommand(
|
|
UserLookupCommand User,
|
|
Guid? RegionId,
|
|
Guid? SelectedSchoolId,
|
|
Guid? SelectedMajorId,
|
|
string? AvatarPreset,
|
|
JsonElement Stats,
|
|
JsonElement Progress,
|
|
JsonElement ModuleSelections,
|
|
JsonElement RawProfile);
|
|
|
|
public sealed record UpdateTenantAdminStudentStatusCommand(
|
|
Guid UserId,
|
|
string? Status,
|
|
string? Reason);
|
|
|
|
public sealed record TenantAdminStudentImportRowCommand(
|
|
UserLookupCommand User,
|
|
Guid? RegionId,
|
|
Guid? ClassId,
|
|
string? AvatarPreset,
|
|
JsonElement RawProfile);
|
|
|
|
public sealed record TenantAdminStudentImportCommand(
|
|
IReadOnlyCollection<TenantAdminStudentImportRowCommand> Rows);
|
|
|
|
public sealed record TenantAdminStudentImportPreview(
|
|
int TotalCount,
|
|
int ValidCount,
|
|
int InvalidCount,
|
|
IReadOnlyCollection<TenantAdminStudentImportPreviewItem> Items);
|
|
|
|
public sealed record TenantAdminStudentImportPreviewItem(
|
|
int RowNo,
|
|
bool Valid,
|
|
string? Reason,
|
|
string? Phone,
|
|
string? Email,
|
|
string? Name,
|
|
Guid? RegionId,
|
|
Guid? ClassId);
|
|
|
|
public sealed record TenantAdminStudentImportResult(
|
|
int TotalCount,
|
|
int CreatedOrUpdatedCount,
|
|
int ClassAssignedCount,
|
|
IReadOnlyCollection<TenantAdminStudentImportPreviewItem> InvalidItems);
|
|
|
|
public sealed record TenantAdminBulkAssignClassCommand(
|
|
Guid ClassId,
|
|
IReadOnlyCollection<Guid> UserIds);
|
|
|
|
public sealed record TenantAdminBulkStatusCommand(
|
|
IReadOnlyCollection<Guid> UserIds,
|
|
string? Status,
|
|
string? Reason);
|
|
|
|
public sealed record TenantAdminBulkOperationResult(
|
|
int RequestedCount,
|
|
int SucceededCount,
|
|
int FailedCount,
|
|
IReadOnlyCollection<Guid> FailedUserIds);
|
|
|
|
public sealed record TenantSupervisionRuleItem(
|
|
string Code,
|
|
string Title,
|
|
bool Enabled,
|
|
int? DaysWithoutCheckIn,
|
|
int? MaxQuestionsAnsweredToday,
|
|
string FollowupType,
|
|
string Priority,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record UpsertTenantSupervisionRuleCommand(
|
|
string Code,
|
|
string Title,
|
|
bool Enabled,
|
|
int? DaysWithoutCheckIn,
|
|
int? MaxQuestionsAnsweredToday,
|
|
string? FollowupType,
|
|
string? Priority,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record TenantSupervisionPreview(
|
|
IReadOnlyCollection<TenantSupervisionRiskStudentItem> Items);
|
|
|
|
public sealed record TenantSupervisionRiskStudentItem(
|
|
Guid UserId,
|
|
string? Name,
|
|
string? PhoneMasked,
|
|
Guid? RegionId,
|
|
IReadOnlyCollection<string> RuleCodes,
|
|
IReadOnlyCollection<string> Reasons);
|
|
|
|
public sealed record TenantSupervisionGenerateCommand(
|
|
IReadOnlyCollection<Guid>? UserIds,
|
|
Guid? AssignedToUserId,
|
|
DateTimeOffset? DueAt);
|
|
|
|
public sealed record TenantSupervisionGenerateResult(int CreatedCount);
|
|
|
|
public sealed record TenantFollowupReport(
|
|
int OpenCount,
|
|
int InProgressCount,
|
|
int DoneCount,
|
|
int OverdueCount);
|
|
|
|
public sealed record TenantFeedbackReport(
|
|
int PendingCount,
|
|
int AcceptedCount,
|
|
int RejectedCount,
|
|
int ResolvedCount,
|
|
int ClosedCount);
|
|
|
|
public sealed record TenantPointRiskReport(
|
|
int NegativeScoreUserCount,
|
|
int HighPointClaimUserCount,
|
|
int CancelledExchangeOrderCount);
|
|
|
|
public sealed record UpsertTenantAdminStudentNoteCommand(
|
|
Guid? Id,
|
|
Guid StudentUserId,
|
|
string? NoteType,
|
|
string Content,
|
|
string? Visibility,
|
|
bool? IsPinned,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record UpsertTenantAdminStudentFollowupCommand(
|
|
Guid? Id,
|
|
Guid StudentUserId,
|
|
Guid? AssignedToUserId,
|
|
Guid? ClassId,
|
|
string Title,
|
|
string? Description,
|
|
string? FollowupType,
|
|
string? Priority,
|
|
string? Status,
|
|
DateTimeOffset? DueAt,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record UpsertTenantAdminMemberCommand(
|
|
Guid? MembershipId,
|
|
UserLookupCommand User,
|
|
string? Role,
|
|
string? Status,
|
|
string? PrimaryRole);
|
|
|
|
public sealed record UpsertTenantBrandingCommand(
|
|
string BrandName,
|
|
string? ShortName,
|
|
string? Slogan,
|
|
string? OrganizationName,
|
|
string? LogoUrl,
|
|
string? FaviconUrl,
|
|
string? ServiceWechat,
|
|
string? ServiceAccountName);
|
|
|
|
public sealed record UpsertTenantSettingsCommand(
|
|
JsonElement FeatureFlags,
|
|
JsonElement AdminFeatureFlags,
|
|
JsonElement PublicConfig);
|
|
|
|
public sealed record PreviewTenantThemeCommand(
|
|
string TemplateCode,
|
|
JsonElement Theme,
|
|
JsonElement PublicAssets);
|
|
|
|
public sealed record PublishTenantThemeCommand(
|
|
bool UseDraft,
|
|
string? TemplateCode,
|
|
JsonElement Theme,
|
|
JsonElement PublicAssets);
|
|
|
|
public sealed record CreateTenantDomainCommand(
|
|
string Host,
|
|
string? DomainType,
|
|
bool IsPrimary);
|
|
|
|
public sealed record UpsertTenantIdentityProviderCommand(
|
|
string Provider,
|
|
string? Status,
|
|
string? DisplayName,
|
|
string? SecretRef,
|
|
int? Priority,
|
|
JsonElement ConfigPublic);
|
|
|
|
public sealed record UpsertTenantAdminBadgeCommand(
|
|
Guid? Id,
|
|
string? LegacyId,
|
|
string Name,
|
|
string? Description,
|
|
string? Category,
|
|
string? IconUrl,
|
|
int? Level,
|
|
string? UnlockType,
|
|
string? ConditionField,
|
|
string? ConditionOperator,
|
|
decimal? ConditionValue,
|
|
JsonElement ConditionExtra,
|
|
int? Order,
|
|
bool? IsActive);
|
|
|
|
public sealed record GrantTenantAdminBadgeCommand(
|
|
Guid UserId,
|
|
Guid BadgeId,
|
|
string? LegacyId,
|
|
string? Note,
|
|
DateTimeOffset? GrantedAt,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record UpsertTenantAdminNotificationCommand(
|
|
Guid UserId,
|
|
string NotificationType,
|
|
string? Severity,
|
|
string Title,
|
|
string Message,
|
|
string? ActionLabel,
|
|
string? ActionPath,
|
|
string? SourceType,
|
|
Guid? SourceId,
|
|
string? DedupeKey,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record UpdateTenantAdminFeedbackCommand(
|
|
Guid FeedbackId,
|
|
string? Status,
|
|
string? Priority,
|
|
string? Resolution,
|
|
string? Note,
|
|
JsonElement Metadata);
|
|
|
|
public sealed record TenantAdminClassList(
|
|
IReadOnlyCollection<TenantAdminClassItem> Items,
|
|
bool Scoped);
|
|
|
|
public sealed record TenantAdminStudentList(
|
|
IReadOnlyCollection<TenantAdminStudentItem> Items,
|
|
bool Scoped);
|
|
|
|
public sealed record TenantAdminClassItem(
|
|
Guid Id,
|
|
Guid? RegionId,
|
|
string? RegionName,
|
|
string? LegacyId,
|
|
string? Code,
|
|
string Name,
|
|
string? Description,
|
|
TenantRecordStatus Status,
|
|
int Order,
|
|
JsonElement Metadata,
|
|
int StudentCount,
|
|
int StaffCount,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminClassMemberItem(
|
|
Guid Id,
|
|
Guid ClassId,
|
|
Guid UserId,
|
|
TenantClassMemberType MemberType,
|
|
TenantClassMemberStatus Status,
|
|
DateTimeOffset JoinedAt,
|
|
DateTimeOffset? LeftAt,
|
|
JsonElement Metadata,
|
|
TenantAdminUserSummary User);
|
|
|
|
public sealed record TenantAdminUserSummary(
|
|
Guid Id,
|
|
string? Username,
|
|
string? Email,
|
|
string? Phone,
|
|
string? Name,
|
|
string? AvatarUrl,
|
|
string PrimaryRole);
|
|
|
|
public sealed record TenantAdminStudentItem(
|
|
Guid MembershipId,
|
|
Guid UserId,
|
|
MembershipStatus Status,
|
|
TenantAdminUserSummary User,
|
|
Guid? ProfileId,
|
|
string AvatarPreset,
|
|
Guid? RegionId,
|
|
string? RegionName,
|
|
Guid? SelectedSchoolId,
|
|
string? SelectedSchoolName,
|
|
Guid? SelectedMajorId,
|
|
string? SelectedMajorName,
|
|
JsonElement Stats,
|
|
JsonElement Progress,
|
|
JsonElement ModuleSelections,
|
|
IReadOnlyCollection<TenantAdminStudentClassSummary> Classes,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminStudentClassSummary(
|
|
Guid ClassId,
|
|
string ClassName,
|
|
string? ClassCode,
|
|
TenantClassMemberType MemberType,
|
|
DateTimeOffset JoinedAt);
|
|
|
|
public sealed record TenantAdminStudentStatusItem(
|
|
Guid MembershipId,
|
|
Guid UserId,
|
|
TenantRole Role,
|
|
MembershipStatus Status,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminStudentNoteItem(
|
|
Guid Id,
|
|
Guid StudentUserId,
|
|
StudentNoteType NoteType,
|
|
string Content,
|
|
StudentNoteVisibility Visibility,
|
|
bool IsPinned,
|
|
JsonElement Metadata,
|
|
Guid? CreatedBy,
|
|
Guid? UpdatedBy,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminStudentFollowupItem(
|
|
Guid Id,
|
|
Guid StudentUserId,
|
|
Guid? AssignedToUserId,
|
|
Guid? ClassId,
|
|
string Title,
|
|
string? Description,
|
|
StudentFollowupType FollowupType,
|
|
StudentFollowupPriority Priority,
|
|
StudentFollowupStatus Status,
|
|
DateTimeOffset? DueAt,
|
|
DateTimeOffset? CompletedAt,
|
|
Guid? CompletedBy,
|
|
JsonElement Metadata,
|
|
Guid? CreatedBy,
|
|
Guid? UpdatedBy,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminMemberItem(
|
|
Guid MembershipId,
|
|
Guid UserId,
|
|
TenantRole Role,
|
|
MembershipStatus Status,
|
|
string? LegacyRole,
|
|
TenantAdminUserSummary User,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminAuditLogItem(
|
|
Guid Id,
|
|
Guid? ActorUserId,
|
|
string Action,
|
|
string? TargetType,
|
|
string? TargetId,
|
|
JsonElement Details,
|
|
string? IpAddress,
|
|
string? UserAgent,
|
|
string? ActorName,
|
|
string? ActorPhone,
|
|
DateTimeOffset CreatedAt);
|
|
|
|
public sealed record TenantBrandingItem(
|
|
Guid TenantId,
|
|
string BrandName,
|
|
string? ShortName,
|
|
string? Slogan,
|
|
string? OrganizationName,
|
|
string? LogoUrl,
|
|
string? FaviconUrl,
|
|
string? ServiceWechat,
|
|
string? ServiceAccountName,
|
|
JsonElement Theme,
|
|
JsonElement PublicAssets,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantSettingsItem(
|
|
Guid TenantId,
|
|
JsonElement FeatureFlags,
|
|
JsonElement AdminFeatureFlags,
|
|
JsonElement PublicConfig,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantThemeTemplateItem(
|
|
string Code,
|
|
string Name,
|
|
string? Description,
|
|
string? PreviewImageUrl,
|
|
JsonElement Theme,
|
|
JsonElement PublicAssets,
|
|
int Order);
|
|
|
|
public sealed record TenantThemeItem(
|
|
Guid TenantId,
|
|
string? ActiveTemplateCode,
|
|
JsonElement ActiveTheme,
|
|
JsonElement ActivePublicAssets,
|
|
string? DraftTemplateCode,
|
|
JsonElement DraftTheme,
|
|
JsonElement DraftPublicAssets,
|
|
TenantThemeConfigStatus Status,
|
|
DateTimeOffset? PublishedAt,
|
|
Guid? PublishedBy,
|
|
Guid? DraftUpdatedBy,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantDomainItem(
|
|
Guid Id,
|
|
string Host,
|
|
TenantDomainType DomainType,
|
|
TenantDomainStatus Status,
|
|
bool IsPrimary,
|
|
string? VerificationToken,
|
|
DateTimeOffset? VerifiedAt,
|
|
DateTimeOffset? LastCheckedAt,
|
|
DateTimeOffset? DnsVerifiedAt,
|
|
DateTimeOffset? TlsReadyAt,
|
|
string? LastFailureReason,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantIdentityProviderItem(
|
|
Guid Id,
|
|
string Provider,
|
|
TenantExternalProviderStatus Status,
|
|
string? DisplayName,
|
|
string? SecretRef,
|
|
int Priority,
|
|
JsonElement ConfigPublic,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminBadgeItem(
|
|
Guid Id,
|
|
string? LegacyId,
|
|
string Name,
|
|
string? Description,
|
|
string? Category,
|
|
string? IconUrl,
|
|
int? Level,
|
|
string? UnlockType,
|
|
string? ConditionField,
|
|
string? ConditionOperator,
|
|
decimal? ConditionValue,
|
|
JsonElement ConditionExtra,
|
|
int Order,
|
|
bool IsActive,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminBadgeGrantItem(
|
|
Guid Id,
|
|
string? LegacyId,
|
|
Guid? UserId,
|
|
string? UserName,
|
|
string? UserPhone,
|
|
Guid? BadgeId,
|
|
string? BadgeName,
|
|
string? BadgeCategory,
|
|
string? IconUrl,
|
|
int? Level,
|
|
Guid? GrantedBy,
|
|
string? GrantedByName,
|
|
string? Note,
|
|
DateTimeOffset? GrantedAt,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminNotificationItem(
|
|
Guid Id,
|
|
Guid UserId,
|
|
string NotificationType,
|
|
NotificationStatus Status,
|
|
NotificationSeverity Severity,
|
|
string Title,
|
|
string Message,
|
|
string? ActionLabel,
|
|
string? ActionPath,
|
|
string? SourceType,
|
|
Guid? SourceId,
|
|
string? DedupeKey,
|
|
JsonElement Metadata,
|
|
Guid? CreatedBy,
|
|
DateTimeOffset? ReadAt,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed record TenantAdminFeedbackItem(
|
|
Guid Id,
|
|
Guid? UserId,
|
|
string? UserName,
|
|
string? UserPhone,
|
|
Guid? QuestionId,
|
|
ReportType? Type,
|
|
string? Title,
|
|
string? Category,
|
|
string? Description,
|
|
ReportStatus Status,
|
|
ReportPriority Priority,
|
|
Guid? HandledBy,
|
|
DateTimeOffset? HandledAt,
|
|
string? Resolution,
|
|
string? Contact,
|
|
JsonElement Attachments,
|
|
JsonElement Metadata,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt);
|
|
|
|
public sealed class TenantAdminDirectException(string message, string code) : Exception(message)
|
|
{
|
|
public string Code { get; } = code;
|
|
}
|