246 lines
8.2 KiB
TypeScript
246 lines
8.2 KiB
TypeScript
import { ZError, SyncLocker, BaseController, router } from 'zutils'
|
|
import { TaskCfg, TaskTypeEnum } from 'models/ActivityInfo'
|
|
import { TaskStatus, TaskStatusEnum } from 'models/ActivityUser'
|
|
import { join } from 'path'
|
|
import { formatDate } from 'utils/utcdate.util'
|
|
import { GeneralScription } from 'models/chain/GeneralScription'
|
|
import { BASE_TASK_TICKET } from 'common/Constants'
|
|
import { BASE_TASK_REWARD, SIGN_SEQ, TicketRecord } from 'models/TicketRecord'
|
|
import { ActivityGame } from 'models/ActivityGame'
|
|
import { formatNumShow } from 'common/Utils'
|
|
import { fetchClaimStatus } from 'services/chain.svr'
|
|
import logger from 'logger/logger'
|
|
const fs = require('fs')
|
|
|
|
const prod = process.env.NODE_ENV === 'production'
|
|
|
|
const initTasks = () => {
|
|
const tasks = join(__dirname, '../tasks')
|
|
const list = new Map()
|
|
fs.readdirSync(tasks)
|
|
.filter((file: string) => ~file.search(/^[^.].*\.(ts|js)$/))
|
|
.forEach((file: any) => {
|
|
const Task = require('../tasks/' + file)
|
|
list.set(Task.default.name, {
|
|
name: Task.default.name,
|
|
desc: Task.default.desc,
|
|
show: Task.default.show,
|
|
})
|
|
})
|
|
return list
|
|
}
|
|
const allTasks = initTasks()
|
|
|
|
export default class TasksController extends BaseController {
|
|
@router('post /api/tasks/progress')
|
|
async taskProgress(req) {
|
|
let user = req.user
|
|
let activity = req.activity
|
|
const dateTag = formatDate(new Date())
|
|
let taskAddedSet = new Set()
|
|
for (let task of user.taskProgress) {
|
|
if (task.dateTag) {
|
|
taskAddedSet.add(task.id + ':' + task.dateTag)
|
|
} else {
|
|
taskAddedSet.add(task.id)
|
|
}
|
|
}
|
|
let modified = false
|
|
let visibleTasks = new Set()
|
|
for (let task of activity.tasks) {
|
|
if (!allTasks.has(task.task)) {
|
|
continue
|
|
}
|
|
if (!task.isVaild()) {
|
|
continue
|
|
}
|
|
if (task.type === TaskTypeEnum.DAILY) {
|
|
let id = `${task.id}:${dateTag}`
|
|
if (!taskAddedSet.has(id)) {
|
|
modified = true
|
|
user.taskProgress.push({ id, task: task.task, dateTag: dateTag, status: TaskStatusEnum.NOT_START })
|
|
}
|
|
if (task.show) visibleTasks.add(id)
|
|
} else if (task.type === TaskTypeEnum.ONCE) {
|
|
if (!taskAddedSet.has(task.id)) {
|
|
modified = true
|
|
user.taskProgress.push({ id: task.id, task: task.task, status: TaskStatusEnum.NOT_START })
|
|
}
|
|
if (task.show) visibleTasks.add(task.id)
|
|
}
|
|
}
|
|
if (modified) {
|
|
await user.save()
|
|
}
|
|
return user.taskProgress.filter((t: TaskStatus) => visibleTasks.has(t.id))
|
|
}
|
|
|
|
@router('post /api/tasks/begin_task')
|
|
async beginTask(req) {
|
|
new SyncLocker().checkLock(req)
|
|
logger.db('begin_task', req)
|
|
let user = req.user
|
|
let activity = req.activity
|
|
let { task } = req.params
|
|
const [taskId, dateTag] = task.split(':')
|
|
const currentDateTag = formatDate(new Date())
|
|
if (dateTag && currentDateTag !== dateTag) {
|
|
throw new ZError(11, 'task date not match')
|
|
}
|
|
if (!activity.isVaild()) {
|
|
throw new ZError(15, 'activity not start or end')
|
|
}
|
|
let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId)
|
|
if (!cfg) {
|
|
throw new ZError(12, 'task not found')
|
|
}
|
|
if (!cfg.isVaild()) {
|
|
throw new ZError(16, 'task not start or end')
|
|
}
|
|
if (dateTag && cfg.type !== TaskTypeEnum.DAILY) {
|
|
throw new ZError(13, 'task is not daily task')
|
|
}
|
|
if (cfg.pretasks && cfg.pretasks.length > 0) {
|
|
for (let preTask of cfg.pretasks) {
|
|
let preTaskData = user.taskProgress.find((t: TaskStatus) => {
|
|
if (preTask.type === TaskTypeEnum.DAILY) {
|
|
//这种情况下只要完成一次就算完成了
|
|
return t.id.indexOf(preTask) > -1
|
|
}
|
|
return t.id === preTask
|
|
})
|
|
if (
|
|
!preTaskData ||
|
|
preTaskData.status === TaskStatusEnum.NOT_START ||
|
|
preTaskData.status === TaskStatusEnum.RUNNING
|
|
) {
|
|
throw new ZError(14, 'task previous not finish')
|
|
}
|
|
}
|
|
}
|
|
let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task)
|
|
if (
|
|
currentTask.status === TaskStatusEnum.PART_SUCCESS ||
|
|
currentTask.status === TaskStatusEnum.SUCCESS ||
|
|
currentTask.status === TaskStatusEnum.CLAIMED
|
|
) {
|
|
throw new ZError(15, 'task already end')
|
|
}
|
|
if (currentTask.status === TaskStatusEnum.NOT_START) {
|
|
currentTask.timeStart = Date.now()
|
|
currentTask.status = TaskStatusEnum.RUNNING
|
|
}
|
|
await user.save()
|
|
return currentTask
|
|
}
|
|
|
|
@router('post /api/tasks/check_task')
|
|
async checkTask(req) {
|
|
const user = req.user
|
|
logger.db('chect_task', req)
|
|
const activity = req.activity
|
|
const { task } = req.params
|
|
const [taskId, dateTag] = task.split(':')
|
|
const currentDateTag = formatDate(new Date())
|
|
if (dateTag && currentDateTag !== dateTag) {
|
|
throw new ZError(11, 'task date not match')
|
|
}
|
|
if (!activity?.isVaild()) {
|
|
throw new ZError(15, 'activity not start or end')
|
|
}
|
|
let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId)
|
|
if (!cfg) {
|
|
throw new ZError(12, 'task not found')
|
|
}
|
|
if (!cfg.isVaild()) {
|
|
throw new ZError(16, 'task not start or end')
|
|
}
|
|
let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task)
|
|
if (!currentTask) {
|
|
throw new ZError(11, 'task not found')
|
|
}
|
|
if (currentTask.status === TaskStatusEnum.CLAIMED) {
|
|
return currentTask
|
|
}
|
|
if (currentTask.status === TaskStatusEnum.NOT_START) {
|
|
throw new ZError(11, 'task not begin')
|
|
}
|
|
if (currentTask.status === TaskStatusEnum.RUNNING || currentTask.status === TaskStatusEnum.PART_SUCCESS) {
|
|
let Task = require('../tasks/' + currentTask.task)
|
|
let taskInstance = new Task.default({ user, activity })
|
|
await taskInstance.execute({ task: currentTask })
|
|
}
|
|
return currentTask
|
|
}
|
|
|
|
@router('post /api/tasks/claim')
|
|
async claimTask(req) {
|
|
new SyncLocker().checkLock(req)
|
|
logger.db('claim_task', req)
|
|
const user = req.user
|
|
const activity = req.activity
|
|
const { task } = req.params
|
|
const [taskId, dateTag] = task.split(':')
|
|
if (!activity?.isVaild()) {
|
|
throw new ZError(15, 'activity not start or end')
|
|
}
|
|
let cfg = activity.tasks.find((t: TaskCfg) => t.id === taskId)
|
|
if (!cfg) {
|
|
throw new ZError(14, 'task not found')
|
|
}
|
|
if (!cfg.isVaild()) {
|
|
throw new ZError(16, 'task not start or end')
|
|
}
|
|
let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task)
|
|
if (!currentTask) {
|
|
throw new ZError(11, 'task not found')
|
|
}
|
|
if (currentTask.status === TaskStatusEnum.CLAIMED) {
|
|
throw new ZError(12, 'task already claimed')
|
|
}
|
|
if (currentTask.status !== TaskStatusEnum.SUCCESS && currentTask.status !== TaskStatusEnum.PART_SUCCESS) {
|
|
throw new ZError(13, 'task not end')
|
|
}
|
|
if (cfg.checkChain) {
|
|
const chainRecord = await GeneralScription.findOne({
|
|
from: user.address.toLowerCase(),
|
|
op: 'task_claim',
|
|
data: task,
|
|
})
|
|
if (!chainRecord) {
|
|
throw new ZError(14, 'waiting for chain confirm')
|
|
}
|
|
// let result = await fetchClaimStatus(user.address.toLowerCase(), task)
|
|
// if (!result) {
|
|
// throw new ZError(15, 'waiting for chain confirm')
|
|
// }
|
|
}
|
|
|
|
const Task = require('../tasks/' + currentTask.task)
|
|
const taskInstance = new Task.default({ user, activity })
|
|
const { score } = await taskInstance.claimReward(currentTask)
|
|
const baseTaskSet = new Set(activity.baseTasks)
|
|
let count = 0
|
|
for (let task of user.taskProgress) {
|
|
if (baseTaskSet.has(task.id) && task.status === TaskStatusEnum.CLAIMED) {
|
|
count++
|
|
}
|
|
}
|
|
let ticket = 0
|
|
if (count === baseTaskSet.size && baseTaskSet.has(taskId)) {
|
|
// all base task finished, give ticket
|
|
ticket = BASE_TASK_TICKET
|
|
const ticketRecord = new TicketRecord({
|
|
user: user.id,
|
|
activity: user.activity,
|
|
type: BASE_TASK_REWARD,
|
|
data: { task: currentTask.task },
|
|
score: ticket,
|
|
})
|
|
await ActivityGame.updateOne({ user: user.id, activity: user.activity }, { $inc: { tickets: ticket } })
|
|
await ticketRecord.save()
|
|
}
|
|
return { status: currentTask.status, score: formatNumShow(score), ticket }
|
|
}
|
|
}
|