140 lines
4.4 KiB
TypeScript
140 lines
4.4 KiB
TypeScript
import cloud from "@lafjs/cloud";
|
||
import Utils from "@/Utils";
|
||
import * as JungleConfigModule from "@/jungleConfig";
|
||
|
||
// Laf 会为共享云函数生成 index 类型声明,该声明可能滞后于实际命名导出。
|
||
const JungleConfig: any = JungleConfigModule as any;
|
||
|
||
const db = cloud.database();
|
||
const USER_STATE_FIELD = "jungleTreasureState";
|
||
|
||
/**
|
||
* action=read:返回 51 个档位状态及完整配置。
|
||
* action=claim:将指定档位从 1 改为 2,不检查前置档位。
|
||
*/
|
||
export default async function jungleTreasure(ctx: FunctionContext) {
|
||
const body = ctx.body || {};
|
||
const action = String(body.action || "").toLowerCase();
|
||
const uid = body.uid;
|
||
const dbname = body.gameName === "iaa" ? "usersAd" : "users";
|
||
|
||
if (action !== "read" && action !== "claim") {
|
||
return fail("无效的操作类型,请使用 read 或 claim");
|
||
}
|
||
if (!uid) {
|
||
return fail("未获取到uid");
|
||
}
|
||
|
||
const userResult = await db.collection(dbname).where({ _id: uid }).getOne();
|
||
const user = userResult.data;
|
||
if (!user) {
|
||
return fail("未获取到玩家信息");
|
||
}
|
||
if (user.token && !Utils.checkToken(body.token, user.token)) {
|
||
return fail("token校验失败");
|
||
}
|
||
|
||
let storedStateValue: unknown = user[USER_STATE_FIELD];
|
||
let serverState = JungleConfig.normalizeStoredJungleState(storedStateValue);
|
||
// 兼容旧对象/数组格式:玩家下次读取时自动改存为 JSON 字符串。
|
||
if (!JungleConfig.isCurrentJungleState(storedStateValue) || typeof storedStateValue !== "string") {
|
||
storedStateValue = JungleConfig.serializeJungleState(serverState);
|
||
const initialized = await db.collection(dbname).where({ _id: uid }).update({
|
||
[USER_STATE_FIELD]: storedStateValue,
|
||
});
|
||
if (isUpdateFailed(initialized)) {
|
||
return fail("礼包状态初始化失败");
|
||
}
|
||
}
|
||
|
||
if (action === "read") {
|
||
return readSuccess(serverState);
|
||
}
|
||
|
||
const claimResult = JungleConfig.claimJungleTier(serverState, body.tierId);
|
||
if (!claimResult.ok || !claimResult.state || !claimResult.tierId) {
|
||
return fail(claimResult.message || "礼包领取失败");
|
||
}
|
||
|
||
// 字符串不能按数组下标查询;比较完整旧值,避免并发请求覆盖彼此或重复领取。
|
||
const updateResult = await db.collection(dbname).where({
|
||
_id: uid,
|
||
[USER_STATE_FIELD]: storedStateValue,
|
||
}).update({
|
||
[USER_STATE_FIELD]: JungleConfig.serializeJungleState(claimResult.state),
|
||
});
|
||
if (isUpdateFailed(updateResult)) {
|
||
return fail("礼包状态已变化,请重新读取后再领取");
|
||
}
|
||
|
||
// 付费档领取完成后,将支付订单从待发货改为已发货,避免登录补单重复处理。
|
||
if (claimResult.paidOrderNo) {
|
||
await db.collection("order").where({
|
||
outTradeNo: claimResult.paidOrderNo,
|
||
openid: user.openid,
|
||
state: 1,
|
||
}).update({
|
||
state: 2,
|
||
getTime: new Date(Date.now() + 8 * 3600000),
|
||
});
|
||
}
|
||
|
||
// 按需求重新读取并返回服务器中的最新状态数组。
|
||
const latestUserResult = await db.collection(dbname).where({ _id: uid }).getOne();
|
||
if (!latestUserResult.data) {
|
||
return fail("礼包领取后读取状态失败");
|
||
}
|
||
serverState = JungleConfig.normalizeStoredJungleState(latestUserResult.data[USER_STATE_FIELD]);
|
||
return claimSuccess(claimResult.tierId, serverState);
|
||
}
|
||
|
||
function readSuccess(state: any) {
|
||
const serverNow = Date.now();
|
||
// Laf 共享函数更新后可能暂时使用旧的 JungleStoredState 类型缓存。
|
||
// 运行时仍由新版 jungleConfig 保证 expiresAt 已存在。
|
||
const stateWithExpiry: any = state;
|
||
const expiresAt = stateWithExpiry.expiresAt;
|
||
return {
|
||
code: 1,
|
||
data: {
|
||
tierStates: state.tierStates,
|
||
revision: state.revision,
|
||
expiresAt,
|
||
serverNow,
|
||
updatedAt: state.updatedAt,
|
||
config: {
|
||
...JungleConfig.JUNGLE_TREASURE_CONFIG,
|
||
endAt: expiresAt,
|
||
},
|
||
},
|
||
msg: "读取成功",
|
||
};
|
||
}
|
||
|
||
function claimSuccess(
|
||
claimedTierId: number,
|
||
state: any,
|
||
) {
|
||
const stateWithExpiry: any = state;
|
||
return {
|
||
code: 1,
|
||
data: {
|
||
claimedTierId,
|
||
tierStates: state.tierStates,
|
||
revision: state.revision,
|
||
expiresAt: stateWithExpiry.expiresAt,
|
||
serverNow: Date.now(),
|
||
updatedAt: state.updatedAt,
|
||
},
|
||
msg: "领取成功",
|
||
};
|
||
}
|
||
|
||
function isUpdateFailed(result: any): boolean {
|
||
return Boolean(result && (result.ok === false || result.updated === 0));
|
||
}
|
||
|
||
function fail(msg: string) {
|
||
return { code: 0, data: null, msg };
|
||
}
|