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
@@ -20,6 +20,7 @@ public class QuestionsController : ControllerBase
private readonly QuestionBookmarkStore _bookmarks;
private readonly ReviewQueueStore _reviewQueue;
private readonly CreditStore _credits;
private readonly MasteryAwardService _masteryAwards;
public QuestionsController(
QuestionStore questions,
@@ -30,7 +31,8 @@ public class QuestionsController : ControllerBase
ServiceMetrics metrics,
QuestionBookmarkStore bookmarks,
ReviewQueueStore reviewQueue,
CreditStore credits)
CreditStore credits,
MasteryAwardService masteryAwards)
{
_questions = questions;
_attempts = attempts;
@@ -41,6 +43,7 @@ public class QuestionsController : ControllerBase
_bookmarks = bookmarks;
_reviewQueue = reviewQueue;
_credits = credits;
_masteryAwards = masteryAwards;
}
[HttpGet("systems")]
@@ -152,6 +155,8 @@ public class QuestionsController : ControllerBase
if (string.IsNullOrWhiteSpace(userId))
return Unauthorized();
await _masteryAwards.PrepareAsync(userId);
var record = await _attempts.RecordFirstAttemptAsync(
question.ExternalId,
userId,
@@ -178,8 +183,18 @@ public class QuestionsController : ControllerBase
_metrics.AttemptDuplicate();
}
await _reviewQueue.AddFromFirstAttemptAsync(record.Attempt);
if (record.Inserted && record.Attempt.IsCorrect && req.MetTimeLimit)
await _credits.AwardForFirstAnswerAsync(userId, record.Attempt.Id ?? "");
var creditAwards = new List<CreditAward>();
if (record.Inserted && record.Attempt.IsCorrect)
{
var amount = CreditEconomy.FirstCorrectAmount(question.Difficulty);
var difficult = question.Difficulty >= 4;
var award = await _credits.AwardAsync(userId, new CreditAwardRequest(
$"answer:{record.Attempt.Id}", amount,
difficult ? CreditReasonCodes.DifficultCorrect : CreditReasonCodes.FirstCorrect,
difficult ? $"Difficulty {question.Difficulty} first correct answer" : "First correct answer",
"question_attempt", record.Attempt.Id ?? ""));
if (award.Award is not null) creditAwards.Add(award.Award);
}
var ratingOwner = Guid.NewGuid().ToString("N");
var shouldRate = await _attempts.TryAcquireRatingLeaseAsync(
@@ -205,6 +220,7 @@ public class QuestionsController : ControllerBase
throw;
}
creditAwards.AddRange(await _masteryAwards.AwardPromotionsAsync(userId));
return Ok(new FirstQuestionAttemptResponse
{
QuestionExternalId = question.ExternalId,
@@ -213,7 +229,8 @@ public class QuestionsController : ControllerBase
await _statistics.GetPercentagesForQuestionAsync(question.ExternalId)),
QuestionElo = rating.QuestionElo,
Rating = rating,
Credits = await _credits.GetStatusAsync(userId)
Credits = await _credits.GetStatusAsync(userId),
CreditAwards = creditAwards
});
}