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:
@@ -9,15 +9,18 @@ public sealed class MissionAssembler : IMissionAssembler
|
|||||||
private readonly IMissionCatalogRepository _catalog;
|
private readonly IMissionCatalogRepository _catalog;
|
||||||
private readonly IViewerMissionRepository _viewerRepo;
|
private readonly IViewerMissionRepository _viewerRepo;
|
||||||
private readonly TimeProvider _time;
|
private readonly TimeProvider _time;
|
||||||
|
private readonly IGameCalendarService _calendar;
|
||||||
|
|
||||||
public MissionAssembler(
|
public MissionAssembler(
|
||||||
IMissionCatalogRepository catalog,
|
IMissionCatalogRepository catalog,
|
||||||
IViewerMissionRepository viewerRepo,
|
IViewerMissionRepository viewerRepo,
|
||||||
TimeProvider time)
|
TimeProvider time,
|
||||||
|
IGameCalendarService calendar)
|
||||||
{
|
{
|
||||||
_catalog = catalog;
|
_catalog = catalog;
|
||||||
_viewerRepo = viewerRepo;
|
_viewerRepo = viewerRepo;
|
||||||
_time = time;
|
_time = time;
|
||||||
|
_calendar = calendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MissionInfoDataDto> BuildAsync(Viewer viewer, CancellationToken ct = default)
|
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);
|
if (c.EventType is not null) counterEventKeys.Add(c.EventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// BP monthly missions for current JST month.
|
// BP monthly missions for the current calendar month (UTC).
|
||||||
var jstNow = now.ToOffset(TimeSpan.FromHours(9)).AddHours(-2);
|
var monthlyMissions = await _catalog.GetMonthlyMissionsAsync(now.Year, now.Month, ct);
|
||||||
var monthlyMissions = await _catalog.GetMonthlyMissionsAsync(jstNow.Year, jstNow.Month, ct);
|
|
||||||
foreach (var mm in monthlyMissions)
|
foreach (var mm in monthlyMissions)
|
||||||
{
|
{
|
||||||
if (mm.EventType is not null) counterEventKeys.Add(mm.EventType);
|
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
|
var counters = counterEventKeys.Count == 0
|
||||||
? new List<ViewerEventCounter>()
|
? new List<ViewerEventCounter>()
|
||||||
: await _viewerRepo.GetCountersAsync(viewer.Id, counterEventKeys.ToList(), periods, ct);
|
: await _viewerRepo.GetCountersAsync(viewer.Id, counterEventKeys.ToList(), periods, ct);
|
||||||
@@ -78,7 +80,7 @@ public sealed class MissionAssembler : IMissionAssembler
|
|||||||
int total = 0;
|
int total = 0;
|
||||||
if (cat.EventType is not null)
|
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);
|
total = GetCounter(cat.EventType, period);
|
||||||
}
|
}
|
||||||
dto.UserMissionList.Add(new UserMissionDto
|
dto.UserMissionList.Add(new UserMissionDto
|
||||||
@@ -105,7 +107,7 @@ public sealed class MissionAssembler : IMissionAssembler
|
|||||||
foreach (var a in viewerAchievements.OrderBy(a => a.AchievementType))
|
foreach (var a in viewerAchievements.OrderBy(a => a.AchievementType))
|
||||||
{
|
{
|
||||||
if (!achievementCatalogByKey.TryGetValue((a.AchievementType, a.Level), out var catalog)) continue;
|
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;
|
int maxLevel = maxLevelByType.TryGetValue(a.AchievementType, out var ml) ? ml : a.Level;
|
||||||
dto.UserAchievementList.Add(new UserAchievementDto
|
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.
|
// BP monthly missions block — omit when no rows for current month.
|
||||||
if (monthlyMissions.Count > 0)
|
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);
|
var endUtc = startUtc.AddMonths(1).AddSeconds(-1);
|
||||||
dto.BattlePassMonthlyMission = new BPMonthlyMissionsDto
|
dto.BattlePassMonthlyMission = new BPMonthlyMissionsDto
|
||||||
{
|
{
|
||||||
@@ -151,7 +153,7 @@ public sealed class MissionAssembler : IMissionAssembler
|
|||||||
MissionList = monthlyMissions.Select(mm =>
|
MissionList = monthlyMissions.Select(mm =>
|
||||||
{
|
{
|
||||||
int done = mm.EventType is null ? 0
|
int done = mm.EventType is null ? 0
|
||||||
: GetCounter(mm.EventType, JstPeriod.MonthKey(now));
|
: GetCounter(mm.EventType, _calendar.MonthKey(now));
|
||||||
var entry = new BPMonthlyMissionDto
|
var entry = new BPMonthlyMissionDto
|
||||||
{
|
{
|
||||||
Name = mm.Name,
|
Name = mm.Name,
|
||||||
|
|||||||
@@ -9,24 +9,27 @@ public sealed class MissionProgressService : IMissionProgressService
|
|||||||
private readonly IMissionCatalogRepository _catalog;
|
private readonly IMissionCatalogRepository _catalog;
|
||||||
private readonly IViewerMissionRepository _viewerRepo;
|
private readonly IViewerMissionRepository _viewerRepo;
|
||||||
private readonly TimeProvider _time;
|
private readonly TimeProvider _time;
|
||||||
|
private readonly IGameCalendarService _calendar;
|
||||||
|
|
||||||
public MissionProgressService(
|
public MissionProgressService(
|
||||||
SVSimDbContext db,
|
SVSimDbContext db,
|
||||||
IMissionCatalogRepository catalog,
|
IMissionCatalogRepository catalog,
|
||||||
IViewerMissionRepository viewerRepo,
|
IViewerMissionRepository viewerRepo,
|
||||||
TimeProvider time)
|
TimeProvider time,
|
||||||
|
IGameCalendarService calendar)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_catalog = catalog;
|
_catalog = catalog;
|
||||||
_viewerRepo = viewerRepo;
|
_viewerRepo = viewerRepo;
|
||||||
_time = time;
|
_time = time;
|
||||||
|
_calendar = calendar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RecordEventAsync(long viewerId, IReadOnlyList<string> eventKeys, int delta = 1, CancellationToken ct = default)
|
public async Task RecordEventAsync(long viewerId, IReadOnlyList<string> eventKeys, int delta = 1, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
if (eventKeys.Count == 0) return;
|
if (eventKeys.Count == 0) return;
|
||||||
var now = _time.GetUtcNow();
|
var now = _time.GetUtcNow();
|
||||||
var periods = JstPeriod.AllPeriods(now);
|
var periods = _calendar.AllPeriods(now);
|
||||||
|
|
||||||
// 1. Increment counters for every (key, period).
|
// 1. Increment counters for every (key, period).
|
||||||
foreach (var key in eventKeys)
|
foreach (var key in eventKeys)
|
||||||
@@ -50,7 +53,7 @@ public sealed class MissionProgressService : IMissionProgressService
|
|||||||
var atLevel = catalogRows.FirstOrDefault(r => r.Level == viewerRow.Level);
|
var atLevel = catalogRows.FirstOrDefault(r => r.Level == viewerRow.Level);
|
||||||
if (atLevel is null || atLevel.EventType is null) continue;
|
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)
|
if (count >= atLevel.RequireNumber && viewerRow.AchievementStatus == 0)
|
||||||
{
|
{
|
||||||
viewerRow.AchievementStatus = 1;
|
viewerRow.AchievementStatus = 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user