UrsActiveUserService.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <?php
  2. namespace App\Module\UrsPromotion\Services;
  3. use App\Module\UrsPromotion\Models\UrsUserMapping;
  4. use App\Module\User\Models\User;
  5. use Illuminate\Support\Facades\DB;
  6. use Illuminate\Support\Facades\Log;
  7. use Carbon\Carbon;
  8. /**
  9. * URS活跃用户服务
  10. *
  11. * 负责管理URS用户的活跃状态,包括活跃状态检查、更新和统计
  12. */
  13. class UrsActiveUserService
  14. {
  15. /**
  16. * 活跃用户定义:最近15天有活动的用户
  17. */
  18. const ACTIVE_DAYS_THRESHOLD = 15;
  19. /**
  20. * 更新单个用户的活跃状态
  21. *
  22. * @param int $ursUserId URS用户ID
  23. * @return bool
  24. */
  25. public static function updateUserActiveStatus(int $ursUserId): bool
  26. {
  27. try {
  28. // 获取用户映射关系
  29. $mapping = UrsUserMapping::where('urs_user_id', $ursUserId)
  30. ->where('status', UrsUserMapping::STATUS_VALID)
  31. ->with('user')
  32. ->first();
  33. if (!$mapping || !$mapping->user) {
  34. Log::warning('URS用户映射关系不存在或农场用户不存在', [
  35. 'urs_user_id' => $ursUserId
  36. ]);
  37. return false;
  38. }
  39. // 检查用户活跃状态
  40. $isActive = self::checkUserActivity($mapping->user);
  41. $activeDaysCount = self::calculateActiveDaysCount($mapping->user);
  42. // 更新活跃状态
  43. $mapping->update([
  44. 'is_active' => $isActive ? UrsUserMapping::ACTIVE_YES : UrsUserMapping::ACTIVE_NO,
  45. 'last_activity_check' => now(),
  46. 'active_days_count' => $activeDaysCount,
  47. ]);
  48. Log::info('URS用户活跃状态更新成功', [
  49. 'urs_user_id' => $ursUserId,
  50. 'farm_user_id' => $mapping->user_id,
  51. 'is_active' => $isActive,
  52. 'active_days_count' => $activeDaysCount
  53. ]);
  54. return true;
  55. } catch (\Exception $e) {
  56. Log::error('URS用户活跃状态更新失败', [
  57. 'urs_user_id' => $ursUserId,
  58. 'error' => $e->getMessage()
  59. ]);
  60. return false;
  61. }
  62. }
  63. /**
  64. * 批量更新用户活跃状态
  65. *
  66. * @param int $limit 每次处理的用户数量限制
  67. * @return array 更新结果统计
  68. */
  69. public static function batchUpdateActiveStatus(int $limit = 1000): array
  70. {
  71. $stats = [
  72. 'total_processed' => 0,
  73. 'successful_updates' => 0,
  74. 'failed_updates' => 0,
  75. 'active_users' => 0,
  76. 'inactive_users' => 0,
  77. ];
  78. try {
  79. // 获取需要检查的用户(预加载用户和用户信息关联)
  80. $mappings = UrsUserMapping::getUsersNeedActivityCheck($limit);
  81. $stats['total_processed'] = $mappings->count();
  82. Log::info('开始批量更新URS用户活跃状态', [
  83. 'total_users' => $stats['total_processed'],
  84. 'limit' => $limit
  85. ]);
  86. foreach ($mappings as $mapping) {
  87. try {
  88. if (!$mapping->user) {
  89. $stats['failed_updates']++;
  90. continue;
  91. }
  92. // 检查用户活跃状态
  93. $isActive = self::checkUserActivity($mapping->user);
  94. $activeDaysCount = self::calculateActiveDaysCount($mapping->user);
  95. // 更新活跃状态
  96. $mapping->update([
  97. 'is_active' => $isActive ? UrsUserMapping::ACTIVE_YES : UrsUserMapping::ACTIVE_NO,
  98. 'last_activity_check' => now(),
  99. 'active_days_count' => $activeDaysCount,
  100. ]);
  101. $stats['successful_updates']++;
  102. if ($isActive) {
  103. $stats['active_users']++;
  104. } else {
  105. $stats['inactive_users']++;
  106. }
  107. } catch (\Exception $e) {
  108. $stats['failed_updates']++;
  109. Log::error('单个用户活跃状态更新失败', [
  110. 'urs_user_id' => $mapping->urs_user_id,
  111. 'error' => $e->getMessage()
  112. ]);
  113. }
  114. }
  115. Log::info('批量更新URS用户活跃状态完成', $stats);
  116. } catch (\Exception $e) {
  117. Log::error('批量更新URS用户活跃状态失败', [
  118. 'error' => $e->getMessage(),
  119. 'stats' => $stats
  120. ]);
  121. }
  122. return $stats;
  123. }
  124. /**
  125. * 强制更新用户活跃状态(忽略last_activity_check时间限制)
  126. *
  127. * @param int $limit 每次处理的用户数量限制
  128. * @return array 更新结果统计
  129. */
  130. public static function forceUpdateActiveStatus(int $limit = 1000): array
  131. {
  132. $stats = [
  133. 'total_processed' => 0,
  134. 'successful_updates' => 0,
  135. 'failed_updates' => 0,
  136. 'active_users' => 0,
  137. 'inactive_users' => 0,
  138. ];
  139. try {
  140. // 获取所有有效的用户映射(忽略last_activity_check限制)
  141. $mappings = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
  142. ->with(['user', 'user.info'])
  143. ->limit($limit)
  144. ->get();
  145. $stats['total_processed'] = $mappings->count();
  146. Log::info('开始强制更新URS用户活跃状态', [
  147. 'total_users' => $stats['total_processed'],
  148. 'limit' => $limit
  149. ]);
  150. foreach ($mappings as $mapping) {
  151. try {
  152. if (!$mapping->user) {
  153. $stats['failed_updates']++;
  154. continue;
  155. }
  156. // 检查用户活跃状态
  157. $isActive = self::checkUserActivity($mapping->user);
  158. $activeDaysCount = self::calculateActiveDaysCount($mapping->user);
  159. // 更新活跃状态
  160. $mapping->update([
  161. 'is_active' => $isActive ? UrsUserMapping::ACTIVE_YES : UrsUserMapping::ACTIVE_NO,
  162. 'last_activity_check' => now(),
  163. 'active_days_count' => $activeDaysCount,
  164. ]);
  165. $stats['successful_updates']++;
  166. if ($isActive) {
  167. $stats['active_users']++;
  168. } else {
  169. $stats['inactive_users']++;
  170. }
  171. } catch (\Exception $e) {
  172. $stats['failed_updates']++;
  173. Log::error('单个用户活跃状态强制更新失败', [
  174. 'urs_user_id' => $mapping->urs_user_id,
  175. 'error' => $e->getMessage()
  176. ]);
  177. }
  178. }
  179. Log::info('强制更新URS用户活跃状态完成', $stats);
  180. } catch (\Exception $e) {
  181. Log::error('强制更新URS用户活跃状态失败', [
  182. 'error' => $e->getMessage(),
  183. 'stats' => $stats
  184. ]);
  185. }
  186. return $stats;
  187. }
  188. /**
  189. * 检查用户活跃状态
  190. *
  191. * @param User $user 农场用户对象
  192. * @return bool
  193. */
  194. public static function checkUserActivity(User $user): bool
  195. {
  196. // 获取用户信息中的活动时间
  197. $userInfo = $user->info;
  198. if (!$userInfo || !$userInfo->last_activity_time) {
  199. return false;
  200. }
  201. $threshold = Carbon::now()->subDays(self::ACTIVE_DAYS_THRESHOLD);
  202. return $userInfo->last_activity_time >= $threshold;
  203. }
  204. /**
  205. * 计算用户活跃天数
  206. *
  207. * @param User $user 农场用户对象
  208. * @return int
  209. */
  210. public static function calculateActiveDaysCount(User $user): int
  211. {
  212. // 获取用户信息中的活动时间
  213. $userInfo = $user->info;
  214. if (!$userInfo || !$userInfo->last_activity_time) {
  215. return 0;
  216. }
  217. $daysSinceLastActivity = Carbon::now()->diffInDays($userInfo->last_activity_time);
  218. // 如果在活跃期内,返回1,否则返回0
  219. return $daysSinceLastActivity <= self::ACTIVE_DAYS_THRESHOLD ? 1 : 0;
  220. }
  221. /**
  222. * 获取活跃用户统计信息
  223. *
  224. * @return array
  225. */
  226. public static function getActiveUserStats(): array
  227. {
  228. return UrsUserMapping::getActiveUserStats();
  229. }
  230. /**
  231. * 获取指定URS用户的活跃团队成员
  232. *
  233. * @param int $ursUserId URS用户ID
  234. * @return array
  235. */
  236. public static function getActiveTeamMembers(int $ursUserId): array
  237. {
  238. // 获取用户的推荐关系(使用配置的团队统计深度,支持达人等级统计)
  239. $teamMembers = UrsReferralService::getTeamMembers($ursUserId);
  240. if (empty($teamMembers)) {
  241. return [
  242. 'active_direct_count' => 0,
  243. 'active_total_count' => 0,
  244. 'active_members' => []
  245. ];
  246. }
  247. // 提取所有团队成员的URS用户ID
  248. $allMemberIds = [];
  249. foreach ($teamMembers as $level => $members) {
  250. // $members 是一个简单的用户ID数组,不是对象数组
  251. $allMemberIds = array_merge($allMemberIds, $members);
  252. }
  253. // 获取活跃的团队成员
  254. $activeMemberIds = UrsUserMapping::getActiveUrsUserIds($allMemberIds);
  255. // 统计各层级的活跃人数
  256. $activeDirectCount = 0;
  257. $activeTotalCount = count($activeMemberIds);
  258. $activeMembers = [];
  259. foreach ($teamMembers as $level => $members) {
  260. foreach ($members as $memberId) {
  261. if (in_array($memberId, $activeMemberIds)) {
  262. $activeMembers[] = [
  263. 'urs_user_id' => $memberId,
  264. 'level' => $level
  265. ];
  266. if ($level === 1) { // 直推
  267. $activeDirectCount++;
  268. }
  269. }
  270. }
  271. }
  272. return [
  273. 'active_direct_count' => $activeDirectCount,
  274. 'active_total_count' => $activeTotalCount,
  275. 'active_members' => $activeMembers
  276. ];
  277. }
  278. /**
  279. * 获取指定URS用户的活跃直推成员(仅直推层级)
  280. *
  281. * 针对达人等级升级条件优化,只统计直推活跃用户
  282. * 相比getActiveTeamMembers方法,此方法只关注直推层级,性能更优
  283. *
  284. * @param int $ursUserId URS用户ID
  285. * @return array 返回活跃直推统计信息
  286. */
  287. public static function getActiveDirectMembers(int $ursUserId): array
  288. {
  289. // 只获取直推成员(第1层级)
  290. $directMembers = UrsReferralService::getDirectReferrals($ursUserId);
  291. if (empty($directMembers)) {
  292. return [
  293. 'active_direct_count' => 0,
  294. 'active_direct_members' => []
  295. ];
  296. }
  297. // 获取活跃的直推成员
  298. $activeDirectIds = UrsUserMapping::getActiveUrsUserIds($directMembers);
  299. // 构建活跃直推成员详情
  300. $activeDirectMembers = [];
  301. foreach ($activeDirectIds as $memberId) {
  302. $activeDirectMembers[] = [
  303. 'urs_user_id' => $memberId,
  304. 'level' => 1 // 直推层级
  305. ];
  306. }
  307. return [
  308. 'active_direct_count' => count($activeDirectIds),
  309. 'active_direct_members' => $activeDirectMembers
  310. ];
  311. }
  312. /**
  313. * 获取活跃用户详细统计
  314. *
  315. * @return array
  316. */
  317. public static function getDetailedActiveStats(): array
  318. {
  319. $baseStats = self::getActiveUserStats();
  320. // 获取最近更新统计
  321. $recentUpdates = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
  322. ->where('last_activity_check', '>=', now()->subDay())
  323. ->count();
  324. // 获取需要检查的用户数量
  325. $needCheckCount = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
  326. ->where(function($query) {
  327. $query->whereNull('last_activity_check')
  328. ->orWhere('last_activity_check', '<', now()->subDay());
  329. })
  330. ->count();
  331. return array_merge($baseStats, [
  332. 'recent_updates' => $recentUpdates,
  333. 'need_check_count' => $needCheckCount,
  334. 'last_update_time' => UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
  335. ->whereNotNull('last_activity_check')
  336. ->max('last_activity_check'),
  337. ]);
  338. }
  339. /**
  340. * 重置所有用户的活跃状态(用于测试或重新初始化)
  341. *
  342. * @return array
  343. */
  344. public static function resetAllActiveStatus(): array
  345. {
  346. try {
  347. DB::beginTransaction();
  348. // 重置所有用户的活跃状态
  349. $updated = UrsUserMapping::where('status', UrsUserMapping::STATUS_VALID)
  350. ->update([
  351. 'is_active' => UrsUserMapping::ACTIVE_NO,
  352. 'last_activity_check' => null,
  353. 'active_days_count' => 0,
  354. ]);
  355. DB::commit();
  356. Log::info('重置所有URS用户活跃状态完成', [
  357. 'updated_count' => $updated
  358. ]);
  359. return [
  360. 'success' => true,
  361. 'updated_count' => $updated,
  362. 'message' => '重置完成'
  363. ];
  364. } catch (\Exception $e) {
  365. DB::rollBack();
  366. Log::error('重置URS用户活跃状态失败', [
  367. 'error' => $e->getMessage()
  368. ]);
  369. return [
  370. 'success' => false,
  371. 'updated_count' => 0,
  372. 'message' => '重置失败:' . $e->getMessage()
  373. ];
  374. }
  375. }
  376. }