import { COUNTER, DEFAULT_LV, ADULT_AGE, GUEST_MAX_TIME, TASK_TYPE } from '@consts'; import { RoleModel } from '@db/Role'; import { UserModel, UserType } from '@db/User'; import { STATUS, GET_SMS_TYPE, ADDICTION_PREVENTION_CODE } from '@consts'; import { smsModel } from '@db/Sms'; import { Service } from 'egg'; import Counter from '@db/Counter'; import { getExpByLv } from '../pubUtils/data'; import { isString } from 'underscore'; import { getAge } from '../pubUtils/timeUtil'; import { resResult } from '../pubUtils/util'; import { checkTeeanAgerTime } from '../pubUtils/authenticateUtil'; import { authenticate } from '../pubUtils/httpUtil'; import { checkTask, getCreateUserFuncs } from 'app/pubUtils/taskUtil'; /** * Test Service */ export default class Auth extends Service { /** * 设备免密登录 * @param telNo - 用户手机号 */ public async deviceLogin(isGuest: boolean, token: string, deviceId: string, platform: string, pkgName: string, serverType: string, getuiCID: string) { const ctx = this.ctx; const ip = ctx.request.ip; let user = await UserModel.findUserByToken(token); if (!user) { if (isGuest) { const tel = ctx.service.utils.genCode(10); const token = ctx.service.utils.generateStr(256); let lastGuest = await UserModel.getLastDeviceGuest(deviceId, token); if (lastGuest) { let param = this.getReturnParam(lastGuest); return this.ctx.service.utils.resResult(STATUS.SUCCESS, { canLogin: true, // 未设置密码等于未创建账号 ...param }); } else { user = await UserModel.createUser(isGuest, tel, token, platform, pkgName, serverType, deviceId, 0, ip); if (getuiCID) {//更新个推cid await UserModel.updateGetuiCID(tel, getuiCID); } 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, ip); if (getuiCID) {//更新个推cid await UserModel.updateGetuiCID(user.tel, getuiCID); } let param = this.getReturnParam(user); let canLogin = true; if (!user.isGuest && !user.hasSetPw) canLogin = false; return this.ctx.service.utils.resResult(STATUS.SUCCESS, { canLogin, // 未设置密码等于未创建账号 ...param }); } } private getReturnParam(user: UserType) { let age = getAge(user.birthday); let isAdult = age >= ADULT_AGE; let todayPlayTime = user.todayPlayTime; let type = checkTeeanAgerTime(isAdult, todayPlayTime); if ((user.isGuest || !user.hasAuthenticated) && user.guestTime > GUEST_MAX_TIME) { type = ADDICTION_PREVENTION_CODE.GUEST; } return { tel: user.tel, isGuest: !!user.isGuest, guestTime: user.guestTime, // 游客已体验时间 hasAuthenticated: !!user.hasAuthenticated, // 是否进行过实名认证 isAdult, // 是否已成年 todayPlayTime, // 今天已游戏时长 type, // 防沉迷类型 hasSetPw: user.hasSetPw, // 是否设置了密码 token: user.token, // 用户token userCode: user.userCode // 用户标识 } } public checkTelNo(telNo) { if (!isString(telNo)) { return { status: 1, resResult: resResult(STATUS.WRONG_PARMS) }; } if (telNo.length !== 11) { return { status: 1, resResult: resResult(STATUS.TEL_LEN_ERR) }; } return { status: 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', }); return result.data; } testLimit(sms, interval) { if (sms.updateTime.getTime() > Date.now() - interval) { return true; } return false; } /** * 用户获取手机验证码 * @param telNo - 用户手机号 */ public async getSms(type: number, tel: string) { const ctx = this.ctx; const telVerify = this.checkTelNo(tel); if (telVerify.status !== 0) { return telVerify.resResult; } if (type == GET_SMS_TYPE.BIND) { let telUser = await UserModel.findUserByTel(tel); if (telUser && telUser.uid != ctx.uid) { return ctx.service.utils.resResult(STATUS.TEL_HAS_USED); } } 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); let user = await UserModel.findUserByTel(tel); return this.ctx.service.utils.resResult(STATUS.SUCCESS, { hasAccount: !!user && !!user.hasSetPw }); } /** * 用户获取到手机验证码之后发送验证登录请求 * @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, getuiCID: string) { const ctx = this.ctx; const ip = ctx.request.ip; // 参数检查 const telVerify = this.checkTelNo(tel); if (telVerify.status !== 0) { return telVerify.resResult; } // ! 测试阶段允许客户端不传验证码,此时不做验证码验证,直接注册或登录账号 // 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, ip); if (getuiCID) {//更新个推cid await UserModel.updateGetuiCID(tel, getuiCID); } let param = this.getReturnParam(user); return ctx.service.utils.resResult(STATUS.SUCCESS, param); } /** * 给策划用于直接获得手机号验证码 * @param tel 登录手机号 */ public async getSmsCode(tel: string) { const ctx = this.ctx; const sms = await smsModel.findByTel(tel); if (sms) { return ctx.service.utils.resResult(STATUS.SUCCESS, { code: sms?.code }); } else { return ctx.service.utils.resResult(STATUS.SMS_INVALID); } } /** * 设置密码 * @param password 密码 */ public async setPassword(password: string) { const { ctx } = this; const { uid } = ctx; if (!password) return ctx.service.utils.resResult(STATUS.PASSWORD_ILLEGEL); 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, getuiCID: string) { const ctx = this.ctx; // 参数检查 const telVerify = this.checkTelNo(tel); if (telVerify.status !== 0) { return telVerify.resResult; } // 用户注册登录 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); if (getuiCID) {//更新个推cid await UserModel.updateGetuiCID(tel, getuiCID); } 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) { 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: roleId, seqId, lv: DEFAULT_LV, exp: (getExpByLv(DEFAULT_LV - 1) || { sum: 0 }).sum || 0 }); if (role) { let funcs = await getCreateUserFuncs(role.funcs, role.lv); // 任务 await checkTask(roleId, TASK_TYPE.ROLE_LV, role.lv, false, {}, funcs); return ctx.service.utils.resResult(STATUS.SUCCESS, { roleId: role.roleId }); } return ctx.service.utils.resResult(STATUS.ROLE_NOT_FOUND); } /** * 绑定账号 * @param tel 手机号 * @param code 验证码 */ public async bind(tel: string, code: string) { const ctx = this.ctx; // 参数检查 const telVerify = this.checkTelNo(tel); if (telVerify.status !== 0) { return telVerify.resResult; } 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); } } let telUser = await UserModel.findUserByTel(tel); if (telUser && telUser.uid != ctx.uid) { return ctx.service.utils.resResult(STATUS.TEL_HAS_USED); } 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); } /** * 实名认证 * @param password 密码 */ public async authentication(name: string, idNum: string) { const ctx = this.ctx; // TODO 接SDK console.log(name, idNum); let result = await authenticate(name, idNum, ctx.userCode, ctx.pkgName); if (!result) return ctx.service.utils.resResult(STATUS.AUTHEN_FAIL); let birthday = this.getBirthdayByIdCard(idNum); let user = await UserModel.authentication(ctx.uid, birthday, ''); let param = this.getReturnParam(user); return ctx.service.utils.resResult(STATUS.SUCCESS, param); } private getBirthdayByIdCard(idNum: string) { let year = idNum.slice(6, 10); let month = idNum.slice(10, 12); let date = idNum.slice(12, 14); return `${year}-${month}-${date}`; } }