170 lines
5.5 KiB
TypeScript
170 lines
5.5 KiB
TypeScript
import { ActivityInfo } from 'models/ActivityInfo'
|
|
import { ActivityUser } from 'models/ActivityUser'
|
|
import { rankKey, rankLevel } from 'services/rank.svr'
|
|
import { BaseController, ROLE_ANON, ZError, ZRedisClient, role, router } from 'zutils'
|
|
import { SyncLocker } from 'common/SyncLocker'
|
|
import { ScoreRecord } from 'models/ScoreRecord'
|
|
import { formatAddress } from 'zutils/utils/chain.util'
|
|
import { formatNumShow, isValidShareCode } from 'common/Utils'
|
|
import { checkReCaptcha } from 'services/google.svr'
|
|
import { RANK_SCORE_SCALE, SCORE_INVITE_REBATE } from 'common/Constants'
|
|
import logger from 'logger/logger'
|
|
|
|
const MAX_LIMIT = 100
|
|
export default class ActivityController extends BaseController {
|
|
@role(ROLE_ANON)
|
|
@router('get /api/activity/:id')
|
|
async info(req) {
|
|
let { id } = req.params
|
|
if (!id) {
|
|
throw new ZError(11, 'invalid activity id')
|
|
}
|
|
let activity = await ActivityInfo.findById(id)
|
|
if (!activity) {
|
|
throw new ZError(12, 'activity not found')
|
|
}
|
|
return activity.toJson()
|
|
}
|
|
|
|
/**
|
|
* 更新邀请码
|
|
* @param req
|
|
* @returns
|
|
*/
|
|
@router('post /api/activity/upload_invite_code')
|
|
async uploadInviteCode(req) {
|
|
await new SyncLocker().checkLock(req)
|
|
logger.db('upload_invite_code', req)
|
|
await checkReCaptcha(req, 'invite_user')
|
|
let { code } = req.params
|
|
if (!isValidShareCode(code)) {
|
|
throw new ZError(11, 'invalid invite code')
|
|
}
|
|
let user = req.user
|
|
if (user.inviteUser) {
|
|
throw new ZError(11, 'invite user already set')
|
|
}
|
|
// if (!user.allTaskFinished()) {
|
|
// throw new ZError(12, 'not all tasks finished')
|
|
// }
|
|
// In production, users cannot invite themselves
|
|
if (process.env.NODE_ENV === 'production' && code === user.inviteCode) {
|
|
throw new ZError(13, 'cannot invite yourself')
|
|
}
|
|
let inviteUser = await ActivityUser.findByCode(code, user.activity)
|
|
if (!inviteUser) {
|
|
throw new ZError(12, 'invalid invite code')
|
|
}
|
|
// const dateTag = formatDate(new Date())
|
|
// // 更新邀请积分-邀请人
|
|
// await updateRankScore({
|
|
// user: inviteUser.id,
|
|
// score: shareCfg.inviter,
|
|
// activity: user.activity,
|
|
// scoreType: SCORE_INVITE_USER,
|
|
// scoreParams: {
|
|
// date: dateTag,
|
|
// user: user.id,
|
|
// },
|
|
// })
|
|
// // 更新邀请积分-被邀请人
|
|
// await updateRankScore({
|
|
// user: user.id,
|
|
// score: shareCfg.invitee,
|
|
// activity: user.activity,
|
|
// scoreType: SCORE_INVITE_INVITEE,
|
|
// scoreParams: {
|
|
// date: dateTag,
|
|
// user: inviteUser.id,
|
|
// },
|
|
// })
|
|
user.inviteUser = inviteUser.id
|
|
await user.save()
|
|
// return { score: shareCfg.invitee }
|
|
return { score: 0 }
|
|
}
|
|
|
|
/**
|
|
* 邀请列表
|
|
*/
|
|
@router('get /api/activity/invite_list')
|
|
async inviteUserList(req) {
|
|
let user = req.user
|
|
const totalKey = rankKey(user.activity)
|
|
const keyInvite = `${user.activity}:invite`
|
|
let users = await ActivityUser.find({ inviteUser: user.id })
|
|
let records = await ScoreRecord.find({ type: SCORE_INVITE_REBATE, user: user.id, activity: user.activity })
|
|
let scoreMap = new Map()
|
|
for (let r of records) {
|
|
if (!scoreMap.has(r.data.fromUser)) {
|
|
scoreMap.set(r.data.fromUser, 0)
|
|
}
|
|
scoreMap.set(r.data.fromUser, scoreMap.get(r.data.fromUser) + r.score)
|
|
}
|
|
let results = []
|
|
for (let u of users) {
|
|
const totalScore = await new ZRedisClient().zscore(totalKey, u.id)
|
|
// const inviteScore = await new ZRedisClient().zscore(keyInvite, `${user.id}_${u.id}`)
|
|
const score = totalScore ? parseInt(totalScore + '') / RANK_SCORE_SCALE : 0
|
|
const scoreInvite = scoreMap.get(u.id) || 0
|
|
results.push({
|
|
// user: u.id,
|
|
level: rankLevel(score),
|
|
nickname: u.twitterName || u.discordName || formatAddress(u.address),
|
|
avatar: u.twitterAvatar || '',
|
|
score: formatNumShow(score),
|
|
scoreInvite: formatNumShow(scoreInvite),
|
|
})
|
|
}
|
|
return results
|
|
}
|
|
|
|
/**
|
|
* 积分详情列表
|
|
*/
|
|
@router('get /api/activity/score_list')
|
|
async scoreList(req) {
|
|
let user = req.user
|
|
const records = await ScoreRecord.find({ user: user.id, activity: user.activity, score: { $gt: 0 } }).sort({
|
|
_id: -1,
|
|
})
|
|
return records.map(record => {
|
|
return {
|
|
score: formatNumShow(record.score),
|
|
type: record.type,
|
|
//@ts-ignore
|
|
time: record.createdAt.getTime(),
|
|
}
|
|
})
|
|
}
|
|
|
|
@role(ROLE_ANON)
|
|
@router('get /api/activity/leaderboard/:activity/:page')
|
|
async inviteCode(req) {
|
|
let { page, activity, limit } = req.params
|
|
page = parseInt(page || '0')
|
|
limit = parseInt(limit || MAX_LIMIT)
|
|
page = page < 0 ? 0 : page
|
|
const start = page * limit
|
|
const end = start + limit - 1
|
|
const records = await new ZRedisClient().zrevrange(`${activity}:score`, start, end)
|
|
let results: any = []
|
|
// const yesterdayKey = rankKey(activity, yesterday())
|
|
for (let i = 0; i < records.length; i += 2) {
|
|
const id = records[i]
|
|
let score = formatNumShow(parseInt(records[i + 1]) / RANK_SCORE_SCALE)
|
|
const user = await ActivityUser.findById(id)
|
|
const rank = start + i / 2 + 1
|
|
results.push({
|
|
rank,
|
|
level: rankLevel(rank),
|
|
nickname: user?.twitterName || user?.discordName || user?.address ? formatAddress(user.address) : 'unknown',
|
|
avatar: user?.twitterAvatar || '',
|
|
score,
|
|
// yesterday: yesterdayScore ? parseInt(yesterdayScore + '') : 0,
|
|
})
|
|
}
|
|
return results
|
|
}
|
|
}
|