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 Rows); public sealed record TenantAdminStudentImportPreview( int TotalCount, int ValidCount, int InvalidCount, IReadOnlyCollection 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 InvalidItems); public sealed record TenantAdminBulkAssignClassCommand( Guid ClassId, IReadOnlyCollection UserIds); public sealed record TenantAdminBulkStatusCommand( IReadOnlyCollection UserIds, string? Status, string? Reason); public sealed record TenantAdminBulkOperationResult( int RequestedCount, int SucceededCount, int FailedCount, IReadOnlyCollection 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 Items); public sealed record TenantSupervisionRiskStudentItem( Guid UserId, string? Name, string? PhoneMasked, Guid? RegionId, IReadOnlyCollection RuleCodes, IReadOnlyCollection Reasons); public sealed record TenantSupervisionGenerateCommand( IReadOnlyCollection? 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 Items, bool Scoped); public sealed record TenantAdminStudentList( IReadOnlyCollection 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 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; }