193 lines
4.8 KiB
JavaScript
193 lines
4.8 KiB
JavaScript
const {
|
||
dbCmd,
|
||
userCollection
|
||
} = require('../../common/constants')
|
||
const {
|
||
ERROR
|
||
} = require('../../common/error')
|
||
/**
|
||
* 获取随机邀请码,邀请码由大写字母加数字组成,由于存在手动输入邀请码的场景,从可选字符中去除 0、1、I、O
|
||
* @param {number} len 邀请码长度,默认6位
|
||
* @returns {string} 随机邀请码
|
||
*/
|
||
function getRandomInviteCode (len = 6) {
|
||
const charArr = ['2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
|
||
let code = ''
|
||
for (let i = 0; i < len; i++) {
|
||
code += charArr[Math.floor(Math.random() * charArr.length)]
|
||
}
|
||
return code
|
||
}
|
||
|
||
/**
|
||
* 获取可用的邀请码,至多尝试十次以获取可用邀请码。从10亿可选值中随机,碰撞概率较低
|
||
* 也有其他方案可以尝试,比如在数据库内设置一个从0开始计数的数字,每次调用此方法时使用updateAndReturn使数字加1并返回加1后的值,根据这个值生成对应的邀请码,比如(22222A + 1 == 22222B),此方式性能理论更好,但是不适用于旧项目
|
||
* @param {object} param
|
||
* @param {string} param.inviteCode 初始随机邀请码
|
||
*/
|
||
async function getValidInviteCode () {
|
||
let retry = 10
|
||
let code
|
||
let codeValid = false
|
||
while (retry > 0 && !codeValid) {
|
||
retry--
|
||
code = getRandomInviteCode()
|
||
const getUserRes = await userCollection.where({
|
||
my_invite_code: code
|
||
}).limit(1).get()
|
||
if (getUserRes.data.length === 0) {
|
||
codeValid = true
|
||
break
|
||
}
|
||
}
|
||
if (!codeValid) {
|
||
throw {
|
||
errCode: ERROR.SET_INVITE_CODE_FAILED
|
||
}
|
||
}
|
||
return code
|
||
}
|
||
|
||
/**
|
||
* 根据邀请码查询邀请人
|
||
* @param {object} param
|
||
* @param {string} param.inviteCode 邀请码
|
||
* @param {string} param.queryUid 受邀人id,非空时校验不可被下家或自己邀请
|
||
* @returns
|
||
*/
|
||
async function findUserByInviteCode ({
|
||
inviteCode,
|
||
queryUid
|
||
} = {}) {
|
||
if (typeof inviteCode !== 'string') {
|
||
throw {
|
||
errCode: ERROR.SYSTEM_ERROR
|
||
}
|
||
}
|
||
// 根据邀请码查询邀请人
|
||
let getInviterRes
|
||
if (queryUid) {
|
||
getInviterRes = await userCollection.where({
|
||
_id: dbCmd.neq(queryUid),
|
||
inviter_uid: dbCmd.not(dbCmd.all([queryUid])),
|
||
my_invite_code: inviteCode
|
||
}).get()
|
||
} else {
|
||
getInviterRes = await userCollection.where({
|
||
my_invite_code: inviteCode
|
||
}).get()
|
||
}
|
||
if (getInviterRes.data.length > 1) {
|
||
// 正常情况下不可能进入此条件,以防用户自行修改数据库出错,在此做出判断
|
||
throw {
|
||
errCode: ERROR.SYSTEM_ERROR
|
||
}
|
||
}
|
||
const inviterRecord = getInviterRes.data[0]
|
||
if (!inviterRecord) {
|
||
throw {
|
||
errCode: ERROR.INVALID_INVITE_CODE
|
||
}
|
||
}
|
||
return inviterRecord
|
||
}
|
||
|
||
/**
|
||
* 根据邀请码生成邀请信息
|
||
* @param {object} param
|
||
* @param {string} param.inviteCode 邀请码
|
||
* @param {string} param.queryUid 受邀人id,非空时校验不可被下家或自己邀请
|
||
* @returns
|
||
*/
|
||
async function generateInviteInfo ({
|
||
inviteCode,
|
||
queryUid
|
||
} = {}) {
|
||
const inviterRecord = await findUserByInviteCode({
|
||
inviteCode,
|
||
queryUid
|
||
})
|
||
// 倒叙拼接当前用户邀请链
|
||
const inviterUid = inviterRecord.inviter_uid || []
|
||
inviterUid.unshift(inviterRecord._id)
|
||
return {
|
||
inviterUid,
|
||
inviteTime: Date.now()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查当前用户是否可以接受邀请,如果可以返回用户记录
|
||
* @param {string} uid
|
||
*/
|
||
async function checkInviteInfo (uid) {
|
||
// 检查当前用户是否已有邀请人
|
||
const getUserRes = await userCollection.doc(uid).field({
|
||
my_invite_code: true,
|
||
inviter_uid: true
|
||
}).get()
|
||
const userRecord = getUserRes.data[0]
|
||
if (!userRecord) {
|
||
throw {
|
||
errCode: ERROR.ACCOUNT_NOT_EXISTS
|
||
}
|
||
}
|
||
if (userRecord.inviter_uid && userRecord.inviter_uid.length > 0) {
|
||
throw {
|
||
errCode: ERROR.CHANGE_INVITER_FORBIDDEN
|
||
}
|
||
}
|
||
return userRecord
|
||
}
|
||
|
||
/**
|
||
* 指定用户接受邀请码邀请
|
||
* @param {object} param
|
||
* @param {string} param.uid 用户uid
|
||
* @param {string} param.inviteCode 邀请人的邀请码
|
||
* @returns
|
||
*/
|
||
async function acceptInvite ({
|
||
uid,
|
||
inviteCode
|
||
} = {}) {
|
||
await checkInviteInfo(uid)
|
||
const {
|
||
inviterUid,
|
||
inviteTime
|
||
} = await generateInviteInfo({
|
||
inviteCode,
|
||
queryUid: uid
|
||
})
|
||
|
||
if (inviterUid === uid) {
|
||
throw {
|
||
errCode: ERROR.INVALID_INVITE_CODE
|
||
}
|
||
}
|
||
|
||
// 更新当前用户的邀请人信息
|
||
await userCollection.doc(uid).update({
|
||
inviter_uid: inviterUid,
|
||
invite_time: inviteTime
|
||
})
|
||
|
||
// 更新当前用户邀请的用户的邀请人信息,这步可能较为耗时
|
||
await userCollection.where({
|
||
inviter_uid: uid
|
||
}).update({
|
||
inviter_uid: dbCmd.push(inviterUid)
|
||
})
|
||
|
||
return {
|
||
errCode: 0,
|
||
errMsg: ''
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
acceptInvite,
|
||
generateInviteInfo,
|
||
getValidInviteCode
|
||
}
|