diff --git a/package.json b/package.json index f48925a..c5e8c49 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,8 @@ "additem": "ts-node -r tsconfig-paths/register src/addboxdata.ts", "taskid": "ts-node -r tsconfig-paths/register src/generateTaskId.ts", "token": "ts-node -r tsconfig-paths/register src/generateToken.ts", + "ingame": "ts-node -r tsconfig-paths/register src/fixIngame.ts", + "testdraw": "ts-node -r tsconfig-paths/register src/testdraw.ts", "test:watch": "jest --watch", "test": "jest" }, diff --git a/src/controllers/mail.controller.ts b/src/controllers/mail.controller.ts index f277a64..c770b0b 100644 --- a/src/controllers/mail.controller.ts +++ b/src/controllers/mail.controller.ts @@ -16,7 +16,7 @@ import { SyncLocker } from 'common/SyncLocker' class MailController extends BaseController { /** - * 通过邮件, 密码形式的登录 + * 通过邮件验证码形式的登录 */ @router('post /api/user/verify_email') async loginWithEmail(req, res) { @@ -41,13 +41,10 @@ class MailController extends BaseController { if (userCheck && userCheck.id !== user.id) { throw new ZError(13, 'Email already binded to another account') } - let recordCode = await CodeRecord.findByEmail(email, CodeType.LOGIN) + let recordCode = await CodeRecord.findByEmail(user.id, email, CodeType.LOGIN) if (!recordCode) { throw new ZError(14, 'code expired') } - if (recordCode.status !== CodeStatus.PENDING) { - throw new ZError(15, 'code expired') - } if (recordCode.code !== code) { throw new ZError(16, 'code error') } @@ -84,8 +81,8 @@ class MailController extends BaseController { throw new ZError(13, 'Email already binded to another account') } type = parseInt(type) - let record = await CodeRecord.findByEmail(email, type) - if (!record || record.status === CodeStatus.EXPIRED || record.status === CodeStatus.FAIL) { + let record = await CodeRecord.findByEmail(user.id, email, type) + if (!record || record.user !== user.id) { record = new CodeRecord({ email, type, code: DEFAULT_CODE, user: user.id }) await record.save() } @@ -109,14 +106,19 @@ class MailController extends BaseController { } setImmediate(async () => { try { - let result = await new EmailSvr().sendMail(msgData) - record.mailSend = true - record.emailId = result.messageId - record.expiredAt = Date.now() + DEFAULT_EXPIRE_TIME + let { errcode, errmsg, data } = await new EmailSvr().sendMail(msgData) + if (errcode) { + logger.info(`error send mail:: email: ${email}, type: ${type}, errcode: ${errcode}, errmsg: ${errmsg}`) + record.status = CodeStatus.FAIL + } else { + logger.info(`success send mail:: email: ${email}, type: ${type}, messageId: ${data.messageId}`) + record.mailSend = true + record.emailId = data.messageId + record.expiredAt = Date.now() + DEFAULT_EXPIRE_TIME + } await record.save() } catch (err) { - logger.info(`error send mail:: email: ${email}, type: ${type}`) - logger.error(err) + logger.info(`error send mail:: email: ${email}, type: ${type}, errmsg: ${err.message || err}`) record.status = CodeStatus.FAIL await record.save() } diff --git a/src/fixIngame.ts b/src/fixIngame.ts new file mode 100644 index 0000000..0f8995f --- /dev/null +++ b/src/fixIngame.ts @@ -0,0 +1,67 @@ +import mongoose from 'mongoose' +import * as dotenv from 'dotenv' + +const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development' +dotenv.config({ path: envFile }) +console.log(process.env.DB_MAIN) +import { InGameScoreRecord } from 'models/InGameScoreRecord' +import { InGameTaskRecord } from 'models/InGameTaskRecord' +import { InGameStats } from 'models/InGameStats' +import { NftClaimRecord } from 'models/NftClaimRecord' +import { DeleteRecord } from 'models/DeleteRecord' +;(async () => { + try { + let taskRecords = await InGameTaskRecord.find({ ticket: { $gt: 0 } }) + let scoreRecords = await InGameScoreRecord.find({ type: 'draw_ingame' }) + let claimRecords = await NftClaimRecord.find({}) + let taskMap = new Map() + let scoreMap = new Map() + for (let record of taskRecords) { + if (!taskMap.has(record.user)) { + taskMap.set(record.user, 0) + } + taskMap.set(record.user, taskMap.get(record.user) + record.ticket) + } + for (let record of claimRecords) { + if (!taskMap.has(record.user)) { + taskMap.set(record.user, 0) + } + taskMap.set(record.user, taskMap.get(record.user) + 1) + } + for (let record of scoreRecords) { + if (!scoreMap.has(record.user)) { + scoreMap.set(record.user, []) + } + scoreMap.get(record.user).push(record.score) + } + for (let [user, records] of scoreMap) { + if (!taskMap.has(user)) { + console.log('User', user, 'has no task record') + for (let record of records) { + let dRecord = new DeleteRecord({ + type: 'ingame_draw', + data: record, + }) + await dRecord.save() + await InGameScoreRecord.deleteOne({ _id: record.id }) + } + await InGameStats.findOneAndUpdate( + { user: user }, + { score: 0 }, + { + new: false, + upsert: false, + includeResultMetadata: true, + }, + ) + console.log(`fix user data: ${user}`) + } else if (taskMap.get(user) < records.length) { + console.log('User', user, 'has less task record than score record') + } + } + console.log('Finished repair ingame data in the database') + } catch (e) { + console.log(e) + } + process.exit(0) +})() diff --git a/src/models/CodeRecord.ts b/src/models/CodeRecord.ts index 8066a6a..dd951c3 100644 --- a/src/models/CodeRecord.ts +++ b/src/models/CodeRecord.ts @@ -34,9 +34,9 @@ export const isValiedCode = (code: string) => { * 邮件验证码发送记录 */ @dbconn() -@index({ email: 1, type: 1, status: 1 }, { unique: true, partialFilterExpression: { status: 1 } }) +@index({ user: 1, email: 1, type: 1, status: 1 }, { unique: true, partialFilterExpression: { status: 1 } }) @index({ code: 1 }, { unique: true }) -@index({ expiredAt: 1 }, { unique: false }) +@index({ expiredAt: 1, status: 1 }, { unique: false }) @modelOptions({ schemaOptions: { collection: 'code_send_record', timestamps: true }, }) @@ -82,8 +82,13 @@ class CodeRecordClass extends BaseModule { return this.findOne({ code }).exec() } - public static async findByEmail(this: ReturnModelType, email: string, type: CodeType) { - return this.findOne({ email, type, status: CodeStatus.PENDING }).exec() + public static async findByEmail( + this: ReturnModelType, + user: string, + email: string, + type: CodeType, + ) { + return this.findOne({ user, email, type, status: CodeStatus.PENDING }).exec() } } diff --git a/src/schedule/codetask.schedule.ts b/src/schedule/codetask.schedule.ts index 7d79078..a9833c0 100644 --- a/src/schedule/codetask.schedule.ts +++ b/src/schedule/codetask.schedule.ts @@ -9,7 +9,7 @@ import * as schedule from 'node-schedule' export default class CodeTaskSchedule { async parseAllRecord() { let now = Date.now() - await CodeRecord.deleteMany({ expiredAt: { $lt: now } }) + await CodeRecord.updateMany({ expiredAt: { $lt: now }, status: CodeStatus.PENDING }, { status: CodeStatus.EXPIRED }) } scheduleAll() { const job = schedule.scheduleJob('*/1 * * * *', async () => { diff --git a/src/testdraw.ts b/src/testdraw.ts new file mode 100644 index 0000000..995c0f0 --- /dev/null +++ b/src/testdraw.ts @@ -0,0 +1,28 @@ +import mongoose from 'mongoose' +import * as dotenv from 'dotenv' + +const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development' +dotenv.config({ path: envFile }) +console.log(process.env.DB_MAIN) + +import { drawOnce } from 'services/game.svr' +import { ZRedisClient } from 'zutils' +;(async () => { + try { + let opts = { url: process.env.REDIS } + new ZRedisClient(opts) + let resultMap = new Map() + const total = 10000 + for (let i = 0; i < total; i++) { + let reward = await drawOnce(false) + // console.log(reward) + resultMap.set(reward.amount, (resultMap.get(reward.amount) || 0) + 1) + } + for (let [key, value] of resultMap) { + console.log(key, value, parseFloat(value) / parseFloat(total + '')) + } + process.exit(0) + } catch (e) { + console.error(e) + } +})()