优化邮件发送

This commit is contained in:
CounterFire2023 2024-05-15 15:36:26 +08:00
parent d4e875e423
commit 305d784426
6 changed files with 122 additions and 18 deletions

View File

@ -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"
},

View File

@ -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()
}

67
src/fixIngame.ts Normal file
View File

@ -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)
})()

View File

@ -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<typeof CodeRecordClass>, email: string, type: CodeType) {
return this.findOne({ email, type, status: CodeStatus.PENDING }).exec()
public static async findByEmail(
this: ReturnModelType<typeof CodeRecordClass>,
user: string,
email: string,
type: CodeType,
) {
return this.findOne({ user, email, type, status: CodeStatus.PENDING }).exec()
}
}

View File

@ -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 () => {

28
src/testdraw.ts Normal file
View File

@ -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)
}
})()