UrsReferralSyncLogic.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. <?php
  2. namespace App\Module\UrsPromotion\Logics;
  3. use App\Module\UrsPromotion\Models\UrsUserReferral;
  4. use App\Module\UrsPromotion\Models\UrsUserTalent;
  5. use App\Module\UrsPromotion\Models\UrsUserMapping;
  6. use App\Module\UrsPromotion\Services\UrsUserMappingService;
  7. use App\Module\UrsPromotion\Services\UrsReferralService;
  8. use App\Module\UrsPromotion\Logics\UrsRelationCacheLogic;
  9. use Illuminate\Support\Facades\Log;
  10. use ThirdParty\Urs\Services\UrsService;
  11. /**
  12. * URS推荐关系同步逻辑类
  13. *
  14. * 按照文档《URS推荐关系同步机制.md》实现,将同步流程拆分为独立的节点方法,
  15. * 每个节点功能单一,职责清晰,按顺序调用。
  16. */
  17. class UrsReferralSyncLogic
  18. {
  19. /**
  20. * 执行完整的URS推荐关系同步流程
  21. *
  22. * @param int $ursUserId URS用户ID
  23. * @param int $farmUserId 农场用户ID
  24. * @return bool 是否为首次进入农场(新创建了推荐关系)
  25. */
  26. public function syncReferralRelations(int $ursUserId, int $farmUserId): bool
  27. {
  28. try {
  29. Log::info('开始URS推荐关系同步流程', [
  30. 'urs_user_id' => $ursUserId,
  31. 'farm_user_id' => $farmUserId
  32. ]);
  33. // 1. 创建用户映射
  34. $this->createUserMapping($ursUserId, $farmUserId);
  35. // 2. 请求URS获取上级关系
  36. $teamData = $this->requestUrsTeamRelations($ursUserId);
  37. if (!$teamData) {
  38. Log::info('URS用户无上级关系,同步结束', ['urs_user_id' => $ursUserId]);
  39. return false;
  40. }
  41. // 3. 验证和创建推荐关系
  42. $isFirstEntry = $this->validateAndCreateReferrals($ursUserId, $teamData);
  43. if (!$isFirstEntry) {
  44. Log::info('URS用户推荐关系已存在,同步结束', ['urs_user_id' => $ursUserId]);
  45. return false;
  46. }
  47. // 4. 生成关系缓存
  48. $this->generateRelationCache($ursUserId);
  49. // 5. 更新团队统计
  50. $this->updateTeamStatistics($ursUserId, $teamData);
  51. // 6. 触发事件
  52. $this->triggerEvents($ursUserId, $farmUserId, $teamData);
  53. Log::info('URS推荐关系同步流程完成', [
  54. 'urs_user_id' => $ursUserId,
  55. 'farm_user_id' => $farmUserId,
  56. 'is_first_entry' => $isFirstEntry
  57. ]);
  58. return $isFirstEntry;
  59. } catch (\Exception $e) {
  60. Log::error('URS推荐关系同步流程失败', [
  61. 'urs_user_id' => $ursUserId,
  62. 'farm_user_id' => $farmUserId,
  63. 'error' => $e->getMessage(),
  64. 'trace' => $e->getTraceAsString()
  65. ]);
  66. return false;
  67. }
  68. }
  69. /**
  70. * 节点1:创建用户映射
  71. * 建立URS用户ID与农场用户ID的映射关系
  72. *
  73. * @param int $ursUserId URS用户ID
  74. * @param int $farmUserId 农场用户ID
  75. * @return void
  76. */
  77. private function createUserMapping(int $ursUserId, int $farmUserId): void
  78. {
  79. Log::info('节点1:创建用户映射', [
  80. 'urs_user_id' => $ursUserId,
  81. 'farm_user_id' => $farmUserId
  82. ]);
  83. // 检查映射是否已存在
  84. $existingMapping = UrsUserMappingService::getFarmUserId($ursUserId);
  85. if ($existingMapping) {
  86. Log::info('用户映射已存在', [
  87. 'urs_user_id' => $ursUserId,
  88. 'existing_farm_user_id' => $existingMapping,
  89. 'current_farm_user_id' => $farmUserId
  90. ]);
  91. return;
  92. }
  93. // 创建新的映射关系
  94. try {
  95. UrsUserMappingService::createMapping($ursUserId, $farmUserId);
  96. Log::info('用户映射创建成功', [
  97. 'urs_user_id' => $ursUserId,
  98. 'farm_user_id' => $farmUserId
  99. ]);
  100. } catch (\Exception $e) {
  101. Log::warning('用户映射创建失败', [
  102. 'urs_user_id' => $ursUserId,
  103. 'farm_user_id' => $farmUserId,
  104. 'error' => $e->getMessage()
  105. ]);
  106. // 映射创建失败不影响后续流程
  107. }
  108. }
  109. /**
  110. * 节点2:请求URS获取上级关系
  111. * 调用URS接口获取20级上级关系数据
  112. *
  113. * @param int $ursUserId URS用户ID
  114. * @return array|null 团队关系数据,失败返回null
  115. */
  116. private function requestUrsTeamRelations(int $ursUserId): ?array
  117. {
  118. Log::info('节点2:请求URS获取上级关系', ['urs_user_id' => $ursUserId]);
  119. try {
  120. $teamResult = UrsService::getUserTeam($ursUserId);
  121. Log::info('URS团队关系获取结果', [
  122. 'urs_user_id' => $ursUserId,
  123. 'success' => $teamResult['success'] ?? false,
  124. 'has_data' => !empty($teamResult['data']),
  125. 'team_data' => $teamResult['data'] ?? null
  126. ]);
  127. if (!$teamResult['success'] || empty($teamResult['data'])) {
  128. Log::info('URS用户无上级关系', ['urs_user_id' => $ursUserId]);
  129. return null;
  130. }
  131. return $teamResult['data'];
  132. } catch (\Exception $e) {
  133. Log::error('URS团队关系获取失败', [
  134. 'urs_user_id' => $ursUserId,
  135. 'error' => $e->getMessage()
  136. ]);
  137. return null;
  138. }
  139. }
  140. /**
  141. * 节点3:验证和创建推荐关系
  142. * 补充缺失的推荐关系记录(20代关系)
  143. *
  144. * @param int $ursUserId URS用户ID
  145. * @param array $teamData 团队关系数据,格式:['team' => ['1' => 10001, '2' => 10002, ...]]
  146. * @return bool 是否为首次创建推荐关系
  147. */
  148. private function validateAndCreateReferrals(int $ursUserId, array $teamData): bool
  149. {
  150. Log::info('节点3:验证和创建推荐关系', [
  151. 'urs_user_id' => $ursUserId,
  152. 'team_data_structure' => array_keys($teamData)
  153. ]);
  154. // 检查是否已存在推荐关系
  155. $existingReferral = UrsUserReferral::where('urs_user_id', $ursUserId)->first();
  156. if ($existingReferral) {
  157. Log::info('URS用户推荐关系已存在', [
  158. 'urs_user_id' => $ursUserId,
  159. 'urs_referrer_id' => $existingReferral->urs_referrer_id
  160. ]);
  161. return false;
  162. }
  163. // 获取团队关系数据
  164. $teamRelations = $teamData['team'] ?? [];
  165. if (empty($teamRelations)) {
  166. Log::info('URS用户无团队关系数据', [
  167. 'urs_user_id' => $ursUserId,
  168. 'team_data' => $teamData
  169. ]);
  170. return false;
  171. }
  172. Log::info('解析团队关系数据', [
  173. 'urs_user_id' => $ursUserId,
  174. 'team_levels' => array_keys($teamRelations),
  175. 'team_relations_count' => count($teamRelations)
  176. ]);
  177. // 获取直接推荐人(一级上级)
  178. $directReferrerId = $teamRelations['1'] ?? null;
  179. if (!$directReferrerId) {
  180. Log::info('URS用户无直接推荐人', [
  181. 'urs_user_id' => $ursUserId,
  182. 'team_relations' => $teamRelations
  183. ]);
  184. return false;
  185. }
  186. try {
  187. Log::info('开始创建20代推荐关系', [
  188. 'urs_user_id' => $ursUserId,
  189. 'direct_referrer_id' => $directReferrerId,
  190. 'available_levels' => array_keys($teamRelations)
  191. ]);
  192. // 检查是否会形成循环推荐(只检查直接推荐人)
  193. if ($this->wouldCreateCircularReferral($ursUserId, $directReferrerId)) {
  194. throw new \Exception("不能形成循环推荐关系");
  195. }
  196. // 获取农场用户ID(如果已进入农场)
  197. $farmUserId = UrsUserMappingService::getFarmUserId($ursUserId);
  198. $referrerFarmUserId = UrsUserMappingService::getFarmUserId($directReferrerId);
  199. // 1. 创建当前用户的直接推荐关系记录
  200. $referral = UrsUserReferral::create([
  201. 'urs_user_id' => $ursUserId,
  202. 'urs_referrer_id' => $directReferrerId,
  203. 'user_id' => $farmUserId ?: 0,
  204. 'referrer_id' => $referrerFarmUserId ?: 0,
  205. 'referral_time' => now(),
  206. 'status' => UrsUserReferral::STATUS_VALID,
  207. ]);
  208. Log::info('直接推荐关系创建成功', [
  209. 'urs_user_id' => $ursUserId,
  210. 'urs_referrer_id' => $directReferrerId,
  211. 'referral_id' => $referral->id
  212. ]);
  213. // 2. 为所有20代上级补全下级关系记录
  214. $createdCount = 1; // 已创建直接关系
  215. $maxLevel = min(count($teamRelations), 20); // 最多20代
  216. for ($level = 1; $level <= $maxLevel; $level++) {
  217. $referrerId = $teamRelations[(string)$level] ?? null;
  218. if (!$referrerId) {
  219. continue;
  220. }
  221. // 检查该推荐人是否已存在推荐关系记录
  222. $existingReferrerRecord = UrsUserReferral::where('urs_user_id', $referrerId)->first();
  223. if ($existingReferrerRecord) {
  224. Log::debug('第' . $level . '级推荐人已有推荐关系,跳过', [
  225. 'urs_referrer_id' => $referrerId,
  226. 'level' => $level
  227. ]);
  228. continue;
  229. }
  230. // 获取该推荐人的上级(如果存在)
  231. $upperReferrerId = null;
  232. if ($level < $maxLevel) {
  233. $upperReferrerId = $teamRelations[(string)($level + 1)] ?? null;
  234. }
  235. if ($upperReferrerId) {
  236. // 获取推荐人的农场用户ID
  237. $referrerFarmId = UrsUserMappingService::getFarmUserId($referrerId);
  238. $upperReferrerFarmId = UrsUserMappingService::getFarmUserId($upperReferrerId);
  239. // 为该推荐人创建推荐关系记录
  240. try {
  241. $upperReferral = UrsUserReferral::create([
  242. 'urs_user_id' => $referrerId,
  243. 'urs_referrer_id' => $upperReferrerId,
  244. 'user_id' => $referrerFarmId ?: 0,
  245. 'referrer_id' => $upperReferrerFarmId ?: 0,
  246. 'referral_time' => now(),
  247. 'status' => UrsUserReferral::STATUS_VALID,
  248. ]);
  249. $createdCount++;
  250. Log::debug('第' . $level . '级推荐人的推荐关系创建成功', [
  251. 'urs_user_id' => $referrerId,
  252. 'urs_referrer_id' => $upperReferrerId,
  253. 'level' => $level,
  254. 'referral_id' => $upperReferral->id
  255. ]);
  256. } catch (\Exception $e) {
  257. // 如果创建失败(可能是重复),记录日志但不中断流程
  258. Log::warning('第' . $level . '级推荐人的推荐关系创建失败', [
  259. 'urs_user_id' => $referrerId,
  260. 'urs_referrer_id' => $upperReferrerId,
  261. 'error' => $e->getMessage()
  262. ]);
  263. }
  264. }
  265. }
  266. Log::info('20代推荐关系补全完成', [
  267. 'urs_user_id' => $ursUserId,
  268. 'created_count' => $createdCount,
  269. 'max_level' => $maxLevel
  270. ]);
  271. return true;
  272. } catch (\Exception $e) {
  273. Log::error('推荐关系创建失败', [
  274. 'urs_user_id' => $ursUserId,
  275. 'direct_referrer_id' => $directReferrerId,
  276. 'error' => $e->getMessage()
  277. ]);
  278. throw $e;
  279. }
  280. }
  281. /**
  282. * 节点4:生成关系缓存
  283. * 创建20级关系缓存
  284. *
  285. * @param int $ursUserId URS用户ID
  286. * @return void
  287. */
  288. private function generateRelationCache(int $ursUserId): void
  289. {
  290. Log::info('节点4:生成关系缓存', ['urs_user_id' => $ursUserId]);
  291. try {
  292. $relationCacheLogic = new UrsRelationCacheLogic();
  293. $result = $relationCacheLogic->generateUserRelationCache($ursUserId);
  294. Log::info('关系缓存生成结果', [
  295. 'urs_user_id' => $ursUserId,
  296. 'success' => $result
  297. ]);
  298. } catch (\Exception $e) {
  299. Log::error('关系缓存生成失败', [
  300. 'urs_user_id' => $ursUserId,
  301. 'error' => $e->getMessage()
  302. ]);
  303. // 缓存生成失败不影响主流程
  304. }
  305. }
  306. /**
  307. * 节点5:更新团队统计
  308. * 更新推荐人的团队统计数据
  309. *
  310. * @param int $ursUserId URS用户ID
  311. * @param array $teamData 团队关系数据
  312. * @return void
  313. */
  314. private function updateTeamStatistics(int $ursUserId, array $teamData): void
  315. {
  316. Log::info('节点5:更新团队统计', ['urs_user_id' => $ursUserId]);
  317. try {
  318. // 获取直接推荐人
  319. $directReferrerId = $teamData['team']['1'] ?? null;
  320. if (!$directReferrerId) {
  321. Log::info('无直接推荐人,跳过团队统计更新', ['urs_user_id' => $ursUserId]);
  322. return;
  323. }
  324. // 更新推荐人的团队统计
  325. $this->updateReferrerTeamStats($directReferrerId);
  326. Log::info('团队统计更新成功', [
  327. 'urs_user_id' => $ursUserId,
  328. 'urs_referrer_id' => $directReferrerId
  329. ]);
  330. } catch (\Exception $e) {
  331. Log::error('团队统计更新失败', [
  332. 'urs_user_id' => $ursUserId,
  333. 'error' => $e->getMessage()
  334. ]);
  335. // 统计更新失败不影响主流程
  336. }
  337. }
  338. /**
  339. * 节点6:触发事件
  340. * 条件触发UrsReferralCreatedEvent事件(双方都进入农场时)
  341. *
  342. * @param int $ursUserId URS用户ID
  343. * @param int $farmUserId 农场用户ID
  344. * @param array $teamData 团队关系数据
  345. * @return void
  346. */
  347. private function triggerEvents(int $ursUserId, int $farmUserId, array $teamData): void
  348. {
  349. Log::info('节点6:触发事件', [
  350. 'urs_user_id' => $ursUserId,
  351. 'farm_user_id' => $farmUserId
  352. ]);
  353. try {
  354. // 获取直接推荐人
  355. $directReferrerId = $teamData['team']['1'] ?? null;
  356. if (!$directReferrerId) {
  357. Log::info('无直接推荐人,跳过事件触发', ['urs_user_id' => $ursUserId]);
  358. return;
  359. }
  360. // 获取推荐人的农场用户ID
  361. $referrerFarmUserId = UrsUserMappingService::getFarmUserId($directReferrerId);
  362. // 只有双方都进入农场时才触发事件
  363. if ($farmUserId && $referrerFarmUserId) {
  364. event(new \App\Module\UrsPromotion\Events\UrsReferralCreatedEvent(
  365. $farmUserId,
  366. $referrerFarmUserId,
  367. null
  368. ));
  369. Log::info('URS推荐关系创建事件已触发', [
  370. 'user_id' => $farmUserId,
  371. 'referrer_id' => $referrerFarmUserId,
  372. 'urs_user_id' => $ursUserId,
  373. 'urs_referrer_id' => $directReferrerId
  374. ]);
  375. } else {
  376. Log::info('URS推荐关系创建但未触发事件(用户未进入农场)', [
  377. 'urs_user_id' => $ursUserId,
  378. 'urs_referrer_id' => $directReferrerId,
  379. 'farm_user_id' => $farmUserId,
  380. 'referrer_farm_user_id' => $referrerFarmUserId
  381. ]);
  382. }
  383. } catch (\Exception $e) {
  384. Log::error('事件触发失败', [
  385. 'urs_user_id' => $ursUserId,
  386. 'farm_user_id' => $farmUserId,
  387. 'error' => $e->getMessage()
  388. ]);
  389. // 事件触发失败不影响主流程
  390. }
  391. }
  392. /**
  393. * 检查是否会形成循环推荐
  394. *
  395. * @param int $ursUserId URS用户ID
  396. * @param int $ursReferrerId URS推荐人ID
  397. * @return bool
  398. */
  399. private function wouldCreateCircularReferral(int $ursUserId, int $ursReferrerId): bool
  400. {
  401. // 检查推荐人的上级链中是否包含当前用户
  402. $referrerChain = UrsReferralService::getReferralChain($ursReferrerId);
  403. return in_array($ursUserId, $referrerChain);
  404. }
  405. /**
  406. * 更新推荐人的团队统计
  407. *
  408. * @param int $ursReferrerId URS推荐人ID
  409. * @return void
  410. */
  411. private function updateReferrerTeamStats(int $ursReferrerId): void
  412. {
  413. try {
  414. $farmUserId = UrsUserMapping::getFarmUserIdByUrsUserId($ursReferrerId);
  415. if (!$farmUserId) {
  416. Log::info('推荐人未进入农场,跳过团队统计更新', [
  417. 'urs_referrer_id' => $ursReferrerId
  418. ]);
  419. return;
  420. }
  421. // 获取团队成员统计(使用配置的团队统计深度)
  422. $teamMembers = UrsReferralService::getTeamMembers($ursReferrerId);
  423. $directCount = count($teamMembers[1] ?? []);
  424. $indirectCount = count($teamMembers[2] ?? []);
  425. $thirdCount = count($teamMembers[3] ?? []);
  426. // 计算20代总人数
  427. $totalCount = 0;
  428. foreach ($teamMembers as $members) {
  429. $totalCount += count($members);
  430. }
  431. UrsUserTalent::updateOrCreate(
  432. ['user_id' => $farmUserId],
  433. [
  434. 'direct_count' => $directCount,
  435. 'indirect_count' => $indirectCount,
  436. 'third_count' => $thirdCount,
  437. 'promotion_count' => $totalCount, // 现在包含20代总人数
  438. ]
  439. );
  440. Log::info('推荐人团队统计更新成功', [
  441. 'urs_referrer_id' => $ursReferrerId,
  442. 'farm_user_id' => $farmUserId,
  443. 'direct_count' => $directCount,
  444. 'total_count' => $totalCount
  445. ]);
  446. } catch (\Exception $e) {
  447. Log::error('推荐人团队统计更新失败', [
  448. 'urs_referrer_id' => $ursReferrerId,
  449. 'error' => $e->getMessage()
  450. ]);
  451. }
  452. }
  453. }