diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index b2548ba6f..992735142 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -7,6 +7,8 @@ export const ENCRYPT_KEY = 'fiqaxijabbantusmprc234fj'; export const AUTH_SMS_CNT_PER_DAY = 8; +export const ADULT_AGE = 18; + export const COUNTER = { UID: { name: 'uid', def: 1 }, GMUID: { name: 'gmuid', def: 1 }, diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index ffa93e871..88e3a72d1 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -17,6 +17,7 @@ export const STATUS = { DUP_LOGIN: { code: 10006, simStr: '重复登录' }, NEW_SERVER_ERR: { code: 10007, simStr: '添加新服务器失败' }, SERVER_EXISTS: { code: 10008, simStr: '服务器已存在' }, + PASSWORD_ERR: { code: 10009, simStr: '密码错误' }, // 战斗相关状态 20000 - 29999 // 战斗通用 20000 - 20099 BATTLE_MISS_INFO: { code: 20001, simStr: '缺少关卡信息' }, diff --git a/shared/db/User.ts b/shared/db/User.ts index aa8a17c5f..7a41edcb7 100644 --- a/shared/db/User.ts +++ b/shared/db/User.ts @@ -3,6 +3,8 @@ import { CounterModel } from './Counter'; import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; 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; @prop({ required: true }) - tel: string; + tel: string; // 账号 @prop({ required: true, default: '' }) telHash: string; + @prop({ required: true }) + password: string; + + @prop({ required: true }) + salt: string; + @prop({ required: true, default: '' }) channelId: string; + // 游客相关 + @prop({ required: false, default: false }) + isGuest: boolean; // 是否是游客 @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 @prop({ required: false }) @@ -52,6 +80,9 @@ export default class User extends BaseModel { unionId: string; // 用户标识 }]; + @prop({ required: true, type: String, default: [], _id: false }) + device: string[]; + @prop({ required: true, default: 'com.bantu.zyz' }) pkgName: string; @@ -67,23 +98,68 @@ export default class User extends BaseModel { @prop({ required: true, default: 0 }) auth: number; - public static async updateToken(tel: string, token: string, platform: string, pkgName: string, serverType: string, lean = true) { - let user: UserType = await UserModel.findOne({ tel }).lean(); + public static async createUser(isGuest: boolean, tel: string, token: string, platform: string, pkgName: string, serverType: string, deviceId: string, lean = true) { const curTime: Date = new Date(); + + const uid = await CounterModel.getNewCounter(COUNTER.UID); + const userCode = genCode(8); + const doc = new UserModel(); let update = {}; - if (!user) { - const uid = await CounterModel.getNewCounter(COUNTER.UID); - const userCode = genCode(8); - const doc = new UserModel(); - update = Object.assign(update, { platform, pkgName, serverType, createTime: curTime, uid, userCode, username: `用户${uid}` }, doc.toJSON()); - } + 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 }); - 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; } + 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) { - 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; } @@ -119,6 +195,7 @@ export default class User extends BaseModel { } } + export const UserModel = getModelForClass(User); export interface UserType extends Pick, keyof User>{}; diff --git a/shared/pubUtils/timeUtil.ts b/shared/pubUtils/timeUtil.ts index d4335bbfd..035124519 100644 --- a/shared/pubUtils/timeUtil.ts +++ b/shared/pubUtils/timeUtil.ts @@ -93,4 +93,24 @@ export function getTodayZeroDate() { date.setMinutes(0); date.setSeconds(0); 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 } \ No newline at end of file diff --git a/web-server/app/controller/account.ts b/web-server/app/controller/account.ts index d3c2c6041..007d6e424 100644 --- a/web-server/app/controller/account.ts +++ b/web-server/app/controller/account.ts @@ -1,6 +1,13 @@ import { Controller } from 'egg'; 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() { const { ctx } = this; const { tel } = ctx.request.body; @@ -9,8 +16,20 @@ export default class AccountController extends Controller { public async smsLogin() { const { ctx } = this; - const { tel, code, platform, pkgName, serverType } = ctx.request.body; - ctx.body = await ctx.service.auth.smsLogin(tel, code, platform, pkgName, serverType); + const { tel, code, platform, pkgName, serverType, deviceId } = ctx.request.body; + 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() { diff --git a/web-server/app/router.ts b/web-server/app/router.ts index e7c535fb2..15f983671 100644 --- a/web-server/app/router.ts +++ b/web-server/app/router.ts @@ -5,8 +5,11 @@ export default (app: Application) => { const tokenParser = app.middleware.tokenParser(); router.get('/dev', controller.home.dev); router.get('/', controller.home.index); + router.post('/user/devicelogin', controller.account.deviceLogin); router.post('/user/getsms', controller.account.getSms); 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/createrole', tokenParser, controller.account.createRole); router.post('/game/getserverlist', tokenParser, controller.game.getServerList); diff --git a/web-server/app/service/Auth.ts b/web-server/app/service/Auth.ts index a02f4d372..a7a4e7891 100644 --- a/web-server/app/service/Auth.ts +++ b/web-server/app/service/Auth.ts @@ -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 { HeroModel } from '@db/Hero'; import { RoleModel } from '@db/Role'; -import { UserModel } from '@db/User'; +import { UserModel, UserType } from '@db/User'; import { STATUS } from '@consts'; import { smsModel } from '@db/Sms'; import { Service } from 'egg'; @@ -11,178 +11,283 @@ import { getHeroInfoById } from 'app/pubUtils/gamedata'; import { calPlayerCeAndSave, reCalAllHeroCe } from 'app/pubUtils/playerCe'; import { getExpByLv, getHeroExpByLv, gameData } from 'app/pubUtils/data'; import { isString } from 'underscore'; +import { getAge } from 'app/pubUtils/timeUtil'; +import { shouldRefresh } from 'app/pubUtils/util'; /** * Test Service */ export default class Auth extends Service { - public checkTelNo(telNo) { - if (!isString(telNo)) { - return { status: 1, data: '参数类型错误' }; - } - if (telNo.length !== 11) { - return { status: 1, data: '手机号长度错误' }; - } - return { status: 0, data: '手机号合法' }; - } + /** + * 设备免密登录 + * @param telNo - 用户手机号 + */ - async sendSmsCodeByGuodu(tel, code) { - const ctx = this.ctx; - const url = `http://221.179.172.68:8000/QxtSms/QxtFirewall?OperID=bantu3&OperPass=c8XcTffG&DesMobile=${tel}&Content=${encodeURIComponent(`【同人游戏】验证码${code},您正在登录赵云传,若非本人操作,请勿泄露`)}&Content_Code=1`; - const result = await ctx.curl(url, { - method: 'GET', - }); - return result.data; - } + public async deviceLogin(isGuest: boolean, token: string, deviceId: string, platform: string, pkgName: string, serverType: string) { + const ctx = this.ctx; - testLimit(sms, interval) { - if (sms.updateTime.getTime() > Date.now() - interval) { - return true; - } - return false; - } + let user = await UserModel.findUserByToken(token); - /** - * 用户获取手机验证码 - * @param telNo - 用户手机号 - */ - - public async getSms(tel: string) { - - const telVerify = this.checkTelNo(tel); - if (telVerify.status !== 0) { - return telVerify; - } - const sms = await smsModel.findByTel(tel, false); - if (sms) { - if (await sms.timeLimit(10000)) { - return this.ctx.service.utils.resResult(STATUS.SMS_IN_60S); - } - if (await sms.cntLimit(8)) { - return this.ctx.service.utils.resResult(STATUS.SMS_CNT_LIMIT); - } - } - - let code = ''; - if (sms && (!sms.used || sms.isFixed)) { - code = sms.code; - } else { - code = this.ctx.service.utils.generateNum(6); - } - - const smsResult = await this.sendSmsCodeByGuodu(tel, code); - console.log(smsResult); - await smsModel.updateByTel(tel, code, false, new Date(), sms?.hasSendToday() ? sms.countToday + 1 : 1); - return this.ctx.service.utils.resResult(STATUS.SUCCESS); - } - - /** - * 用户获取到手机验证码之后发送验证登录请求 - * @param tel 登录手机号 - * @param code 登录验证码 - * @param platform 平台 - * @param pkgName 包名 - * @param serverType 服务器类型 - */ - public async smsLogin(tel: string, code: string, platform: string, pkgName: string, serverType: string) { - const ctx = this.ctx; - // 参数检查 - const telVerify = this.checkTelNo(tel); - if (telVerify.status !== 0) { - return telVerify; - } - - // ! 测试阶段允许客户端不传验证码,此时不做验证码验证,直接注册或登录账号 - // TODO 上线前改掉或仅保留在测试服 - if (code !== '') { - if (!isString(code) || code.length !== 6) { - return ctx.service.utils.resResult(STATUS.WRONG_PARMS); - } - - // 手机验证码核验 - const smsValid: boolean = await smsModel.validateSms(tel, code); - if (!smsValid) { - const sms = await smsModel.findByTel(tel); - if(sms && sms.isFixed) { // 固定手机号登录 - if (sms.code !== code) { - return ctx.service.utils.resResult(STATUS.SMS_INVALID); - } + 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 { - return ctx.service.utils.resResult(STATUS.SMS_INVALID); + 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 + }); } - } + } - // 用户注册登录 - const token = ctx.service.utils.generateStr(256); - const user = await UserModel.updateToken(tel, token, platform, pkgName, serverType); - return ctx.service.utils.resResult(STATUS.SUCCESS, { token, userCode: user?.userCode }); - } - - public async checkRole(serverId: number) { - const ctx = this.ctx; - const { uid } = ctx; - const role = await RoleModel.findByUid(uid, serverId); - if (role) { - return ctx.service.utils.resResult(STATUS.SUCCESS, { roleId: role.roleId }); + 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 // 用户标识 + } } - return ctx.service.utils.resResult(STATUS.ROLE_NOT_FOUND); - } - public async createRole(serverId: number, roleName: string) { - console.log('enter Auth createRole'); - const ctx = this.ctx; - const { uid } = ctx; - const roleId = ctx.service.utils.genCode(10); - const code = ctx.service.utils.genCode(6); - const seqId = await Counter.getNewCounter(COUNTER.ROLE) || -1; - const role = await RoleModel.createRole(uid, serverId, { roleId, code, roleName, seqId, lv: DEFAULT_LV, exp: (getExpByLv(DEFAULT_LV - 1)||{sum:0}).sum||0 }); - if (role) { - let skinIds = new Array(); - for (let hid of DEFAULT_HEROES) { - let hero = await HeroModel.findByHidAndRole(hid, roleId); - if(hero) { - continue; + public checkTelNo(telNo) { + if (!isString(telNo)) { + return { status: 1, data: '参数类型错误' }; } - - let dicHero = getHeroInfoById(hid); - if(!dicHero) { - break; + if (telNo.length !== 11) { + return { status: 1, data: '手机号长度错误' }; } - let {quality, initialStars: star, jobid: job, name: hName, initialSkin} = dicHero; + return { status: 0, data: '手机号合法' }; + } - hero = await HeroModel.createHero({ - roleId, roleName: role.roleName, hid, hName, star, quality, job, serverId: role.serverId, - skins:[{id: initialSkin, enable: true}], lv: DEFAULT_HERO_LV, exp: getHeroExpByLv(DEFAULT_HERO_LV - 1)||0 + async sendSmsCodeByGuodu(tel, code) { + const ctx = this.ctx; + const url = `http://221.179.172.68:8000/QxtSms/QxtFirewall?OperID=bantu3&OperPass=c8XcTffG&DesMobile=${tel}&Content=${encodeURIComponent(`【同人游戏】验证码${code},您正在登录赵云传,若非本人操作,请勿泄露`)}&Content_Code=1`; + const result = await ctx.curl(url, { + method: 'GET', }); - skinIds.push(initialSkin); - await calPlayerCeAndSave(HERO_SYSTEM_TYPE.INIT, roleId, hero, {}, role); - - } - await reCalAllHeroCe(HERO_SYSTEM_TYPE.ADD_SKIN, roleId, {}, skinIds) - - for(let {id, count} of DEFAULT_ITEMS) { - let dicGoods = gameData.goods.get(id); - if(!dicGoods) continue; - let itidObj = ITID.get(dicGoods.itid); - if(!itidObj) continue; - await ctx.service.utils.addBags(roleId, role.roleName, {id, itemName: dicGoods.name, count, type: itidObj.type, hid: 0}); - } - - for(let {id, count} of DEFAULT_EQUIPS) { - for(let j = 0; j < count; j++) { - let dicGoods = gameData.goods.get(id); - if(!dicGoods) continue; - - await ctx.service.utils.addEquips(roleId, role.roleName, {id, ...dicGoods, hid: 0}); - } - } - await RoleModel.addGoldFree(roleId, DEFAULT_GOLD); - await RoleModel.addCoin(roleId, DEFAULT_COIN); - - return ctx.service.utils.resResult(STATUS.SUCCESS, { roleId: role.roleId }); + return result.data; + } + + testLimit(sms, interval) { + if (sms.updateTime.getTime() > Date.now() - interval) { + return true; + } + return false; + } + + /** + * 用户获取手机验证码 + * @param telNo - 用户手机号 + */ + + public async getSms(tel: string) { + + const telVerify = this.checkTelNo(tel); + if (telVerify.status !== 0) { + return telVerify; + } + const sms = await smsModel.findByTel(tel, false); + if (sms) { + if (await sms.timeLimit(10000)) { + return this.ctx.service.utils.resResult(STATUS.SMS_IN_60S); + } + if (await sms.cntLimit(8)) { + return this.ctx.service.utils.resResult(STATUS.SMS_CNT_LIMIT); + } + } + + let code = ''; + if (sms && (!sms.used || sms.isFixed)) { + code = sms.code; + } else { + code = this.ctx.service.utils.generateNum(6); + } + + const smsResult = await this.sendSmsCodeByGuodu(tel, code); + console.log(smsResult); + await smsModel.updateByTel(tel, code, false, new Date(), sms?.hasSendToday() ? sms.countToday + 1 : 1); + return this.ctx.service.utils.resResult(STATUS.SUCCESS); + } + + /** + * 用户获取到手机验证码之后发送验证登录请求 + * @param tel 登录手机号 + * @param code 登录验证码 + * @param platform 平台 + * @param pkgName 包名 + * @param serverType 服务器类型 + */ + public async smsLogin(tel: string, deviceId: string, code: string, platform: string, pkgName: string, serverType: string) { + const ctx = this.ctx; + // 参数检查 + const telVerify = this.checkTelNo(tel); + if (telVerify.status !== 0) { + return telVerify; + } + + // ! 测试阶段允许客户端不传验证码,此时不做验证码验证,直接注册或登录账号 + // TODO 上线前改掉或仅保留在测试服 + if (code !== '') { + if (!isString(code) || code.length !== 6) { + return ctx.service.utils.resResult(STATUS.WRONG_PARMS); + } + + // 手机验证码核验 + const smsValid: boolean = await smsModel.validateSms(tel, code); + if (!smsValid) { + const sms = await smsModel.findByTel(tel); + if (sms && sms.isFixed) { // 固定手机号登录 + if (sms.code !== code) { + return ctx.service.utils.resResult(STATUS.SMS_INVALID); + } + } else { + return ctx.service.utils.resResult(STATUS.SMS_INVALID); + } + } + } + + // 用户注册登录 + const token = ctx.service.utils.generateStr(256); + const user = await UserModel.createOrUpdate(false, tel, token, platform, pkgName, serverType, deviceId); + 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) { + const ctx = this.ctx; + const { uid } = ctx; + const role = await RoleModel.findByUid(uid, serverId); + if (role) { + return ctx.service.utils.resResult(STATUS.SUCCESS, { roleId: role.roleId }); + } + return ctx.service.utils.resResult(STATUS.ROLE_NOT_FOUND); + } + + public async createRole(serverId: number, roleName: string) { + console.log('enter Auth createRole'); + const ctx = this.ctx; + const { uid } = ctx; + const roleId = ctx.service.utils.genCode(10); + const code = ctx.service.utils.genCode(6); + const seqId = await Counter.getNewCounter(COUNTER.ROLE) || -1; + const role = await RoleModel.createRole(uid, serverId, { roleId, code, roleName, seqId, lv: DEFAULT_LV, exp: (getExpByLv(DEFAULT_LV - 1) || { sum: 0 }).sum || 0 }); + if (role) { + let skinIds = new Array(); + for (let hid of DEFAULT_HEROES) { + let hero = await HeroModel.findByHidAndRole(hid, roleId); + if (hero) { + continue; + } + + let dicHero = getHeroInfoById(hid); + if (!dicHero) { + break; + } + let { quality, initialStars: star, jobid: job, name: hName, initialSkin } = dicHero; + + hero = await HeroModel.createHero({ + roleId, roleName: role.roleName, hid, hName, star, quality, job, serverId: role.serverId, + skins: [{ id: initialSkin, enable: true }], lv: DEFAULT_HERO_LV, exp: getHeroExpByLv(DEFAULT_HERO_LV - 1) || 0 + }); + skinIds.push(initialSkin); + await calPlayerCeAndSave(HERO_SYSTEM_TYPE.INIT, roleId, hero, {}); + + } + await reCalAllHeroCe(HERO_SYSTEM_TYPE.ADD_SKIN, roleId, {}, skinIds) + + for (let { id, count } of DEFAULT_ITEMS) { + let dicGoods = gameData.goods.get(id); + if (!dicGoods) continue; + let itidObj = ITID.get(dicGoods.itid); + if (!itidObj) continue; + await ctx.service.utils.addBags(roleId, role.roleName, { id, itemName: dicGoods.name, count, type: itidObj.type, hid: 0 }); + } + + for (let { id, count } of DEFAULT_EQUIPS) { + for (let j = 0; j < count; j++) { + let dicGoods = gameData.goods.get(id); + if (!dicGoods) continue; + + await ctx.service.utils.addEquips(roleId, role.roleName, { id, ...dicGoods, hid: 0 }); + } + } + await RoleModel.addGoldFree(roleId, DEFAULT_GOLD); + await RoleModel.addCoin(roleId, DEFAULT_COIN); + + return ctx.service.utils.resResult(STATUS.SUCCESS, { roleId: role.roleId }); + } + return ctx.service.utils.resResult(STATUS.ROLE_NOT_FOUND); } - return ctx.service.utils.resResult(STATUS.ROLE_NOT_FOUND); - } }