Files
server/laf-cloud/functions/dailyTaskCompatibility.ts
T

78 lines
3.2 KiB
TypeScript

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<boolean> {
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() };
}