|
|
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
|
|
|
}
|