From 5001ca558fe70897eeae5dd414a6acfa9e48fe16 Mon Sep 17 00:00:00 2001 From: CounterFire2023 <136581895+CounterFire2023@users.noreply.github.com> Date: Thu, 16 May 2024 09:59:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=82=AE=E7=AE=B1=E7=99=BB?= =?UTF-8?q?=E5=BD=95,=20=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E7=94=9F=E6=88=90=E5=88=86=E4=BA=AB=E7=A0=81=E7=9A=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/main.controllers.ts | 22 ++++++++ src/modules/Account.ts | 1 + src/modules/ShareCodeRecord.ts | 80 +++++++++++++++++++++++++++++ src/plats/PlatEmail.ts | 6 ++- src/schedule/codetask.schedule.ts | 5 ++ 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/modules/ShareCodeRecord.ts diff --git a/src/controllers/main.controllers.ts b/src/controllers/main.controllers.ts index a2a5f6d..fe86ec0 100644 --- a/src/controllers/main.controllers.ts +++ b/src/controllers/main.controllers.ts @@ -1,4 +1,5 @@ import logger from 'logger/logger' +import { ShareCodeRecord, DEFAULT_SHARE_CODE, ShareCodeStatus, DEFAULT_EXPIRE_TIME } from 'modules/ShareCodeRecord' import { BaseController, router } from 'zutils' class MainController extends BaseController { @@ -9,4 +10,25 @@ class MainController extends BaseController { await user.updateOne({ $inc: { accountVersion: 1 } }) return {} } + + @router('post /wallet/code/generate') + async shareCode(req, res) { + logger.db('code_generate', req) + let user = req.user + let { type } = req.body + let record = await ShareCodeRecord.findOne({ account: user.id, type, status: ShareCodeStatus.PENDING }) + if (!record) { + record = new ShareCodeRecord({ + account: user.id, + openId: user.openId, + plat: user.plat + '', + code: DEFAULT_SHARE_CODE, + email: user.email, + type, + }) + } + record.expiredAt = Date.now() + DEFAULT_EXPIRE_TIME + await record.save() + return { code: record.code } + } } diff --git a/src/modules/Account.ts b/src/modules/Account.ts index 39c6f84..566b074 100644 --- a/src/modules/Account.ts +++ b/src/modules/Account.ts @@ -30,6 +30,7 @@ export function verifyPass(userpassword: string, passwordDb: string, salt: strin export interface AccountClass extends Base, TimeStamps {} @dbconn() @index({ plat: 1, openId: 1 }, { unique: true }) +@index({ plat: 1, email: 1 }, { collation: { locale: 'en', strength: 2 } }) @modelOptions({ schemaOptions: { collection: 'account', timestamps: true }, options: { allowMixed: Severity.ALLOW } }) export class AccountClass extends BaseModule { @prop({ enum: PlatEnum, default: PlatEnum.GOOGLE }) diff --git a/src/modules/ShareCodeRecord.ts b/src/modules/ShareCodeRecord.ts new file mode 100644 index 0000000..3f26943 --- /dev/null +++ b/src/modules/ShareCodeRecord.ts @@ -0,0 +1,80 @@ +import { getModelForClass, index, modelOptions, pre, prop, ReturnModelType } from '@typegoose/typegoose' +import { dbconn } from 'decorators/dbconn' +import { BaseModule } from './Base' + +import { customAlphabet } from 'nanoid' + +const nanoid = customAlphabet('23456789abcdefghjkmnpqrstuvwxy', 8) + +export const DEFAULT_SHARE_CODE = '00000000' +export const DEFAULT_EXPIRE_TIME = 5 * 60 * 1000 + +export enum ShareCodeType { + BIND_UAW = 1, // uaw 绑定 +} + +export enum ShareCodeStatus { + PENDING = 1, + SUCCESS = 2, + FAIL = 3, + EXPIRED = 4, +} + +/** + * 分享码记录 + */ +@dbconn() +@index({ type: 1, code: 1 }, { unique: true, partialFilterExpression: { status: 1 } }) +@index({ account: 1, type: 1, status: 1 }, { unique: true, partialFilterExpression: { status: 1 } }) +@index({ expiredAt: 1, status: 1 }, { unique: false }) +@modelOptions({ + schemaOptions: { collection: 'share_code_record', timestamps: true }, +}) +@pre('save', async function () { + if (this.code === DEFAULT_SHARE_CODE) { + let exists = false + while (!exists) { + const code = nanoid() + const record = await ShareCodeRecord.findByCode(code, this.type) + if (!record) { + exists = true + this.code = code + } + } + } +}) +class ShareCodeRecordClass extends BaseModule { + @prop({ required: true }) + public account: string + + @prop({ required: true }) + public openId: string + + @prop() + public email?: string + + @prop({ required: true }) + public plat: string + + @prop({ required: true }) + public code!: string + + @prop({ default: Date.now() + DEFAULT_EXPIRE_TIME }) + public expiredAt?: number + + @prop({ required: true, default: ShareCodeType.BIND_UAW }) + public type: ShareCodeType + + @prop({ required: true, default: ShareCodeStatus.PENDING }) + public status: ShareCodeStatus + + public static async findByCode( + this: ReturnModelType, + code: string, + type: ShareCodeType, + ) { + return this.findOne({ code, type, status: ShareCodeStatus.PENDING }).exec() + } +} + +export const ShareCodeRecord = getModelForClass(ShareCodeRecordClass, { existingConnection: ShareCodeRecordClass.db }) diff --git a/src/plats/PlatEmail.ts b/src/plats/PlatEmail.ts index 2f04029..d7dfd64 100644 --- a/src/plats/PlatEmail.ts +++ b/src/plats/PlatEmail.ts @@ -2,6 +2,8 @@ import { ZError } from 'zutils' import { IPlat } from './IPlat' import { CodeRecord, CodeStatus, CodeType } from 'modules/CodeRecord' import { sha1 } from 'zutils/utils/security.util' +import { Account } from 'modules/Account' +import { PlatEnum } from 'enums/PlatEnum' const isEmail = (email: string) => { const reg = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/ @@ -34,7 +36,9 @@ export class PlatEmail implements IPlat { if (recordCode.status !== CodeStatus.PENDING) { throw new ZError(13, 'code expired') } - const openId = sha1(email) + email = email.toLowerCase() + let account = await Account.findOne({ plat: PlatEnum.EMAIL, email }).collation({ locale: 'en', strength: 2 }) + const openId = account ? account.openId : sha1(email) let data: any = { email, emailReal: email, emailVerified: true } const { api_platform } = req.headers if (api_platform) { diff --git a/src/schedule/codetask.schedule.ts b/src/schedule/codetask.schedule.ts index 49c2acb..caecf0f 100644 --- a/src/schedule/codetask.schedule.ts +++ b/src/schedule/codetask.schedule.ts @@ -1,6 +1,7 @@ import { singleton } from 'zutils' import { CodeRecord, CodeStatus } from 'modules/CodeRecord' import * as schedule from 'node-schedule' +import { ShareCodeRecord } from 'modules/ShareCodeRecord' /** * 定时更新发送邮件验证码的过期状态 @@ -10,6 +11,10 @@ export default class CodeTaskSchedule { async parseAllRecord() { let now = Date.now() await CodeRecord.updateMany({ expiredAt: { $lt: now }, status: CodeStatus.PENDING }, { status: CodeStatus.EXPIRED }) + await ShareCodeRecord.updateMany( + { expiredAt: { $lt: now }, status: CodeStatus.PENDING }, + { status: CodeStatus.EXPIRED }, + ) } scheduleAll() { const job = schedule.scheduleJob('*/1 * * * *', async () => {