登录:装备,密码,短信登录

This commit is contained in:
luying
2021-03-04 15:34:01 +08:00
parent 95deef6230
commit 9dfee7f49c
7 changed files with 397 additions and 170 deletions
+2
View File
@@ -7,6 +7,8 @@ export const ENCRYPT_KEY = 'fiqaxijabbantusmprc234fj';
export const AUTH_SMS_CNT_PER_DAY = 8; export const AUTH_SMS_CNT_PER_DAY = 8;
export const ADULT_AGE = 18;
export const COUNTER = { export const COUNTER = {
UID: { name: 'uid', def: 1 }, UID: { name: 'uid', def: 1 },
GMUID: { name: 'gmuid', def: 1 }, GMUID: { name: 'gmuid', def: 1 },
+1
View File
@@ -17,6 +17,7 @@ export const STATUS = {
DUP_LOGIN: { code: 10006, simStr: '重复登录' }, DUP_LOGIN: { code: 10006, simStr: '重复登录' },
NEW_SERVER_ERR: { code: 10007, simStr: '添加新服务器失败' }, NEW_SERVER_ERR: { code: 10007, simStr: '添加新服务器失败' },
SERVER_EXISTS: { code: 10008, simStr: '服务器已存在' }, SERVER_EXISTS: { code: 10008, simStr: '服务器已存在' },
PASSWORD_ERR: { code: 10009, simStr: '密码错误' },
// 战斗相关状态 20000 - 29999 // 战斗相关状态 20000 - 29999
// 战斗通用 20000 - 20099 // 战斗通用 20000 - 20099
BATTLE_MISS_INFO: { code: 20001, simStr: '缺少关卡信息' }, BATTLE_MISS_INFO: { code: 20001, simStr: '缺少关卡信息' },
+87 -10
View File
@@ -3,6 +3,8 @@ import { CounterModel } from './Counter';
import BaseModel from './BaseModel'; import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
import { genCode } from '../pubUtils/util'; import { genCode } from '../pubUtils/util';
const bcrypt = require('bcrypt');
const SALT_WORK_FACTOR = 5
/** /**
* 用户字段接口 * 用户字段接口
@@ -24,16 +26,42 @@ export default class User extends BaseModel {
token: string; token: string;
@prop({ required: true }) @prop({ required: true })
tel: string; tel: string; // 账号
@prop({ required: true, default: '' }) @prop({ required: true, default: '' })
telHash: string; telHash: string;
@prop({ required: true })
password: string;
@prop({ required: true })
salt: string;
@prop({ required: true, default: '' }) @prop({ required: true, default: '' })
channelId: string; channelId: string;
// 游客相关
@prop({ required: false, default: false })
isGuest: boolean; // 是否是游客
@prop({ required: false }) @prop({ required: false })
guestId: string; guestId: string; // 游客id
@prop({ required: false, default: 0 })
guestTime: number; // 游客体验时间
// 防沉迷相关
@prop({ required: false, default: false })
hasAuthenticated: boolean; // 是否是已认证
@prop({ required: false })
birthday: string; // 生日年月
@prop({ required: false })
pi: string; // 已通过实名认证用户唯一标识
@prop({ required: false, default: 0 })
todayPlayTime: number; // 今日游戏时间
@prop({ required: false })
refDaily: Date; // 刷新todayPlayTime时间
@prop({ required: false, default: false })
hasSetPw: boolean; // 是否设置过密码
// 最后登录 IP // 最后登录 IP
@prop({ required: false }) @prop({ required: false })
@@ -52,6 +80,9 @@ export default class User extends BaseModel {
unionId: string; // 用户标识 unionId: string; // 用户标识
}]; }];
@prop({ required: true, type: String, default: [], _id: false })
device: string[];
@prop({ required: true, default: 'com.bantu.zyz' }) @prop({ required: true, default: 'com.bantu.zyz' })
pkgName: string; pkgName: string;
@@ -67,23 +98,68 @@ export default class User extends BaseModel {
@prop({ required: true, default: 0 }) @prop({ required: true, default: 0 })
auth: number; auth: number;
public static async updateToken(tel: string, token: string, platform: string, pkgName: string, serverType: string, lean = true) { public static async createUser(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, lean = true) {
let user: UserType = await UserModel.findOne({ tel }).lean();
const curTime: Date = new Date(); const curTime: Date = new Date();
let update = {};
if (!user) {
const uid = await CounterModel.getNewCounter(COUNTER.UID); const uid = await CounterModel.getNewCounter(COUNTER.UID);
const userCode = genCode(8); const userCode = genCode(8);
const doc = new UserModel(); const doc = new UserModel();
update = Object.assign(update, { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}` }, doc.toJSON()); let update = {};
} update = Object.assign(update, doc.toJSON(), { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}`, isGuest });
if(isGuest) update["guestId"] = tel;
update = Object.assign(update, { token, lastLoginTime: curTime }); update = Object.assign(update, { token, lastLoginTime: curTime });
user = await UserModel.findOneAndUpdate({ tel }, update, { upsert: true, new: true }).lean(lean); delete update["device"];
const user = await UserModel.findOneAndUpdate({ tel }, { $set: update, $addToSet: {device: deviceId}}, { upsert: true, new: true }).lean(lean);
return user; return user;
} }
public static async updateToken(tel: string, token: string, deviceId: string, lean = true) {
const curTime: Date = new Date();
let user = await UserModel.findOneAndUpdate({ tel }, { $set: { token, lastLoginTime: curTime }, $addToSet: {device: deviceId}}, { new: true }).lean(lean);
return user;
}
public static async createOrUpdate(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string) {
let user: UserType = await UserModel.findOne({ tel }).lean();
if (!user) {
user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId);
} else {
user = await UserModel.updateToken(tel, token, deviceId);
}
return user;
}
private static async encryptPass(password: string, salt?: string) {
if (!salt) {
salt = await bcrypt.genSalt(SALT_WORK_FACTOR);
}
let npassword = await bcrypt.hash(password, salt);
return { npassword, salt };
}
public static async setPass(uid: number, password: string, lean = true) {
let r = await this.encryptPass(password);
const user: UserType = await UserModel.findOneAndUpdate({ uid }, { $set: { password: r.npassword, salt: r.salt, hasSetPw: true }}).lean(lean);
return user;
}
public static async checkPass(tel: string, password: string, token: string, deviceId: string, lean = true) {
const user: UserType = await UserModel.findOne({ tel }).select('salt').lean();
if (user) {
const curTime: Date = new Date();
let { salt } = user;
let { npassword } = await this.encryptPass(password, salt);
const checkUser: UserType = await UserModel.findOneAndUpdate({ tel, password: npassword }, { $set: { token, lastLoginTime: curTime }, $addToSet: {device: deviceId}}).select('uid username name token').lean(lean);
return checkUser;
} else {
return null
}
}
public static async findUserByToken(token: string, lean = true) { public static async findUserByToken(token: string, lean = true) {
const user: UserType = await UserModel.findOne({ token }).select('uid token serverType auth userCode').lean(lean); const user: UserType = await UserModel.findOne({ token }).select('uid token serverType auth tel userCode').lean(lean);
return user; return user;
} }
@@ -119,6 +195,7 @@ export default class User extends BaseModel {
} }
} }
export const UserModel = getModelForClass(User); export const UserModel = getModelForClass(User);
export interface UserType extends Pick<DocumentType<User>, keyof User>{}; export interface UserType extends Pick<DocumentType<User>, keyof User>{};
+20
View File
@@ -94,3 +94,23 @@ export function getTodayZeroDate() {
date.setSeconds(0); date.setSeconds(0);
return date; return date;
} }
export function getAge(birthday: string) {
let d = new Date(birthday + ' 00:00:00');
let _year = d.getFullYear();
let _month = d.getMonth() + 1;
let _day = d.getDate();
let age = 0;
let now = new Date();
let year = now.getFullYear();
let month = now.getMonth() + 1;
let day = now.getDate();
if (year >= _year && month >= _month && day >= _day) {
age = year - _year;
}else{
age = year - _year - 1;
}
return age
}
+21 -2
View File
@@ -1,6 +1,13 @@
import { Controller } from 'egg'; import { Controller } from 'egg';
export default class AccountController extends Controller { export default class AccountController extends Controller {
public async deviceLogin() {
const { ctx } = this;
const { token, isGuest, deviceId, platform, pkgName, serverType } = ctx.request.body;
ctx.body = await ctx.service.auth.deviceLogin(isGuest, token, deviceId, platform, pkgName, serverType);
}
public async getSms() { public async getSms() {
const { ctx } = this; const { ctx } = this;
const { tel } = ctx.request.body; const { tel } = ctx.request.body;
@@ -9,8 +16,20 @@ export default class AccountController extends Controller {
public async smsLogin() { public async smsLogin() {
const { ctx } = this; const { ctx } = this;
const { tel, code, platform, pkgName, serverType } = ctx.request.body; const { tel, code, platform, pkgName, serverType, deviceId } = ctx.request.body;
ctx.body = await ctx.service.auth.smsLogin(tel, code, platform, pkgName, serverType); ctx.body = await ctx.service.auth.smsLogin(tel, deviceId, code, platform, pkgName, serverType);
}
public async setPassword() {
const { ctx } = this;
const { password } = ctx.request.body;
ctx.body = await ctx.service.auth.setPassword(password);
}
public async pwLogin() {
const { ctx } = this;
const { tel, deviceId, pw } = ctx.request.body;
ctx.body = await ctx.service.auth.pwLogin(tel, deviceId, pw);
} }
public async checkRole() { public async checkRole() {
+3
View File
@@ -5,8 +5,11 @@ export default (app: Application) => {
const tokenParser = app.middleware.tokenParser(); const tokenParser = app.middleware.tokenParser();
router.get('/dev', controller.home.dev); router.get('/dev', controller.home.dev);
router.get('/', controller.home.index); router.get('/', controller.home.index);
router.post('/user/devicelogin', controller.account.deviceLogin);
router.post('/user/getsms', controller.account.getSms); router.post('/user/getsms', controller.account.getSms);
router.post('/user/smslogin', controller.account.smsLogin); router.post('/user/smslogin', controller.account.smsLogin);
router.post('/user/setpassword', tokenParser, controller.account.setPassword);
router.post('/user/pwlogin', controller.account.pwLogin);
router.post('/user/checkrole', tokenParser, controller.account.checkRole); router.post('/user/checkrole', tokenParser, controller.account.checkRole);
router.post('/user/createrole', tokenParser, controller.account.createRole); router.post('/user/createrole', tokenParser, controller.account.createRole);
router.post('/game/getserverlist', tokenParser, controller.game.getServerList); router.post('/game/getserverlist', tokenParser, controller.game.getServerList);
+111 -6
View File
@@ -1,8 +1,8 @@
import { COUNTER, HERO_SYSTEM_TYPE, DEFAULT_LV, DEFAULT_ITEMS, ITID, DEFAULT_GOLD, DEFAULT_HERO_LV, DEFAULT_EQUIPS, DEFAULT_COIN } from '@consts'; import { COUNTER, HERO_SYSTEM_TYPE, DEFAULT_LV, DEFAULT_ITEMS, ITID, DEFAULT_GOLD, DEFAULT_HERO_LV, DEFAULT_EQUIPS, DEFAULT_COIN, ADULT_AGE } from '@consts';
import { DEFAULT_HEROES } from '@consts'; import { DEFAULT_HEROES } from '@consts';
import { HeroModel } from '@db/Hero'; import { HeroModel } from '@db/Hero';
import { RoleModel } from '@db/Role'; import { RoleModel } from '@db/Role';
import { UserModel } from '@db/User'; import { UserModel, UserType } from '@db/User';
import { STATUS } from '@consts'; import { STATUS } from '@consts';
import { smsModel } from '@db/Sms'; import { smsModel } from '@db/Sms';
import { Service } from 'egg'; import { Service } from 'egg';
@@ -11,12 +11,73 @@ import { getHeroInfoById } from 'app/pubUtils/gamedata';
import { calPlayerCeAndSave, reCalAllHeroCe } from 'app/pubUtils/playerCe'; import { calPlayerCeAndSave, reCalAllHeroCe } from 'app/pubUtils/playerCe';
import { getExpByLv, getHeroExpByLv, gameData } from 'app/pubUtils/data'; import { getExpByLv, getHeroExpByLv, gameData } from 'app/pubUtils/data';
import { isString } from 'underscore'; import { isString } from 'underscore';
import { getAge } from 'app/pubUtils/timeUtil';
import { shouldRefresh } from 'app/pubUtils/util';
/** /**
* Test Service * Test Service
*/ */
export default class Auth extends Service { export default class Auth extends Service {
/**
* 设备免密登录
* @param telNo - 用户手机号
*/
public async deviceLogin(isGuest: boolean, token: string, deviceId: string, platform: string, pkgName: string, serverType: string) {
const ctx = this.ctx;
let user = await UserModel.findUserByToken(token);
if (!user) {
if(isGuest) {
const tel = ctx.service.utils.genCode(10);
const token = ctx.service.utils.generateStr(256);
user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId);
let param = this.getReturnParam(user);
return this.ctx.service.utils.resResult(STATUS.SUCCESS, {
canLogin: true,
...param
});
} else {
return this.ctx.service.utils.resResult(STATUS.SUCCESS, {
canLogin: false
});
}
} else {
const token = ctx.service.utils.generateStr(256);
user = await UserModel.updateToken(user.tel, token, deviceId);
let param = this.getReturnParam(user);
return this.ctx.service.utils.resResult(STATUS.SUCCESS, {
canLogin: true,
...param
});
}
}
private getReturnParam(user: UserType) {
let age = getAge(user.birthday);
let isAdult = age >= ADULT_AGE;
let todayPlayTime = user.todayPlayTime;
if(shouldRefresh(user.refDaily, new Date(), 0)) {
todayPlayTime = 0;
}
return {
tel: user.tel,
isGuest: !!user.isGuest,
guestTime: user.guestTime, // 游客已体验时间
hasAuthenticated: !!user.hasAuthenticated, // 是否进行过实名认证
isAdult, // 是否已成年
todayPlayTime, // 今天已游戏时长
hasSetPw: user.hasSetPw, // 是否设置了密码
token: user.token, // 用户token
userCode: user.userCode // 用户标识
}
}
public checkTelNo(telNo) { public checkTelNo(telNo) {
if (!isString(telNo)) { if (!isString(telNo)) {
return { status: 1, data: '参数类型错误' }; return { status: 1, data: '参数类型错误' };
@@ -85,7 +146,7 @@ export default class Auth extends Service {
* @param pkgName 包名 * @param pkgName 包名
* @param serverType 服务器类型 * @param serverType 服务器类型
*/ */
public async smsLogin(tel: string, code: string, platform: string, pkgName: string, serverType: string) { public async smsLogin(tel: string, deviceId: string, code: string, platform: string, pkgName: string, serverType: string) {
const ctx = this.ctx; const ctx = this.ctx;
// 参数检查 // 参数检查
const telVerify = this.checkTelNo(tel); const telVerify = this.checkTelNo(tel);
@@ -116,8 +177,52 @@ export default class Auth extends Service {
// 用户注册登录 // 用户注册登录
const token = ctx.service.utils.generateStr(256); const token = ctx.service.utils.generateStr(256);
const user = await UserModel.updateToken(tel, token, platform, pkgName, serverType); const user = await UserModel.createOrUpdate(false, tel, token, platform, pkgName, serverType, deviceId);
return ctx.service.utils.resResult(STATUS.SUCCESS, { token, userCode: user?.userCode }); let param = this.getReturnParam(user);
return ctx.service.utils.resResult(STATUS.SUCCESS, param);
}
/**
* 设置密码
* @param password 密码
*/
public async setPassword(password: string) {
const { ctx } = this;
const { uid } = ctx;
let user = await UserModel.setPass(uid, password);
if (user) {
let param = this.getReturnParam(user);
return ctx.service.utils.resResult(STATUS.SUCCESS, param);
} else {
return ctx.service.utils.resResult(STATUS.INTERNAL_ERR);
}
}
/**
* 密码登录
* @param tel 登录手机号
* @param code 登录验证码
* @param platform 平台
* @param pkgName 包名
* @param serverType 服务器类型
*/
public async pwLogin(tel: string, deviceId: string, pw: string) {
const ctx = this.ctx;
// 参数检查
const telVerify = this.checkTelNo(tel);
if (telVerify.status !== 0) {
return telVerify;
}
// 用户注册登录
const token = ctx.service.utils.generateStr(256);
const user = await UserModel.checkPass(tel, pw, token, deviceId);
if(!user) return ctx.service.utils.resResult(STATUS.PASSWORD_ERR);
let param = this.getReturnParam(user);
return ctx.service.utils.resResult(STATUS.SUCCESS, { ...param });
} }
public async checkRole(serverId: number) { public async checkRole(serverId: number) {
@@ -157,7 +262,7 @@ export default class Auth extends Service {
skins: [{ id: initialSkin, enable: true }], lv: DEFAULT_HERO_LV, exp: getHeroExpByLv(DEFAULT_HERO_LV - 1) || 0 skins: [{ id: initialSkin, enable: true }], lv: DEFAULT_HERO_LV, exp: getHeroExpByLv(DEFAULT_HERO_LV - 1) || 0
}); });
skinIds.push(initialSkin); skinIds.push(initialSkin);
await calPlayerCeAndSave(HERO_SYSTEM_TYPE.INIT, roleId, hero, {}, role); await calPlayerCeAndSave(HERO_SYSTEM_TYPE.INIT, roleId, hero, {});
} }
await reCalAllHeroCe(HERO_SYSTEM_TYPE.ADD_SKIN, roleId, {}, skinIds) await reCalAllHeroCe(HERO_SYSTEM_TYPE.ADD_SKIN, roleId, {}, skinIds)