121 lines
5.0 KiB
TypeScript
121 lines
5.0 KiB
TypeScript
import { ApiCall } from "tsrpc";
|
|
import { ReqReceive, ResReceive } from '../../../shared/protocols/event/payForDiamond/PtlReceive';
|
|
import { playerCanReceive } from './ApiCanReceive';
|
|
import { PublicShared } from "../../../shared/public/public";
|
|
import { PlayerFun } from "../../../public/player";
|
|
|
|
type diamondWeightGroup = {
|
|
weight: number;
|
|
numrange: [ min: number, max: number];
|
|
};
|
|
|
|
const hasGotKeyPrefix = 'payForDiamond:hasGot:'; // 已领取额度
|
|
const hasGotLockKey = "payForDiamond:lock"; // 更新锁, TTL 1s
|
|
const showOffListKeyPrefix = 'payForDiamond:ShowOff:'; // 需要炫耀的玩家列表。 其中需要包含信息:玩家区服, 玩家玩家名称, 领到的数量
|
|
/**
|
|
* @param groups 各分组及其权重
|
|
*/
|
|
function randomWithWeight(groups: diamondWeightGroup[]) {
|
|
let maxAmount: number;
|
|
let totalWeights = 0;
|
|
for (const group of groups) {
|
|
totalWeights += group.weight;
|
|
if (!maxAmount) {
|
|
maxAmount = group.numrange[1];
|
|
} else {
|
|
maxAmount = Math.max(maxAmount, group.numrange[1]);
|
|
}
|
|
}
|
|
const randomValue = Math.random() * totalWeights; // 随机值落在[0, totalWeights) 之间
|
|
let currSum = 0;
|
|
for (const group of groups) {
|
|
if (currSum <= randomValue && randomValue < currSum + group.weight) {
|
|
return { group, maxAmount };
|
|
} else {
|
|
currSum += group.weight;
|
|
}
|
|
}
|
|
const length = groups.length;
|
|
return { group: groups[length - 1], maxAmount };
|
|
}
|
|
|
|
/**
|
|
* 计算玩家分得的钻石数量
|
|
* @param remaining
|
|
* @param group
|
|
* @param maxAmount
|
|
*/
|
|
function calcDiamondGot(remaining: number, group: diamondWeightGroup, maxAmount: number) {
|
|
const [min, max] = group.numrange;
|
|
const randomAmount = Math.floor(Math.random() * (max - min)) + min + 1; // max 值应能够取到, 故 +1
|
|
// 剩余数额小于组内随机得到的值, 全给吧
|
|
if (randomAmount > remaining) {
|
|
return remaining;
|
|
}
|
|
// 随机值大于最大值, 取最大值
|
|
if (randomAmount > maxAmount) {
|
|
return maxAmount;
|
|
}
|
|
return randomAmount;
|
|
}
|
|
|
|
export default async function (call: ApiCall<ReqReceive, ResReceive>) {
|
|
const canReceiveResult = await playerCanReceive(call);
|
|
if (canReceiveResult) { // 该值不存在的情况已被函数直接返回 error, 无需再处理
|
|
if (!canReceiveResult.result) {
|
|
return call.succ({
|
|
amount: 0,
|
|
timesRemaining: 0,
|
|
showOff: false
|
|
});
|
|
}
|
|
const activityData = canReceiveResult.activityInfo.data;
|
|
// 更新 redis 领取记录之前先加锁, 防止多领
|
|
const lockResult = await G.crossioredis.setnx(hasGotLockKey, 1);
|
|
if (lockResult) {
|
|
await G.crossioredis.expire(hasGotLockKey, 1); // 设置 ttl 避免死锁
|
|
const activityId = call.req.activityId;
|
|
const hasReceivedKey = hasGotKeyPrefix + activityId;
|
|
const hasReceivedStr = await G.crossioredis.get(hasReceivedKey);
|
|
const hasReceived = hasReceivedStr? parseInt(hasReceivedStr) : 0;
|
|
const remaining = activityData['totalmoney'] - hasReceived;
|
|
if (remaining <= 0) {
|
|
return call.succ({
|
|
amount: 0
|
|
});
|
|
} else {
|
|
const { group, maxAmount } = randomWithWeight(activityData['groupConf']['base']['arr']);
|
|
const gotAmount = calcDiamondGot(remaining, group, maxAmount);
|
|
await G.crossioredis.incrby(hasReceivedKey, Math.abs(gotAmount)); // 添加已领取的额度
|
|
await G.crossioredis.del(hasGotLockKey); // 移除锁
|
|
await PlayerFun.sendPrize(call, [{ 'a': 'attr', 't': 'rmbmoney', 'n': gotAmount }]);
|
|
const showOff = gotAmount >= activityData['groupConf']['base']['loglimit'];
|
|
call.succ({ // 领取核心逻辑完成, 请求可以返回了
|
|
amount: gotAmount,
|
|
timesRemaining: 0,
|
|
showOff,
|
|
});
|
|
// 添加玩家领取记录
|
|
const zeroTime = PublicShared.getToDayZeroTime();
|
|
const setObj = {};
|
|
setObj[zeroTime] = gotAmount;
|
|
await G.mongodb.cEvent('payForDiamond').updateOne({ uid: call.uid, type: 'payForDiamond' }, {
|
|
$set: setObj
|
|
}, {upsert: true});
|
|
// 炫耀
|
|
if (showOff) {
|
|
const msg = JSON.stringify({
|
|
name: call.conn.gud.name, gotAmount, serverID: call.conn.gud.sid
|
|
});
|
|
const showOffListKey = showOffListKeyPrefix + activityId;
|
|
await G.crossioredis.lpush(showOffListKey, msg);
|
|
await G.crossioredis.ltrim(showOffListKey, 0, 49); // 限制列表保存 50 条消息, 避免无限增长
|
|
}
|
|
}
|
|
} else {
|
|
return call.succ({
|
|
amount: 0
|
|
});
|
|
}
|
|
}
|
|
} |