fission.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. const {
  2. dbCmd,
  3. userCollection
  4. } = require('../../common/constants')
  5. const {
  6. ERROR
  7. } = require('../../common/error')
  8. /**
  9. * 获取随机邀请码,邀请码由大写字母加数字组成,由于存在手动输入邀请码的场景,从可选字符中去除 0、1、I、O
  10. * @param {number} len 邀请码长度,默认6位
  11. * @returns {string} 随机邀请码
  12. */
  13. function getRandomInviteCode (len = 6) {
  14. 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']
  15. let code = ''
  16. for (let i = 0; i < len; i++) {
  17. code += charArr[Math.floor(Math.random() * charArr.length)]
  18. }
  19. return code
  20. }
  21. /**
  22. * 获取可用的邀请码,至多尝试十次以获取可用邀请码。从10亿可选值中随机,碰撞概率较低
  23. * 也有其他方案可以尝试,比如在数据库内设置一个从0开始计数的数字,每次调用此方法时使用updateAndReturn使数字加1并返回加1后的值,根据这个值生成对应的邀请码,比如(22222A + 1 == 22222B),此方式性能理论更好,但是不适用于旧项目
  24. * @param {object} param
  25. * @param {string} param.inviteCode 初始随机邀请码
  26. */
  27. async function getValidInviteCode () {
  28. let retry = 10
  29. let code
  30. let codeValid = false
  31. while (retry > 0 && !codeValid) {
  32. retry--
  33. code = getRandomInviteCode()
  34. const getUserRes = await userCollection.where({
  35. my_invite_code: code
  36. }).limit(1).get()
  37. if (getUserRes.data.length === 0) {
  38. codeValid = true
  39. break
  40. }
  41. }
  42. if (!codeValid) {
  43. throw {
  44. errCode: ERROR.SET_INVITE_CODE_FAILED
  45. }
  46. }
  47. return code
  48. }
  49. /**
  50. * 根据邀请码查询邀请人
  51. * @param {object} param
  52. * @param {string} param.inviteCode 邀请码
  53. * @param {string} param.queryUid 受邀人id,非空时校验不可被下家或自己邀请
  54. * @returns
  55. */
  56. async function findUserByInviteCode ({
  57. inviteCode,
  58. queryUid
  59. } = {}) {
  60. if (typeof inviteCode !== 'string') {
  61. throw {
  62. errCode: ERROR.SYSTEM_ERROR
  63. }
  64. }
  65. // 根据邀请码查询邀请人
  66. let getInviterRes
  67. if (queryUid) {
  68. getInviterRes = await userCollection.where({
  69. _id: dbCmd.neq(queryUid),
  70. inviter_uid: dbCmd.not(dbCmd.all([queryUid])),
  71. my_invite_code: inviteCode
  72. }).get()
  73. } else {
  74. getInviterRes = await userCollection.where({
  75. my_invite_code: inviteCode
  76. }).get()
  77. }
  78. if (getInviterRes.data.length > 1) {
  79. // 正常情况下不可能进入此条件,以防用户自行修改数据库出错,在此做出判断
  80. throw {
  81. errCode: ERROR.SYSTEM_ERROR
  82. }
  83. }
  84. const inviterRecord = getInviterRes.data[0]
  85. if (!inviterRecord) {
  86. throw {
  87. errCode: ERROR.INVALID_INVITE_CODE
  88. }
  89. }
  90. return inviterRecord
  91. }
  92. /**
  93. * 根据邀请码生成邀请信息
  94. * @param {object} param
  95. * @param {string} param.inviteCode 邀请码
  96. * @param {string} param.queryUid 受邀人id,非空时校验不可被下家或自己邀请
  97. * @returns
  98. */
  99. async function generateInviteInfo ({
  100. inviteCode,
  101. queryUid
  102. } = {}) {
  103. const inviterRecord = await findUserByInviteCode({
  104. inviteCode,
  105. queryUid
  106. })
  107. // 倒叙拼接当前用户邀请链
  108. const inviterUid = inviterRecord.inviter_uid || []
  109. inviterUid.unshift(inviterRecord._id)
  110. return {
  111. inviterUid,
  112. inviteTime: Date.now()
  113. }
  114. }
  115. /**
  116. * 检查当前用户是否可以接受邀请,如果可以返回用户记录
  117. * @param {string} uid
  118. */
  119. async function checkInviteInfo (uid) {
  120. // 检查当前用户是否已有邀请人
  121. const getUserRes = await userCollection.doc(uid).field({
  122. my_invite_code: true,
  123. inviter_uid: true
  124. }).get()
  125. const userRecord = getUserRes.data[0]
  126. if (!userRecord) {
  127. throw {
  128. errCode: ERROR.ACCOUNT_NOT_EXISTS
  129. }
  130. }
  131. if (userRecord.inviter_uid && userRecord.inviter_uid.length > 0) {
  132. throw {
  133. errCode: ERROR.CHANGE_INVITER_FORBIDDEN
  134. }
  135. }
  136. return userRecord
  137. }
  138. /**
  139. * 指定用户接受邀请码邀请
  140. * @param {object} param
  141. * @param {string} param.uid 用户uid
  142. * @param {string} param.inviteCode 邀请人的邀请码
  143. * @returns
  144. */
  145. async function acceptInvite ({
  146. uid,
  147. inviteCode
  148. } = {}) {
  149. await checkInviteInfo(uid)
  150. const {
  151. inviterUid,
  152. inviteTime
  153. } = await generateInviteInfo({
  154. inviteCode,
  155. queryUid: uid
  156. })
  157. if (inviterUid === uid) {
  158. throw {
  159. errCode: ERROR.INVALID_INVITE_CODE
  160. }
  161. }
  162. // 更新当前用户的邀请人信息
  163. await userCollection.doc(uid).update({
  164. inviter_uid: inviterUid,
  165. invite_time: inviteTime
  166. })
  167. // 更新当前用户邀请的用户的邀请人信息,这步可能较为耗时
  168. await userCollection.where({
  169. inviter_uid: uid
  170. }).update({
  171. inviter_uid: dbCmd.push(inviterUid)
  172. })
  173. return {
  174. errCode: 0,
  175. errMsg: ''
  176. }
  177. }
  178. module.exports = {
  179. acceptInvite,
  180. generateInviteInfo,
  181. getValidInviteCode
  182. }