UrsTalentService.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. namespace App\Module\UrsPromotion\Services;
  3. use App\Module\UrsPromotion\Dtos\UrsUserTalentDto;
  4. use App\Module\UrsPromotion\Models\UrsUserTalent;
  5. use App\Module\UrsPromotion\Models\UrsTalentConfig;
  6. use Illuminate\Support\Facades\DB;
  7. use Illuminate\Support\Facades\Log;
  8. /**
  9. * URS达人等级服务
  10. *
  11. * 管理URS用户的达人等级和权益(重构版本,支持分离映射关系)
  12. */
  13. class UrsTalentService
  14. {
  15. /**
  16. * 计算并更新用户的达人等级
  17. *
  18. * @param int $ursUserId URS用户ID
  19. * @return UrsUserTalentDto
  20. */
  21. public static function updateTalentLevel(int $ursUserId): UrsUserTalentDto
  22. {
  23. try {
  24. DB::beginTransaction();
  25. // 获取用户的团队统计
  26. $stats = UrsReferralService::getReferralStats($ursUserId);
  27. // 获取活跃团队成员统计
  28. $activeStats = UrsActiveUserService::getActiveTeamMembers($ursUserId);
  29. // 计算应该的达人等级
  30. $newLevel = self::calculateTalentLevel(
  31. $stats['direct_count'],
  32. $stats['total_team_count'],
  33. $activeStats['active_direct_count'],
  34. $activeStats['active_total_count']
  35. );
  36. // 获取或创建达人记录
  37. $talent = UrsUserTalent::firstOrCreate(
  38. ['user_id' => $ursUserId],
  39. [
  40. 'talent_level' => 0,
  41. 'direct_count' => 0,
  42. 'indirect_count' => 0,
  43. 'third_count' => 0,
  44. 'promotion_count' => 0,
  45. ]
  46. );
  47. $oldLevel = $talent->talent_level;
  48. // 更新统计数据
  49. $talent->update([
  50. 'talent_level' => $newLevel,
  51. 'direct_count' => $stats['direct_count'],
  52. 'indirect_count' => $stats['indirect_count'],
  53. 'third_count' => $stats['third_count'],
  54. 'promotion_count' => $stats['total_team_count'],
  55. 'last_level_update_time' => $oldLevel !== $newLevel ? now() : $talent->last_level_update_time,
  56. ]);
  57. DB::commit();
  58. if ($oldLevel !== $newLevel) {
  59. Log::info('URS达人等级更新', [
  60. 'urs_user_id' => $ursUserId,
  61. 'old_level' => $oldLevel,
  62. 'new_level' => $newLevel,
  63. 'direct_count' => $stats['direct_count'],
  64. 'total_team_count' => $stats['total_team_count']
  65. ]);
  66. }
  67. // 获取配置信息用于创建DTO
  68. $config = UrsTalentConfig::where('level', $talent->talent_level)->first();
  69. $nextConfig = UrsTalentConfig::where('level', $talent->talent_level + 1)->first();
  70. $currentConfig = $config ? [
  71. 'name' => $config->name,
  72. 'description' => $config->description,
  73. 'icon' => $config->icon,
  74. ] : null;
  75. $nextConfigArray = $nextConfig ? [
  76. 'level' => $nextConfig->level,
  77. 'name' => $nextConfig->name,
  78. 'direct_count_required' => $nextConfig->direct_count_required,
  79. 'promotion_count_required' => $nextConfig->promotion_count_required,
  80. ] : null;
  81. return UrsUserTalentDto::fromModel($talent, $currentConfig, $nextConfigArray);
  82. } catch (\Exception $e) {
  83. DB::rollBack();
  84. Log::error('URS达人等级更新失败', [
  85. 'urs_user_id' => $ursUserId,
  86. 'error' => $e->getMessage()
  87. ]);
  88. throw $e;
  89. }
  90. }
  91. /**
  92. * 根据团队数据计算达人等级
  93. *
  94. * @param int $directCount 直推人数
  95. * @param int $totalCount 团队总人数
  96. * @param int $activeDirectCount 活跃直推人数
  97. * @param int $activeTotalCount 活跃团队总人数
  98. * @return int 达人等级
  99. */
  100. private static function calculateTalentLevel(int $directCount, int $totalCount, int $activeDirectCount = 0, int $activeTotalCount = 0): int
  101. {
  102. // 获取等级配置(按等级倒序排列)
  103. $configs = UrsTalentConfig::where('status', 1)
  104. ->orderBy('level', 'desc')
  105. ->get();
  106. foreach ($configs as $config) {
  107. // 检查基础条件
  108. $directCountMet = $directCount >= $config->direct_count_required;
  109. $totalCountMet = $totalCount >= $config->promotion_count_required;
  110. // 检查活跃用户条件
  111. $activeDirectMet = $activeDirectCount >= $config->active_direct_required;
  112. $activeTotalMet = $activeTotalCount >= $config->active_count_required;
  113. if ($directCountMet && $totalCountMet && $activeDirectMet && $activeTotalMet) {
  114. return $config->level;
  115. }
  116. }
  117. return 0; // 默认等级
  118. }
  119. /**
  120. * 获取用户的达人信息
  121. *
  122. * @param int $ursUserId URS用户ID
  123. * @return UrsUserTalentDto|null
  124. */
  125. public static function getTalentInfo(int $ursUserId): ?UrsUserTalentDto
  126. {
  127. $talent = UrsUserTalent::where('user_id', $ursUserId)->first();
  128. if (!$talent) {
  129. return null;
  130. }
  131. $config = UrsTalentConfig::where('level', $talent->talent_level)->first();
  132. $nextConfig = UrsTalentConfig::where('level', $talent->talent_level + 1)->first();
  133. $currentConfig = $config ? [
  134. 'name' => $config->name,
  135. 'description' => $config->description,
  136. 'icon' => $config->icon,
  137. ] : null;
  138. $nextConfigArray = $nextConfig ? [
  139. 'level' => $nextConfig->level,
  140. 'name' => $nextConfig->name,
  141. 'direct_count_required' => $nextConfig->direct_count_required,
  142. 'promotion_count_required' => $nextConfig->promotion_count_required,
  143. ] : null;
  144. return UrsUserTalentDto::fromModel($talent, $currentConfig, $nextConfigArray);
  145. }
  146. /**
  147. * 批量更新达人等级
  148. *
  149. * @param array $ursUserIds URS用户ID数组
  150. * @return array 更新结果
  151. */
  152. public static function batchUpdateTalentLevels(array $ursUserIds): array
  153. {
  154. $results = [];
  155. foreach ($ursUserIds as $ursUserId) {
  156. try {
  157. $talent = self::updateTalentLevel($ursUserId);
  158. $results[$ursUserId] = [
  159. 'success' => true,
  160. 'talent_level' => $talent->talentLevel,
  161. 'direct_count' => $talent->directCount,
  162. 'promotion_count' => $talent->promotionCount,
  163. ];
  164. } catch (\Exception $e) {
  165. $results[$ursUserId] = [
  166. 'success' => false,
  167. 'error' => $e->getMessage(),
  168. ];
  169. }
  170. }
  171. return $results;
  172. }
  173. /**
  174. * 获取所有达人等级配置
  175. *
  176. * @return array
  177. */
  178. public static function getAllTalentConfigs(): array
  179. {
  180. return UrsTalentConfig::where('status', 1)
  181. ->orderBy('level')
  182. ->get()
  183. ->map(function ($config) {
  184. return [
  185. 'level' => $config->level,
  186. 'name' => $config->name,
  187. 'description' => $config->description,
  188. 'icon' => $config->icon,
  189. 'direct_count_required' => $config->direct_count_required,
  190. 'promotion_count_required' => $config->promotion_count_required,
  191. 'promotion_reward_rates' => $config->promotion_reward_rates,
  192. 'planting_reward_rates' => $config->planting_reward_rates,
  193. ];
  194. })
  195. ->toArray();
  196. }
  197. /**
  198. * 检查用户是否满足升级条件
  199. *
  200. * @param int $ursUserId URS用户ID
  201. * @return array
  202. */
  203. public static function checkUpgradeEligibility(int $ursUserId): array
  204. {
  205. $talent = UrsUserTalent::where('user_id', $ursUserId)->first();
  206. if (!$talent) {
  207. return ['eligible' => false, 'reason' => '用户达人信息不存在'];
  208. }
  209. $nextLevel = $talent->talent_level + 1;
  210. $nextConfig = UrsTalentConfig::where('level', $nextLevel)->first();
  211. if (!$nextConfig) {
  212. return ['eligible' => false, 'reason' => '已达到最高等级'];
  213. }
  214. // 获取活跃团队成员统计
  215. $activeStats = UrsActiveUserService::getActiveTeamMembers($ursUserId);
  216. // 检查各项条件
  217. $directCountMet = $talent->direct_count >= $nextConfig->direct_count_required;
  218. $promotionCountMet = $talent->promotion_count >= $nextConfig->promotion_count_required;
  219. $activeDirectMet = $activeStats['active_direct_count'] >= $nextConfig->active_direct_required;
  220. $activeTotalMet = $activeStats['active_total_count'] >= $nextConfig->active_count_required;
  221. return [
  222. 'eligible' => $directCountMet && $promotionCountMet && $activeDirectMet && $activeTotalMet,
  223. 'next_level' => $nextLevel,
  224. 'next_level_name' => $nextConfig->name,
  225. 'requirements' => [
  226. 'direct_count' => [
  227. 'required' => $nextConfig->direct_count_required,
  228. 'current' => $talent->direct_count,
  229. 'met' => $directCountMet,
  230. 'gap' => max(0, $nextConfig->direct_count_required - $talent->direct_count),
  231. ],
  232. 'promotion_count' => [
  233. 'required' => $nextConfig->promotion_count_required,
  234. 'current' => $talent->promotion_count,
  235. 'met' => $promotionCountMet,
  236. 'gap' => max(0, $nextConfig->promotion_count_required - $talent->promotion_count),
  237. ],
  238. 'active_direct_count' => [
  239. 'required' => $nextConfig->active_direct_required,
  240. 'current' => $activeStats['active_direct_count'],
  241. 'met' => $activeDirectMet,
  242. 'gap' => max(0, $nextConfig->active_direct_required - $activeStats['active_direct_count']),
  243. ],
  244. 'active_total_count' => [
  245. 'required' => $nextConfig->active_count_required,
  246. 'current' => $activeStats['active_total_count'],
  247. 'met' => $activeTotalMet,
  248. 'gap' => max(0, $nextConfig->active_count_required - $activeStats['active_total_count']),
  249. ],
  250. ],
  251. ];
  252. }
  253. }