将机器人的信息存入account表, 增加服务端获取机器人信息的接口

This commit is contained in:
zhl 2021-01-28 21:57:57 +08:00
parent 8dcac22067
commit 483af1973c
12 changed files with 197 additions and 61 deletions

View File

@ -666,15 +666,24 @@
{ {
nickname: '阿三', // 英雄id nickname: '阿三', // 英雄id
avatar: true, // 头像 avatar: true, // 头像
accountid: ''
score: 1000
} }
``` ```
### 5. 随机获取一个机器人信息 ### 5. 随机获取一个机器人信息
1. Method: GET 1. Method: POST
2. URI: /svr/randomrobot 2. URI: /svr/randomrobot
> POST参数
| 字段 | 说明 |
| -------- | -------------------------------------- |
| min |最小天梯分 |
| max |最高天梯分 |
| accounts |需要过滤的accountid数组 |
3. Response: JSON 3. Response: JSON
@ -683,6 +692,8 @@
{ {
nickname: '阿三', // 英雄id nickname: '阿三', // 英雄id
avatar: true, // 头像 avatar: true, // 头像
accountid: ''
score: 1000
} }
``` ```

5
package-lock.json generated
View File

@ -909,6 +909,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "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": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",

View File

@ -28,6 +28,7 @@
"mime-types": "^2.1.28", "mime-types": "^2.1.28",
"mongoose": "5.10.3", "mongoose": "5.10.3",
"mongoose-findorcreate": "^3.0.0", "mongoose-findorcreate": "^3.0.0",
"nanoid": "^3.1.20",
"redis": "^2.8.0", "redis": "^2.8.0",
"tracer": "^1.1.4", "tracer": "^1.1.4",
"urlencode": "^1.1.0" "urlencode": "^1.1.0"

View File

@ -81,4 +81,20 @@ export class BaseConst {
*/ */
public static readonly ROBOT_INFO_SEP = '|||' 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'
} }

View File

@ -1,64 +1,23 @@
import BaseController from '../common/base.controller' import BaseController from '../common/base.controller'
import { role, router } from '../decorators/router' import { role, router } from '../decorators/router'
import { User } from '../models/User'
import { ZError } from '../common/ZError'
import { Card } from '../models/subdoc/Card' import { Card } from '../models/subdoc/Card'
import { BaseConst } from '../constants/BaseConst' import { BaseConst } from '../constants/BaseConst'
import { Hero } from '../models/subdoc/Hero'
import { BagItem, ItemType } from '../models/BagItem' import { BagItem, ItemType } from '../models/BagItem'
import { addHeroDefaultCardGroup } from '../dao/CardGroupDao'
import { RedisClient } from '../redis/RedisClient' 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 { export default class AccountController extends BaseController {
@role('anon') @role('anon')
@router('post /api/:accountid/uinfo') @router('post /api/:accountid/uinfo')
async info(req: any) { async info(req: any) {
let { accountid, nickname, avatar } = req.params 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 } 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()) result.cards = [...account.cardMap.values()].map(o => o.toJson())
let heros: any[] = [] let heros: any[] = []
for (let [key, hero] of account.heros) { for (let [key, hero] of account.heros) {
@ -66,7 +25,6 @@ export default class AccountController extends BaseController {
} }
result.heros = heros result.heros = heros
await account.save()
const moneyList = await BagItem.find({accountid, itemtype: ItemType.MONEY}) const moneyList = await BagItem.find({accountid, itemtype: ItemType.MONEY})
result.moneys = moneyList.map(o => o.toJson()) result.moneys = moneyList.map(o => o.toJson())
result.normal_stat = account.normal_stat result.normal_stat = account.normal_stat
@ -80,6 +38,7 @@ export default class AccountController extends BaseController {
async simpleInfo(req: any) { async simpleInfo(req: any) {
let account = req.user let account = req.user
return { return {
accountid: account._id,
nickname: account.nickname, nickname: account.nickname,
avatar: account.avatar, avatar: account.avatar,
score: account.season_score score: account.season_score
@ -88,15 +47,46 @@ export default class AccountController extends BaseController {
@router('post /svr/randomrobot') @router('post /svr/randomrobot')
async randomRobot(req: any) { async randomRobot(req: any) {
let { min, max } = req.params let { min, max, accounts } = req.params
// @ts-ignore let accountSet = new Set(accounts)
let str: string = await new RedisClient().srandmember(BaseConst.ROBOT_INFO) let datas: any = await usersByScore(min, max)
let arr = ['default', 'https://resource.kingsome.cn/matchvs_cdn/1.0.0.1/avatar/21_1.jpg'] let accountid, targetScore
if (str) if (datas.length > 0) {
arr = str.split(BaseConst.ROBOT_INFO_SEP) 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 { return {
nickname: arr[0], accounid: account._id,
avatar: arr[1] nickname: account.nickname,
avatar: account.avatar,
score: account.season_score
} }
} }

View File

@ -9,6 +9,7 @@ import { MatchCfg } from '../cfg/parsers/MatchCfg'
import ItemCtrl from '../logic/ItemCtrl' import ItemCtrl from '../logic/ItemCtrl'
import { ItemInfo } from '../logic/ItemDef' import { ItemInfo } from '../logic/ItemDef'
import { BagItem } from '../models/BagItem' import { BagItem } from '../models/BagItem'
import { updateRank } from '../service/rank'
export default class RecordController extends BaseController { export default class RecordController extends BaseController {
@role('anon') @role('anon')
@ -56,7 +57,8 @@ export default class RecordController extends BaseController {
continue 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) { if (!user.season_data) {
user.season_data = new Map() user.season_data = new Map()
} }

62
src/dao/AccountDao.ts Normal file
View File

@ -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
}

View File

@ -63,6 +63,9 @@ class UserClass extends FindOrCreate {
@prop({default: 0}) @prop({default: 0})
public robot: number public robot: number
@prop({default: 1})
public isnew: number
/** /**
* *
*/ */

View File

@ -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) { public async hset(key: string, field: string, value: string) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.pub.hset(key, field, value, resolve); this.pub.hset(key, field, value, resolve);

View File

@ -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)
}

10
src/utils/number.util.ts Normal file
View File

@ -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);
}

View File

@ -1,5 +1,5 @@
import crypto from 'crypto' import crypto from 'crypto'
import { nanoid } from 'nanoid'
// 生成签名字段 // 生成签名字段
// paramStr 为key1=val1&key2=val2, key1, key2按字母升序 // paramStr 为key1=val1&key2=val2, key1, key2按字母升序
@ -8,3 +8,7 @@ export function createSign(secretKey: string, paramStr: string, timestamp: numbe
paramStr = `${paramStr}:${timestamp}${secretKey}`; paramStr = `${paramStr}:${timestamp}${secretKey}`;
return crypto.createHash('md5').update(paramStr, 'utf8').digest('hex'); return crypto.createHash('md5').update(paramStr, 'utf8').digest('hex');
} }
export function generateId(length: number = 21) {
return nanoid(length);
}