增加一个用于验证客户端分享码的接口
This commit is contained in:
parent
305d784426
commit
62af6b9332
37
docs/uaw.md
37
docs/uaw.md
@ -68,6 +68,11 @@
|
|||||||
1. 用户状态(10) 增加返回emailId, email, gameId, gameMail
|
1. 用户状态(10) 增加返回emailId, email, gameId, gameMail
|
||||||
2. 增加接口: 发送邮件验证码(33), 验证邮件地址(34)
|
2. 增加接口: 发送邮件验证码(33), 验证邮件地址(34)
|
||||||
|
|
||||||
|
#### 20240515
|
||||||
|
1. 增加接口: 验证客户端分享码(35)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 1. 钱包预登录
|
### 1. 钱包预登录
|
||||||
|
|
||||||
#### Request
|
#### Request
|
||||||
@ -1060,6 +1065,38 @@ export const isValiedCode = (code) => {
|
|||||||
|
|
||||||
#### Response
|
#### Response
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
```js
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,9 @@ class InGameController extends BaseController {
|
|||||||
} else if (user.emailId) {
|
} else if (user.emailId) {
|
||||||
let res = await queryInGameInfo(user.emailId, '6')
|
let res = await queryInGameInfo(user.emailId, '6')
|
||||||
Object.assign(gameData, res)
|
Object.assign(gameData, res)
|
||||||
|
} else if (user.clientId) {
|
||||||
|
let res = await queryInGameInfo(user.clientId, user.clientPlat)
|
||||||
|
Object.assign(gameData, res)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.info('queryInGameInfo with err: ', e.message || e)
|
logger.info('queryInGameInfo with err: ', e.message || e)
|
||||||
|
@ -13,12 +13,16 @@ import { DEFAULT_LOGIN_MAIL_HTML, DEFAULT_LOGIN_MAIL_SUBJECT, EmailSvr } from 's
|
|||||||
import { BaseController, router, ZError } from 'zutils'
|
import { BaseController, router, ZError } from 'zutils'
|
||||||
import { sha1 } from 'zutils/utils/security.util'
|
import { sha1 } from 'zutils/utils/security.util'
|
||||||
import { SyncLocker } from 'common/SyncLocker'
|
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 {
|
class MailController extends BaseController {
|
||||||
/**
|
/**
|
||||||
* 通过邮件验证码形式的登录
|
* 通过邮件验证码形式的登录
|
||||||
*/
|
*/
|
||||||
@router('post /api/user/verify_email')
|
// @router('post /api/user/verify_email')
|
||||||
async loginWithEmail(req, res) {
|
async loginWithEmail(req, res) {
|
||||||
await new SyncLocker().checkLock(req)
|
await new SyncLocker().checkLock(req)
|
||||||
logger.db('verify_email', req)
|
logger.db('verify_email', req)
|
||||||
@ -56,10 +60,47 @@ class MailController extends BaseController {
|
|||||||
return {}
|
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) {
|
async sendVerifyCode(req, res) {
|
||||||
await new SyncLocker().checkLock(req)
|
await new SyncLocker().checkLock(req)
|
||||||
logger.db('send_mail_code', req)
|
logger.db('send_mail_code', req)
|
||||||
|
@ -191,7 +191,7 @@ class SignController extends BaseController {
|
|||||||
@router('post /api/user/verify_google')
|
@router('post /api/user/verify_google')
|
||||||
async verifyGoogleToken(req) {
|
async verifyGoogleToken(req) {
|
||||||
const user = req.user
|
const user = req.user
|
||||||
if (user.googleId) {
|
if (user.gameAccountBinded()) {
|
||||||
throw new ZError(11, 'already bind google')
|
throw new ZError(11, 'already bind google')
|
||||||
}
|
}
|
||||||
const { openId, data } = await verifyToken(req)
|
const { openId, data } = await verifyToken(req)
|
||||||
|
@ -51,6 +51,7 @@ export interface ActivityUserClass extends Base, TimeStamps {}
|
|||||||
@index({ twitterId: 1 }, { unique: true, partialFilterExpression: { twitterId: { $exists: true } } })
|
@index({ twitterId: 1 }, { unique: true, partialFilterExpression: { twitterId: { $exists: true } } })
|
||||||
@index({ googleId: 1 }, { unique: true, partialFilterExpression: { googleId: { $exists: true } } })
|
@index({ googleId: 1 }, { unique: true, partialFilterExpression: { googleId: { $exists: true } } })
|
||||||
@index({ emailId: 1 }, { unique: true, partialFilterExpression: { emailId: { $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 } } })
|
@index({ discordId: 1 }, { unique: true, partialFilterExpression: { discordId: { $exists: true } } })
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
schemaOptions: { collection: 'activity_user', timestamps: true },
|
schemaOptions: { collection: 'activity_user', timestamps: true },
|
||||||
@ -117,6 +118,13 @@ export class ActivityUserClass extends BaseModule {
|
|||||||
@prop()
|
@prop()
|
||||||
public email?: string
|
public email?: string
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public clientId?: string
|
||||||
|
@prop()
|
||||||
|
public clientMail?: string
|
||||||
|
@prop()
|
||||||
|
public clientPlat?: string
|
||||||
|
|
||||||
@prop({ default: false })
|
@prop({ default: false })
|
||||||
public inWhiteList: boolean
|
public inWhiteList: boolean
|
||||||
|
|
||||||
@ -150,14 +158,14 @@ export class ActivityUserClass extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public gameAccountBinded() {
|
public gameAccountBinded() {
|
||||||
return this.googleId || this.emailId
|
return this.googleId || this.emailId || this.clientId
|
||||||
}
|
}
|
||||||
|
|
||||||
public gameId() {
|
public gameId() {
|
||||||
return this.googleId || this.emailId
|
return this.googleId || this.emailId || this.clientId
|
||||||
}
|
}
|
||||||
public gameMail() {
|
public gameMail() {
|
||||||
return this.googleEmail || this.email
|
return this.googleEmail || this.email || this.clientMail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
67
src/models/wallet/ShareCodeRecord.ts
Normal file
67
src/models/wallet/ShareCodeRecord.ts
Normal 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 })
|
@ -33,7 +33,14 @@ export abstract class ITask {
|
|||||||
|
|
||||||
public async claimReward(cfg: any) {
|
public async claimReward(cfg: any) {
|
||||||
const user = this.user
|
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 gameId = user.gameId()
|
||||||
let gameData = await queryInGameInfo(gameId, channel)
|
let gameData = await queryInGameInfo(gameId, channel)
|
||||||
if (!(await this.check(cfg, gameData))) {
|
if (!(await this.check(cfg, gameData))) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user