Merge remote-tracking branch 'origin/feature/pay_for_diamond' into release
# Conflicts: # src/shared/protocols/serviceProto.ts
This commit is contained in:
commit
466d02221d
69
src/api_s2c/event/payForDiamond/ApiCanReceive.ts
Normal file
69
src/api_s2c/event/payForDiamond/ApiCanReceive.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { ApiCall } from "tsrpc";
|
||||||
|
import { HuoDongFun } from "../../../public/huodongfun";
|
||||||
|
import { ReqCanReceive, ResCanReceive } from '../../../shared/protocols/event/payForDiamond/PtlCanReceive';
|
||||||
|
import { PublicShared } from "../../../shared/public/public";
|
||||||
|
import { ActionLog } from "../../../public/actionLog/actionLog";
|
||||||
|
import { WithId, OptionalId } from "mongodb";
|
||||||
|
import { ReqAddHuoDong } from "../../../monopoly/protocols/PtlAddHuoDong";
|
||||||
|
|
||||||
|
const hasGotKeyPrefix = 'payForDiamond:hasGot:'; // 已领取额度
|
||||||
|
const showOffListKeyPrefix = 'payForDiamond:ShowOff:'; // 需要炫耀的玩家列表。 其中需要包含信息:玩家区服, 玩家玩家名称, 领到的数量
|
||||||
|
|
||||||
|
export async function playerCanReceive(call: ApiCall, callError : boolean = true) {
|
||||||
|
let activityId = call.req.activityId;
|
||||||
|
let activityInfo: WithId<OptionalId<ReqAddHuoDong>>;
|
||||||
|
if (!activityId) {
|
||||||
|
activityInfo = (await HuoDongFun.gethdList(call, 13))[0];
|
||||||
|
} else {
|
||||||
|
activityInfo = await HuoDongFun.getHdidInfo(call, activityId);
|
||||||
|
}
|
||||||
|
if (!activityInfo) {
|
||||||
|
if (callError) {
|
||||||
|
return call.error('', { code: -1, message: lng.huodong_open_1 });
|
||||||
|
} else {
|
||||||
|
return { result: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activityId = activityInfo.hdid;
|
||||||
|
const hasReceivedKey = hasGotKeyPrefix + activityId;
|
||||||
|
const hasReceivedStr = await G.iorediscross.get(hasReceivedKey);
|
||||||
|
const hasReceived = hasReceivedStr? parseInt(hasReceivedStr) : 0;
|
||||||
|
const remaining = activityInfo.data['totalmoney'] - hasReceived;
|
||||||
|
const showOffResult = await G.iorediscross.lrange(showOffListKeyPrefix + activityId, 0, -1);
|
||||||
|
const showOffList = showOffResult.map(result => JSON.parse(result));
|
||||||
|
const zeroTime = PublicShared.getToDayZeroTime();
|
||||||
|
const vipScore = await ActionLog.getDayLog(call.uid, 'pay');
|
||||||
|
const price = activityInfo.data['price'];
|
||||||
|
if (!vipScore) {
|
||||||
|
return {
|
||||||
|
payNum: 0, remaining, result: false, activityInfo, showOffList, price, gotAmount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const payNum = vipScore;
|
||||||
|
// 玩家充值未达标或者奖池余额耗尽则不能领取
|
||||||
|
if (payNum < price || remaining <= 0) {
|
||||||
|
return {
|
||||||
|
payNum, remaining, result: false, activityInfo, showOffList, price, gotAmount: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 检查玩家今日是否已经领取
|
||||||
|
const playerActivityInfo = await G.mongodb.cEvent('payForDiamond').findOne({ uid: call.uid, type: 'payForDiamond' });
|
||||||
|
if (playerActivityInfo) {
|
||||||
|
if (playerActivityInfo[zeroTime]) {
|
||||||
|
return {
|
||||||
|
payNum, remaining, result: false, activityInfo, showOffList, price, gotAmount: playerActivityInfo[zeroTime]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
payNum, remaining, result: true, activityInfo, showOffList, price, gotAmount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function (call: ApiCall<ReqCanReceive, ResCanReceive>) {
|
||||||
|
const canReceiveResult = await playerCanReceive(call);
|
||||||
|
if (canReceiveResult) {
|
||||||
|
const { payNum, remaining, result, showOffList, price, gotAmount } = canReceiveResult;
|
||||||
|
call.succ({ payNum, remaining, result, showOffList, price, gotAmount });
|
||||||
|
}
|
||||||
|
}
|
121
src/api_s2c/event/payForDiamond/ApiReceive.ts
Normal file
121
src/api_s2c/event/payForDiamond/ApiReceive.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
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.iorediscross.setnx(hasGotLockKey, 1);
|
||||||
|
if (lockResult) {
|
||||||
|
await G.iorediscross.expire(hasGotLockKey, 1); // 设置 ttl 避免死锁
|
||||||
|
const activityId = call.req.activityId;
|
||||||
|
const hasReceivedKey = hasGotKeyPrefix + activityId;
|
||||||
|
const hasReceivedStr = await G.iorediscross.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.iorediscross.incrby(hasReceivedKey, Math.abs(gotAmount)); // 添加已领取的额度
|
||||||
|
await G.iorediscross.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.iorediscross.lpush(showOffListKey, msg);
|
||||||
|
await G.ioredis.ltrim(showOffListKey, 0, 49); // 限制列表保存 50 条消息, 避免无限增长
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return call.succ({
|
||||||
|
amount: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,11 +15,12 @@ import {md_redPoint} from '../gongyu/mingdao/ApiOpen';
|
|||||||
import {HongDianFun, HuoDongHongDianFun} from "./fun";
|
import {HongDianFun, HuoDongHongDianFun} from "./fun";
|
||||||
import {FunWeiXiuChang} from "../../public/weixiuchang";
|
import {FunWeiXiuChang} from "../../public/weixiuchang";
|
||||||
import {getShouChongRedPoint} from "../event/shouchong/ApiReceive";
|
import {getShouChongRedPoint} from "../event/shouchong/ApiReceive";
|
||||||
|
import { playerCanReceive } from '../event/payForDiamond/ApiCanReceive';
|
||||||
|
|
||||||
const defaultKeys: hongdianKey[] = ['jiuba', 'jiaotang', 'shouchong', 'clslhd', 'dixiaqianzhuanghd', 'gonghuihd', 'hbzbhd', 'jjchd', 'taskhd',
|
const defaultKeys: hongdianKey[] = ['jiuba', 'jiaotang', 'shouchong', 'clslhd', 'dixiaqianzhuanghd', 'gonghuihd', 'hbzbhd', 'jjchd', 'taskhd',
|
||||||
'xstaskhd', 'lingzhulaixihd', 'dxlthd', 'wzcjhd', 'slzdhd', 'qjzzdhd', 'kuangdonghd', 'qiandaohd', 'kaifukuanghuanhd', 'jijinhd', 'zhuishalinghd',
|
'xstaskhd', 'lingzhulaixihd', 'dxlthd', 'wzcjhd', 'slzdhd', 'qjzzdhd', 'kuangdonghd', 'qiandaohd', 'kaifukuanghuanhd', 'jijinhd', 'zhuishalinghd',
|
||||||
'yibaichouhd', 'huobanzhaomuhd', 'qirileichonghd', 'jierihd', 'kbzzhd', 'wzryhd', 'yuedujijin', 'mingdao', 'patahd',
|
'yibaichouhd', 'huobanzhaomuhd', 'qirileichonghd', 'jierihd', 'kbzzhd', 'wzryhd', 'yuedujijin', 'mingdao', 'patahd',
|
||||||
'heishihd', 'huodonghd', 'renown', 'weixiuchang', 'kaifujingsai', 'zhoumolibao'];
|
'heishihd', 'huodonghd', 'renown', 'weixiuchang', 'kaifujingsai', 'zhoumolibao', 'payForDiamond'];
|
||||||
|
|
||||||
export default async function (call: ApiCall<ReqGet, ResGet>) {
|
export default async function (call: ApiCall<ReqGet, ResGet>) {
|
||||||
|
|
||||||
@ -163,6 +164,10 @@ export default async function (call: ApiCall<ReqGet, ResGet>) {
|
|||||||
case 'zhoumolibao':
|
case 'zhoumolibao':
|
||||||
res[key] = await HongDianFun.zhoumolibao(call);
|
res[key] = await HongDianFun.zhoumolibao(call);
|
||||||
break;
|
break;
|
||||||
|
case 'payForDiamond':
|
||||||
|
const receiveResult = await playerCanReceive(call, false);
|
||||||
|
res[key] = { show: receiveResult && receiveResult.result };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,8 @@ class _G {
|
|||||||
redis: redisJsonFun;
|
redis: redisJsonFun;
|
||||||
/**ioredis连接对象 */
|
/**ioredis连接对象 */
|
||||||
ioredis: Redis;
|
ioredis: Redis;
|
||||||
|
/** 跨服 ioredis 连接对象 */
|
||||||
|
iorediscross: Redis;
|
||||||
/**mongodb连接对象 */
|
/**mongodb连接对象 */
|
||||||
mongodb: _mongodb;
|
mongodb: _mongodb;
|
||||||
/**crossmongodb连接对象 */
|
/**crossmongodb连接对象 */
|
||||||
|
@ -16,4 +16,7 @@ export async function initIORedis() {
|
|||||||
G.ioredis = new Redis(G.argv.serverType == 'cross' ? G.config.crossRedisUrl : G.config.redisUrl,{
|
G.ioredis = new Redis(G.argv.serverType == 'cross' ? G.config.crossRedisUrl : G.config.redisUrl,{
|
||||||
keyPrefix: preKey,
|
keyPrefix: preKey,
|
||||||
});
|
});
|
||||||
|
G.iorediscross = new Redis(G.config.crossRedisUrl,{
|
||||||
|
keyPrefix: "cross_",
|
||||||
|
});
|
||||||
}
|
}
|
@ -50,7 +50,10 @@ export type eventType = {
|
|||||||
qirichongzhi: Omit<ResOpenQirichongzhi, 'finished'>;
|
qirichongzhi: Omit<ResOpenQirichongzhi, 'finished'>;
|
||||||
jierihuodong: Omit<ResOpenJierihuodong, 'taskFinish'> & { refreshTime: number; };
|
jierihuodong: Omit<ResOpenJierihuodong, 'taskFinish'> & { refreshTime: number; };
|
||||||
kaifujingsai: ResOpenKaifujingsai;
|
kaifujingsai: ResOpenKaifujingsai;
|
||||||
zhoumolibao: ResOpenZhoumolibao & { refreshTime: number; }
|
zhoumolibao: ResOpenZhoumolibao & { refreshTime: number; };
|
||||||
|
payForDiamond: {
|
||||||
|
[time: number]: number
|
||||||
|
}
|
||||||
} & {
|
} & {
|
||||||
[k: `${number}jijin`]: ResOpenYuedujijin;
|
[k: `${number}jijin`]: ResOpenYuedujijin;
|
||||||
[k: `yangchengmubiao${number}`]: yangchengmubiao;
|
[k: `yangchengmubiao${number}`]: yangchengmubiao;
|
||||||
|
12
src/shared/protocols/event/payForDiamond/PtlCanReceive.ts
Normal file
12
src/shared/protocols/event/payForDiamond/PtlCanReceive.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export type ReqCanReceive = {
|
||||||
|
activityId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ResCanReceive = {
|
||||||
|
payNum: number;
|
||||||
|
remaining?: number;
|
||||||
|
result: boolean;
|
||||||
|
price: number;
|
||||||
|
showOffList: any[];
|
||||||
|
gotAmount: number;
|
||||||
|
};
|
9
src/shared/protocols/event/payForDiamond/PtlReceive.ts
Normal file
9
src/shared/protocols/event/payForDiamond/PtlReceive.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export type ReqReceive = {
|
||||||
|
activityId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ResReceive = {
|
||||||
|
amount: number,
|
||||||
|
timesRemaining?: number;
|
||||||
|
showOff?: boolean;
|
||||||
|
};
|
@ -51,7 +51,8 @@ export type hongdianKey =
|
|||||||
| 'heishiMrjx'
|
| 'heishiMrjx'
|
||||||
| 'weixiuchang'
|
| 'weixiuchang'
|
||||||
| 'kaifujingsai'
|
| 'kaifujingsai'
|
||||||
| 'zhoumolibao';
|
| 'zhoumolibao'
|
||||||
|
| 'payForDiamond';
|
||||||
|
|
||||||
|
|
||||||
export type hongdianVal = {
|
export type hongdianVal = {
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user