UrsTalentService.php 11 KB

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