Add server-authoritative RZ Coin rewards

This commit is contained in:
hz
2026-08-01 01:28:38 -05:00
parent 8c439c6294
commit ddc36a8048
24 changed files with 670 additions and 104 deletions
+73 -1
View File
@@ -5,7 +5,7 @@ namespace QuestionsApi.Models;
public static class CreditEconomy
{
public const int RollingEarningLimit = 20;
public const int RollingEarningLimit = 30;
public static readonly TimeSpan EarningWindow = TimeSpan.FromHours(24);
public const string ExamTokenSku = "exam_token_1";
public const string QBankResetTokenSku = "qbank_reset_1";
@@ -34,6 +34,9 @@ public static class CreditEconomy
public static RzStoreProduct? FindProduct(string sku) =>
Products.FirstOrDefault(value => value.Sku.Equals(sku, StringComparison.Ordinal));
public static int FirstCorrectAmount(int difficulty) => difficulty >= 5 ? 3 : difficulty >= 4 ? 2 : 1;
public static int ReviewAmount(bool delayedRecovery) => delayedRecovery ? 3 : 1;
}
public static class SlotMachineEconomy
@@ -92,6 +95,65 @@ public sealed class RzStoreProduct
public sealed class CreditEvent
{
public string OperationId { get; set; } = "";
public int CoinAmount { get; set; } = 1;
public DateTime CreatedUtc { get; set; }
}
public static class CreditReasonCodes
{
public const string FirstCorrect = "qbank_first_correct";
public const string DifficultCorrect = "qbank_difficult_correct";
public const string ScheduledReview = "scheduled_review_correct";
public const string DelayedRecovery = "delayed_recovery";
public const string MasteryPracticing = "mastery_practicing";
public const string MasteryStrong = "mastery_strong";
public const string MasteryMastered = "mastery_mastered";
public const string ExamSimulationComplete = "exam_simulation_complete";
public const string Purchase = "purchase";
}
public sealed class CreditLedgerEntry
{
[BsonId]
public string Id { get; set; } = "";
public string UserId { get; set; } = "";
public string OperationId { get; set; } = "";
public int RequestedAmount { get; set; }
public int Amount { get; set; }
public string ReasonCode { get; set; } = "";
public string Description { get; set; } = "";
public string SourceType { get; set; } = "";
public string SourceId { get; set; } = "";
public bool SubjectToRollingCap { get; set; }
public string State { get; set; } = "pending";
public DateTime CreatedUtc { get; set; }
public DateTime? AppliedUtc { get; set; }
}
public sealed class CreditAward
{
public int Amount { get; set; }
public string ReasonCode { get; set; } = "";
public string Description { get; set; } = "";
}
public sealed class CreditAwardResult
{
public CreditAward? Award { get; set; }
public CreditStatusResponse Status { get; set; } = new();
}
public sealed class CreditLedgerPage
{
public List<CreditLedgerEntryResponse> Items { get; set; } = [];
public DateTime? NextBefore { get; set; }
}
public sealed class CreditLedgerEntryResponse
{
public int Amount { get; set; }
public string ReasonCode { get; set; } = "";
public string Description { get; set; } = "";
public DateTime CreatedUtc { get; set; }
}
@@ -122,6 +184,7 @@ public sealed class UserCreditAccount
public int QBankResetTokens { get; set; }
public int BankCycle { get; set; }
public bool InitialExamGrantApplied { get; set; }
public bool MasteryAwardBaselineApplied { get; set; }
public List<CreditEvent> RecentEarnings { get; set; } = [];
public List<CreditPurchaseEvent> Purchases { get; set; } = [];
public List<SpinEvent> Spins { get; set; } = [];
@@ -142,6 +205,15 @@ public sealed class CreditStatusResponse
public IReadOnlyList<RzStoreProduct> Products { get; set; } = CreditEconomy.Products;
}
public sealed record CreditAwardRequest(
string OperationId,
int Amount,
string ReasonCode,
string Description,
string SourceType,
string SourceId,
bool SubjectToRollingCap = true);
public sealed class CreditPurchaseRequest
{
public string Sku { get; set; } = "";
@@ -117,6 +117,8 @@ public class ExamSimulationStateResponse
public Dictionary<string, int> BlueprintTargets { get; set; } = [];
public Dictionary<string, int> BlueprintActual { get; set; } = [];
public List<QuestionResponse> ReviewQuestions { get; set; } = [];
public CreditStatusResponse? Credits { get; set; }
public List<CreditAward> CreditAwards { get; set; } = [];
}
public class ExamSimulationReadinessResponse
@@ -146,6 +146,7 @@ public class FirstQuestionAttemptResponse
public int QuestionElo { get; set; } = 1000;
public RatingChangeResponse? Rating { get; set; }
public CreditStatusResponse? Credits { get; set; }
public List<CreditAward> CreditAwards { get; set; } = [];
}
public class SkipQuestionResponse
@@ -81,6 +81,8 @@ public sealed class ReviewQueueAttemptResponse
public bool IsCorrect { get; set; }
public string QueueState { get; set; } = ReviewQueueStates.Pending;
public int PendingCount { get; set; }
public CreditStatusResponse? Credits { get; set; }
public List<CreditAward> CreditAwards { get; set; } = [];
}
public sealed class ReviewQueueRecordResult