增加用户状态的接口
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"], //前置任务
|
"pretasks": ["task id 1"], //前置任务
|
||||||
"score": 0, // 完成任务可获得的积分
|
"score": 0, // 完成任务可获得的积分
|
||||||
"category": "", // 任务分类
|
"category": "", // 任务分类
|
||||||
"data": {}, // 其他一些任务相关配置参数, 比如icon, 或者其他未考虑的参数
|
"cfg": {}, // 其他一些任务相关配置参数, 比如icon, 或者其他未考虑的参数
|
||||||
"autoclaim": false // 任务完成后是否自动获取奖励
|
"autoclaim": false // 任务完成后是否自动获取奖励
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -204,7 +204,7 @@ body:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
###7.\* 获取任务奖励
|
### 7.\* 获取任务奖励
|
||||||
|
|
||||||
#### Request
|
#### Request
|
||||||
|
|
||||||
@ -277,9 +277,37 @@ body:
|
|||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"rank": 1, // 排名
|
"rank": 1, // 排名, 从1开始
|
||||||
"address": "钱包地址",
|
"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,
|
"score": 100,
|
||||||
"category": "",
|
"category": "",
|
||||||
"autoclaim": true,
|
"autoclaim": true,
|
||||||
"data": {},
|
"cfg": {"icon": "twitter"},
|
||||||
"params": {}
|
"params": {}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2fclylj30vwcpe0szl",
|
"id": "e2fclylj30vwcpe0szl",
|
||||||
@ -23,8 +23,8 @@
|
|||||||
"category": "Social Tasks",
|
"category": "Social Tasks",
|
||||||
"score": 100,
|
"score": 100,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": ["TwitterConnect"],
|
"pretasks": ["e2yhq2lj30vwcpedv7p"],
|
||||||
"data": {},
|
"cfg": {"account": "@_CounterFire", "icon": "twitter"},
|
||||||
"params": {"time": 6, "failRate": 60}
|
"params": {"time": 6, "failRate": 60}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2feyflj30vwcpe0sjy",
|
"id": "e2feyflj30vwcpe0sjy",
|
||||||
@ -35,8 +35,8 @@
|
|||||||
"category": "Social Tasks",
|
"category": "Social Tasks",
|
||||||
"score": 100,
|
"score": 100,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": ["TwitterConnect"],
|
"pretasks": ["e2yhq2lj30vwcpedv7p"],
|
||||||
"data": {},
|
"cfg": {"icon": "twitter"},
|
||||||
"params": {"time": 6, "failRate": 60}
|
"params": {"time": 6, "failRate": 60}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2fuah0j30vwcpe0my7",
|
"id": "e2fuah0j30vwcpe0my7",
|
||||||
@ -47,32 +47,32 @@
|
|||||||
"category": "Social Tasks",
|
"category": "Social Tasks",
|
||||||
"score": 100,
|
"score": 100,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": ["TwitterConnect"],
|
"pretasks": ["e2yhq2lj30vwcpedv7p"],
|
||||||
"data": {},
|
"cfg": {"icon": "twitter"},
|
||||||
"params": {"time": 6, "failRate": 60}
|
"params": {"time": 6, "failRate": 60}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2far3lj30vwcpe0mh7",
|
"id": "e2far3lj30vwcpe0mh7",
|
||||||
"task": "DiscordConnect",
|
"task": "DiscordConnect",
|
||||||
"title": "Connect Discord",
|
"title": "Connect Discord",
|
||||||
"type": 2,
|
"type": 1,
|
||||||
"desc": "",
|
"desc": "",
|
||||||
"category": "",
|
"category": "",
|
||||||
"score": 100,
|
"score": 100,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": [],
|
"pretasks": [],
|
||||||
"data": {},
|
"cfg": {"icon": "discord"},
|
||||||
"params": {}
|
"params": {}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2far3lj30vwcpe0mf8",
|
"id": "e2far3lj30vwcpe0mf8",
|
||||||
"task": "DiscordJoin",
|
"task": "DiscordJoin",
|
||||||
"title": "Join Discord",
|
"title": "Join Discord",
|
||||||
"type": 2,
|
"type": 2,
|
||||||
"desc": "",
|
"desc": "Join Counter Fire’s official Discord server",
|
||||||
"category": "",
|
"category": "Social Tasks",
|
||||||
"score": 100,
|
"score": 100,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": [],
|
"pretasks": [],
|
||||||
"data": {},
|
"cfg": {"icon": "discord"},
|
||||||
"params": {"time": 6, "failRate": 60}
|
"params": {"time": 6, "failRate": 60}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2fak2lj30vwcpe0awc",
|
"id": "e2fak2lj30vwcpe0awc",
|
||||||
@ -83,8 +83,8 @@
|
|||||||
"category": "Social Tasks",
|
"category": "Social Tasks",
|
||||||
"score": 100,
|
"score": 100,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": ["DiscordJoin"],
|
"pretasks": ["e2far3lj30vwcpe0mf8"],
|
||||||
"data": {},
|
"cfg": {"icon": "discord"},
|
||||||
"params": {"time": 6, "failRate": 60}
|
"params": {"time": 6, "failRate": 60}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2f7fplj30vwcpe0l98",
|
"id": "e2f7fplj30vwcpe0l98",
|
||||||
@ -96,7 +96,7 @@
|
|||||||
"score": 100,
|
"score": 100,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": [],
|
"pretasks": [],
|
||||||
"data": {},
|
"cfg": {"account": "okx", "icon": "okx"},
|
||||||
"params": {}
|
"params": {}
|
||||||
}, {
|
}, {
|
||||||
"id": "e2f7t4lj30vwcpe0ldr",
|
"id": "e2f7t4lj30vwcpe0ldr",
|
||||||
@ -105,7 +105,7 @@
|
|||||||
"show": false,
|
"show": false,
|
||||||
"autoclaim": false,
|
"autoclaim": false,
|
||||||
"pretasks": [],
|
"pretasks": [],
|
||||||
"data": {},
|
"cfg": {},
|
||||||
"params": {"score": [100, 20]}
|
"params": {"score": [100, 20]}
|
||||||
}],
|
}],
|
||||||
"startTime": 1702628292366,
|
"startTime": 1702628292366,
|
||||||
|
@ -4,6 +4,8 @@ import { role, router } from "decorators/router";
|
|||||||
import { ActivityInfo } from "models/ActivityInfo";
|
import { ActivityInfo } from "models/ActivityInfo";
|
||||||
import { ActivityUser } from "models/ActivityUser";
|
import { ActivityUser } from "models/ActivityUser";
|
||||||
import { RedisClient } from "redis/RedisClient";
|
import { RedisClient } from "redis/RedisClient";
|
||||||
|
import { rankKey } from "services/rank.svr";
|
||||||
|
import { yesterday } from "utils/date.util";
|
||||||
|
|
||||||
const MAX_LIMIT = 50
|
const MAX_LIMIT = 50
|
||||||
export default class ActivityController extends BaseController {
|
export default class ActivityController extends BaseController {
|
||||||
@ -46,25 +48,33 @@ export default class ActivityController extends BaseController {
|
|||||||
@role(ROLE_ANON)
|
@role(ROLE_ANON)
|
||||||
@router('get /api/activity/leaderboard/:activity/:page')
|
@router('get /api/activity/leaderboard/:activity/:page')
|
||||||
async inviteCode(req) {
|
async inviteCode(req) {
|
||||||
let user = req.user;
|
|
||||||
let { page, activity, limit } = req.params
|
let { page, activity, limit } = req.params
|
||||||
page = parseInt(page || '0')
|
page = parseInt(page || '0')
|
||||||
limit = parseInt(limit || MAX_LIMIT)
|
limit = parseInt(limit || MAX_LIMIT)
|
||||||
if (page < 0) {
|
page = page < 0 ? 0 : page
|
||||||
page = 0
|
|
||||||
}
|
|
||||||
const start = page * limit
|
const start = page * limit
|
||||||
const end = start + limit - 1
|
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 = []
|
let results: any = []
|
||||||
|
const yesterdayKey = rankKey(activity, yesterday());
|
||||||
for (let i = 0; i < records.length; i+=2) {
|
for (let i = 0; i < records.length; i+=2) {
|
||||||
let id = records[i]
|
const id = records[i]
|
||||||
let score = parseInt(records[i + 1])
|
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({
|
results.push({
|
||||||
rank: start + i / 2 + 1,
|
rank: start + i / 2 + 1,
|
||||||
address: user?.address || 'unknow',
|
address: user?.address || 'unknow',
|
||||||
score
|
invite,
|
||||||
|
score,
|
||||||
|
yesterday: yesterdayScore ? parseInt(yesterdayScore+'') : 0
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
|
@ -5,7 +5,10 @@ import logger from 'logger/logger'
|
|||||||
import { ActivityUser } from 'models/ActivityUser'
|
import { ActivityUser } from 'models/ActivityUser'
|
||||||
import {DEFAULT_EXPIRED, NonceRecord} from 'models/NonceRecord'
|
import {DEFAULT_EXPIRED, NonceRecord} from 'models/NonceRecord'
|
||||||
import { LoginRecordQueue } from 'queue/loginrecord.queue'
|
import { LoginRecordQueue } from 'queue/loginrecord.queue'
|
||||||
|
import { RedisClient } from 'redis/RedisClient'
|
||||||
|
import { rankKey } from 'services/rank.svr'
|
||||||
import {SiweMessage} from 'siwe'
|
import {SiweMessage} from 'siwe'
|
||||||
|
import { yesterday } from 'utils/date.util'
|
||||||
import { checkParamsNeeded } from 'utils/net.util'
|
import { checkParamsNeeded } from 'utils/net.util'
|
||||||
import { aesDecrypt, base58ToHex } from 'utils/security.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 })
|
const token = await res.jwtSign({ id: accountData.id, address: accountData.address, activity: accountData.activity })
|
||||||
return { token }
|
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
|
* regist user by token from wallet-svr
|
||||||
* TODO::
|
* TODO::
|
||||||
|
@ -33,7 +33,7 @@ export class TaskCfg {
|
|||||||
@prop({default: true})
|
@prop({default: true})
|
||||||
show: boolean
|
show: boolean
|
||||||
@prop({ type: mongoose.Schema.Types.Mixed })
|
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||||
data: any
|
cfg: any
|
||||||
@prop({ type: mongoose.Schema.Types.Mixed })
|
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||||
params: any
|
params: any
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ class ActivityInfoClass extends BaseModule {
|
|||||||
score: task.score,
|
score: task.score,
|
||||||
category: task.category,
|
category: task.category,
|
||||||
autoclaim: task.autoclaim,
|
autoclaim: task.autoclaim,
|
||||||
data: task.data,
|
cfg: task.cfg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,8 +79,17 @@ class ActivityUserClass extends BaseModule {
|
|||||||
|
|
||||||
@prop()
|
@prop()
|
||||||
public twitterId?: string
|
public twitterId?: string
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public twitterName?: string
|
||||||
|
|
||||||
@prop()
|
@prop()
|
||||||
public discordId?: string
|
public discordId?: string
|
||||||
|
@prop()
|
||||||
|
public discordName?: string
|
||||||
|
|
||||||
|
@prop({default: 1})
|
||||||
|
public boost: number
|
||||||
|
|
||||||
@prop()
|
@prop()
|
||||||
public lastLogin?: Date
|
public lastLogin?: Date
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { ScoreRecord } from "models/ScoreRecord";
|
import { ScoreRecord } from "models/ScoreRecord";
|
||||||
import { RedisClient } from "redis/RedisClient";
|
import { RedisClient } from "redis/RedisClient";
|
||||||
|
import { formatDate } from "utils/date.util";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新排行榜
|
* 更新排行榜
|
||||||
@ -31,13 +32,34 @@ export const updateRankScore = async ({
|
|||||||
data: scoreParams
|
data: scoreParams
|
||||||
})
|
})
|
||||||
await record.save();
|
await record.save();
|
||||||
const key = `${activity}:score`
|
const key = rankKey(activity);
|
||||||
let scoreSaved = await new RedisClient().zscore(key, user) + '';
|
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) {
|
if (scoreSaved) {
|
||||||
scoreSaved = scoreSaved.substring(0, scoreSaved.indexOf('.'))
|
scoreSaved = scoreSaved.substring(0, scoreSaved.indexOf('.'))
|
||||||
}
|
}
|
||||||
let scoreOld = parseInt(scoreSaved || '0');
|
let scoreOld = parseInt(scoreSaved || '0');
|
||||||
score = score + scoreOld;
|
score = score + scoreOld;
|
||||||
const scoreToSave = score + 1 - (Date.now() / 1000 / 10000000000)
|
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.status = TaskStatusEnum.SUCCESS
|
||||||
task.timeFinish = Date.now()
|
task.timeFinish = Date.now()
|
||||||
task.data = res.data.data
|
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 {
|
try {
|
||||||
await this.params.user.save()
|
await this.params.user.save()
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
@ -22,7 +22,8 @@ export default class TwitterConnect extends ITask {
|
|||||||
task.status = TaskStatusEnum.SUCCESS
|
task.status = TaskStatusEnum.SUCCESS
|
||||||
task.timeFinish = Date.now()
|
task.timeFinish = Date.now()
|
||||||
task.data = res.data.data
|
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 {
|
try {
|
||||||
await this.params.user.save()
|
await this.params.user.save()
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
@ -5,3 +5,10 @@ export const formatDate = (date: Date): string => {
|
|||||||
const day = (date.getDate() + '').padStart(2, '0');
|
const day = (date.getDate() + '').padStart(2, '0');
|
||||||
return `${year}${month}${day}`;
|
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