task-svr/src/controllers/tasks.controller.ts
2024-04-17 11:15:59 +08:00

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