增加用户状态的接口

This commit is contained in:
CounterFire2023 2024-01-05 12:54:57 +08:00
parent eb9ce07ff5
commit c08d7a465a
10 changed files with 146 additions and 36 deletions

View File

@ -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": "自己的邀请码"
}
```

View File

@ -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 Fires 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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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