防沉迷:定时任务
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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锁
|
||||
}
|
||||
/**
|
||||
* 释放锁
|
||||
|
||||
@@ -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}]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
9
game-server/package-lock.json
generated
9
game-server/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"ts-node": "^8.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node-schedule": "^1.3.1",
|
||||
"tslint": "^5.9.1",
|
||||
"typescript": "^3.9.7"
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user