防沉迷:定时任务

This commit is contained in:
luying
2021-03-04 20:36:59 +08:00
parent 32bb6cb2e3
commit 8a122b6a4d
11 changed files with 141 additions and 32 deletions

View File

@@ -54,7 +54,7 @@ export class EntryHandler {
await self.app.rpc.connector.connectorRemote.remoteLogin.toServer(connect.sid, role.roleId);
}
let serverName = this.app.getServerId();
await roleLogin(role.roleId, user.userCode, serverName); // 保存在线用户
await roleLogin(role.roleId, user.userCode, serverName, user.pkgName); // 保存在线用户
await session.abind(role.roleId);
session.set('uid', role.roleId);
session.set('roleId', role.roleId);

View File

@@ -10,6 +10,7 @@ import { SystemConfigModel } from '../db/SystemConfig';
import { GuildRankParam, GuildLeader, RankParam } from '../domain/rank';
import { GuildModel } from '../db/Guild';
import { comBtlRanges } from '../pubUtils/gamedata';
import { stringify } from 'querystring';
/**
* 在服务重新启动时将信息存入redis
*/
@@ -369,9 +370,9 @@ export function redisSidKey(roleId: string) {
* @param userCode user表唯一字符串标识
* @param sid connector服的那个sid
*/
export async function roleLogin(roleId: string, userCode: string, sid: string) {
export async function roleLogin(roleId: string, userCode: string, sid: string, pkgName: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
let param = { userCode, sid };
let param = { userCode, sid, pkgName };
return await client.hsetAsync(REDIS_KEY.ONLINE_USERS, roleId, JSON.stringify(param));
}
@@ -407,7 +408,8 @@ export async function getRoleOnlineInfo(roleId: string) {
return {
isOnline: true,
userCode: result.userCode,
sid: result.sid
sid: result.sid,
pkgName: result.pkgName
}
} catch(e) {
return { isOnline: false }
@@ -423,7 +425,18 @@ export async function getRoleOnlineInfo(roleId: string) {
export async function getAllOnlineRoles() {
const client: Redis.RedisClient = pinus.app.get('redis');
let allRoles = await client.hgetallAsync(REDIS_KEY.ONLINE_USERS);
return allRoles;
let result = new Array<{roleId: string, userCode: string, sid: string, pkgName: string}>();
for(let roleId in allRoles) {
try{
let param = JSON.parse(allRoles[roleId]);
if(param) {
result.push({ roleId, userCode: param.userCode, sid: param.sid, pkgName: param.pkgName });
}
} catch(e) {
continue;
}
}
return result;
}
export async function resetPvpRanks() {

View File

@@ -9,7 +9,7 @@ interface UserCache {
var userCacheMap = new Map<string, UserCache>();
export function init() {
scheduleJob("0/5 * * * * *", clearDirtyData, {name:'clearDirtyData'});//每个5秒钟释放redis锁
scheduleJob('clearDirtyData', "0/5 * * * * *", clearDirtyData);//每个5秒钟释放redis锁
}
/**
* 释放锁

View File

@@ -3,12 +3,12 @@ import { scheduleJob, Job } from 'node-schedule';
import { SystemConfigModel } from '../db/SystemConfig';
import PvpDefenseType,{ PvpDefenseModel } from '../db/PvpDefense';
import { PVP } from '../pubUtils/dicParam';
import { nowSeconds, getTodayZeroPoint } from '../pubUtils/timeUtil';
import { nowSeconds, getTodayZeroPoint, getAge } from '../pubUtils/timeUtil';
import { getPvpGkWarIds, getPvpRankRewards, getPvpHeroRewards, getResultMaxRank } from '../pubUtils/data';
import { deepCopy, getRandomArr, resResult } from '../pubUtils/util';
import { deepCopy, getRandomArr, resResult, shouldRefresh } from '../pubUtils/util';
import { getLvByScore } from './pvpService';
import { getMyRank, setRank, resetPvpRanks } from './redisService';
import { MAIL_TYPE, REDIS_KEY } from '../consts';
import { getMyRank, setRank, resetPvpRanks, getAllOnlineRoles } from './redisService';
import { MAIL_TYPE, REDIS_KEY, ADULT_AGE, GUEST_MAX_TIME } from '../consts';
import { RankParam } from '../domain/rank';
import { RoleModel } from '../db/Role';
import { MailModel, MailType } from '../db/Mail';
@@ -18,6 +18,8 @@ import { PvpSeasonResultModel } from '../db/PvpSeasonResult';
import { settleGuildWeekly } from './guildService';
import { STATUS } from '../consts/statusCode';
import { getMailContent, sendMail } from './mailService';
import { reportOnline } from '../pubUtils/httpUtil';
import User, { UserModel } from '../db/User';
const PER_SECOND = 1 * 1000;
const PER_DAY = 24 * 60 * 60;
const SETTLE_DIFF = 29 * 60;
@@ -50,21 +52,27 @@ export async function init() {
}
}
let settleTime = (seasonEndTime - SETTLE_DIFF)* PER_SECOND;
seasonJobId = scheduleJob(settleTime, setPvpSeasonResult, { name: 'setPvpSeasonResult' });//设置实际赛季结算时间
seasonJobId = scheduleJob('setPvpSeasonResult', settleTime, setPvpSeasonSchdule);//设置实际赛季结算时间
seasonEndTimeJobId = scheduleJob(seasonEndTime* PER_SECOND, resetPvpRanks, { name: 'resetRank' });//由于24之后才展示结算之后的信息为保排行榜信息一致性设置实际重置排行榜的时间为12点
seasonEndTimeJobId = scheduleJob('resetRank', seasonEndTime * PER_SECOND, resetPvpRanks);//由于24之后才展示结算之后的信息为保排行榜信息一致性设置实际重置排行榜的时间为12点
warJobId = scheduleJob("0 0 0 * * 1", resetPvpWarId);//每周1零点重置地图
await resetPvpRanks();//服务器重启,重置排行榜的信息
// 周功勋结算任务
guildWeeklyJobId = scheduleJob('0 0 0 * * 1', settleGuildWeekly, { name: 'settleGuildWeekly' });
guildWeeklyJobId = scheduleJob('settleGuildWeekly', '0 0 0 * * 1', settleGuildWeekly);
scheduleJob('reportOnline', '* 0/5 * * * *', reportOnlineSchedule)
}
function setPvpSeasonSchdule() {
setPvpSeasonResult();
}
/**
* pvp定时任务赛季结算
* @param obj
*/
export async function setPvpSeasonResult(obj:{ name:string, notSetNext?: boolean, notPush?: boolean }) {
export async function setPvpSeasonResult(obj?:{ name:string, notSetNext?: boolean, notPush?: boolean }) {
console.log('exce setPvpSeasonResult'+ obj?.name);
let { seasonNum, seasonEndTime, oldSeasonEndTime } = await setNextPvpTime(obj?.notSetNext);//设置下个结算任务
let resultMaxRank = getResultMaxRank();//根据排行榜的奖励表获得最大排名挡位的最小值其余不在结算中结算的玩家按照最大排名挡位在登录或进入pvp时结算
@@ -214,8 +222,8 @@ async function setNextPvpTime(notSetNext: boolean) {
seasonEndTime = (PVP.PVP_SEASON_DAYS + 1) * PER_DAY + getTodayZeroPoint();
await SystemConfigModel.updateSeason(seasonEndTime, oldSeasonEndTime);
let settleTime = (seasonEndTime - SETTLE_DIFF)* PER_SECOND;
seasonJobId = scheduleJob(settleTime, setPvpSeasonResult, { name: 'setPvpSeasonResult' });
seasonEndTimeJobId = scheduleJob(seasonEndTime* PER_SECOND, resetPvpRanks, { name: 'resetRank' });
seasonJobId = scheduleJob('setPvpSeasonResult', settleTime, setPvpSeasonSchdule);
seasonEndTimeJobId = scheduleJob('resetRank', seasonEndTime* PER_SECOND, resetPvpRanks);
return { seasonEndTime, seasonNum: seasonNum + 1, oldSeasonEndTime };
}
@@ -254,6 +262,51 @@ export async function resetPvpSeasonTime(hour: number) {
}
let settleTime = (seasonEndTime - SETTLE_DIFF)* PER_SECOND;
console.log('settleTime = ' + settleTime)
seasonJobId = scheduleJob(settleTime, setPvpSeasonResult, { name: 'setPvpSeasonResult' });
seasonJobId = scheduleJob('setPvpSeasonResult', settleTime, setPvpSeasonSchdule);
return { seasonEndTime, seasonNum };
}
export async function reportOnlineSchedule() {
let allRoles = await getAllOnlineRoles();
console.log('reportOnlineSchedule all roles count: ', allRoles.length)
for(let { roleId, userCode, sid, pkgName } of allRoles) {
let result = await reportOnline(userCode, pkgName); // 连接sdk
if(!result || result.code == -1) continue;
let user = await UserModel.findUserByUserCode(userCode);
if(!user) continue;
let { reportTime = new Date(), lastLoginTime = new Date(), guestTime, isGuest, hasAuthenticated, birthday } = user;
let age = getAge(birthday);
let isAdult = age >= ADULT_AGE;
// TODO 将code含义写入const
if(result.code != 1) { // 未成年人防沉迷
pinus.app.channelService.pushMessageByUids('onPlayTime', resResult(STATUS.SUCCESS, {
isGuest,
guestTime, // 游客已体验时间
hasAuthenticated, // 是否进行过实名认证
isAdult, // 是否已成年
todayPlayTime: result.total, // 今天已游戏时长
type: result.code
} ), [{uid: roleId, sid: sid}]);
} else {
let lastTime = lastLoginTime > reportTime? lastLoginTime.getTime(): reportTime.getTime();
let guestTimeInc = Math.floor((Date.now() - lastTime)/1000);
user = await UserModel.updatePlayTime(userCode, guestTimeInc, result.total); // 记录时间
guestTime = user.guestTime;
if (isGuest && guestTime > GUEST_MAX_TIME) {
pinus.app.channelService.pushMessageByUids('onPlayTime', resResult(STATUS.SUCCESS, {
isGuest,
guestTime, // 游客已体验时间
hasAuthenticated, // 是否进行过实名认证
isAdult, // 是否已成年
todayPlayTime: result.total, // 今天已游戏时长
type: 1
} ), [{uid: roleId, sid: sid}]);
}
}
}
}

View File

@@ -126,6 +126,15 @@
"resolved": "https://registry.npm.taobao.org/@types/node/download/@types/node-8.10.54.tgz?cache=0&sync_timestamp=1595281257528&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-8.10.54.tgz",
"integrity": "sha1-HIjrJTrBIQ8aWHaVP7cPfMSShAI="
},
"@types/node-schedule": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@types/node-schedule/-/node-schedule-1.3.1.tgz",
"integrity": "sha512-xAY/ZATrThUkMElSDfOk+5uXprCrV6c6GQ5gTw3U04qPS6NofE1dhOUW+yrOF2UyrUiAax/Zc4WtagrbPAN3Tw==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/redis": {
"version": "2.8.25",
"resolved": "https://registry.npm.taobao.org/@types/redis/download/@types/redis-2.8.25.tgz",

View File

@@ -52,6 +52,7 @@
"ts-node": "^8.2.0"
},
"devDependencies": {
"@types/node-schedule": "^1.3.1",
"tslint": "^5.9.1",
"typescript": "^3.9.7"
}

View File

@@ -1,5 +1,6 @@
export enum BANTU_VID_ADDR {
HOST = 'https://sdks.trgame.cn',
IDCARD = '/vid/idcard' // 实名认证
IDCARD = '/vid/idcard', // 实名认证
REPORT_ONLINE = '/addiction_prevention/report_online'
}
export const BANTU_VID_APP_KEY = '05c1c495369769e3c5d98426e9c8c2c0';

View File

@@ -58,7 +58,7 @@ export default class User extends BaseModel {
@prop({ required: false, default: 0 })
todayPlayTime: number; // 今日游戏时间
@prop({ required: false })
refDaily: Date; // 刷新todayPlayTime时间
reportTime: Date; // 汇报时间
@prop({ required: false, default: false })
hasSetPw: boolean; // 是否设置过密码
@@ -149,9 +149,8 @@ export default class User extends BaseModel {
return user;
}
public static async bindTel(uid: number, tel: string, password: string, lean = true) {
let r = await this.encryptPass(password);
const user: UserType = await UserModel.findOneAndUpdate({ uid, isGuest: true }, { $set: { password: r.npassword, salt: r.salt, hasSetPw: true, tel, isGuest: false }}, {new: true}).lean(lean);
public static async bindTel(uid: number, tel: string, lean = true) {
const user: UserType = await UserModel.findOneAndUpdate({ uid, isGuest: true }, { $set: { tel, isGuest: false }}, {new: true}).lean(lean);
return user;
}
@@ -188,6 +187,11 @@ export default class User extends BaseModel {
return user;
}
public static async findUserByUserCode(userCode: string, lean = true) {
const user: UserType = await UserModel.findOne({ userCode }).lean(lean);
return user;
}
public static async addAuth(uid: number, auth: number, lean = true) {
const user: UserType = await UserModel.findOneAndUpdate({ uid }, { auth }).select('uid tel').lean(lean);
return user;
@@ -208,6 +212,12 @@ export default class User extends BaseModel {
const user: UserType[] = await UserModel.find(searchObj).select('uid tel username serverType auth').lean(lean);
return user;
}
public static async updatePlayTime(userCode: string, guestTimeInc: number, todayPlayTime: number, lean = true) {
const user: UserType = await UserModel.findOneAndUpdate({ userCode }, { $inc: { guestTime: guestTimeInc }, $set: { todayPlayTime, reportTime: new Date() }}, { new: true }).lean(lean);
return user;
}
}

View File

@@ -2,20 +2,41 @@ import * as request from "request-promise";
import * as crypto from 'crypto'
import { BANTU_VID_ADDR, BANTU_VID_APP_KEY } from '../consts';
/**
* 在线报告
* @param userCode 账号
* @param packageName 包名
*/
export async function reportOnline ( userCode: string, packageName: string) {
if(!packageName || packageName==''){
packageName = 'com.bantu.nfsg'
}
let result = await vidHttpRequest(BANTU_VID_ADDR.REPORT_ONLINE, {
account: userCode,
package: packageName
});
if(result && result.code !== 1) {
console.error(result.msg);
}
return result;
}
/**
* 实名认证
* @param name 真实姓名
* @param idNum 身份证号
* @param roleId 账号
* @param userCode 账号
* @param packageName 包名
*/
export async function authenticate (name: string, idNum: string, roleId: string, packageName: string) {
export async function authenticate (name: string, idNum: string, userCode: string, packageName: string) {
if(!packageName || packageName==''){
packageName = 'com.bantu.nfsg'
}
let result = await vidHttpRequest(BANTU_VID_ADDR.IDCARD, {
account: roleId,
account: userCode,
cardno: idNum,
name,
appkey: BANTU_VID_APP_KEY,

View File

@@ -46,8 +46,8 @@ export default class AccountController extends Controller {
public async bind() {
const { ctx } = this;
const { tel, code, password } = ctx.request.body;
ctx.body = await ctx.service.auth.bind(tel, code, password);
const { tel, code } = ctx.request.body;
ctx.body = await ctx.service.auth.bind(tel, code);
}
public async authentication() {

View File

@@ -85,7 +85,7 @@ export default class Auth extends Service {
let age = getAge(user.birthday);
let isAdult = age >= ADULT_AGE;
let todayPlayTime = user.todayPlayTime;
if(shouldRefresh(user.refDaily, new Date(), 0)) {
if(shouldRefresh(user.reportTime, new Date(), 0)) {
todayPlayTime = 0;
}
return {
@@ -322,9 +322,10 @@ export default class Auth extends Service {
/**
* 绑定账号
* @param password 密码
* @param tel 手机号
* @param code 验证码
*/
public async bind(tel: string, code: string, password: string) {
public async bind(tel: string, code: string) {
const ctx = this.ctx;
// 参数检查
const telVerify = this.checkTelNo(tel);
@@ -332,7 +333,7 @@ export default class Auth extends Service {
return telVerify.resResult;
}
if (!isString(code) || code.length !== 6 || !password) {
if (!isString(code) || code.length !== 6) {
return ctx.service.utils.resResult(STATUS.WRONG_PARMS);
}
@@ -354,7 +355,7 @@ export default class Auth extends Service {
return ctx.service.utils.resResult(STATUS.TEL_HAS_USED);
}
let user = await UserModel.bindTel(ctx.uid, tel, password);
let user = await UserModel.bindTel(ctx.uid, tel);
if(!user) return ctx.service.utils.resResult(STATUS.ACCOUNT_NOT_GUEST);
let param = this.getReturnParam(user);
return ctx.service.utils.resResult(STATUS.SUCCESS, param);