增加任务repeat的支持

This commit is contained in:
CounterFire2023 2024-01-09 22:05:48 +08:00
parent a41fb9aa3a
commit 145a5cb06c
8 changed files with 78 additions and 45 deletions

View File

@ -105,6 +105,7 @@ SiweMessage的nonce说明(具体参考例子):
"title": "任务名", "title": "任务名",
"desc": "任务描述", "desc": "任务描述",
"type": 1, //任务类型, 1: 一次性任务, 2: 日常任务 "type": 1, //任务类型, 1: 一次性任务, 2: 日常任务
"repeat": 1, // 任务可重复次数
"pretasks": ["task id 1"], //前置任务 "pretasks": ["task id 1"], //前置任务
"score": 0, // 完成任务可获得的积分 "score": 0, // 完成任务可获得的积分
"category": "", // 任务分类 "category": "", // 任务分类

View File

@ -199,6 +199,7 @@
"task": "BurnNft", "task": "BurnNft",
"type": 1, "type": 1,
"show": true, "show": true,
"repeat": 2,
"title": "Hero NFT", "title": "Hero NFT",
"desc": "Click to burn and redeem Flame", "desc": "Click to burn and redeem Flame",
"category": "CF Pal", "category": "CF Pal",
@ -212,6 +213,7 @@
"task": "BurnNft", "task": "BurnNft",
"type": 1, "type": 1,
"show": true, "show": true,
"repeat": 2,
"title": "Candy Badge", "title": "Candy Badge",
"desc": "Click to burn and redeem Flame", "desc": "Click to burn and redeem Flame",
"category": "CF Pal", "category": "CF Pal",
@ -224,6 +226,7 @@
"task": "BurnNft", "task": "BurnNft",
"type": 1, "type": 1,
"show": true, "show": true,
"repeat": 2,
"title": "Explorer Badge", "title": "Explorer Badge",
"desc": "Click to burn and redeem Flame", "desc": "Click to burn and redeem Flame",
"category": "CF Pal", "category": "CF Pal",
@ -237,6 +240,7 @@
"task": "BurnNft", "task": "BurnNft",
"type": 1, "type": 1,
"show": true, "show": true,
"repeat": 2,
"title": "Gacha Badge", "title": "Gacha Badge",
"desc": "Click to burn and redeem Flame", "desc": "Click to burn and redeem Flame",
"category": "CF Pal", "category": "CF Pal",

View File

