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); }