PromotionProfitLogic.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. namespace App\Module\Promotion\Logics;
  3. use App\Module\Promotion\Enums\PROFIT_SOURCE_TYPE;
  4. use App\Module\Promotion\Enums\REFERRAL_LEVEL;
  5. use App\Module\Promotion\Models\PromotionProfit;
  6. use App\Module\Promotion\Models\PromotionProfitRule;
  7. use Illuminate\Support\Facades\Log;
  8. /**
  9. * 团队收益逻辑类
  10. *
  11. * 处理团队收益的核心业务逻辑,包括计算收益分成、记录收益、
  12. * 获取收益规则等功能。该类仅供内部使用,不对外提供服务。
  13. */
  14. class PromotionProfitLogic
  15. {
  16. /**
  17. * @var ReferralLogic
  18. */
  19. protected $referralLogic;
  20. /**
  21. * @var TalentLogic
  22. */
  23. protected $talentLogic;
  24. /**
  25. * 构造函数
  26. *
  27. * @param ReferralLogic $referralLogic
  28. * @param TalentLogic $talentLogic
  29. */
  30. public function __construct(ReferralLogic $referralLogic, TalentLogic $talentLogic)
  31. {
  32. $this->referralLogic = $referralLogic;
  33. $this->talentLogic = $talentLogic;
  34. }
  35. /**
  36. * 计算并记录团队收益分成
  37. *
  38. * @param int $userId 产生收益的用户ID
  39. * @param string $sourceType 收益来源类型
  40. * @param int $sourceId 收益来源ID
  41. * @param int $itemId 物品ID
  42. * @param int $amount 收益数量
  43. * @return bool
  44. */
  45. public function calculateAndRecordProfit(int $userId, string $sourceType, int $sourceId, int $itemId, int $amount): bool
  46. {
  47. try {
  48. // 获取收益分成规则
  49. $rule = $this->getProfitRule($sourceType);
  50. if (!$rule || !$rule->isActive()) {
  51. Log::info("收益分成规则不存在或未激活: {$sourceType}");
  52. return false;
  53. }
  54. // 检查收益是否满足最小数量要求
  55. $minAmount = $rule->getRuleAttribute('min_amount', 0);
  56. if ($amount < $minAmount) {
  57. Log::info("收益数量不满足最小要求: {$amount} < {$minAmount}");
  58. return false;
  59. }
  60. // 检查收益是否超过最大数量限制
  61. $maxAmount = $rule->getRuleAttribute('max_amount', PHP_INT_MAX);
  62. if ($amount > $maxAmount) {
  63. $amount = $maxAmount;
  64. Log::info("收益数量超过最大限制,已调整为: {$maxAmount}");
  65. }
  66. // 检查物品是否在排除列表中
  67. $excludedItems = $rule->getRuleAttribute('excluded_items', []);
  68. if (in_array($itemId, $excludedItems)) {
  69. Log::info("物品在排除列表中: {$itemId}");
  70. return false;
  71. }
  72. // 获取用户的所有上级
  73. $referrers = $this->referralLogic->getAllReferrers($userId, $rule->max_indirect_level);
  74. if (empty($referrers)) {
  75. Log::info("用户没有上级: {$userId}");
  76. return true;
  77. }
  78. // 检查事务是否已开启
  79. \UCore\Db\Helper::check_tr();
  80. // 记录收益分成
  81. $success = true;
  82. foreach ($referrers as $referrer) {
  83. $referrerId = $referrer['user_id'];
  84. $level = $referrer['level'];
  85. $depth = $referrer['depth'];
  86. // 计算分成比例
  87. $profitRate = $this->calculateProfitRate($level, $depth, $rule);
  88. // 计算分成数量
  89. $profitAmount = (int)($amount * $profitRate);
  90. if ($profitAmount <= 0) {
  91. continue;
  92. }
  93. // 记录收益
  94. $profit = new PromotionProfit();
  95. $profit->user_id = $referrerId;
  96. $profit->promotion_member_id = $userId;
  97. $profit->source_id = $sourceId;
  98. $profit->source_type = $sourceType;
  99. $profit->item_id = $itemId;
  100. $profit->profit_amount = $profitAmount;
  101. $profit->profit_rate = $profitRate;
  102. $profit->relation_type = $level;
  103. if (!$profit->save()) {
  104. $success = false;
  105. Log::error("记录收益分成失败: 用户 {$referrerId}, 来源 {$sourceType}, 数量 {$profitAmount}");
  106. }
  107. }
  108. return $success;
  109. } catch (\Exception $e) {
  110. Log::error("计算并记录团队收益分成失败: " . $e->getMessage());
  111. return false;
  112. }
  113. }
  114. /**
  115. * 计算分成比例
  116. *
  117. * @param int $level 关系层级
  118. * @param int $depth 层级深度
  119. * @param PromotionProfitRule $rule 分成规则
  120. * @return float
  121. */
  122. private function calculateProfitRate(int $level, int $depth, PromotionProfitRule $rule): float
  123. {
  124. // 直推关系
  125. if ($level == REFERRAL_LEVEL::DIRECT) {
  126. return $rule->direct_profit_rate;
  127. }
  128. // 间推关系
  129. if ($level == REFERRAL_LEVEL::INDIRECT) {
  130. // 获取用户的达人等级
  131. $talent = $this->talentLogic->getUserTalent($depth);
  132. if (!$talent || $talent->talent_level == 0) {
  133. return 0.0;
  134. }
  135. // 获取达人等级对应的分成比例
  136. $profitRate = $this->talentLogic->getTalentProfitRate($talent->talent_level);
  137. // 应用特殊规则
  138. $specialRates = $rule->getRuleAttribute('special_rates', []);
  139. foreach ($specialRates as $specialRate) {
  140. if (isset($specialRate['talent_level']) && $specialRate['talent_level'] == $talent->talent_level) {
  141. if (isset($specialRate['bonus_rate'])) {
  142. $profitRate += $specialRate['bonus_rate'];
  143. }
  144. }
  145. }
  146. return $profitRate;
  147. }
  148. return 0.0;
  149. }
  150. /**
  151. * 获取收益分成规则
  152. *
  153. * @param string $sourceType 收益来源类型
  154. * @return PromotionProfitRule|null
  155. */
  156. public function getProfitRule(string $sourceType): ?PromotionProfitRule
  157. {
  158. return PromotionProfitRule::where('source_type', $sourceType)
  159. ->where('status', 1)
  160. ->first();
  161. }
  162. /**
  163. * 获取用户的团队收益记录
  164. *
  165. * @param int $userId 用户ID
  166. * @param string|null $sourceType 收益来源类型
  167. * @param int $page 页码
  168. * @param int $pageSize 每页数量
  169. * @return array
  170. */
  171. public function getUserProfits(int $userId, ?string $sourceType = null, int $page = 1, int $pageSize = 20): array
  172. {
  173. $query = PromotionProfit::where('user_id', $userId);
  174. if ($sourceType) {
  175. $query->where('source_type', $sourceType);
  176. }
  177. $total = $query->count();
  178. $profits = $query->orderBy('created_at', 'desc')
  179. ->offset(($page - 1) * $pageSize)
  180. ->limit($pageSize)
  181. ->with(['promotionMember', 'item'])
  182. ->get();
  183. return [
  184. 'total' => $total,
  185. 'page' => $page,
  186. 'page_size' => $pageSize,
  187. 'total_pages' => ceil($total / $pageSize),
  188. 'profits' => $profits
  189. ];
  190. }
  191. /**
  192. * 统计用户的团队收益
  193. *
  194. * @param int $userId 用户ID
  195. * @param string|null $sourceType 收益来源类型
  196. * @param int|null $itemId 物品ID
  197. * @return array
  198. */
  199. public function sumUserProfits(int $userId, ?string $sourceType = null, ?int $itemId = null): array
  200. {
  201. $query = PromotionProfit::where('user_id', $userId);
  202. if ($sourceType) {
  203. $query->where('source_type', $sourceType);
  204. }
  205. if ($itemId) {
  206. $query->where('item_id', $itemId);
  207. }
  208. $directSum = (clone $query)->where('relation_type', REFERRAL_LEVEL::DIRECT)->sum('profit_amount');
  209. $indirectSum = (clone $query)->where('relation_type', REFERRAL_LEVEL::INDIRECT)->sum('profit_amount');
  210. $totalSum = $directSum + $indirectSum;
  211. return [
  212. 'direct_profit' => $directSum,
  213. 'indirect_profit' => $indirectSum,
  214. 'total_profit' => $totalSum
  215. ];
  216. }
  217. }