增加一个用于验证客户端分享码的接口

This commit is contained in:
CounterFire2023 2024-05-15 18:57:22 +08:00
parent 305d784426
commit 62af6b9332
7 changed files with 170 additions and 7 deletions

View File

@ -68,6 +68,11 @@
1. 用户状态(10) 增加返回emailId, email, gameId, gameMail
2. 增加接口: 发送邮件验证码(33), 验证邮件地址(34)
#### 20240515
1. 增加接口: 验证客户端分享码(35)
### 1. 钱包预登录
#### Request
@ -1064,3 +1069,35 @@ export const isValiedCode = (code) => {
{
}
```
### 35.\* 验证客户端分享码
#### Request
- URL`/api/user/verify_client`
- 方法POST
- 头部:
- Authorization: Bearer JWT_token
body:
```js
{
"code": "12322100"
}
```
> 验证code的正则
```js
export const isValiedCode = (code) => {
return /^[23456789abcdefghjkmnpqrstuvwxy]{8}$/.test(code)
}
```
#### Response
```js
{
}
```

View File

@ -76,6 +76,9 @@ class InGameController extends BaseController {
} else if (user.emailId) {
let res = await queryInGameInfo(user.emailId, '6')
Object.assign(gameData, res)
} else if (user.clientId) {
let res = await queryInGameInfo(user.clientId, user.clientPlat)
Object.assign(gameData, res)
}
} catch (e) {
logger.info('queryInGameInfo with err: ', e.message || e)

View File

@ -13,12 +13,16 @@ import { DEFAULT_LOGIN_MAIL_HTML, DEFAULT_LOGIN_MAIL_SUBJECT, EmailSvr } from 's
import { BaseController, router, ZError } from 'zutils'
import { sha1 } from 'zutils/utils/security.util'
import { SyncLocker } from 'common/SyncLocker'
import { ShareCodeRecord, ShareCodeStatus, ShareCodeType } from 'models/wallet/ShareCodeRecord'
export const isValiedShareCode = (code: string) => {
return /^[23456789abcdefghjkmnpqrstuvwxy]{8}$/.test(code)
}
class MailController extends BaseController {
/**
*
*/
@router('post /api/user/verify_email')
// @router('post /api/user/verify_email')
async loginWithEmail(req, res) {
await new SyncLocker().checkLock(req)
logger.db('verify_email', req)
@ -56,10 +60,47 @@ class MailController extends BaseController {
return {}
}
@router('post /api/user/verify_client')
async loginWithGameClient(req, res) {
await new SyncLocker().checkLock(req)
logger.db('verify_client', req)
let user = req.user
const { code } = req.params
if (!code) {
throw new ZError(10, 'params mismatch')
}
if (!isValiedShareCode(code)) {
throw new ZError(11, 'code error')
}
if (user.gameAccountBinded()) {
throw new ZError(12, 'already bind game account')
}
let recordCode = await ShareCodeRecord.findByCode(code, ShareCodeType.BIND_UAW)
if (!recordCode) {
throw new ZError(14, 'code expired')
}
const openId = recordCode.openId
let userCheck = await ActivityUser.findOne({ clientId: openId, clientPlat: recordCode.plat })
if (userCheck && userCheck.id !== user.id) {
throw new ZError(13, 'Email already binded to another account')
}
user.clientId = openId
user.clientMail = recordCode.email
user.clientPlat = recordCode.plat
recordCode.status = ShareCodeStatus.SUCCESS
await ShareCodeRecord.updateOne(
{ code, type: ShareCodeType.BIND_UAW, status: ShareCodeStatus.PENDING },
{ status: ShareCodeStatus.SUCCESS },
)
await user.save()
return {}
}
/**
*
*/
@router('post /api/email/send_code')
// @router('post /api/email/send_code')
async sendVerifyCode(req, res) {
await new SyncLocker().checkLock(req)
logger.db('send_mail_code', req)

View File

@ -191,7 +191,7 @@ class SignController extends BaseController {
@router('post /api/user/verify_google')
async verifyGoogleToken(req) {
const user = req.user
if (user.googleId) {
if (user.gameAccountBinded()) {
throw new ZError(11, 'already bind google')
}
const { openId, data } = await verifyToken(req)

View File

@ -51,6 +51,7 @@ export interface ActivityUserClass extends Base, TimeStamps {}
@index({ twitterId: 1 }, { unique: true, partialFilterExpression: { twitterId: { $exists: true } } })
@index({ googleId: 1 }, { unique: true, partialFilterExpression: { googleId: { $exists: true } } })
@index({ emailId: 1 }, { unique: true, partialFilterExpression: { emailId: { $exists: true } } })
@index({ clientId: 1, clientPlat: 1 }, { unique: true, partialFilterExpression: { clientId: { $exists: true } } })
@index({ discordId: 1 }, { unique: true, partialFilterExpression: { discordId: { $exists: true } } })
@modelOptions({
schemaOptions: { collection: 'activity_user', timestamps: true },
@ -117,6 +118,13 @@ export class ActivityUserClass extends BaseModule {
@prop()
public email?: string
@prop()
public clientId?: string
@prop()
public clientMail?: string
@prop()
public clientPlat?: string
@prop({ default: false })
public inWhiteList: boolean
@ -150,14 +158,14 @@ export class ActivityUserClass extends BaseModule {
}
public gameAccountBinded() {
return this.googleId || this.emailId
return this.googleId || this.emailId || this.clientId
}
public gameId() {
return this.googleId || this.emailId
return this.googleId || this.emailId || this.clientId
}
public gameMail() {
return this.googleEmail || this.email
return this.googleEmail || this.email || this.clientMail
}
}

View File

@ -0,0 +1,67 @@
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('1234567890', 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('wallet')
@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 },
})
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<typeof ShareCodeRecordClass>,
code: string,
type: ShareCodeType,
) {
return this.findOne({ code, type, status: ShareCodeStatus.PENDING }).exec()
}
}
export const ShareCodeRecord = getModelForClass(ShareCodeRecordClass, { existingConnection: ShareCodeRecordClass.db })

View File

@ -33,7 +33,14 @@ export abstract class ITask {
public async claimReward(cfg: any) {
const user = this.user
let channel = user.googleId ? '0' : '6'
let channel = '0'
if (user.googleId) {
channel = '0'
} else if (user.emailId) {
channel = '6'
} else if (user.clientId) {
channel = user.clientPlat
}
let gameId = user.gameId()
let gameData = await queryInGameInfo(gameId, channel)
if (!(await this.check(cfg, gameData))) {