增加用户状态的接口
This commit is contained in:
parent
eb9ce07ff5
commit
c08d7a465a
36
docs/api.md
36
docs/api.md
@ -108,7 +108,7 @@ SiweMessage的nonce说明(具体参考例子):
|
||||
"pretasks": ["task id 1"], //前置任务
|
||||
"score": 0, // 完成任务可获得的积分
|
||||
"category": "", // 任务分类
|
||||
"data": {}, // 其他一些任务相关配置参数, 比如icon, 或者其他未考虑的参数
|
||||
"cfg": {}, // 其他一些任务相关配置参数, 比如icon, 或者其他未考虑的参数
|
||||
"autoclaim": false // 任务完成后是否自动获取奖励
|
||||
}
|
||||
],
|
||||
@ -204,7 +204,7 @@ body:
|
||||
}
|
||||
```
|
||||
|
||||
###7.\* 获取任务奖励
|
||||
### 7.\* 获取任务奖励
|
||||
|
||||
#### Request
|
||||
|
||||
@ -277,9 +277,37 @@ body:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"rank": 1, // 排名
|
||||
"rank": 1, // 排名, 从1开始
|
||||
"address": "钱包地址",
|
||||
"score": 获得的积分
|
||||
"invite": "邀请人的地址",
|
||||
"score": 获得的积分,
|
||||
"yesterday": 上一日得分
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 10.\* 用户状态
|
||||
|
||||
#### Request
|
||||
|
||||
- URL:`/api/user/state`
|
||||
- 方法:`GET`
|
||||
- 头部:
|
||||
- Authorization: Bearer JWT_token
|
||||
|
||||
#### Response
|
||||
|
||||
```json
|
||||
{
|
||||
"address": "钱包地址",
|
||||
"boost": 1, // 正常值为1
|
||||
"twitterId": "",
|
||||
"twitterName: "",
|
||||
"discordId": "",
|
||||
"discordName": "",
|
||||
"scoreToday": 100, // 今日获得积分
|
||||
"scoreTotal": 200, // 总积分
|
||||
"invite": "邀请人address",
|
||||
"code": "自己的邀请码"
|
||||
}
|
||||
```
|
@ -12,7 +12,7 @@
|
||||
"score": 100,
|
||||
"category": "",
|
||||
"autoclaim": true,
|
||||
"data": {},
|
||||
"cfg": {"icon": "twitter"},
|
||||
"params": {}
|
||||
}, {
|
||||
"id": "e2fclylj30vwcpe0szl",
|
||||
@ -23,8 +23,8 @@
|
||||
"category": "Social Tasks",
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": ["TwitterConnect"],
|
||||
"data": {},
|
||||
"pretasks": ["e2yhq2lj30vwcpedv7p"],
|
||||
"cfg": {"account": "@_CounterFire", "icon": "twitter"},
|
||||
"params": {"time": 6, "failRate": 60}
|
||||
}, {
|
||||
"id": "e2feyflj30vwcpe0sjy",
|
||||
@ -35,8 +35,8 @@
|
||||
"category": "Social Tasks",
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": ["TwitterConnect"],
|
||||
"data": {},
|
||||
"pretasks": ["e2yhq2lj30vwcpedv7p"],
|
||||
"cfg": {"icon": "twitter"},
|
||||
"params": {"time": 6, "failRate": 60}
|
||||
}, {
|
||||
"id": "e2fuah0j30vwcpe0my7",
|
||||
@ -47,32 +47,32 @@
|
||||
"category": "Social Tasks",
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": ["TwitterConnect"],
|
||||
"data": {},
|
||||
"pretasks": ["e2yhq2lj30vwcpedv7p"],
|
||||
"cfg": {"icon": "twitter"},
|
||||
"params": {"time": 6, "failRate": 60}
|
||||
}, {
|
||||
"id": "e2far3lj30vwcpe0mh7",
|
||||
"task": "DiscordConnect",
|
||||
"title": "Connect Discord",
|
||||
"type": 2,
|
||||
"type": 1,
|
||||
"desc": "",
|
||||
"category": "",
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": [],
|
||||
"data": {},
|
||||
"cfg": {"icon": "discord"},
|
||||
"params": {}
|
||||
}, {
|
||||
"id": "e2far3lj30vwcpe0mf8",
|
||||
"task": "DiscordJoin",
|
||||
"title": "Join Discord",
|
||||
"type": 2,
|
||||
"desc": "",
|
||||
"category": "",
|
||||
"desc": "Join Counter Fire’s official Discord server",
|
||||
"category": "Social Tasks",
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": [],
|
||||
"data": {},
|
||||
"cfg": {"icon": "discord"},
|
||||
"params": {"time": 6, "failRate": 60}
|
||||
}, {
|
||||
"id": "e2fak2lj30vwcpe0awc",
|
||||
@ -83,8 +83,8 @@
|
||||
"category": "Social Tasks",
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": ["DiscordJoin"],
|
||||
"data": {},
|
||||
"pretasks": ["e2far3lj30vwcpe0mf8"],
|
||||
"cfg": {"icon": "discord"},
|
||||
"params": {"time": 6, "failRate": 60}
|
||||
}, {
|
||||
"id": "e2f7fplj30vwcpe0l98",
|
||||
@ -96,7 +96,7 @@
|
||||
"score": 100,
|
||||
"autoclaim": false,
|
||||
"pretasks": [],
|
||||
"data": {},
|
||||
"cfg": {"account": "okx", "icon": "okx"},
|
||||
"params": {}
|
||||
}, {
|
||||
"id": "e2f7t4lj30vwcpe0ldr",
|
||||
@ -105,7 +105,7 @@
|
||||
"show": false,
|
||||
"autoclaim": false,
|
||||
"pretasks": [],
|
||||
"data": {},
|
||||
"cfg": {},
|
||||
"params": {"score": [100, 20]}
|
||||
}],
|
||||
"startTime": 1702628292366,
|
||||
|
@ -4,6 +4,8 @@ import { role, router } from "decorators/router";
|
||||
import { ActivityInfo } from "models/ActivityInfo";
|
||||
import { ActivityUser } from "models/ActivityUser";
|
||||
import { RedisClient } from "redis/RedisClient";
|
||||
import { rankKey } from "services/rank.svr";
|
||||
import { yesterday } from "utils/date.util";
|
||||
|
||||
const MAX_LIMIT = 50
|
||||
export default class ActivityController extends BaseController {
|
||||
@ -46,25 +48,33 @@ export default class ActivityController extends BaseController {
|
||||
@role(ROLE_ANON)
|
||||
@router('get /api/activity/leaderboard/:activity/:page')
|
||||
async inviteCode(req) {
|
||||
let user = req.user;
|
||||
let { page, activity, limit } = req.params
|
||||
page = parseInt(page || '0')
|
||||
limit = parseInt(limit || MAX_LIMIT)
|
||||
if (page < 0) {
|
||||
page = 0
|
||||
}
|
||||
page = page < 0 ? 0 : page
|
||||
const start = page * limit
|
||||
const end = start + limit - 1
|
||||
let records = await new RedisClient().zrevrange(`${activity}:score`, start, end)
|
||||
const records = await new RedisClient().zrevrange(`${activity}:score`, start, end)
|
||||
let results: any = []
|
||||
const yesterdayKey = rankKey(activity, yesterday());
|
||||
for (let i = 0; i < records.length; i+=2) {
|
||||
let id = records[i]
|
||||
const id = records[i]
|
||||
let score = parseInt(records[i + 1])
|
||||
let user = await ActivityUser.findById(id)
|
||||
const user = await ActivityUser.findById(id)
|
||||
let invite = ''
|
||||
if (user.inviteUser) {
|
||||
const inviteUser = await ActivityUser.findById(user.inviteUser)
|
||||
if (inviteUser) {
|
||||
invite = inviteUser.address
|
||||
}
|
||||
}
|
||||
const yesterdayScore = await new RedisClient().zscore(yesterdayKey, id)
|
||||
results.push({
|
||||
rank: start + i / 2 + 1,
|
||||
address: user?.address || 'unknow',
|
||||
score
|
||||
invite,
|
||||
score,
|
||||
yesterday: yesterdayScore ? parseInt(yesterdayScore+'') : 0
|
||||
})
|
||||
}
|
||||
return results;
|
||||
|
@ -5,7 +5,10 @@ import logger from 'logger/logger'
|
||||
import { ActivityUser } from 'models/ActivityUser'
|
||||
import {DEFAULT_EXPIRED, NonceRecord} from 'models/NonceRecord'
|
||||
import { LoginRecordQueue } from 'queue/loginrecord.queue'
|
||||
import { RedisClient } from 'redis/RedisClient'
|
||||
import { rankKey } from 'services/rank.svr'
|
||||
import {SiweMessage} from 'siwe'
|
||||
import { yesterday } from 'utils/date.util'
|
||||
import { checkParamsNeeded } from 'utils/net.util'
|
||||
import { aesDecrypt, base58ToHex } from 'utils/security.util'
|
||||
|
||||
@ -74,6 +77,35 @@ class SignController extends BaseController {
|
||||
const token = await res.jwtSign({ id: accountData.id, address: accountData.address, activity: accountData.activity })
|
||||
return { token }
|
||||
}
|
||||
|
||||
@router('get /api/user/state')
|
||||
async userInfo(req){
|
||||
const user = req.user;
|
||||
const todayKey = rankKey(user.activity, new Date());
|
||||
const todayScore = await new RedisClient().zscore(todayKey, user.id)
|
||||
const totalKey = rankKey(user.activity);
|
||||
const totalScore = await new RedisClient().zscore(totalKey, user.id)
|
||||
let invite = ''
|
||||
if (user.inviteUser) {
|
||||
const inviteUser = await ActivityUser.findById(user.inviteUser)
|
||||
if (inviteUser) {
|
||||
invite = inviteUser.address
|
||||
}
|
||||
}
|
||||
let result = {
|
||||
address: user.address,
|
||||
boost: user.boost || 1,
|
||||
twitterId: user.twitterId,
|
||||
twitterName: user.twitterName,
|
||||
discordId: user.discordId,
|
||||
discordName: user.discordName,
|
||||
scoreToday: todayScore ? parseInt(todayScore+'') : 0,
|
||||
scoreTotal: totalScore ? parseInt(totalScore+'') : 0,
|
||||
invite,
|
||||
code: user.inviteCode,
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* regist user by token from wallet-svr
|
||||
* TODO::
|
||||
|
@ -33,7 +33,7 @@ export class TaskCfg {
|
||||
@prop({default: true})
|
||||
show: boolean
|
||||
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||
data: any
|
||||
cfg: any
|
||||
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||
params: any
|
||||
}
|
||||
@ -80,7 +80,7 @@ class ActivityInfoClass extends BaseModule {
|
||||
score: task.score,
|
||||
category: task.category,
|
||||
autoclaim: task.autoclaim,
|
||||
data: task.data,
|
||||
cfg: task.cfg,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -79,8 +79,17 @@ class ActivityUserClass extends BaseModule {
|
||||
|
||||
@prop()
|
||||
public twitterId?: string
|
||||
|
||||
@prop()
|
||||
public twitterName?: string
|
||||
|
||||
@prop()
|
||||
public discordId?: string
|
||||
@prop()
|
||||
public discordName?: string
|
||||
|
||||
@prop({default: 1})
|
||||
public boost: number
|
||||
|
||||
@prop()
|
||||
public lastLogin?: Date
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ScoreRecord } from "models/ScoreRecord";
|
||||
import { RedisClient } from "redis/RedisClient";
|
||||
import { formatDate } from "utils/date.util";
|
||||
|
||||
/**
|
||||
* 更新排行榜
|
||||
@ -31,13 +32,34 @@ export const updateRankScore = async ({
|
||||
data: scoreParams
|
||||
})
|
||||
await record.save();
|
||||
const key = `${activity}:score`
|
||||
let scoreSaved = await new RedisClient().zscore(key, user) + '';
|
||||
const key = rankKey(activity);
|
||||
await updateRank(key, score, user);
|
||||
// add daily score
|
||||
const dailyKey = rankKey(activity, new Date());
|
||||
await updateRank(dailyKey, score, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新排行榜
|
||||
* @param key
|
||||
* @param score
|
||||
* @param member
|
||||
*/
|
||||
const updateRank = async (key: string, score: number, member: string) => {
|
||||
let scoreSaved = await new RedisClient().zscore(key, member) + '';
|
||||
if (scoreSaved) {
|
||||
scoreSaved = scoreSaved.substring(0, scoreSaved.indexOf('.'))
|
||||
}
|
||||
let scoreOld = parseInt(scoreSaved || '0');
|
||||
score = score + scoreOld;
|
||||
const scoreToSave = score + 1 - (Date.now() / 1000 / 10000000000)
|
||||
await new RedisClient().zadd(key, scoreToSave, user);
|
||||
await new RedisClient().zadd(key, scoreToSave, member);
|
||||
}
|
||||
|
||||
export const rankKey = (activity: string, date?: Date) => {
|
||||
if (!date) {
|
||||
return `${activity}:score`
|
||||
}
|
||||
const dateTag = formatDate(date);
|
||||
return `${activity}:score:${dateTag}`
|
||||
}
|
@ -24,7 +24,8 @@ export default class DiscordConnect extends ITask {
|
||||
task.status = TaskStatusEnum.SUCCESS
|
||||
task.timeFinish = Date.now()
|
||||
task.data = res.data.data
|
||||
task.discordId = res.data.data.userid
|
||||
this.params.user.discordId = res.data.data.userid
|
||||
this.params.user.discordName = res.data.data.username
|
||||
try {
|
||||
await this.params.user.save()
|
||||
} catch(err) {
|
||||
|
@ -22,7 +22,8 @@ export default class TwitterConnect extends ITask {
|
||||
task.status = TaskStatusEnum.SUCCESS
|
||||
task.timeFinish = Date.now()
|
||||
task.data = res.data.data
|
||||
task.twitterId = res.data.data.userid
|
||||
this.params.user.twitterId = res.data.data.userid
|
||||
this.params.user.twitterName = res.data.data.username
|
||||
try {
|
||||
await this.params.user.save()
|
||||
} catch(err) {
|
||||
|
@ -5,3 +5,10 @@ export const formatDate = (date: Date): string => {
|
||||
const day = (date.getDate() + '').padStart(2, '0');
|
||||
return `${year}${month}${day}`;
|
||||
};
|
||||
|
||||
// get formated datestring of yesterday
|
||||
export const yesterday = () => {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 1);
|
||||
return date;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user