InvitationService.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <?php
  2. namespace app\common\service;
  3. use think\facade\Db;
  4. use think\facade\Log;
  5. class InvitationService
  6. {
  7. /**
  8. * 生成唯一邀请码
  9. */
  10. public function generateInvitationCode(): string
  11. {
  12. $maxAttempts = 10;
  13. $attempts = 0;
  14. while ($attempts < $maxAttempts) {
  15. // 方法1:使用数字+字母组合
  16. $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // 去掉容易混淆的字符
  17. $code = '';
  18. for ($i = 0; $i < 6; $i++) {
  19. $code .= $chars[mt_rand(0, strlen($chars) - 1)];
  20. }
  21. // 方法2:或者使用更短的邀请码
  22. // $code = strtoupper(substr(md5(uniqid()), 0, 6));
  23. // 检查邀请码是否已存在
  24. $exists = Db::table('ba_user')
  25. ->where('invitation_code', $code)
  26. ->count();
  27. if (!$exists) {
  28. return $code;
  29. }
  30. $attempts++;
  31. // 如果冲突,尝试不同的算法
  32. if ($attempts >= 5) {
  33. $code = 'INV' . mt_rand(1000, 9999);
  34. $exists = Db::table('ba_user')
  35. ->where('invitation_code', $code)
  36. ->count();
  37. if (!$exists) {
  38. return $code;
  39. }
  40. }
  41. }
  42. // 如果还是冲突,使用时间戳+随机数
  43. return 'INV' . time() . mt_rand(10, 99);
  44. }
  45. /**
  46. * 处理用户注册邀请关系(完整版)
  47. */
  48. public function processRegistration(int $newUserId, string $invitationCode = ''): bool
  49. {
  50. try {
  51. // 1. 为注册用户生成自己的邀请码
  52. $userInvitationCode = $this->generateInvitationCode();
  53. // 2. 将邀请码保存到用户表
  54. Db::table('ba_user')
  55. ->where('id', $newUserId)
  56. ->update([
  57. 'invitation_code' => $userInvitationCode,
  58. 'update_time' => time()
  59. ]);
  60. // 3. 处理邀请关系(如果提供了邀请码)
  61. if (!empty($invitationCode)) {
  62. $this->processInvitationRelationship($newUserId, $invitationCode);
  63. }
  64. // 4. 初始化用户的佣金记录
  65. $this->initUserCommission($newUserId);
  66. Log::info("用户邀请码生成成功: 用户ID={$newUserId}, 邀请码={$userInvitationCode}");
  67. return true;
  68. } catch (\Throwable $e) {
  69. Log::error("处理用户注册邀请关系失败: " . $e->getMessage());
  70. return false;
  71. }
  72. }
  73. /**
  74. * 处理邀请关系(新用户使用别人的邀请码注册)
  75. */
  76. private function processInvitationRelationship(int $newUserId, string $invitationCode): void
  77. {
  78. try {
  79. // 查找邀请人(通过邀请码)
  80. $inviter = Db::table('ba_user')
  81. ->where('invitation_code', $invitationCode)
  82. ->field('id, username')
  83. ->find();
  84. if (!$inviter) {
  85. Log::warning("邀请码不存在: {$invitationCode}");
  86. return;
  87. }
  88. // 检查是否已存在邀请关系
  89. $exists = Db::table('ba_user_invitation')
  90. ->where('invitee_id', $newUserId)
  91. ->find();
  92. if ($exists) {
  93. return; // 已有邀请关系,直接返回
  94. }
  95. // 创建邀请关系记录
  96. Db::table('ba_user_invitation')->insert([
  97. 'inviter_id' => $inviter['id'],
  98. 'invitee_id' => $newUserId,
  99. 'invitation_code' => $invitationCode,
  100. 'create_time' => date('Y-m-d H:i:s')
  101. ]);
  102. // 更新邀请人统计
  103. $this->updateInviterStats($inviter['id']);
  104. Log::info("邀请关系创建成功: 邀请人ID={$inviter['id']}, 被邀请人ID={$newUserId}");
  105. } catch (\Throwable $e) {
  106. Log::error("处理邀请关系失败: " . $e->getMessage());
  107. }
  108. }
  109. /**
  110. * 更新邀请人统计
  111. */
  112. private function updateInviterStats(int $inviterId): void
  113. {
  114. try {
  115. // 统计邀请人数
  116. $invitationCount = Db::table('ba_user_invitation')
  117. ->where('inviter_id', $inviterId)
  118. ->count();
  119. // 获取当前佣金记录
  120. $commission = Db::table('ba_user_commission')
  121. ->where('user_id', $inviterId)
  122. ->find();
  123. if ($commission) {
  124. // 更新现有记录
  125. Db::table('ba_user_commission')
  126. ->where('user_id', $inviterId)
  127. ->update([
  128. 'invitation_count' => $invitationCount,
  129. // 'total_invitation' => $invitationCount,
  130. 'update_time' => date('Y-m-d H:i:s')
  131. ]);
  132. } else {
  133. // 创建新记录(如果邀请人还没有佣金记录)
  134. $promotionLink = $this->generatePromotionLink($inviterId);
  135. Db::table('ba_user_commission')->insert([
  136. 'user_id' => $inviterId,
  137. 'total_commission' => 0.00,
  138. 'current_commission' => 0.00,
  139. 'withdrawn_commission' => 0.00,
  140. 'frozen_commission' => 0.00,
  141. 'promotion_link' => $promotionLink,
  142. 'invitation_count' => $invitationCount,
  143. // 'total_invitation' => $invitationCount,
  144. 'update_time' => date('Y-m-d H:i:s')
  145. ]);
  146. }
  147. } catch (\Throwable $e) {
  148. Log::error("更新邀请人统计失败: " . $e->getMessage());
  149. }
  150. }
  151. /**
  152. * 初始化用户佣金记录
  153. */
  154. private function initUserCommission(int $userId): void
  155. {
  156. try {
  157. $exists = Db::table('ba_user_commission')
  158. ->where('user_id', $userId)
  159. ->find();
  160. if (!$exists) {
  161. $promotionLink = $this->generatePromotionLink($userId);
  162. Db::table('ba_user_commission')->insert([
  163. 'user_id' => $userId,
  164. 'total_commission' => 0.00,
  165. 'current_commission' => 0.00,
  166. 'withdrawn_commission' => 0.00,
  167. 'frozen_commission' => 0.00,
  168. 'promotion_link' => $promotionLink,
  169. 'invitation_count' => 0,
  170. 'update_time' => date('Y-m-d H:i:s')
  171. ]);
  172. }
  173. } catch (\Throwable $e) {
  174. Log::error("初始化用户佣金失败: " . $e->getMessage());
  175. }
  176. }
  177. /**
  178. * 生成推广链接
  179. */
  180. private function generatePromotionLink(int $userId): string
  181. {
  182. // 获取用户的邀请码
  183. $user = Db::table('ba_user')
  184. ->where('id', $userId)
  185. ->field('invitation_code')
  186. ->find();
  187. $invitationCode = $user['invitation_code'] ?? '';
  188. if (empty($invitationCode)) {
  189. // 如果用户还没有邀请码,先生成一个
  190. $invitationCode = $this->generateInvitationCode();
  191. Db::table('ba_user')
  192. ->where('id', $userId)
  193. ->update(['invitation_code' => $invitationCode]);
  194. }
  195. $domain = request()->domain();
  196. return $domain . '/#/user/register?invite=' . $invitationCode;
  197. }
  198. /**
  199. * 获取用户邀请统计
  200. */
  201. public function getUserInvitationStats(int $userId): array
  202. {
  203. try {
  204. // 获取用户的邀请码
  205. $user = Db::table('ba_user')
  206. ->where('id', $userId)
  207. ->field('invitation_code')
  208. ->find();
  209. // 获取总邀请人数
  210. $totalInvitations = Db::table('ba_user_invitation')
  211. ->alias('ui')
  212. ->leftJoin('ba_user u', 'u.id = ui.invitee_id')
  213. ->where('ui.inviter_id', $userId)
  214. ->field('u.id, u.username, u.email, u.mobile, u.create_time, ui.create_time as invite_time')
  215. ->order('ui.create_time', 'desc')
  216. ->select()
  217. ->toArray();
  218. // 获取今日邀请人数
  219. $todayStart = date('Y-m-d 00:00:00');
  220. $todayEnd = date('Y-m-d 23:59:59');
  221. $todayInvitations = Db::table('ba_user_invitation')
  222. ->alias('ui')
  223. ->leftJoin('ba_user u', 'u.id = ui.invitee_id')
  224. ->where('ui.inviter_id', $userId)
  225. ->whereBetween('ui.create_time', [$todayStart, $todayEnd])
  226. ->select()
  227. ->toArray();
  228. // 获取佣金信息
  229. $commission = Db::table('ba_user_commission')
  230. ->where('user_id', $userId)
  231. ->find();
  232. // 获取佣金流水
  233. $flows = Db::table('ba_commission_flow')
  234. ->where('user_id', $userId)
  235. ->order('create_time', 'desc')
  236. ->limit(20)
  237. ->select()
  238. ->toArray();
  239. return [
  240. 'invitation_code' => $user['invitation_code'] ?? '', // 用户的邀请码
  241. 'invitations' => $totalInvitations, // 所有邀请记录
  242. 'today_invitation' => count($todayInvitations), // 今日邀请人数
  243. 'total_invitation' => count($totalInvitations), // 总邀请人数
  244. 'commission' => $commission ?: [
  245. 'total_commission' => 0,
  246. 'current_commission' => 0,
  247. 'withdrawn_commission' => 0,
  248. 'frozen_commission' => 0,
  249. 'promotion_link' => $this->generatePromotionLink($userId),
  250. 'invitation_count' => 0,
  251. 'total_invitation' => 0
  252. ],
  253. 'flows' => $flows
  254. ];
  255. } catch (\Throwable $e) {
  256. Log::error("获取用户邀请统计失败: " . $e->getMessage());
  257. return [
  258. 'invitation_code' => '',
  259. 'invitations' => [],
  260. 'invitation_count' => 0,
  261. 'commission' => [
  262. 'total_commission' => 0,
  263. 'current_commission' => 0,
  264. 'withdrawn_commission' => 0,
  265. 'frozen_commission' => 0,
  266. 'promotion_link' => $this->generatePromotionLink($userId),
  267. 'invitation_count' => 0,
  268. 'total_invitation' => 0
  269. ],
  270. 'flows' => []
  271. ];
  272. }
  273. }
  274. /**
  275. * 获取下级用户列表
  276. */
  277. public function getSubordinateList(int $userId, int $page = 1, int $pageSize = 10): array
  278. {
  279. try {
  280. $query = Db::table('ba_user_invitation')
  281. ->alias('ui')
  282. ->leftJoin('ba_user u', 'u.id = ui.invitee_id')
  283. ->where('ui.inviter_id', $userId);
  284. $total = $query->count();
  285. $list = $query->field('u.id, u.username, u.nickname, u.email, u.mobile, u.create_time,
  286. u.last_login_time, u.status, ui.create_time as invite_time')
  287. ->page($page, $pageSize)
  288. ->order('ui.create_time', 'desc')
  289. ->select()
  290. ->toArray();
  291. return [
  292. 'list' => $list,
  293. 'total' => $total,
  294. 'page' => $page,
  295. 'page_size' => $pageSize,
  296. 'page_count' => ceil($total / $pageSize)
  297. ];
  298. } catch (\Throwable $e) {
  299. Log::error("获取下级用户列表失败: " . $e->getMessage());
  300. return [
  301. 'list' => [],
  302. 'total' => 0,
  303. 'page' => $page,
  304. 'page_size' => $pageSize,
  305. 'page_count' => 0
  306. ];
  307. }
  308. }
  309. }