forked from gongxuegit/tiku-backend.net
feat: migrate direct tenant content and learning endpoints
This commit is contained in:
@@ -36,6 +36,37 @@ public sealed record AssetUploadConfirmCommand(
|
||||
long? FileSizeBytes,
|
||||
string? ChecksumSha256);
|
||||
|
||||
public sealed record UpsertAssetCommand(
|
||||
Guid? AssetId,
|
||||
Guid? RegionId,
|
||||
Guid? SubjectId,
|
||||
Guid? CategoryId,
|
||||
Guid? ContentNodeId,
|
||||
string? LegacyId,
|
||||
string? AssetKey,
|
||||
string? Title,
|
||||
string? Category,
|
||||
string? Description,
|
||||
string? FileName,
|
||||
string? CdnUrl,
|
||||
bool? IsPublic,
|
||||
string? AssetType,
|
||||
string? Visibility,
|
||||
string? Status,
|
||||
string? Provider,
|
||||
string? Bucket,
|
||||
string? ObjectKey,
|
||||
string? MimeType,
|
||||
long? FileSizeBytes,
|
||||
string? ChecksumSha256,
|
||||
string? PreviewUrl,
|
||||
string? PreviewObjectKey,
|
||||
int? Order,
|
||||
JsonElement AccessRules,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record AssetAccessSignCommand(Guid AssetId, int? ExpiresInSeconds);
|
||||
|
||||
public sealed record AssetManagementFilter(
|
||||
Guid? RegionId = null,
|
||||
Guid? SubjectId = null,
|
||||
@@ -54,6 +85,8 @@ public sealed record ImportJobFilter(
|
||||
string? SourceFormat = null,
|
||||
int? Limit = null);
|
||||
|
||||
public sealed record AssetEventFilter(Guid? AssetId = null, Guid? UserId = null, int? Limit = null);
|
||||
|
||||
public sealed record ContentAssetManagementItem(
|
||||
Guid Id,
|
||||
string? LegacyId,
|
||||
@@ -96,6 +129,39 @@ public sealed record AssetUploadConfirmResult(
|
||||
ContentAssetManagementItem Item,
|
||||
ObjectStorageMetadata Metadata);
|
||||
|
||||
public sealed record AssetManagementSignedAccessResult(
|
||||
ContentAssetManagementItem Item,
|
||||
ObjectStorageSignedUrl Url);
|
||||
|
||||
public sealed record ContentAssetAccessEventItem(
|
||||
Guid Id,
|
||||
Guid? AssetId,
|
||||
Guid? UserId,
|
||||
AssetAccessActorRole ActorRole,
|
||||
AssetAccessType AccessType,
|
||||
string? Visibility,
|
||||
string? AssetType,
|
||||
string? StorageProvider,
|
||||
AssetAccessDisposition? Disposition,
|
||||
int? ExpiresInSeconds,
|
||||
string? SignatureMode,
|
||||
AssetAccessResult Result,
|
||||
string? DenyCode,
|
||||
string? IpAddress,
|
||||
string? UserAgent,
|
||||
JsonElement Metadata,
|
||||
DateTimeOffset CreatedAt);
|
||||
|
||||
public sealed record ContentAssetSecurityScanEventItem(
|
||||
Guid Id,
|
||||
Guid? AssetId,
|
||||
string Provider,
|
||||
AssetSecurityScanStatus ScanStatus,
|
||||
AssetSecurityRiskLevel RiskLevel,
|
||||
string[] IssueCodes,
|
||||
JsonElement Details,
|
||||
DateTimeOffset CreatedAt);
|
||||
|
||||
public sealed record ContentImportJobItem(
|
||||
Guid Id,
|
||||
Guid? TargetRegionId,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Tiku.Application.Catalog;
|
||||
using Tiku.Application.Content;
|
||||
|
||||
namespace Tiku.Application.Assets;
|
||||
|
||||
@@ -9,6 +10,11 @@ public interface IAssetManagementService
|
||||
AssetManagementFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<ContentManagementResult<ContentAssetManagementItem>> UpsertAssetAsync(
|
||||
AssetManagementActor actor,
|
||||
UpsertAssetCommand command,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<AssetUploadSignResult> SignUploadAsync(
|
||||
AssetManagementActor actor,
|
||||
AssetUploadSignCommand command,
|
||||
@@ -19,6 +25,26 @@ public interface IAssetManagementService
|
||||
AssetUploadConfirmCommand command,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<AssetManagementSignedAccessResult> SignDownloadAsync(
|
||||
AssetManagementActor actor,
|
||||
AssetAccessSignCommand command,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<AssetManagementSignedAccessResult> SignPreviewAsync(
|
||||
AssetManagementActor actor,
|
||||
AssetAccessSignCommand command,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<CatalogList<ContentAssetAccessEventItem>> GetAccessEventsAsync(
|
||||
AssetManagementActor actor,
|
||||
AssetEventFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<CatalogList<ContentAssetSecurityScanEventItem>> GetSecurityScanEventsAsync(
|
||||
AssetManagementActor actor,
|
||||
AssetEventFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<CatalogList<ContentImportJobItem>> GetImportJobsAsync(
|
||||
AssetManagementActor actor,
|
||||
ImportJobFilter filter,
|
||||
|
||||
320
Tiku.Application/Content/DirectContentModels.cs
Normal file
320
Tiku.Application/Content/DirectContentModels.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
using System.Text.Json;
|
||||
using Tiku.Application.Assets;
|
||||
using Tiku.Application.Catalog;
|
||||
using Tiku.Domain.Catalog;
|
||||
using Tiku.Domain.Content;
|
||||
using Tiku.Domain.Learning;
|
||||
using Tiku.Domain.Operations;
|
||||
using Tiku.Domain.QuestionBanks;
|
||||
|
||||
namespace Tiku.Application.Content;
|
||||
|
||||
public sealed record DirectContentActor(Guid TenantId, Guid UserId);
|
||||
|
||||
public sealed record AdminLimitFilter(
|
||||
Guid? RegionId = null,
|
||||
Guid? EntryId = null,
|
||||
Guid? ContentNodeId = null,
|
||||
Guid? ParentId = null,
|
||||
Guid? SubjectId = null,
|
||||
Guid? ChapterId = null,
|
||||
Guid? UnitId = null,
|
||||
Guid? SchoolId = null,
|
||||
Guid? MajorId = null,
|
||||
Guid? QuestionId = null,
|
||||
string? Status = null,
|
||||
string? Keyword = null,
|
||||
int? Year = null,
|
||||
int? Limit = null);
|
||||
|
||||
public sealed record QuestionWriteCommand(
|
||||
Guid? QuestionId,
|
||||
Guid? QuestionBankId,
|
||||
Guid? SubjectId,
|
||||
Guid? CategoryId,
|
||||
Guid? NodeId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
Guid? PrimaryCollectionId,
|
||||
string? LegacyId,
|
||||
string? Type,
|
||||
string? TypeLabel,
|
||||
int? Difficulty,
|
||||
JsonElement Tags,
|
||||
string? Content,
|
||||
JsonElement Options,
|
||||
int? CorrectOptionIndex,
|
||||
JsonElement CorrectOptionIndices,
|
||||
string? AnswerText,
|
||||
string? Explanation,
|
||||
JsonElement SubQuestions,
|
||||
string? CodeLang,
|
||||
string? CodeTemplate,
|
||||
string? MediaUrl,
|
||||
string? Status,
|
||||
JsonElement ExamMarkers,
|
||||
string? SourceHash,
|
||||
bool CreateVersion);
|
||||
|
||||
public sealed record QuestionManagementItem(
|
||||
Guid Id,
|
||||
Guid? VersionId,
|
||||
Guid? QuestionBankId,
|
||||
Guid? SubjectId,
|
||||
Guid? CategoryId,
|
||||
Guid? NodeId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
Guid? PrimaryCollectionId,
|
||||
string? LegacyId,
|
||||
string Type,
|
||||
string? TypeLabel,
|
||||
int? Difficulty,
|
||||
JsonElement Tags,
|
||||
string? Content,
|
||||
JsonElement Options,
|
||||
int? CorrectOptionIndex,
|
||||
JsonElement CorrectOptionIndices,
|
||||
string? AnswerText,
|
||||
string? Explanation,
|
||||
JsonElement SubQuestions,
|
||||
string? CodeLang,
|
||||
string? CodeTemplate,
|
||||
string? MediaUrl,
|
||||
bool HasVideoExplanation,
|
||||
QuestionStatus Status);
|
||||
|
||||
public sealed record VocabularyUnitCommand(
|
||||
Guid? Id,
|
||||
Guid? RegionId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
string? LegacyId,
|
||||
string Name,
|
||||
string? Description,
|
||||
int? WordCount,
|
||||
int? Order,
|
||||
bool? IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record VocabularyWordCommand(
|
||||
Guid? Id,
|
||||
Guid? UnitId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
string? LegacyId,
|
||||
string Word,
|
||||
string? Phonetic,
|
||||
string? Meaning,
|
||||
string? Example,
|
||||
string? ExampleTranslation,
|
||||
int? Difficulty,
|
||||
JsonElement Tags,
|
||||
int? Order,
|
||||
bool? IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record HandbookSubjectCommand(
|
||||
Guid? Id,
|
||||
Guid? RegionId,
|
||||
Guid? SchoolId,
|
||||
Guid? MajorId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
string? LegacyId,
|
||||
string Name,
|
||||
string? Type,
|
||||
string? Icon,
|
||||
string? Color,
|
||||
string? Description,
|
||||
int? Order,
|
||||
bool? IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record HandbookChapterCommand(
|
||||
Guid? Id,
|
||||
Guid? SubjectId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
string? LegacyId,
|
||||
string Name,
|
||||
string? Description,
|
||||
int? Order,
|
||||
bool? IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record HandbookEntryCommand(
|
||||
Guid? Id,
|
||||
Guid? ChapterId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
string? LegacyId,
|
||||
string Title,
|
||||
string? Summary,
|
||||
string? Content,
|
||||
JsonElement Tags,
|
||||
int? Order,
|
||||
bool? IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record SchoolCommand(
|
||||
Guid? Id,
|
||||
Guid? RegionId,
|
||||
string? LegacyId,
|
||||
string Name,
|
||||
string? ProfessionalExamDate,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record MajorCommand(
|
||||
Guid? Id,
|
||||
Guid? RegionId,
|
||||
Guid? SchoolId,
|
||||
string? LegacyId,
|
||||
string Name,
|
||||
string? Description,
|
||||
string? StudyTips,
|
||||
int? Order,
|
||||
bool? IsActive);
|
||||
|
||||
public sealed record VideoExplanationCommand(
|
||||
Guid? Id,
|
||||
Guid? SubjectId,
|
||||
string? LegacyId,
|
||||
string Title,
|
||||
string? Description,
|
||||
string? VideoUrl,
|
||||
string? ThumbnailUrl,
|
||||
int? DurationSeconds,
|
||||
JsonElement KnowledgeTags,
|
||||
bool? IsGeneral,
|
||||
int? Difficulty,
|
||||
int? Order,
|
||||
bool? IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record QuestionVideoCommand(
|
||||
Guid QuestionId,
|
||||
Guid VideoId,
|
||||
string? LegacyId,
|
||||
string? VideoType,
|
||||
int? Order,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record OperationContentCommand(
|
||||
Guid? Id,
|
||||
Guid? RegionId,
|
||||
Guid? SchoolId,
|
||||
string? LegacyId,
|
||||
string? Title,
|
||||
string? Subtitle,
|
||||
string? Content,
|
||||
string? Question,
|
||||
string? Answer,
|
||||
string? Link,
|
||||
string? ButtonText,
|
||||
string? ButtonLink,
|
||||
string? BackgroundColor,
|
||||
string? BorderColor,
|
||||
string? ExamName,
|
||||
DateTimeOffset? ExamAt,
|
||||
string? ExamType,
|
||||
string? Description,
|
||||
int? Order,
|
||||
bool? IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record SimpleImportCommand(
|
||||
string ImportType,
|
||||
string? SourceFormat,
|
||||
string? SourceName,
|
||||
Guid? RegionId,
|
||||
Guid? EntryId,
|
||||
Guid? ContentNodeId,
|
||||
Guid? SubjectId,
|
||||
Guid? CategoryId,
|
||||
Guid? QuestionBankId,
|
||||
Guid? CollectionId,
|
||||
IReadOnlyCollection<JsonElement> Items,
|
||||
bool DryRun);
|
||||
|
||||
public sealed record SimpleImportResult(
|
||||
ContentImportJobItem Job,
|
||||
IReadOnlyCollection<ContentImportItemModel> Items,
|
||||
IReadOnlyCollection<ContentImportIssueModel> Issues);
|
||||
|
||||
public sealed record ImportPostCheckResult(Guid JobId, string Status, JsonElement Counts, IReadOnlyCollection<ContentImportIssueModel> Issues);
|
||||
|
||||
public sealed record VideoManagementItem(
|
||||
Guid Id,
|
||||
Guid? SubjectId,
|
||||
string? LegacyId,
|
||||
string Title,
|
||||
string? Description,
|
||||
string? VideoUrl,
|
||||
string? ThumbnailUrl,
|
||||
int? DurationSeconds,
|
||||
JsonElement KnowledgeTags,
|
||||
bool IsGeneral,
|
||||
int? Difficulty,
|
||||
int Order,
|
||||
bool IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record QuestionVideoManagementItem(
|
||||
Guid Id,
|
||||
Guid? QuestionId,
|
||||
Guid? VideoId,
|
||||
string? LegacyId,
|
||||
QuestionVideoType VideoType,
|
||||
int Order,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record OperationContentItem(
|
||||
Guid Id,
|
||||
string Kind,
|
||||
Guid? RegionId,
|
||||
Guid? SchoolId,
|
||||
string? LegacyId,
|
||||
string? Title,
|
||||
string? Content,
|
||||
string? Question,
|
||||
string? Answer,
|
||||
DateTimeOffset? ExamAt,
|
||||
string? ExamType,
|
||||
int Order,
|
||||
bool IsActive,
|
||||
JsonElement Metadata);
|
||||
|
||||
public sealed record ScorelineTrendItem(int Year, int SchoolCount, int MajorCount);
|
||||
|
||||
public interface IDirectContentService
|
||||
{
|
||||
Task<ContentManagementResult<QuestionManagementItem>> CreateQuestionAsync(DirectContentActor actor, QuestionWriteCommand command, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<QuestionManagementItem>> UpdateQuestionAsync(DirectContentActor actor, QuestionWriteCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<VocabularyUnit>> GetVocabularyUnitsAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<VocabularyUnit>> UpsertVocabularyUnitAsync(DirectContentActor actor, VocabularyUnitCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<VocabularyWord>> GetVocabularyWordsAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<VocabularyWord>> UpsertVocabularyWordAsync(DirectContentActor actor, VocabularyWordCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<HandbookSubject>> GetHandbookSubjectsAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<HandbookSubject>> UpsertHandbookSubjectAsync(DirectContentActor actor, HandbookSubjectCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<HandbookChapter>> GetHandbookChaptersAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<HandbookChapter>> UpsertHandbookChapterAsync(DirectContentActor actor, HandbookChapterCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<HandbookEntry>> GetHandbookEntriesAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<HandbookEntry>> UpsertHandbookEntryAsync(DirectContentActor actor, HandbookEntryCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<School>> GetSchoolsAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<School>> UpsertSchoolAsync(DirectContentActor actor, SchoolCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<Major>> GetMajorsAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<Major>> UpsertMajorAsync(DirectContentActor actor, MajorCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<int>> GetScorelineYearsAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<ScorelineTrendItem>> GetScorelineTrendAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<VideoManagementItem>> GetVideosAsync(DirectContentActor actor, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<VideoManagementItem>> UpsertVideoAsync(DirectContentActor actor, VideoExplanationCommand command, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<QuestionVideoManagementItem>> BindQuestionVideoAsync(DirectContentActor actor, QuestionVideoCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<OperationContentItem>> GetOperationContentAsync(DirectContentActor actor, string kind, AdminLimitFilter filter, CancellationToken cancellationToken = default);
|
||||
Task<ContentManagementResult<OperationContentItem>> UpsertOperationContentAsync(DirectContentActor actor, string kind, OperationContentCommand command, CancellationToken cancellationToken = default);
|
||||
Task<SimpleImportResult> PreviewImportAsync(DirectContentActor actor, SimpleImportCommand command, CancellationToken cancellationToken = default);
|
||||
Task<SimpleImportResult> ExecuteImportAsync(DirectContentActor actor, SimpleImportCommand command, CancellationToken cancellationToken = default);
|
||||
Task<CatalogList<ContentImportIssueModel>> GetImportIssuesAsync(DirectContentActor actor, Guid jobId, CancellationToken cancellationToken = default);
|
||||
Task<ImportPostCheckResult> RunImportPostCheckAsync(DirectContentActor actor, Guid jobId, CancellationToken cancellationToken = default);
|
||||
Task<ImportPostCheckResult> GetImportPostCheckAsync(DirectContentActor actor, Guid jobId, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -2,6 +2,20 @@ namespace Tiku.Application.Learning;
|
||||
|
||||
public interface ILearningActivityService
|
||||
{
|
||||
Task<LearningStatsItem> GetStatsAsync(
|
||||
LearningActor actor,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<LearningList<LearningTrendItem>> GetTrendAsync(
|
||||
LearningActor actor,
|
||||
LearningLimitFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<LearningLeaderboardResult> GetLeaderboardAsync(
|
||||
LearningActor actor,
|
||||
LearningLimitFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<AnswerRecordItem> SubmitAnswerAsync(
|
||||
LearningActor actor,
|
||||
SubmitAnswerCommand command,
|
||||
@@ -22,6 +36,11 @@ public interface ILearningActivityService
|
||||
LearningLimitFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<WrongQuestionReviewPlan> GetWrongQuestionReviewPlanAsync(
|
||||
LearningActor actor,
|
||||
LearningLimitFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<LearningActionResult> ResolveWrongQuestionAsync(
|
||||
LearningActor actor,
|
||||
QuestionActionCommand command,
|
||||
@@ -32,11 +51,26 @@ public interface ILearningActivityService
|
||||
LearningLimitFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<WordReviewPlan> GetWordReviewPlanAsync(
|
||||
LearningActor actor,
|
||||
LearningLimitFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<WordProgressItem> UpdateWordProgressAsync(
|
||||
LearningActor actor,
|
||||
WordProgressCommand command,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<WordProgressItem> ReviewWordAsync(
|
||||
LearningActor actor,
|
||||
WordReviewCommand command,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<WordStatsItem> GetWordStatsAsync(
|
||||
LearningActor actor,
|
||||
LearningLimitFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
Task<LearningList<FavoriteWordItem>> GetFavoriteWordsAsync(
|
||||
LearningActor actor,
|
||||
LearningLimitFilter filter,
|
||||
|
||||
@@ -28,6 +28,62 @@ public sealed record FavoriteWordCommand(Guid WordId, bool? Favorite, string? No
|
||||
|
||||
public sealed record LearningLimitFilter(int? Limit = null, string? Status = null, Guid? UnitId = null);
|
||||
|
||||
public sealed record LearningStatsItem(
|
||||
int AnswerCount,
|
||||
int CorrectCount,
|
||||
int WrongCount,
|
||||
int FavoriteQuestionCount,
|
||||
int FavoriteWordCount,
|
||||
int WordProgressCount,
|
||||
int PracticeSessionCount,
|
||||
int PracticeReportCount);
|
||||
|
||||
public sealed record LearningTrendItem(DateOnly Date, int AnswerCount, int CorrectCount, int WrongCount);
|
||||
|
||||
public sealed record LearningLeaderboardItem(
|
||||
Guid UserId,
|
||||
string? DisplayName,
|
||||
int AnswerCount,
|
||||
int CorrectCount,
|
||||
int WrongCount,
|
||||
decimal Accuracy);
|
||||
|
||||
public sealed record LearningLeaderboardResult(
|
||||
string Metric,
|
||||
string Period,
|
||||
IReadOnlyCollection<LearningLeaderboardItem> Items,
|
||||
LearningLeaderboardItem? CurrentUser,
|
||||
DateTimeOffset GeneratedAt);
|
||||
|
||||
public sealed record WrongQuestionReviewPlanItem(Guid QuestionId, int WrongCount, DateTimeOffset LastWrongAt);
|
||||
|
||||
public sealed record WrongQuestionReviewPlan(
|
||||
IReadOnlyCollection<WrongQuestionReviewPlanItem> Items,
|
||||
JsonElement NextAction);
|
||||
|
||||
public sealed record WordReviewPlanItem(
|
||||
Guid WordId,
|
||||
WordProgressStatus Status,
|
||||
DateTimeOffset? NextReviewAt,
|
||||
int CorrectCount,
|
||||
int WrongCount,
|
||||
WordDueLevel DueLevel);
|
||||
|
||||
public sealed record WordReviewPlan(
|
||||
IReadOnlyCollection<WordReviewPlanItem> Items,
|
||||
JsonElement NextAction);
|
||||
|
||||
public sealed record WordReviewCommand(Guid WordId, string? Result, DateTimeOffset? NextReviewAt);
|
||||
|
||||
public sealed record WordStatsItem(
|
||||
int Total,
|
||||
int NewCount,
|
||||
int LearningCount,
|
||||
int ReviewingCount,
|
||||
int MasteredCount,
|
||||
int DueCount,
|
||||
int FavoriteCount);
|
||||
|
||||
public sealed record PracticeSessionCommand(
|
||||
string? Mode,
|
||||
string? TargetType,
|
||||
|
||||
Reference in New Issue
Block a user