FixReferralFarmUserIdCommand.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <?php
  2. namespace App\Module\UrsPromotion\Commands;
  3. use App\Module\UrsPromotion\Models\UrsUserMapping;
  4. use App\Module\UrsPromotion\Models\UrsUserReferral;
  5. use App\Module\UrsPromotion\Models\UrsUserRelationCache;
  6. use App\Module\UrsPromotion\Services\UrsReferralService;
  7. use App\Module\UrsPromotion\Logics\UrsRelationCacheLogic;
  8. use Illuminate\Console\Command;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Log;
  11. /**
  12. * 修复推荐关系农场用户ID命令
  13. *
  14. * 用于修复推荐关系表中农场用户ID字段为0的问题
  15. * 当推荐关系在用户进入农场之前就存在时,农场用户ID字段可能为0,需要更新
  16. *
  17. * php artisan urs:fix-referral-farm-user-id --dry-run
  18. * php artisan urs:fix-referral-farm-user-id --batch-size=100
  19. */
  20. class FixReferralFarmUserIdCommand extends Command
  21. {
  22. /**
  23. * 命令签名
  24. */
  25. protected $signature = 'urs:fix-referral-farm-user-id
  26. {--batch-size=50 : 批处理大小}
  27. {--dry-run : 仅模拟运行,不实际执行修复}
  28. {--force : 强制修复所有记录,包括已有农场用户ID的}';
  29. /**
  30. * 命令描述
  31. */
  32. protected $description = '修复推荐关系表中农场用户ID字段为0的问题';
  33. /**
  34. * 执行命令
  35. */
  36. public function handle()
  37. {
  38. $batchSize = (int) $this->option('batch-size');
  39. $dryRun = $this->option('dry-run');
  40. $force = $this->option('force');
  41. $this->info('=== URS推荐关系农场用户ID修复命令 ===');
  42. if ($dryRun) {
  43. $this->warn('模拟运行模式 - 不会实际执行修复操作');
  44. }
  45. try {
  46. // 1. 分析问题数据
  47. $this->analyzeProblematicData($force);
  48. // 2. 执行修复
  49. if (!$dryRun) {
  50. $this->fixReferralFarmUserIds($batchSize, $force);
  51. } else {
  52. $this->info('模拟运行完成,实际修复请移除 --dry-run 参数');
  53. }
  54. return 0;
  55. } catch (\Exception $e) {
  56. $this->error('命令执行失败: ' . $e->getMessage());
  57. Log::error('URS推荐关系农场用户ID修复命令执行失败', [
  58. 'batch_size' => $batchSize,
  59. 'dry_run' => $dryRun,
  60. 'force' => $force,
  61. 'error' => $e->getMessage(),
  62. 'trace' => $e->getTraceAsString()
  63. ]);
  64. return 1;
  65. }
  66. }
  67. /**
  68. * 分析问题数据
  69. *
  70. * @param bool $force 是否强制分析所有数据
  71. * @return void
  72. */
  73. private function analyzeProblematicData(bool $force): void
  74. {
  75. $this->info('正在分析问题数据...');
  76. // === 推荐关系表统计 ===
  77. // 统计被推荐人农场用户ID为0的记录
  78. $zeroUserIdCount = UrsUserReferral::where('status', UrsUserReferral::STATUS_VALID)
  79. ->where('user_id', 0)
  80. ->whereExists(function ($query) {
  81. $query->select(DB::raw(1))
  82. ->from('urs_promotion_user_mappings')
  83. ->whereColumn('urs_promotion_user_mappings.urs_user_id', 'urs_promotion_user_referrals.urs_user_id')
  84. ->where('urs_promotion_user_mappings.status', UrsUserMapping::STATUS_VALID);
  85. })
  86. ->count();
  87. // 统计推荐人农场用户ID为0的记录
  88. $zeroReferrerIdCount = UrsUserReferral::where('status', UrsUserReferral::STATUS_VALID)
  89. ->where('referrer_id', 0)
  90. ->whereExists(function ($query) {
  91. $query->select(DB::raw(1))
  92. ->from('urs_promotion_user_mappings')
  93. ->whereColumn('urs_promotion_user_mappings.urs_user_id', 'urs_promotion_user_referrals.urs_referrer_id')
  94. ->where('urs_promotion_user_mappings.status', UrsUserMapping::STATUS_VALID);
  95. })
  96. ->count();
  97. // 统计总的推荐关系记录数
  98. $totalReferralCount = UrsUserReferral::where('status', UrsUserReferral::STATUS_VALID)->count();
  99. // === 缓存表统计 ===
  100. // 统计缓存表中农场用户ID为0的记录
  101. $cacheZeroUserIdCount = UrsUserRelationCache::where('user_id', 0)->count();
  102. $cacheZeroRelatedUserIdCount = UrsUserRelationCache::where('related_user_id', 0)->count();
  103. $totalCacheCount = UrsUserRelationCache::count();
  104. // 统计已进入农场的用户数
  105. $mappedUserCount = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)->count();
  106. $this->table([
  107. '统计项目', '数量'
  108. ], [
  109. ['总推荐关系记录数', number_format($totalReferralCount)],
  110. ['已进入农场用户数', number_format($mappedUserCount)],
  111. ['推荐关系表-被推荐人农场用户ID为0', number_format($zeroUserIdCount)],
  112. ['推荐关系表-推荐人农场用户ID为0', number_format($zeroReferrerIdCount)],
  113. ['缓存表总记录数', number_format($totalCacheCount)],
  114. ['缓存表-用户ID为0', number_format($cacheZeroUserIdCount)],
  115. ['缓存表-关联用户ID为0', number_format($cacheZeroRelatedUserIdCount)],
  116. ]);
  117. $totalProblems = $zeroUserIdCount + $zeroReferrerIdCount + $cacheZeroUserIdCount + $cacheZeroRelatedUserIdCount;
  118. if ($totalProblems > 0) {
  119. $this->warn("发现 {$totalProblems} 条记录需要修复");
  120. $this->info(" - 推荐关系表: {$zeroUserIdCount} + {$zeroReferrerIdCount} = " . ($zeroUserIdCount + $zeroReferrerIdCount) . " 条");
  121. $this->info(" - 缓存表: {$cacheZeroUserIdCount} + {$cacheZeroRelatedUserIdCount} = " . ($cacheZeroUserIdCount + $cacheZeroRelatedUserIdCount) . " 条");
  122. } else {
  123. $this->info('✅ 没有发现需要修复的数据');
  124. }
  125. }
  126. /**
  127. * 执行修复推荐关系农场用户ID
  128. *
  129. * @param int $batchSize 批处理大小
  130. * @param bool $force 是否强制修复所有记录
  131. * @return void
  132. */
  133. private function fixReferralFarmUserIds(int $batchSize, bool $force): void
  134. {
  135. $this->info('开始修复推荐关系和缓存表的农场用户ID...');
  136. // 1. 修复推荐关系表
  137. $this->info('1. 修复推荐关系表...');
  138. $result = UrsReferralService::batchUpdateFarmUserIds();
  139. if ($result['success']) {
  140. $this->info("✅ 推荐关系表修复完成,共更新 {$result['affected_rows']} 条记录");
  141. } else {
  142. $this->error("❌ 推荐关系表修复失败: {$result['message']}");
  143. return;
  144. }
  145. // 2. 修复缓存表
  146. $this->info('2. 修复缓存表...');
  147. $cacheResult = $this->batchUpdateCacheFarmUserIds();
  148. if ($cacheResult['success']) {
  149. $this->info("✅ 缓存表修复完成,共更新 {$cacheResult['affected_rows']} 条记录");
  150. } else {
  151. $this->error("❌ 缓存表修复失败: {$cacheResult['message']}");
  152. }
  153. Log::info('URS推荐关系和缓存表农场用户ID批量修复完成', [
  154. 'referral_affected_rows' => $result['affected_rows'],
  155. 'cache_affected_rows' => $cacheResult['affected_rows'] ?? 0,
  156. 'batch_size' => $batchSize,
  157. 'force' => $force
  158. ]);
  159. // 验证修复结果
  160. $this->verifyFixResults();
  161. }
  162. /**
  163. * 批量更新缓存表中的农场用户ID
  164. *
  165. * @return array 更新结果
  166. */
  167. private function batchUpdateCacheFarmUserIds(): array
  168. {
  169. try {
  170. Log::info('开始批量更新缓存表中的农场用户ID');
  171. // 使用SQL批量更新缓存表,提高性能
  172. // 分两步更新:先更新user_id,再更新related_user_id
  173. // 第一步:更新user_id字段(当前为0且用户已进入农场)
  174. $updateUserIdSql = "
  175. UPDATE `kku_urs_promotion_user_relation_cache` c
  176. INNER JOIN `kku_urs_promotion_user_mappings` um ON c.urs_user_id = um.urs_user_id AND um.status = 1
  177. SET c.user_id = um.user_id
  178. WHERE c.user_id = 0
  179. ";
  180. $userIdAffectedRows = DB::update($updateUserIdSql);
  181. // 第二步:更新related_user_id字段(当前为0且推荐人已进入农场)
  182. $updateRelatedUserIdSql = "
  183. UPDATE `kku_urs_promotion_user_relation_cache` c
  184. INNER JOIN `kku_urs_promotion_user_mappings` rm ON c.urs_related_user_id = rm.urs_user_id AND rm.status = 1
  185. SET c.related_user_id = rm.user_id
  186. WHERE c.related_user_id = 0
  187. ";
  188. $relatedUserIdAffectedRows = DB::update($updateRelatedUserIdSql);
  189. $affectedRows = $userIdAffectedRows + $relatedUserIdAffectedRows;
  190. Log::info('批量更新缓存表中的农场用户ID完成', [
  191. 'user_id_affected_rows' => $userIdAffectedRows,
  192. 'related_user_id_affected_rows' => $relatedUserIdAffectedRows,
  193. 'total_affected_rows' => $affectedRows
  194. ]);
  195. return [
  196. 'success' => true,
  197. 'affected_rows' => $affectedRows,
  198. 'message' => "成功更新 {$affectedRows} 条缓存记录"
  199. ];
  200. } catch (\Exception $e) {
  201. Log::error('批量更新缓存表中的农场用户ID失败', [
  202. 'error' => $e->getMessage(),
  203. 'trace' => $e->getTraceAsString()
  204. ]);
  205. return [
  206. 'success' => false,
  207. 'affected_rows' => 0,
  208. 'message' => '批量更新失败: ' . $e->getMessage()
  209. ];
  210. }
  211. }
  212. /**
  213. * 验证修复结果
  214. *
  215. * @return void
  216. */
  217. private function verifyFixResults(): void
  218. {
  219. $this->info('正在验证修复结果...');
  220. // === 验证推荐关系表 ===
  221. $remainingZeroUserIdCount = UrsUserReferral::where('status', UrsUserReferral::STATUS_VALID)
  222. ->where('user_id', 0)
  223. ->whereExists(function ($query) {
  224. $query->select(DB::raw(1))
  225. ->from('urs_promotion_user_mappings')
  226. ->whereColumn('urs_promotion_user_mappings.urs_user_id', 'urs_promotion_user_referrals.urs_user_id')
  227. ->where('urs_promotion_user_mappings.status', UrsUserMapping::STATUS_VALID);
  228. })
  229. ->count();
  230. $remainingZeroReferrerIdCount = UrsUserReferral::where('status', UrsUserReferral::STATUS_VALID)
  231. ->where('referrer_id', 0)
  232. ->whereExists(function ($query) {
  233. $query->select(DB::raw(1))
  234. ->from('urs_promotion_user_mappings')
  235. ->whereColumn('urs_promotion_user_mappings.urs_user_id', 'urs_promotion_user_referrals.urs_referrer_id')
  236. ->where('urs_promotion_user_mappings.status', UrsUserMapping::STATUS_VALID);
  237. })
  238. ->count();
  239. // === 验证缓存表 ===
  240. $remainingCacheZeroUserIdCount = UrsUserRelationCache::where('user_id', 0)
  241. ->whereExists(function ($query) {
  242. $query->select(DB::raw(1))
  243. ->from('urs_promotion_user_mappings')
  244. ->whereColumn('urs_promotion_user_mappings.urs_user_id', 'urs_promotion_user_relation_cache.urs_user_id')
  245. ->where('urs_promotion_user_mappings.status', UrsUserMapping::STATUS_VALID);
  246. })
  247. ->count();
  248. $remainingCacheZeroRelatedUserIdCount = UrsUserRelationCache::where('related_user_id', 0)
  249. ->whereExists(function ($query) {
  250. $query->select(DB::raw(1))
  251. ->from('urs_promotion_user_mappings')
  252. ->whereColumn('urs_promotion_user_mappings.urs_user_id', 'urs_promotion_user_relation_cache.urs_related_user_id')
  253. ->where('urs_promotion_user_mappings.status', UrsUserMapping::STATUS_VALID);
  254. })
  255. ->count();
  256. $totalRemaining = $remainingZeroUserIdCount + $remainingZeroReferrerIdCount +
  257. $remainingCacheZeroUserIdCount + $remainingCacheZeroRelatedUserIdCount;
  258. if ($totalRemaining == 0) {
  259. $this->info('✅ 验证通过:所有推荐关系和缓存的农场用户ID都已正确更新');
  260. } else {
  261. $this->warn("⚠️ 仍有 {$totalRemaining} 条记录的农场用户ID未修复");
  262. $this->info(" - 推荐关系表: {$remainingZeroUserIdCount} + {$remainingZeroReferrerIdCount} = " . ($remainingZeroUserIdCount + $remainingZeroReferrerIdCount) . " 条");
  263. $this->info(" - 缓存表: {$remainingCacheZeroUserIdCount} + {$remainingCacheZeroRelatedUserIdCount} = " . ($remainingCacheZeroUserIdCount + $remainingCacheZeroRelatedUserIdCount) . " 条");
  264. $this->info('这可能是因为对应的用户尚未进入农场,属于正常情况');
  265. }
  266. // 随机抽样验证几条记录
  267. $this->sampleVerification();
  268. }
  269. /**
  270. * 随机抽样验证
  271. *
  272. * @return void
  273. */
  274. private function sampleVerification(): void
  275. {
  276. $this->info('正在进行随机抽样验证...');
  277. // 随机选择5条已修复的记录进行验证
  278. $sampleReferrals = UrsUserReferral::where('status', UrsUserReferral::STATUS_VALID)
  279. ->where('user_id', '>', 0)
  280. ->inRandomOrder()
  281. ->limit(5)
  282. ->get();
  283. $this->table([
  284. 'URS用户ID', '农场用户ID', 'URS推荐人ID', '农场推荐人ID', '验证结果'
  285. ], $sampleReferrals->map(function ($referral) {
  286. // 验证农场用户ID是否正确
  287. $mapping = UrsUserMapping::where('urs_user_id', $referral->urs_user_id)
  288. ->where('status', UrsUserMapping::STATUS_VALID)
  289. ->first();
  290. $isCorrect = $mapping && $mapping->user_id == $referral->user_id;
  291. return [
  292. $referral->urs_user_id,
  293. $referral->user_id,
  294. $referral->urs_referrer_id,
  295. $referral->referrer_id,
  296. $isCorrect ? '✅ 正确' : '❌ 错误'
  297. ];
  298. })->toArray());
  299. }
  300. }