From 04ed8d700fe3bd2f584f9756140ca7b269ef38e5 Mon Sep 17 00:00:00 2001 From: huanghaipeng Date: Mon, 7 Sep 2026 14:46:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(daily-task):=20=E8=BF=94=E5=9B=9E=E5=91=A8?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E5=80=92=E8=AE=A1=E6=97=B6=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- laf-cloud/functions/dailyTaskConfig.ts | 3 ++- laf-cloud/functions/dailyTaskService.ts | 14 ++++++++++++-- laf-cloud/tests/daily-task-v2.test.mjs | 14 ++++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/laf-cloud/functions/dailyTaskConfig.ts b/laf-cloud/functions/dailyTaskConfig.ts index 5a50dbc..1edd9c6 100644 --- a/laf-cloud/functions/dailyTaskConfig.ts +++ b/laf-cloud/functions/dailyTaskConfig.ts @@ -30,6 +30,8 @@ export type DailyWeeklyTaskData = { version: 2; dayKey: string; weekKey: string; + weekEndsAt: number; + serverNow: number; dailyTasks: DailyTask[]; weeklyStars: number; weeklyRewards: WeeklyReward[]; @@ -128,4 +130,3 @@ export const WEEKLY_REWARDS: WeeklyReward[] = [ { rewardId: "weekly_105", target: 105, state: 0, rewards: [{ type: "infinite_health", count: 3600 }, { type: "hammer", count: 1 }] }, { rewardId: "weekly_140", target: 140, state: 0, rewards: [{ type: "infinite_rocket", count: 900 }, { type: "infinite_hourglass", count: 900 }, { type: "coin", count: 1500 }] }, ]; - diff --git a/laf-cloud/functions/dailyTaskService.ts b/laf-cloud/functions/dailyTaskService.ts index 50577c5..230f858 100644 --- a/laf-cloud/functions/dailyTaskService.ts +++ b/laf-cloud/functions/dailyTaskService.ts @@ -18,6 +18,11 @@ export function getWeekKey(nowMs = Date.now()): string { return new Date(shifted.getTime() - (day - 1) * DAY_MS).toISOString().slice(0, 10); } +export function getWeekEndsAt(nowMs = Date.now()): number { + const weekKey = getWeekKey(nowMs); + return Date.parse(`${weekKey}T00:00:00+08:00`) + 7 * DAY_MS; +} + function hash(input: string): number { let value = 2166136261; for (let i = 0; i < input.length; i++) { @@ -56,6 +61,7 @@ export function refreshTaskData( ): { data: DailyWeeklyTaskData; changed: boolean } { const dayKey = getDayKey(nowMs); const weekKey = getWeekKey(nowMs); + const weekEndsAt = getWeekEndsAt(nowMs); const parsed = typeof stored === "string" ? safeParse(stored) : stored; const valid = parsed && (parsed as DailyWeeklyTaskData).version === 2 && Array.isArray((parsed as DailyWeeklyTaskData).dailyTasks) @@ -66,9 +72,12 @@ export function refreshTaskData( const newDay = !old || old.dayKey !== dayKey; if (!newDay && !newWeek) { + const changed = old.weekEndsAt !== weekEndsAt; + old.weekEndsAt = weekEndsAt; + old.serverNow = nowMs; applyLocks(old.dailyTasks); applyWeeklyStates(old); - return { data: old, changed: false }; + return { data: old, changed }; } const previousTaskIds = old ? old.dailyTasks.map(task => task.taskId) : []; @@ -76,6 +85,8 @@ export function refreshTaskData( version: 2, dayKey, weekKey, + weekEndsAt, + serverNow: nowMs, dailyTasks: selectDailyTasks(uid, dayKey, Math.max(1, level), previousTaskIds), weeklyStars: newWeek ? 0 : Number(old?.weeklyStars) || 0, weeklyRewards: newWeek @@ -141,4 +152,3 @@ function applyWeeklyStates(data: DailyWeeklyTaskData): void { function safeParse(value: string): unknown { try { return JSON.parse(value); } catch (_) { return null; } } - diff --git a/laf-cloud/tests/daily-task-v2.test.mjs b/laf-cloud/tests/daily-task-v2.test.mjs index 023e954..cd8974b 100644 --- a/laf-cloud/tests/daily-task-v2.test.mjs +++ b/laf-cloud/tests/daily-task-v2.test.mjs @@ -12,12 +12,23 @@ registerHooks({ }, }); -const { getDayKey, getWeekKey, refreshTaskData, mergeClientProgress, claimDaily, claimWeekly } = await import(serviceUrl); +const { getDayKey, getWeekKey, getWeekEndsAt, refreshTaskData, mergeClientProgress, claimDaily, claimWeekly } = await import(serviceUrl); test("uses Shanghai natural day and Monday week keys", () => { assert.equal(getDayKey(Date.parse("2026-09-06T15:59:59Z")), "2026-09-06"); assert.equal(getDayKey(Date.parse("2026-09-06T16:00:00Z")), "2026-09-07"); assert.equal(getWeekKey(Date.parse("2026-09-06T16:00:00Z")), "2026-09-07"); + assert.equal(getWeekEndsAt(Date.parse("2026-09-06T16:00:00Z")), Date.parse("2026-09-14T00:00:00+08:00")); +}); + +test("returns current server time and refreshes it on repeated reads", () => { + const firstNow = Date.parse("2026-09-07T04:00:00Z"); + const secondNow = firstNow + 5000; + const first = refreshTaskData(null, "user-time", 20, firstNow).data; + const second = refreshTaskData(JSON.stringify(first), "user-time", 20, secondNow); + assert.equal(second.changed, false); + assert.equal(second.data.serverNow, secondNow); + assert.equal(second.data.weekEndsAt, Date.parse("2026-09-14T00:00:00+08:00")); }); test("migrates legacy tasks into five v2 stages totaling twenty stars", () => { @@ -77,4 +88,3 @@ test("daily reset preserves stars while weekly reset clears them", () => { assert.equal(monday.weeklyStars, 0); assert.ok(monday.weeklyRewards.every(reward => reward.state === 0)); }); -