UrsReferralSyncCommand.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php
  2. namespace App\Module\UrsPromotion\Commands;
  3. use App\Module\AppGame\Handler\Public\Login4uHandler;
  4. use App\Module\UrsPromotion\Models\UrsUserMapping;
  5. use App\Module\UrsPromotion\Services\UrsUserMappingService;
  6. use Illuminate\Console\Command;
  7. use Illuminate\Support\Facades\Log;
  8. /**
  9. * URS推荐关系同步命令
  10. *
  11. * 用于从URS获取用户上级关系,同步到本地数据库
  12. * 支持同步指定用户或所有用户的推荐关系
  13. * php artisan urs:sync-referral 32424 --force
  14. */
  15. class UrsReferralSyncCommand extends Command
  16. {
  17. /**
  18. * 命令签名
  19. */
  20. protected $signature = 'urs:sync-referral
  21. {urs_user_id? : URS用户ID,不指定则同步所有用户}
  22. {--batch-size=100 : 批处理大小}
  23. {--force : 强制重新同步已存在的关系}
  24. {--dry-run : 仅模拟运行,不实际执行同步}';
  25. /**
  26. * 命令描述
  27. */
  28. protected $description = 'URS推荐关系同步命令 - 从URS获取用户上级关系并同步到本地数据库';
  29. /**
  30. * 执行命令
  31. */
  32. public function handle()
  33. {
  34. $ursUserId = $this->argument('urs_user_id');
  35. $batchSize = (int) $this->option('batch-size');
  36. $force = $this->option('force');
  37. $dryRun = $this->option('dry-run');
  38. $this->info('=== URS推荐关系同步命令 ===');
  39. if ($dryRun) {
  40. $this->warn('模拟运行模式 - 不会实际执行同步操作');
  41. }
  42. try {
  43. if ($ursUserId) {
  44. // 同步指定用户
  45. return $this->syncSingleUser((int) $ursUserId, $force, $dryRun);
  46. } else {
  47. // 同步所有用户
  48. return $this->syncAllUsers($batchSize, $force, $dryRun);
  49. }
  50. } catch (\Exception $e) {
  51. $this->error('命令执行失败: ' . $e->getMessage());
  52. Log::error('URS推荐关系同步命令执行失败', [
  53. 'urs_user_id' => $ursUserId,
  54. 'batch_size' => $batchSize,
  55. 'force' => $force,
  56. 'dry_run' => $dryRun,
  57. 'error' => $e->getMessage(),
  58. 'trace' => $e->getTraceAsString()
  59. ]);
  60. return 1;
  61. }
  62. }
  63. /**
  64. * 同步指定用户的推荐关系
  65. *
  66. * @param int $ursUserId URS用户ID
  67. * @param bool $force 是否强制重新同步
  68. * @param bool $dryRun 是否仅模拟运行
  69. * @return int 命令退出码
  70. */
  71. private function syncSingleUser(int $ursUserId, bool $force, bool $dryRun): int
  72. {
  73. $this->info("开始同步URS用户 {$ursUserId} 的推荐关系...");
  74. // 检查用户是否已进入农场
  75. $farmUserId = UrsUserMappingService::getFarmUserId($ursUserId);
  76. if (!$farmUserId) {
  77. $this->error("URS用户 {$ursUserId} 尚未进入农场,无法同步推荐关系");
  78. return 1;
  79. }
  80. $this->info("找到农场用户ID: {$farmUserId}");
  81. if ($dryRun) {
  82. $this->info("模拟运行:将调用 Login4uHandler::syncReferralRelations({$ursUserId}, {$farmUserId})");
  83. return 0;
  84. }
  85. try {
  86. // 调用现有的同步方法
  87. $isFirstEntry = Login4uHandler::syncReferralRelations($ursUserId, $farmUserId);
  88. if ($isFirstEntry) {
  89. $this->info("✓ 成功创建URS用户 {$ursUserId} 的推荐关系");
  90. } else {
  91. if ($force) {
  92. $this->info("✓ URS用户 {$ursUserId} 的推荐关系已存在(强制模式)");
  93. } else {
  94. $this->warn("URS用户 {$ursUserId} 的推荐关系已存在,跳过同步");
  95. }
  96. }
  97. Log::info('URS推荐关系同步成功', [
  98. 'urs_user_id' => $ursUserId,
  99. 'farm_user_id' => $farmUserId,
  100. 'is_first_entry' => $isFirstEntry,
  101. 'force' => $force
  102. ]);
  103. return 0;
  104. } catch (\Exception $e) {
  105. $this->error("✗ URS用户 {$ursUserId} 推荐关系同步失败: " . $e->getMessage());
  106. Log::error('URS推荐关系同步失败', [
  107. 'urs_user_id' => $ursUserId,
  108. 'farm_user_id' => $farmUserId,
  109. 'error' => $e->getMessage(),
  110. 'trace' => $e->getTraceAsString()
  111. ]);
  112. return 1;
  113. }
  114. }
  115. /**
  116. * 同步所有用户的推荐关系
  117. *
  118. * @param int $batchSize 批处理大小
  119. * @param bool $force 是否强制重新同步
  120. * @param bool $dryRun 是否仅模拟运行
  121. * @return int 命令退出码
  122. */
  123. private function syncAllUsers(int $batchSize, bool $force, bool $dryRun): int
  124. {
  125. $this->info("开始批量同步所有用户的推荐关系...");
  126. $this->info("批处理大小: {$batchSize}");
  127. // 获取所有有效的用户映射
  128. $totalUsers = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)->count();
  129. if ($totalUsers === 0) {
  130. $this->warn('没有找到需要同步的用户');
  131. return 0;
  132. }
  133. $this->info("找到 {$totalUsers} 个用户需要同步");
  134. if ($dryRun) {
  135. $this->info("模拟运行:将分批处理 {$totalUsers} 个用户");
  136. return 0;
  137. }
  138. $progressBar = $this->output->createProgressBar($totalUsers);
  139. $progressBar->start();
  140. $successCount = 0;
  141. $skipCount = 0;
  142. $errorCount = 0;
  143. $processedCount = 0;
  144. // 分批处理用户
  145. UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
  146. ->chunk($batchSize, function ($mappings) use (
  147. &$successCount, &$skipCount, &$errorCount, &$processedCount,
  148. $progressBar, $force
  149. ) {
  150. foreach ($mappings as $mapping) {
  151. try {
  152. $isFirstEntry = Login4uHandler::syncReferralRelations(
  153. $mapping->urs_user_id,
  154. $mapping->user_id
  155. );
  156. if ($isFirstEntry) {
  157. $successCount++;
  158. } else {
  159. $skipCount++;
  160. }
  161. } catch (\Exception $e) {
  162. $errorCount++;
  163. Log::error('批量同步用户推荐关系失败', [
  164. 'urs_user_id' => $mapping->urs_user_id,
  165. 'farm_user_id' => $mapping->user_id,
  166. 'error' => $e->getMessage()
  167. ]);
  168. }
  169. $processedCount++;
  170. $progressBar->advance();
  171. }
  172. });
  173. $progressBar->finish();
  174. $this->newLine();
  175. // 显示同步结果
  176. $this->info('=== 同步结果统计 ===');
  177. $this->table(['项目', '数量'], [
  178. ['总用户数', $totalUsers],
  179. ['处理用户数', $processedCount],
  180. ['成功同步数', $successCount],
  181. ['跳过数量', $skipCount],
  182. ['失败数量', $errorCount]
  183. ]);
  184. Log::info('URS推荐关系批量同步完成', [
  185. 'total_users' => $totalUsers,
  186. 'processed_count' => $processedCount,
  187. 'success_count' => $successCount,
  188. 'skip_count' => $skipCount,
  189. 'error_count' => $errorCount,
  190. 'batch_size' => $batchSize,
  191. 'force' => $force
  192. ]);
  193. if ($errorCount > 0) {
  194. $this->warn("有 {$errorCount} 个用户同步失败,请检查日志");
  195. return 1;
  196. }
  197. $this->info('所有用户推荐关系同步完成');
  198. return 0;
  199. }
  200. }