refactor(mission): migrate MissionProgressService + MissionAssembler to IGameCalendarService

Swaps 6 JstPeriod.* callsites in mission code to IGameCalendarService.
Also drops the manual `jstNow = now.ToOffset(+9).AddHours(-2)` shift in
MissionAssembler — BP monthly missions now key off UTC calendar month, and
the start/end wire timestamps become UTC-midnight month boundaries. This
matches the plan's all-UTC discipline; wire fidelity for BP monthlies
is not shipped yet.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-04 11:51:27 -04:00
parent 70ec063c0f
commit 2b0cd516f0
2 changed files with 17 additions and 12 deletions

View File

@@ -9,15 +9,18 @@ public sealed class MissionAssembler : IMissionAssembler
private readonly IMissionCatalogRepository _catalog;
private readonly IViewerMissionRepository _viewerRepo;
private readonly TimeProvider _time;
private readonly IGameCalendarService _calendar;
public MissionAssembler(
IMissionCatalogRepository catalog,
IViewerMissionRepository viewerRepo,
TimeProvider time)
TimeProvider time,
IGameCalendarService calendar)
{
_catalog = catalog;
_viewerRepo = viewerRepo;
_time = time;
_calendar = calendar;
}
public async Task<MissionInfoDataDto> BuildAsync(Viewer viewer, CancellationToken ct = default)
@@ -54,15 +57,14 @@ public sealed class MissionAssembler : IMissionAssembler
if (c.EventType is not null) counterEventKeys.Add(c.EventType);
}
// BP monthly missions for current JST month.
var jstNow = now.ToOffset(TimeSpan.FromHours(9)).AddHours(-2);
var monthlyMissions = await _catalog.GetMonthlyMissionsAsync(jstNow.Year, jstNow.Month, ct);
// BP monthly missions for the current calendar month (UTC).
var monthlyMissions = await _catalog.GetMonthlyMissionsAsync(now.Year, now.Month, ct);
foreach (var mm in monthlyMissions)
{
if (mm.EventType is not null) counterEventKeys.Add(mm.EventType);
}
var periods = new[] { JstPeriod.DayKey(now), JstPeriod.WeekKey(now), JstPeriod.MonthKey(now), JstPeriod.AllTime };
var periods = _calendar.AllPeriods(now);
var counters = counterEventKeys.Count == 0
? new List<ViewerEventCounter>()
: await _viewerRepo.GetCountersAsync(viewer.Id, counterEventKeys.ToList(), periods, ct);
@@ -78,7 +80,7 @@ public sealed class MissionAssembler : IMissionAssembler
int total = 0;
if (cat.EventType is not null)
{
string period = cat.LotType == 6 ? JstPeriod.DayKey(now) : JstPeriod.WeekKey(now);
string period = cat.LotType == 6 ? _calendar.DayKey(now) : _calendar.WeekKey(now);
total = GetCounter(cat.EventType, period);
}
dto.UserMissionList.Add(new UserMissionDto
@@ -105,7 +107,7 @@ public sealed class MissionAssembler : IMissionAssembler
foreach (var a in viewerAchievements.OrderBy(a => a.AchievementType))
{
if (!achievementCatalogByKey.TryGetValue((a.AchievementType, a.Level), out var catalog)) continue;
int total = catalog.EventType is null ? 0 : GetCounter(catalog.EventType, JstPeriod.AllTime);
int total = catalog.EventType is null ? 0 : GetCounter(catalog.EventType, GameCalendarService.AllTime);
int maxLevel = maxLevelByType.TryGetValue(a.AchievementType, out var ml) ? ml : a.Level;
dto.UserAchievementList.Add(new UserAchievementDto
{
@@ -142,7 +144,7 @@ public sealed class MissionAssembler : IMissionAssembler
// BP monthly missions block — omit when no rows for current month.
if (monthlyMissions.Count > 0)
{
var startUtc = new DateTimeOffset(jstNow.Year, jstNow.Month, 1, 2, 0, 0, TimeSpan.FromHours(9));
var startUtc = new DateTimeOffset(now.Year, now.Month, 1, 0, 0, 0, TimeSpan.Zero);
var endUtc = startUtc.AddMonths(1).AddSeconds(-1);
dto.BattlePassMonthlyMission = new BPMonthlyMissionsDto
{
@@ -151,7 +153,7 @@ public sealed class MissionAssembler : IMissionAssembler
MissionList = monthlyMissions.Select(mm =>
{
int done = mm.EventType is null ? 0
: GetCounter(mm.EventType, JstPeriod.MonthKey(now));
: GetCounter(mm.EventType, _calendar.MonthKey(now));
var entry = new BPMonthlyMissionDto
{
Name = mm.Name,

View File

@@ -9,24 +9,27 @@ public sealed class MissionProgressService : IMissionProgressService
private readonly IMissionCatalogRepository _catalog;
private readonly IViewerMissionRepository _viewerRepo;
private readonly TimeProvider _time;
private readonly IGameCalendarService _calendar;
public MissionProgressService(
SVSimDbContext db,
IMissionCatalogRepository catalog,
IViewerMissionRepository viewerRepo,
TimeProvider time)
TimeProvider time,
IGameCalendarService calendar)
{
_db = db;
_catalog = catalog;
_viewerRepo = viewerRepo;
_time = time;
_calendar = calendar;
}
public async Task RecordEventAsync(long viewerId, IReadOnlyList<string> eventKeys, int delta = 1, CancellationToken ct = default)
{
if (eventKeys.Count == 0) return;
var now = _time.GetUtcNow();
var periods = JstPeriod.AllPeriods(now);
var periods = _calendar.AllPeriods(now);
// 1. Increment counters for every (key, period).
foreach (var key in eventKeys)
@@ -50,7 +53,7 @@ public sealed class MissionProgressService : IMissionProgressService
var atLevel = catalogRows.FirstOrDefault(r => r.Level == viewerRow.Level);
if (atLevel is null || atLevel.EventType is null) continue;
var count = await _viewerRepo.GetCounterAsync(viewerId, atLevel.EventType, JstPeriod.AllTime, ct);
var count = await _viewerRepo.GetCounterAsync(viewerId, atLevel.EventType, GameCalendarService.AllTime, ct);
if (count >= atLevel.RequireNumber && viewerRow.AchievementStatus == 0)
{
viewerRow.AchievementStatus = 1;