import cloud from "@lafjs/cloud"; import { compareVersions } from "@/versionPolicy"; import { refreshTaskData } from "@/dailyTaskService"; export const DAILY_TASK_MIN_VERSION = "1.0.88"; const OFFSET = 8 * 3600000; const DAY = 86400000; export const LEGACY_TARGETS = { levelPass: 3, share: 1, useEnergy: 10, useProp: 3 }; export function supportsDailyTaskV2(version: unknown): boolean { const compared = compareVersions(version, DAILY_TASK_MIN_VERSION); return compared !== null && compared >= 0; } export function parseTask(stored: any): any { if (typeof stored !== "string") return stored; try { return JSON.parse(stored); } catch { return null; } } export function isV2Task(stored: any): boolean { return Number(parseTask(stored)?.version) >= 2; } export function taskVersionError() { return { code: 0, errorCode: "DAILY_TASK_VERSION_REQUIRED", data: null, msg: "每日任务已升级,请更新客户端后重新进入", minVersion: DAILY_TASK_MIN_VERSION }; } export function taskConflict() { return { code: 0, data: null, msg: "任务数据已更新,请重新读取后重试" }; } /** 旧 taskTime 使用 UTC 时间戳加 8 小时,保留该约定兼容已发布客户端。 */ export function refreshLegacyTask(stored: any, taskTime: any, now = Date.now()): any { const old = parseTask(stored); const reset = Math.floor((now + OFFSET) / DAY) * DAY > (Number(taskTime) || 0); const task: any = {}; Object.keys(LEGACY_TARGETS).forEach(key => { const value = reset ? 0 : Math.max(0, Number(old?.[key]?.value) || 0); const claimed = !reset && old?.[key]?.state === 2; task[key] = { value, target: LEGACY_TARGETS[key], state: claimed ? 2 : value >= LEGACY_TARGETS[key] ? 1 : 0 }; }); return task; } /** 比较读取时的 task/taskTime,防止旧请求覆盖迁移结果,也防止重复领奖。 */ export async function writeTask(dbname: string, user: any, task: any, taskTime: number): Promise { const filter: any = { _id: user._id }; for (const key of ["task", "taskTime"]) { filter[key] = Object.prototype.hasOwnProperty.call(user, key) ? user[key] : { $exists: false }; } const result = await cloud.mongo.db.collection(dbname).updateOne(filter, { $set: { task: JSON.stringify(task), taskTime }, }); return result.matchedCount === 1; } /** 所有登录分支共用;仅从旧协议迁移到新协议,绝不反向覆盖。 */ export async function ensureLoginTasks(dbname: string, user: any, version: unknown) { const modern = supportsDailyTaskV2(version); for (let attempt = 0; attempt < 3; attempt++) { if (!modern && isV2Task(user.task)) return { error: taskVersionError() }; const now = Date.now(); const task = modern ? refreshTaskData(user.task, user._id, Number(user.levelAmount) + 1 || 1, now).data : refreshLegacyTask(user.task, user.taskTime, now); const taskTime = modern ? now : now + OFFSET; if (await writeTask(dbname, user, task, taskTime)) { user.task = JSON.stringify(task); user.taskTime = taskTime; return { user }; } const latest = await cloud.database().collection(dbname).where({ _id: user._id }).getOne(); if (!latest.data) return { error: taskConflict() }; user = latest.data; } return { error: taskConflict() }; }