UrsRewardDistributionService.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php
  2. namespace App\Module\UrsPromotion\Services;
  3. use App\Module\UrsPromotion\Dtos\UrsProfitDto;
  4. use App\Module\UrsPromotion\Models\UrsUserMapping;
  5. use App\Module\UrsPromotion\Models\UrsUserReferral;
  6. use App\Module\UrsPromotion\Models\UrsUserTalent;
  7. use App\Module\UrsPromotion\Models\UrsProfit;
  8. /**
  9. * URS奖励分发服务
  10. *
  11. * 实现奖励发放逻辑:
  12. * 1. 根据URS用户ID查找上级关系链
  13. * 2. 检查每个上级是否已进入农场
  14. * 3. 如果上级未进入农场则跳过,继续处理上上级
  15. * 4. 记录奖励分发结果
  16. */
  17. class UrsRewardDistributionService
  18. {
  19. /**
  20. * 分发推广奖励
  21. *
  22. * @param int $ursUserId 产生收益的URS用户ID
  23. * @param int $sourceId 收益来源ID
  24. * @param string $sourceType 收益来源类型
  25. * @param string $profitType 收益类型
  26. * @param float $originalAmount 原始收益金额
  27. * @param array $rewardConfig 奖励配置 [level => rate]
  28. * @return array 分发结果
  29. */
  30. public static function distributeReward(
  31. int $ursUserId,
  32. int $sourceId,
  33. string $sourceType,
  34. string $profitType,
  35. float $originalAmount,
  36. array $rewardConfig
  37. ): array {
  38. $results = [];
  39. // 获取上级关系链
  40. $referralChain = self::getReferralChain($ursUserId, count($rewardConfig));
  41. $level = 1;
  42. foreach ($referralChain as $referrerUrsUserId) {
  43. if (!isset($rewardConfig[$level])) {
  44. break; // 超出配置层级
  45. }
  46. $rate = $rewardConfig[$level];
  47. $profitAmount = $originalAmount * $rate;
  48. // 检查推荐人是否已进入农场
  49. $farmUserId = UrsUserMapping::getFarmUserIdByUrsUserId($referrerUrsUserId);
  50. if ($farmUserId === null) {
  51. // 推荐人未进入农场,记录跳过状态
  52. $results[] = self::recordSkippedReward(
  53. $referrerUrsUserId,
  54. $ursUserId,
  55. $sourceId,
  56. $sourceType,
  57. $profitType,
  58. $level,
  59. $originalAmount,
  60. $profitAmount,
  61. $rate
  62. );
  63. } else {
  64. // 推荐人已进入农场,记录正常奖励
  65. $results[] = self::recordNormalReward(
  66. $referrerUrsUserId,
  67. $farmUserId,
  68. $ursUserId,
  69. $sourceId,
  70. $sourceType,
  71. $profitType,
  72. $level,
  73. $originalAmount,
  74. $profitAmount,
  75. $rate
  76. );
  77. }
  78. $level++;
  79. }
  80. return $results;
  81. }
  82. /**
  83. * 获取推荐关系链
  84. *
  85. * @param int $ursUserId URS用户ID
  86. * @param int $maxLevels 最大层级数
  87. * @return array URS用户ID数组,按层级排序
  88. */
  89. private static function getReferralChain(int $ursUserId, int $maxLevels): array
  90. {
  91. return UrsReferralService::getReferralChain($ursUserId, $maxLevels);
  92. }
  93. /**
  94. * 记录跳过的奖励
  95. */
  96. private static function recordSkippedReward(
  97. int $ursUserId,
  98. int $promotionMemberUrsUserId,
  99. int $sourceId,
  100. string $sourceType,
  101. string $profitType,
  102. int $relationLevel,
  103. float $originalAmount,
  104. float $profitAmount,
  105. float $profitRate
  106. ): array {
  107. $talent = UrsUserTalent::where('urs_user_id', $ursUserId)->first();
  108. $talentLevel = $talent ? $talent->talent_level : 0;
  109. $profit = UrsProfit::create([
  110. 'urs_user_id' => $ursUserId,
  111. 'urs_promotion_member_id' => $promotionMemberUrsUserId,
  112. 'source_id' => $sourceId,
  113. 'source_type' => $sourceType,
  114. 'profit_type' => $profitType,
  115. 'relation_level' => $relationLevel,
  116. 'original_amount' => $originalAmount,
  117. 'profit_amount' => $profitAmount,
  118. 'profit_rate' => $profitRate,
  119. 'talent_level' => $talentLevel,
  120. 'farm_user_id' => null,
  121. 'status' => UrsProfit::STATUS_SKIPPED,
  122. ]);
  123. return [
  124. 'urs_user_id' => $ursUserId,
  125. 'farm_user_id' => null,
  126. 'relation_level' => $relationLevel,
  127. 'profit_amount' => $profitAmount,
  128. 'status' => 'skipped',
  129. 'reason' => '用户未进入农场',
  130. 'profit_id' => $profit->id,
  131. ];
  132. }
  133. /**
  134. * 记录正常奖励
  135. */
  136. private static function recordNormalReward(
  137. int $ursUserId,
  138. int $farmUserId,
  139. int $promotionMemberUrsUserId,
  140. int $sourceId,
  141. string $sourceType,
  142. string $profitType,
  143. int $relationLevel,
  144. float $originalAmount,
  145. float $profitAmount,
  146. float $profitRate
  147. ): array {
  148. $talent = UrsUserTalent::where('urs_user_id', $ursUserId)->first();
  149. $talentLevel = $talent ? $talent->talent_level : 0;
  150. $profit = UrsProfit::create([
  151. 'urs_user_id' => $ursUserId,
  152. 'urs_promotion_member_id' => $promotionMemberUrsUserId,
  153. 'source_id' => $sourceId,
  154. 'source_type' => $sourceType,
  155. 'profit_type' => $profitType,
  156. 'relation_level' => $relationLevel,
  157. 'original_amount' => $originalAmount,
  158. 'profit_amount' => $profitAmount,
  159. 'profit_rate' => $profitRate,
  160. 'talent_level' => $talentLevel,
  161. 'farm_user_id' => $farmUserId,
  162. 'status' => UrsProfit::STATUS_NORMAL,
  163. ]);
  164. return [
  165. 'urs_user_id' => $ursUserId,
  166. 'farm_user_id' => $farmUserId,
  167. 'relation_level' => $relationLevel,
  168. 'profit_amount' => $profitAmount,
  169. 'status' => 'success',
  170. 'reason' => '奖励发放成功',
  171. 'profit_id' => $profit->id,
  172. ];
  173. }
  174. /**
  175. * 获取用户的奖励统计
  176. *
  177. * @param int $ursUserId URS用户ID
  178. * @param string|null $profitType 收益类型筛选
  179. * @return array
  180. */
  181. public static function getUserRewardStats(int $ursUserId, ?string $profitType = null): array
  182. {
  183. $query = UrsProfit::where('urs_user_id', $ursUserId);
  184. if ($profitType) {
  185. $query->where('profit_type', $profitType);
  186. }
  187. $stats = [
  188. 'total_amount' => $query->sum('profit_amount'),
  189. 'normal_count' => $query->where('status', UrsProfit::STATUS_NORMAL)->count(),
  190. 'skipped_count' => $query->where('status', UrsProfit::STATUS_SKIPPED)->count(),
  191. 'cancelled_count' => $query->where('status', UrsProfit::STATUS_CANCELLED)->count(),
  192. ];
  193. return $stats;
  194. }
  195. /**
  196. * 获取跳过的奖励列表
  197. *
  198. * @param int $ursUserId URS用户ID
  199. * @return UrsProfitDto[]
  200. */
  201. public static function getSkippedRewards(int $ursUserId): array
  202. {
  203. $profits = UrsProfit::where('urs_user_id', $ursUserId)
  204. ->where('status', UrsProfit::STATUS_SKIPPED)
  205. ->orderBy('created_at', 'desc')
  206. ->get();
  207. return $profits->map(function ($profit) {
  208. return UrsProfitDto::fromModel($profit);
  209. })->toArray();
  210. }
  211. }