@ -96,12 +96,12 @@ export default class TasksController extends BaseController {
return t.id === preTask return t.id === preTask
}); });
if (!preTaskData || (preTaskData.status === TaskStatusEnum.NOT_START || preTaskData.status === TaskStatusEnum.RUNNING)) { if (!preTaskData || (preTaskData.status === TaskStatusEnum.NOT_START || preTaskData.status === TaskStatusEnum.RUNNING)) {
throw new ZError(14, 'task previous not end') throw new ZError(14, 'task previous not finish')
} }
} }
} }
let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task); let currentTask = user.taskProgress.find((t: TaskStatus) => t.id === task);
if (currentTask.status === TaskStatusEnum.SUCCESS || currentTask.status === TaskStatusEnum.CLAIMED) { if (currentTask.status === TaskStatusEnum.PART_SUCCESS || currentTask.status === TaskStatusEnum.SUCCESS || currentTask.status === TaskStatusEnum.CLAIMED) {
throw new ZError(15, 'task already end') throw new ZError(15, 'task already end')
} }
if (currentTask.status === TaskStatusEnum.NOT_START) { if (currentTask.status === TaskStatusEnum.NOT_START) {
@ -126,13 +126,13 @@ export default class TasksController extends BaseController {
if (!currentTask) { if (!currentTask) {
throw new ZError(11, 'task not found') throw new ZError(11, 'task not found')
} }
if (currentTask.status === TaskStatusEnum.SUCCESS || currentTask.status === TaskStatusEnum.CLAIMED) { if (currentTask.status === TaskStatusEnum.CLAIMED) {
return currentTask; return currentTask;
} }
if (currentTask.status === TaskStatusEnum.NOT_START) { if (currentTask.status === TaskStatusEnum.NOT_START) {
throw new ZError(11, 'task not begin'); throw new ZError(11, 'task not begin');
} }
if (currentTask.status === TaskStatusEnum.RUNNING) { if (currentTask.status === TaskStatusEnum.RUNNING || currentTask.status === TaskStatusEnum.PART_SUCCESS) {
let Task = require('../tasks/' + currentTask.task); let Task = require('../tasks/' + currentTask.task);
let taskInstance = new Task.default({user, activity}); let taskInstance = new Task.default({user, activity});
await taskInstance.execute({task: currentTask}); await taskInstance.execute({task: currentTask});
@ -152,7 +152,7 @@ export default class TasksController extends BaseController {
if (currentTask.status === TaskStatusEnum.CLAIMED) { if (currentTask.status === TaskStatusEnum.CLAIMED) {
throw new ZError(12, 'task already claimed') throw new ZError(12, 'task already claimed')
} }
if (currentTask.status !== TaskStatusEnum.SUCCESS) { if (currentTask.status !== TaskStatusEnum.SUCCESS && currentTask.status !== TaskStatusEnum.PART_SUCCESS) {
throw new ZError(13, 'task not end') throw new ZError(13, 'task not end')
} }

View File

@ -4,7 +4,7 @@ import { dbconn } from 'decorators/dbconn'
import findOrCreate from 'mongoose-findorcreate' import findOrCreate from 'mongoose-findorcreate'
import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses' import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses'
import { BaseModule } from './Base' import { BaseModule } from './Base'
import exp from 'constants'
export enum TaskTypeEnum { export enum TaskTypeEnum {
ONCE = 1, ONCE = 1,
@ -20,6 +20,8 @@ export class TaskCfg {
title: string title: string
@prop({ enum: TaskTypeEnum, default: TaskTypeEnum.ONCE }) @prop({ enum: TaskTypeEnum, default: TaskTypeEnum.ONCE })
type: TaskTypeEnum type: TaskTypeEnum
@prop({default: 1})
repeat: number
@prop() @prop()
desc: string desc: string
@prop({ type: mongoose.Schema.Types.Mixed }) @prop({ type: mongoose.Schema.Types.Mixed })
@ -76,6 +78,7 @@ class ActivityInfoClass extends BaseModule {
title: task.title, title: task.title,
desc: task.desc, desc: task.desc,
type: task.type, type: task.type,
repeat: task.repeat,
pretasks: task.pretasks, pretasks: task.pretasks,
score: task.score, score: task.score,
category: task.category, category: task.category,

View File

@ -13,6 +13,7 @@ export enum TaskStatusEnum {
RUNNING = 1, RUNNING = 1,
SUCCESS = 2, SUCCESS = 2,
CLAIMED = 3, CLAIMED = 3,
PART_SUCCESS = 4,
} }
@modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW }}) @modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW }})

View File

@ -10,16 +10,12 @@ import { BaseModule } from './Base'
class NftBurnRecordClass extends BaseModule { class NftBurnRecordClass extends BaseModule {
@prop({ required: true}) @prop({ required: true})
public user: string public user: string
@prop() @prop()
public chain: number public chain: number
@prop({ required: true}) @prop({ required: true})
public address: string public address: string
@prop() @prop()
public tokenId: number public tokenId: string
@prop() @prop()
public activity: string public activity: string
@prop() @prop()

View File

@ -21,19 +21,29 @@ export default class BurnNft extends ITask {
const localNft = await NftBurnRecord.find({ const localNft = await NftBurnRecord.find({
user: this.params.user.id, user: this.params.user.id,
chain, chain,
address address,
}) })
const localNftSet = new Set(); const localNftSet = new Set();
let finishAmount = 0;
let tmpNftSet = new Set();
for (let nft of localNft) { for (let nft of localNft) {
localNftSet.add(nft.tokenId) localNftSet.add(nft.tokenId)
if (nft.activity === this.params.activity.id && nft.task === task.id) {
finishAmount += 1
tmpNftSet.add(nft.tokenId)
} }
let finishNft }
let finishNfts = new Set();
for (let nft of nftList) { for (let nft of nftList) {
if (!localNftSet.has(nft.tokenId)) { if (!localNftSet.has(nft.tokenId)) {
finishNft = nft.tokenId finishNfts.add(nft.tokenId)
finishAmount += 1
if (finishAmount >= cfg.repeat) {
break; break;
} }
} }
}
for (let finishNft of finishNfts) {
const record = new NftBurnRecord({ const record = new NftBurnRecord({
user: this.params.user.id, user: this.params.user.id,
chain, chain,
@ -43,9 +53,18 @@ export default class BurnNft extends ITask {
task: task.id task: task.id
}) })
await record.save() await record.save()
}
let dataToSave = task.data || {}
dataToSave.tokenId = [...tmpNftSet, ...finishNfts]
dataToSave.finishAmount = finishAmount
task.data = dataToSave
task.markModified('data')
if (finishAmount >= cfg.repeat) {
task.status = TaskStatusEnum.SUCCESS task.status = TaskStatusEnum.SUCCESS
task.timeFinish = Date.now() task.timeFinish = Date.now()
task.data = {tokenId: finishNft} } else if (finishAmount > 0) {
task.status = TaskStatusEnum.PART_SUCCESS
}
try { try {
await this.params.user.save() await this.params.user.save()
} catch(err) { } catch(err) {
@ -60,5 +79,4 @@ export default class BurnNft extends ITask {
} }
return true return true
} }
} }

View File

@ -14,17 +14,20 @@ export abstract class ITask {
} }
abstract execute(data: any): Promise<boolean> abstract execute(data: any): Promise<boolean>
public async claimReward(task: TaskStatus) { public async claimReward(task: any) {
const user = this.params.user const user = this.params.user
const [taskId, dateTag] = task.id.split(':'); const [taskId, dateTag] = task.id.split(':');
const cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === taskId) const cfg = this.params.activity.tasks.find((t: TaskCfg) => t.id === taskId)
if (!cfg.score) { if (!cfg.score) {
return; return;
} }
let claimAmount = task.data.claimAmount || 0
let score = cfg.score let score = cfg.score
if (user.boost > 1 && Date.now() < user.boostExpire) { if (user.boost > 1 && Date.now() < user.boostExpire) {
score = Math.floor(score * user.boost) score = Math.floor(score * user.boost)
} }
let finishAmount = cfg.repeat > 1 ? task.data.finishAmount || 0 : 1
for (let i = claimAmount; i < Math.min(cfg.repeat, finishAmount); i++) {
await updateRankScore({ await updateRankScore({
user: user.id, user: user.id,
score: score, score: score,
@ -36,8 +39,15 @@ export abstract class ITask {
boost: user.boost boost: user.boost
} }
}) })
claimAmount += 1
}
task.data.claimAmount = claimAmount
task.markModified('data')
if ((cfg.repeat > 1 && claimAmount >= cfg.repeat)
|| (cfg.repeat === 1 && claimAmount >= 1)) {
task.status = TaskStatusEnum.CLAIMED task.status = TaskStatusEnum.CLAIMED
task.timeClaim = Date.now() task.timeClaim = Date.now()
}
await user.save() await user.save()
} }
} }