import { getModelForClass, index, modelOptions, mongoose, pre, prop, ReturnModelType, Severity } from '@typegoose/typegoose' import { dbconn } from 'decorators/dbconn' // @ts-ignore import findOrCreate from 'mongoose-findorcreate' import { Base, TimeStamps } from '@typegoose/typegoose/lib/defaultClasses' import { BaseModule } from './Base' import { convert } from 'utils/number.util' const alphabet = '3fBCM8j17XNA9xYun4wmLWep2oHFlhPcgyEJskqOz6GK0UtV5ZRaDSvrTbidQI' export enum TaskStatusEnum { NOT_START = 0, RUNNING = 1, SUCCESS = 2, CLAIMED = 3, PART_SUCCESS = 4, } @modelOptions({ schemaOptions: { _id: false }, options: { allowMixed: Severity.ALLOW }}) export class TaskStatus { @prop() id: string @prop() task: string // 0: not start, 1: running, 2: success @prop({ enum: TaskStatusEnum, default: TaskStatusEnum.NOT_START }) status: TaskStatusEnum @prop() dateTag: string @prop() timeStart: number @prop() timeFinish: number @prop() timeClaim: number @prop({ type: mongoose.Schema.Types.Mixed }) data: any } interface ActivityUserClass extends Base, TimeStamps {} @dbconn() @index({ address: 1, activity: 1 }, { unique: true }) @index({ inviteCode: 1, activity: 1 }, { unique: true }) @index({ inviteUser: 1, activity: 1 }, { unique: false }) @index({ twitterId: 1 }, { unique: true, partialFilterExpression: { twitterId: { $exists: true } } }) @index({ discordId: 1 }, { unique: true, partialFilterExpression: { discordId: { $exists: true } } }) @modelOptions({ schemaOptions: { collection: 'activity_user', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) @pre('save', async function () { if (!this.inviteCode) { // 取ObjectId的time和inc段, // 将time段倒序(倒序后, 如果以0开始, 则移除0, 随机拼接一个hex字符), 然后拼接inc段, 再转换成52进制 let timeStr = this.id.slice(0, 8).split("").reverse().join(""); if (timeStr.indexOf('0') === 0) { let randomStr = convert({ numStr: (Math.random() * 51 | 0 + 1) + '', base:10, to: 52}) timeStr = randomStr + timeStr.slice(1) } let shortId = timeStr + this.id.slice(-6) this.inviteCode = convert({numStr: shortId, base: 16, to: 52, alphabet}) } }) class ActivityUserClass extends BaseModule { @prop({ required: true }) public address: string @prop({ required: true }) public activity: string // 用户的邀请码 @prop() public inviteCode: string // 用户的邀请人 @prop() public inviteUser: string @prop({ default: false }) public locked: boolean @prop() public lockedTime?: Date @prop() public comment?: string @prop() public twitterId?: string @prop() public twitterName?: string @prop() public discordId?: string @prop() public discordName?: string @prop({default: 1}) public boost: number /** * boost过期时间 * 计算得分时, 如果boost过期, 则不计算boost */ @prop() public boostExpire?: Date @prop() public lastLogin?: Date @prop({ type: () => [TaskStatus], default: [] }) public taskProgress?: TaskStatus[] public static async findByCode(this: ReturnModelType, inviteCode: string, activity: string) { return this.findOne({ inviteCode, activity }).exec() } } export const ActivityUser = getModelForClass(ActivityUserClass, { existingConnection: ActivityUserClass.db })