server/laf-cloud/functions/signInTestAdmin.ts

181 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import cloud from '@lafjs/cloud'
import {
buildClaimId,
getActiveSignInConfig,
getChinaDateKey,
getChinaDayStart,
getSignInClaims,
normalizeSignInState,
} from "@/SignInActivity";
const db = cloud.database();
const USERS_COLLECTION = "users";
const CLAIM_COLLECTION = "sign_in_activity_claims";
const DAY_MS = 24 * 60 * 60 * 1000;
export default async function (ctx: FunctionContext) {
const adminToken = ctx.body?.adminToken || ctx.query?.adminToken;
if (!checkAdminToken(adminToken)) {
return { code: 0, data: null, msg: "测试接口未启用或管理密钥错误" };
}
const action = String(ctx.body?.action || ctx.query?.action || "inspect");
const uid = ctx.body?.uid || ctx.query?.uid;
if (!uid) {
return { code: 0, data: null, msg: "未获取到uid" };
}
const userResult = await db.collection(USERS_COLLECTION).where({ _id: uid }).getOne();
const user = userResult.data;
if (!user) {
return { code: 0, data: null, msg: "未获取到用户信息" };
}
switch (action) {
case "inspect":
return await inspectUser(user);
case "reset":
await clearClaims(uid);
await updateUser(uid, { signInActivity: null });
return await inspectUser({ ...user, signInActivity: null });
case "set_level":
return await setLevel(user, ctx.body?.levelAmount);
case "prepare":
return await prepareScenario(user, String(ctx.body?.scenario || ""));
default:
return { code: 0, data: null, msg: "无效action,请使用 inspect、reset、set_level 或 prepare" };
}
}
function checkAdminToken(adminToken: string) {
const env = (globalThis as any).process?.env || {};
const expected = String(env.SIGN_IN_TEST_ADMIN_TOKEN || "");
return !!expected && !!adminToken && adminToken === expected;
}
async function setLevel(user: any, rawLevel: any) {
const levelAmount = Number(rawLevel);
if (!Number.isInteger(levelAmount) || levelAmount < 0) {
return { code: 0, data: null, msg: "levelAmount必须是大于等于0的整数" };
}
await updateUser(user._id, { levelAmount });
return await inspectUser({ ...user, levelAmount });
}
async function prepareScenario(user: any, scenario: string) {
const config = await getActiveSignInConfig();
if (!config) {
return { code: 0, data: null, msg: "签到活动配置不存在或配置无效" };
}
const now = Date.now();
const triggerLevel = Number(config.triggerLevel);
if (scenario === "locked" || scenario === "eligible") {
const levelAmount = scenario === "locked" ? Math.max(0, triggerLevel - 1) : triggerLevel;
await clearClaims(user._id);
await updateUser(user._id, { levelAmount, signInActivity: null });
return await inspectUser({ ...user, levelAmount, signInActivity: null }, scenario);
}
const dayReadyMatch = /^day_([1-7])_ready$/.exec(scenario);
if (dayReadyMatch) {
const claimedCount = Number(dayReadyMatch[1]) - 1;
return await prepareActivity(user, config, now, scenario, claimedCount, false, false);
}
if (scenario === "claimed_today") {
return await prepareActivity(user, config, now, scenario, 1, true, false);
}
if (scenario === "expired") {
return await prepareActivity(user, config, now, scenario, 0, false, true);
}
if (scenario === "completed") {
return await prepareActivity(user, config, now, scenario, 7, false, false, true);
}
return {
code: 0,
data: null,
msg: "无效scenario,请使用 locked、eligible、day_1_ready至day_7_ready、claimed_today、expired或completed",
};
}
async function prepareActivity(
user: any,
config: any,
now: number,
scenario: string,
claimedCount: number,
todayClaimed: boolean,
expired: boolean,
completed = false,
) {
await clearClaims(user._id);
const todayStart = getChinaDayStart(now);
const startAt = expired
? todayStart - Number(config.durationDays) * DAY_MS
: todayStart - Math.min(claimedCount, 7) * DAY_MS;
const endAt = expired
? todayStart
: startAt + Number(config.durationDays) * DAY_MS;
const signInActivity = {
activityId: config.activityId,
startAt,
endAt,
activatedAt: startAt,
completedAt: completed ? now : null,
};
await updateUser(user._id, {
levelAmount: Number(config.triggerLevel),
signInActivity: JSON.stringify(signInActivity),
});
for (let index = 0; index < claimedCount; index++) {
const rewardDay = index + 1;
const daysAgo = todayClaimed && index === claimedCount - 1
? 0
: claimedCount - index;
const claimedAt = now - daysAgo * DAY_MS;
const dateKey = getChinaDateKey(claimedAt);
const claimId = buildClaimId(user._id, config.activityId, dateKey);
await db.collection(CLAIM_COLLECTION).add({
_id: claimId,
claimId,
uid: user._id,
activityId: config.activityId,
dateKey,
rewardDay,
rewards: config.rewards[index].items,
claimedAt,
testPrepared: true,
});
}
return await inspectUser({
...user,
levelAmount: Number(config.triggerLevel),
signInActivity,
}, scenario);
}
async function inspectUser(user: any, scenario?: string) {
const state = normalizeSignInState(user.signInActivity);
const claims = state ? await getSignInClaims(user._id, state.activityId) : [];
return {
code: 1,
data: {
scenario: scenario || null,
uid: user._id,
levelAmount: Number(user.levelAmount) || 0,
signInActivity: state,
claimedCount: claims.length,
claims,
},
msg: "测试状态准备成功",
};
}
async function clearClaims(uid: string) {
await db.collection(CLAIM_COLLECTION).where({ uid }).remove({ multi: true });
}
async function updateUser(uid: string, data: any) {
await db.collection(USERS_COLLECTION).where({ _id: uid }).update(data);
}