From 483af1973c5a84c51752b7ee59780fb3588d12d7 Mon Sep 17 00:00:00 2001 From: zhl Date: Thu, 28 Jan 2021 21:57:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E6=9C=BA=E5=99=A8=E4=BA=BA=E7=9A=84?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=AD=98=E5=85=A5account=E8=A1=A8,=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=9C=8D=E5=8A=A1=E7=AB=AF=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA=E4=BF=A1=E6=81=AF=E7=9A=84=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api.md | 13 +++- package-lock.json | 5 ++ package.json | 1 + src/constants/BaseConst.ts | 16 +++++ src/controllers/AccountController.ts | 104 ++++++++++++--------------- src/controllers/RecordController.ts | 4 +- src/dao/AccountDao.ts | 62 ++++++++++++++++ src/models/User.ts | 3 + src/redis/RedisClient.ts | 14 ++++ src/service/rank.ts | 20 +++++- src/utils/number.util.ts | 10 +++ src/utils/security.util.ts | 6 +- 12 files changed, 197 insertions(+), 61 deletions(-) create mode 100644 src/dao/AccountDao.ts create mode 100644 src/utils/number.util.ts diff --git a/docs/api.md b/docs/api.md index 2d618cb..48d3da1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -666,15 +666,24 @@ { nickname: '阿三', // 英雄id avatar: true, // 头像 + accountid: '' + score: 1000 } ``` ### 5. 随机获取一个机器人信息 -1. Method: GET +1. Method: POST 2. URI: /svr/randomrobot +> POST参数 +| 字段 | 说明 | +| -------- | -------------------------------------- | +| min |最小天梯分 | +| max |最高天梯分 | +| accounts |需要过滤的accountid数组 | + 3. Response: JSON @@ -683,6 +692,8 @@ { nickname: '阿三', // 英雄id avatar: true, // 头像 + accountid: '' + score: 1000 } ``` diff --git a/package-lock.json b/package-lock.json index 21094c9..4cfccc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -909,6 +909,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/package.json b/package.json index f78f908..1cf7db9 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "mime-types": "^2.1.28", "mongoose": "5.10.3", "mongoose-findorcreate": "^3.0.0", + "nanoid": "^3.1.20", "redis": "^2.8.0", "tracer": "^1.1.4", "urlencode": "^1.1.0" diff --git a/src/constants/BaseConst.ts b/src/constants/BaseConst.ts index b9e886b..ab9cd6c 100644 --- a/src/constants/BaseConst.ts +++ b/src/constants/BaseConst.ts @@ -81,4 +81,20 @@ export class BaseConst { */ public static readonly ROBOT_INFO_SEP = '|||' + + public static readonly RANK_SCORE = 'rank_score' + /** + * 机器人帐号id前缀 + */ + public static readonly ROBOT_PREFIX = '1000_3200_' + /** + * 机器人默认名 + */ + public static readonly DEFAULT_ROBOT_NAME = '匿名玩家' + /** + * 机器人默认头像 + */ + public static readonly DEFAULT_ROBOT_AVATAR = 'https://resource.kingsome.cn/matchvs_cdn/1.0.0.1/avatar/21_1.jpg' + + } diff --git a/src/controllers/AccountController.ts b/src/controllers/AccountController.ts index 614fa1a..4276d59 100644 --- a/src/controllers/AccountController.ts +++ b/src/controllers/AccountController.ts @@ -1,64 +1,23 @@ import BaseController from '../common/base.controller' import { role, router } from '../decorators/router' -import { User } from '../models/User' -import { ZError } from '../common/ZError' import { Card } from '../models/subdoc/Card' import { BaseConst } from '../constants/BaseConst' -import { Hero } from '../models/subdoc/Hero' import { BagItem, ItemType } from '../models/BagItem' -import { addHeroDefaultCardGroup } from '../dao/CardGroupDao' import { RedisClient } from '../redis/RedisClient' +import { checkGameing, setGameing, usersByScore } from '../service/rank' +import { fetchAccount } from '../dao/AccountDao' +import { generateId } from '../utils/security.util' +import { getRandom } from '../utils/number.util' export default class AccountController extends BaseController { @role('anon') @router('post /api/:accountid/uinfo') async info(req: any) { let { accountid, nickname, avatar } = req.params - let account = (await User.findOrCreate({ _id: accountid })).doc + const account = await fetchAccount({accountid, nickname, avatar}) + account.isnew = 0 + await account.save() let result: any = { accountid: account.id } - if (account.locked) { - throw new ZError(4, 'account locked') - } - const formulaCfg = global.$cfg.get(BaseConst.FORMULA) - if (account.season_score == -1) { - account.season_score = formulaCfg.get(70003).number - } - if (nickname) { - account.nickname = nickname - } - if (avatar) { - account.avatar = avatar - } - let cardMap = account.cardMap - for (let [, cfg] of global.$cfg.get(BaseConst.EFFECTCARD)) { - if (cfg.org_gift == 1 && cfg.type_id == 1 && !cardMap.has(cfg.id + '')) { - const card = new Card() - card.cardid = cfg.id - card.owned = true - card.ban = false - card.usetype = 0 - card.free = true - card.free_expire = 0 - card.time = Date.now() - cardMap.set(card.cardid + '', card) - } - } - for (let [, cfg] of global.$cfg.get(BaseConst.HERO)) { - if (cfg.org_gift == 1) { - let hero = new Hero() - hero.heroid = cfg.id - hero.free = true - hero.trial = false - hero.level = 1 - hero.slot = 1 - hero.time = Date.now() - hero.exp = 0 - if (!account.heros.has(cfg.id + '')) { - account.heros.set(cfg.id + '', hero) - await addHeroDefaultCardGroup(accountid, hero.heroid, cardMap) - } - } - } result.cards = [...account.cardMap.values()].map(o => o.toJson()) let heros: any[] = [] for (let [key, hero] of account.heros) { @@ -66,7 +25,6 @@ export default class AccountController extends BaseController { } result.heros = heros - await account.save() const moneyList = await BagItem.find({accountid, itemtype: ItemType.MONEY}) result.moneys = moneyList.map(o => o.toJson()) result.normal_stat = account.normal_stat @@ -80,6 +38,7 @@ export default class AccountController extends BaseController { async simpleInfo(req: any) { let account = req.user return { + accountid: account._id, nickname: account.nickname, avatar: account.avatar, score: account.season_score @@ -88,15 +47,46 @@ export default class AccountController extends BaseController { @router('post /svr/randomrobot') async randomRobot(req: any) { - let { min, max } = req.params - // @ts-ignore - let str: string = await new RedisClient().srandmember(BaseConst.ROBOT_INFO) - let arr = ['default', 'https://resource.kingsome.cn/matchvs_cdn/1.0.0.1/avatar/21_1.jpg'] - if (str) - arr = str.split(BaseConst.ROBOT_INFO_SEP) + let { min, max, accounts } = req.params + let accountSet = new Set(accounts) + let datas: any = await usersByScore(min, max) + let accountid, targetScore + if (datas.length > 0) { + for (let i = 0; i < datas.length; i += 2) { + let gameing = await checkGameing(datas[i]) + if (gameing) { + continue + } + if (!accountSet.has(datas[i]) ) { + accountid = datas[i] + targetScore = datas[i + 1] | 0 + break + } + } + } + if (!accountid) { + accountid = BaseConst.ROBOT_PREFIX + generateId() + targetScore = getRandom(min, max) | 0 + } + let account = await fetchAccount({accountid, robot: true}) + if (account.isnew) { + // @ts-ignore + let str: string = await new RedisClient().srandmember(BaseConst.ROBOT_INFO) + let arr = [BaseConst.DEFAULT_ROBOT_NAME, BaseConst.DEFAULT_ROBOT_AVATAR] + if (str) + arr = str.split(BaseConst.ROBOT_INFO_SEP) + account.nickname = arr[0] + account.avatar = arr[1] + account.season_score = targetScore + account.isnew = 0 + await account.save() + } + await setGameing(accountid) return { - nickname: arr[0], - avatar: arr[1] + accounid: account._id, + nickname: account.nickname, + avatar: account.avatar, + score: account.season_score } } diff --git a/src/controllers/RecordController.ts b/src/controllers/RecordController.ts index 9681d0b..06db6fc 100644 --- a/src/controllers/RecordController.ts +++ b/src/controllers/RecordController.ts @@ -9,6 +9,7 @@ import { MatchCfg } from '../cfg/parsers/MatchCfg' import ItemCtrl from '../logic/ItemCtrl' import { ItemInfo } from '../logic/ItemDef' import { BagItem } from '../models/BagItem' +import { updateRank } from '../service/rank' export default class RecordController extends BaseController { @role('anon') @@ -56,7 +57,8 @@ export default class RecordController extends BaseController { continue } - user.season_score = Math.max(user.season_score + player.scoreChange, fc.get(70002).number) + user.season_score = Math.max((user.season_score + player.scoreChange) | 0, fc.get(70002).number) + await updateRank(user._id, user.season_score) if (!user.season_data) { user.season_data = new Map() } diff --git a/src/dao/AccountDao.ts b/src/dao/AccountDao.ts new file mode 100644 index 0000000..c0db55d --- /dev/null +++ b/src/dao/AccountDao.ts @@ -0,0 +1,62 @@ +import { User } from '../models/User' +import { ZError } from '../common/ZError' +import { BaseConst } from '../constants/BaseConst' +import { updateRank } from '../service/rank' +import { Card } from '../models/subdoc/Card' +import { Hero } from '../models/subdoc/Hero' +import { addHeroDefaultCardGroup } from './CardGroupDao' + +export async function fetchAccount({ accountid, nickname, avatar, robot = false } + : { accountid: string, nickname?: string, avatar?: string, robot?: boolean }) { + let account = (await User.findOrCreate({ _id: accountid })).doc + if (account.locked) { + throw new ZError(4, 'account locked') + } + const formulaCfg = global.$cfg.get(BaseConst.FORMULA) + if (account.season_score == -1) { + account.season_score = formulaCfg.get(70003).number + await updateRank(accountid, account.season_score) + } + if (robot) { + account.robot = 1 + } + if (nickname) { + account.nickname = nickname + } + if (avatar) { + account.avatar = avatar + } + let cardMap = account.cardMap + for (let [, cfg] of global.$cfg.get(BaseConst.EFFECTCARD)) { + if (cfg.org_gift == 1 && cfg.type_id == 1 && !cardMap.has(cfg.id + '')) { + const card = new Card() + card.cardid = cfg.id + card.owned = true + card.ban = false + card.usetype = 0 + card.free = true + card.free_expire = 0 + card.time = Date.now() + cardMap.set(card.cardid + '', card) + } + } + for (let [, cfg] of global.$cfg.get(BaseConst.HERO)) { + if (cfg.org_gift == 1) { + let hero = new Hero() + hero.heroid = cfg.id + hero.free = true + hero.trial = false + hero.level = 1 + hero.slot = 1 + hero.time = Date.now() + hero.exp = 0 + if (!account.heros.has(cfg.id + '')) { + account.heros.set(cfg.id + '', hero) + await addHeroDefaultCardGroup(accountid, hero.heroid, cardMap) + } + } + } + + await account.save() + return account +} diff --git a/src/models/User.ts b/src/models/User.ts index b32546d..2df94f9 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -63,6 +63,9 @@ class UserClass extends FindOrCreate { @prop({default: 0}) public robot: number + + @prop({default: 1}) + public isnew: number /** * 已获得卡牌信息 */ diff --git a/src/redis/RedisClient.ts b/src/redis/RedisClient.ts index c819978..891db0b 100644 --- a/src/redis/RedisClient.ts +++ b/src/redis/RedisClient.ts @@ -156,6 +156,20 @@ export class RedisClient { }); } + public async zadd(key: string, value: any, member: string) { + return new Promise((resolve) => { + this.pub.zadd(key, value, member, resolve); + }); + } + public async zrangebyscore(key: string, min: number, max: number) { + return new Promise((resolve, reject) => { + this.pub.zrangebyscore(key, min, max, 'withscores', (err, data) => { + if (err) { return reject(err); } + resolve(data); + }); + }); + } + public async hset(key: string, field: string, value: string) { return new Promise((resolve) => { this.pub.hset(key, field, value, resolve); diff --git a/src/service/rank.ts b/src/service/rank.ts index fed48dc..dea3ca4 100644 --- a/src/service/rank.ts +++ b/src/service/rank.ts @@ -1,4 +1,22 @@ +import { RedisClient } from '../redis/RedisClient' +import { BaseConst } from '../constants/BaseConst' -export function updateRank(accountid: string, score: number) { +const MAX_TIME = 5000000000000 +export async function updateRank(accountid: string, score: number) { + let scoreL = parseFloat(`${score | 0}.${MAX_TIME - Date.now()}`) + await new RedisClient().zadd(BaseConst.RANK_SCORE, scoreL, accountid) } + +export async function usersByScore(min: number, max: number) { + return await new RedisClient().zrangebyscore(BaseConst.RANK_SCORE, min, max) +} + +export async function setGameing(accountid: string) { + await new RedisClient().setex('gameing_' + accountid, (Date.now() / 1000 | 0) + '', 30) +} + +export async function checkGameing(accountid: string) { + return await new RedisClient().get('gameing_' + accountid) +} + diff --git a/src/utils/number.util.ts b/src/utils/number.util.ts new file mode 100644 index 0000000..592b2df --- /dev/null +++ b/src/utils/number.util.ts @@ -0,0 +1,10 @@ +/** + * 随机数 + * @param {number} max + * @param {number} min + * @return {number} + */ +export function getRandom(max: number, min: number): number { + min = min || 0; + return Math.floor(Math.random()*(max-min)+min); +} diff --git a/src/utils/security.util.ts b/src/utils/security.util.ts index 5cb8e4e..aa2bbda 100644 --- a/src/utils/security.util.ts +++ b/src/utils/security.util.ts @@ -1,5 +1,5 @@ - import crypto from 'crypto' +import { nanoid } from 'nanoid' // 生成签名字段 // paramStr 为key1=val1&key2=val2, key1, key2按字母升序 @@ -8,3 +8,7 @@ export function createSign(secretKey: string, paramStr: string, timestamp: numbe paramStr = `${paramStr}:${timestamp}${secretKey}`; return crypto.createHash('md5').update(paramStr, 'utf8').digest('hex'); } + +export function generateId(length: number = 21) { + return nanoid(length); +}