Agent.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <?php
  2. namespace app\common\service\commission;
  3. use app\common\Service\Commission\Config;
  4. use app\common\model\User as UserModel;
  5. use app\common\model\commission\Agent as AgentModel;
  6. use app\common\model\commission\Level as LevelModel;
  7. use app\common\model\commission\Log as LogModel;
  8. use app\common\model\commission\Share as ShareModel;
  9. use app\common\model\Order as OrderModel;
  10. /**
  11. * 分销商业务
  12. */
  13. class Agent
  14. {
  15. public $user; // 商城用户
  16. public $agent; // 分销商
  17. public $config; // 分销设置
  18. public $parentUserId;
  19. public $nextAgentTeam;
  20. public $nextUserTeam;
  21. /**
  22. * 构造函数
  23. *
  24. * @param mixed $user 用户ID/用户对象
  25. */
  26. public function __construct($user)
  27. {
  28. if (is_numeric($user)) {
  29. $this->user = UserModel::get($user);
  30. } else {
  31. $this->user = UserModel::get($user->id);
  32. }
  33. if (!empty($this->user->id)) {
  34. $this->agent = AgentModel::with(['level_info'])->find($this->user->id);
  35. }
  36. $this->config = new Config();
  37. }
  38. /**
  39. * 获取分销商实时状态
  40. */
  41. public function getAgentStatus($autoCreate = false)
  42. {
  43. if (empty($this->agent)) {
  44. // 自动创建分销商
  45. if ($autoCreate) {
  46. return $this->createNewAgent();
  47. }
  48. return NULL;
  49. }
  50. return $this->agent->status;
  51. }
  52. /**
  53. * 获取分销商可参与状态 正常和冻结都可正常浏览并统计业绩
  54. */
  55. public function isAgentAvaliable()
  56. {
  57. $status = $this->getAgentStatus();
  58. if (in_array($status, [AgentModel::AGENT_STATUS_NORMAL, AgentModel::AGENT_STATUS_FREEZE])) {
  59. return true;
  60. }
  61. return false;
  62. }
  63. /**
  64. * 获取分销商等级
  65. */
  66. public function getAgentLevel()
  67. {
  68. if (empty($this->agent)) {
  69. return 0;
  70. }
  71. if (empty($this->agent->level_info)) {
  72. return 1;
  73. }
  74. return $this->agent->level_info->level;
  75. }
  76. /**
  77. * 分销商升级是否锁定
  78. */
  79. public function getAgentUpgradeLock()
  80. {
  81. if (empty($this->agent)) {
  82. return true;
  83. }
  84. if ($this->agent->upgrade_lock == AgentModel::UPGRADE_LOCK_OPEN) {
  85. return true;
  86. }
  87. return false;
  88. }
  89. /**
  90. * 实时获取上级推荐人
  91. */
  92. public function getParentUserId()
  93. {
  94. if (empty($this->parentUserId)) {
  95. $this->parentUserId = 0;
  96. $parent_user_id = $this->user->parent_user_id;
  97. // 未直接绑定分销商,从分享记录查找最近的分销商
  98. if ($parent_user_id === NULL) {
  99. $shareLog = ShareModel::hasWhere(
  100. 'agent',
  101. function ($query) {
  102. return $query->where('status', 'in', [AgentModel::AGENT_STATUS_NORMAL, AgentModel::AGENT_STATUS_FREEZE]);
  103. }
  104. )->where('Share.user_id', $this->user->id)->order('id desc')->find();
  105. if ($shareLog) {
  106. $parent_user_id = $shareLog['share_id'];
  107. }
  108. }
  109. // 再次检查上级分销商是否可用
  110. if ($parent_user_id > 0) {
  111. $parentUser = UserModel::where('id', $parent_user_id)->find();
  112. $parentAgent = AgentModel::avaliable()->where(['user_id' => $parent_user_id])->find();
  113. if ($parentUser && $parentAgent) {
  114. $this->parentUserId = $parentAgent->user_id;
  115. }
  116. }
  117. }
  118. return $this->parentUserId;
  119. }
  120. /**
  121. * 创建分销商
  122. */
  123. public function createNewAgent($event = '')
  124. {
  125. // 已经是分销商
  126. if (!empty($this->agent)) {
  127. return $this->getAgentStatus();
  128. }
  129. $agentStatus = AgentModel::AGENT_STATUS_NULL;
  130. $condition = $this->config->getBecomeAgentEvent();
  131. $check = false; // 是否满足条件
  132. $needAgentApplyForm = $this->config->isAgentApplyForm();
  133. if ($event !== '' && $condition['type'] !== $event) {
  134. return $agentStatus;
  135. }
  136. switch ($condition['type']) {
  137. case 'apply': // 直接自助申请
  138. $check = true;
  139. $needAgentApplyForm = true;
  140. break;
  141. case 'goods': // 需购买指定产品
  142. $isBuy = OrderModel::hasWhere('items', function ($query) use ($condition) {
  143. return $query->where('goods_id', 'in', $condition['value'])->where('refund_status', 0);
  144. })->where('Order.user_id', $this->user->id)->paid()->find();
  145. if ($isBuy) $check = true;
  146. break;
  147. case 'consume': // 消费累计
  148. if ($this->user->total_consume >= $condition['value']) {
  149. $check = true;
  150. }
  151. break;
  152. case 'user': // 新会员注册
  153. $check = true;
  154. $needAgentApplyForm = false;
  155. break;
  156. }
  157. // 可以成为分销商 检查系统设置
  158. if ($check) {
  159. // 需后台审核
  160. if ($this->config->isAgentCheck()) {
  161. $agentStatus = AgentModel::AGENT_STATUS_PENDING;
  162. } else {
  163. $agentStatus = AgentModel::AGENT_STATUS_NORMAL;
  164. }
  165. // 需要提交资料
  166. if ($needAgentApplyForm) {
  167. $agentStatus = AgentModel::AGENT_STATUS_NEEDINFO; // 需要主动提交资料,暂时不加分销商信息
  168. }
  169. }
  170. // 可以直接添加分销商信息
  171. if ($agentStatus === AgentModel::AGENT_STATUS_NORMAL || $agentStatus === AgentModel::AGENT_STATUS_PENDING) {
  172. AgentModel::create([
  173. 'user_id' => $this->user->id,
  174. 'level' => 1, // 默认分销商等级
  175. 'status' => $agentStatus,
  176. 'apply_num' => 1,
  177. 'become_time' => time()
  178. ]);
  179. // 绑定上级推荐人
  180. if ($this->user->parent_user_id === NULL) {
  181. if ($this->bindUserRelation('agent') && $agentStatus !== AgentModel::AGENT_STATUS_NORMAL) {
  182. $this->createAsyncAgentUpgrade($this->user->id); // 防止真正成为分销商时重新触发升级任务造成冗余
  183. }
  184. }
  185. // 绑定为平台直推
  186. if ($this->user->parent_user_id === NULL) {
  187. $this->user->parent_user_id = 0;
  188. $this->user->save();
  189. }
  190. $this->agent = AgentModel::with(['level_info'])->find($this->user->id);
  191. // 添加分销商状态记录
  192. LogModel::add($this->user->id, 'agent', ['type' => 'status', 'value' => $agentStatus]);
  193. // 统计分销层级单链业绩
  194. if ($agentStatus === AgentModel::AGENT_STATUS_NORMAL) {
  195. $this->createAsyncAgentUpgrade($this->user->id);
  196. }
  197. }
  198. return $agentStatus;
  199. }
  200. /**
  201. * 绑定用户关系
  202. *
  203. * @param string $event 事件标识(share=点击分享链接, pay=首次支付, agent=成为子分销商)
  204. * @param int $bindAgentId 可指定需绑定的分销商用户ID 默认从分享记录中去查
  205. */
  206. public function bindUserRelation($event, $bindAgentId = NULL)
  207. {
  208. $bindCheck = false; // 默认不绑定
  209. // 该用户已经有上级
  210. if ($this->user->parent_user_id !== NULL) {
  211. return false;
  212. }
  213. // 不满足绑定下级事件
  214. if ($this->config->getInviteLockEvent() !== $event) {
  215. return false;
  216. }
  217. switch ($this->config->getInviteLockEvent()) {
  218. case 'share':
  219. $bindCheck = true;
  220. break;
  221. case 'pay':
  222. if ($this->user->total_consume > 0) {
  223. $bindCheck = true;
  224. }
  225. break;
  226. case 'agent':
  227. $bindCheck = true;
  228. break;
  229. }
  230. if (!$bindCheck) {
  231. return false;
  232. }
  233. if ($bindAgentId === NULL) {
  234. $bindAgentId = $this->getParentUserId();
  235. }
  236. if (!$bindAgentId) {
  237. return false;
  238. }
  239. $bindAgent = new Agent($bindAgentId);
  240. if (!$bindAgent->isAgentAvaliable()) {
  241. return false;
  242. }
  243. // 允许绑定用户
  244. $this->user->parent_user_id = $bindAgent->user->id;
  245. $this->user->save();
  246. // 添加推广记录
  247. LogModel::add($bindAgent->user->id, 'share', ['user' => $this->user]);
  248. return true;
  249. }
  250. /**
  251. * 创建[分销商升级&统计业绩]异步队列任务
  252. * 为了防止计算量大而引起阻塞,使用异步递归
  253. */
  254. public function createAsyncAgentUpgrade($user_id = 0)
  255. {
  256. if ($user_id === 0) {
  257. $user_id = $this->user->id;
  258. }
  259. \think\Queue::push('\addons\shopro\job\Commission@agentUpgrade', [
  260. 'user_id' => $user_id
  261. ], 'shopro');
  262. }
  263. /**
  264. * 执行用户统计、分销商信息统计、分销商等级升级计划 (递归往上升级)
  265. *
  266. * @param bool $upgrade 执行分销商等级升级
  267. */
  268. public function runAgentUpgradePlan($upgrade = true)
  269. {
  270. if ($this->isAgentAvaliable()) {
  271. // 获取下级直推团队用户信息
  272. $nextUserTeam = $this->getNextUserTeam();
  273. $nextAgentTeam = $this->getNextAgentTeam();
  274. // 一级用户人数
  275. $this->agent->child_user_count_first = count($nextUserTeam);
  276. // 二级用户人数 = 一级分销商的一级用户人数
  277. $this->agent->child_user_count_second = array_sum(array_column($nextAgentTeam, 'child_user_count_first'));
  278. // 三级用户人数 = 一级分销商的二级用户人数
  279. $this->agent->child_user_count_third = array_sum(array_column($nextAgentTeam, 'child_user_count_second'));
  280. // 团队用户人数 = 一级用户人数 + 一级用户的团队用户人数
  281. $this->agent->child_user_count_all = $this->agent->child_user_count_first + array_sum(array_column($nextAgentTeam, 'child_user_count_all'));
  282. // 一级分销商人数
  283. $this->agent->child_agent_count_first = count($nextAgentTeam);
  284. // 二级分销商人数 = 一级分销商的一级分销商人数
  285. $this->agent->child_agent_count_second = array_sum(array_column($nextAgentTeam, 'child_agent_count_first'));
  286. // 三级分销商人数 = 一级分销商的二级分销商人数
  287. $this->agent->child_agent_count_third = array_sum(array_column($nextAgentTeam, 'child_agent_count_second'));
  288. // 团队分销商人数 = 一级分销商人数 + 一级分销商的团队分销商人数
  289. $this->agent->child_agent_count_all = $this->agent->child_agent_count_first + array_sum(array_column($nextAgentTeam, 'child_agent_count_all'));
  290. // 二级分销订单金额 = 一级分销商的一级分销订单金额
  291. $this->agent->child_order_money_second = array_sum(array_column($nextAgentTeam, 'child_order_money_first'));
  292. // 三级分销订单金额 = 一级分销商的二级分销订单金额
  293. $this->agent->child_order_money_third = array_sum(array_column($nextAgentTeam, 'child_order_money_second'));
  294. // 团队分销订单金额 = 一级分销订单金额 + 二级分销订单金额 + 一级所有分销商的团队分销订单总金额
  295. $this->agent->child_order_money_all = $this->agent->child_order_money_first + $this->agent->child_order_money_second + array_sum(array_column($nextAgentTeam, 'child_order_money_all'));
  296. // 二级分销订单数量 = 一级分销商的一级分销订单数量
  297. $this->agent->child_order_count_second = array_sum(array_column($nextAgentTeam, 'child_order_count_first'));
  298. // 三级分销订单数量 = 一级分销商的二级分销订单数量
  299. $this->agent->child_order_count_third = array_sum(array_column($nextAgentTeam, 'child_order_count_second'));
  300. // 团队分销订单数量 = 一级分销订单数量 + 二级分销订单数量 + 一级所有分销商的团队分销订单总数量
  301. $this->agent->child_order_count_all = $this->agent->child_order_count_first + $this->agent->child_order_count_second + array_sum(array_column($nextAgentTeam, 'child_order_count_all'));
  302. // 一级分销商等级统计
  303. $child_agent_level_first = array_count_values(array_column($nextAgentTeam, 'level'));
  304. ksort($child_agent_level_first);
  305. $this->agent->child_agent_level_first = $child_agent_level_first;
  306. // 团队分销商等级统计 = 一级分销商等级 + 一级分销商的团队分销商等级
  307. $child_agent_level_all = $this->childAgentLevelCount(array_column($nextAgentTeam, 'child_agent_level_all'), $this->agent->child_agent_level_first);
  308. ksort($child_agent_level_all);
  309. $this->agent->child_agent_level_all = $child_agent_level_all;
  310. $this->agent->save();
  311. // 分销商自动升级
  312. if (!$this->getAgentUpgradeLock() && $upgrade) {
  313. $canUpgradeLevel = $this->checkAgentUpgradeLevel();
  314. if ($canUpgradeLevel) {
  315. if ($this->config->isUpgradeCheck()) {
  316. $this->agent->level_status = $canUpgradeLevel;
  317. } else {
  318. $this->agent->level = $canUpgradeLevel;
  319. LogModel::add($this->user->id, 'agent', ['type' => 'level', 'level' => LevelModel::find($canUpgradeLevel)]);
  320. }
  321. $this->agent->save();
  322. }
  323. }
  324. \think\Log::info('统计分销商业绩[ID=' . $this->user->id . '&nickname=' . $this->user->nickname . '] ---Ended');
  325. }
  326. $parentUserId = $this->getParentUserId();
  327. if ($parentUserId) {
  328. $this->createAsyncAgentUpgrade($parentUserId);
  329. }
  330. }
  331. /**
  332. * 统计团队分销商等级排布
  333. */
  334. private function childAgentLevelCount($childAgentLevelArray, $childAgentLevel1Array)
  335. {
  336. $childAgentLevelCount = [];
  337. foreach ($childAgentLevelArray as &$agentLevel) {
  338. if (!empty($agentLevel)) {
  339. $agentLevel = json_decode($agentLevel, true);
  340. array_walk($agentLevel, function ($count, $level) use (&$childAgentLevelCount) {
  341. if (isset($childAgentLevelCount[$level])) {
  342. $childAgentLevelCount[$level] += $count;
  343. } else {
  344. $childAgentLevelCount[$level] = $count;
  345. }
  346. });
  347. }
  348. }
  349. array_walk($childAgentLevel1Array, function ($count, $level) use (&$childAgentLevelCount) {
  350. if (isset($childAgentLevelCount[$level])) {
  351. $childAgentLevelCount[$level] += $count;
  352. } else {
  353. $childAgentLevelCount[$level] = $count;
  354. }
  355. });
  356. return $childAgentLevelCount;
  357. }
  358. /**
  359. * 获取下级分销商团队
  360. */
  361. public function getNextAgentTeam()
  362. {
  363. if (!$this->isAgentAvaliable()) {
  364. return [];
  365. }
  366. if (empty($this->nextAgentTeam)) {
  367. $this->nextAgentTeam = AgentModel::hasWhere('user', function ($query) {
  368. return $query->where('parent_user_id', $this->user->id);
  369. })->column('user_id, Agent.level, child_user_count_first, child_user_count_second, child_user_count_third, child_user_count_all, child_agent_count_first, child_agent_count_second, child_agent_count_third, child_agent_count_all, child_order_money_first, child_order_money_second, child_order_money_third, child_order_money_all, child_order_count_first, child_order_count_second, child_order_count_third, child_order_count_all, child_agent_level_first, child_agent_level_all');
  370. }
  371. return $this->nextAgentTeam;
  372. }
  373. /**
  374. * 获取下级直推团队用户
  375. */
  376. public function getNextUserTeam()
  377. {
  378. if (!$this->isAgentAvaliable()) {
  379. return [];
  380. }
  381. if (empty($this->nextUserTeam)) {
  382. $this->nextUserTeam = UserModel::where(['parent_user_id' => $this->user->id, 'status' => 'normal'])->column('id');
  383. }
  384. return $this->nextUserTeam;
  385. }
  386. /**
  387. * 获取可升级的分销商等级
  388. */
  389. private function getNextAgentLevel()
  390. {
  391. $nextAgentLevel = [];
  392. $agentLevel = $this->getAgentLevel();
  393. if ($agentLevel) {
  394. $nextAgentLevel = LevelModel::where('level', '>', $agentLevel)->order('level asc')->select();
  395. }
  396. return $nextAgentLevel;
  397. }
  398. /**
  399. * 比对当前分销商条件是否满足升级规则
  400. */
  401. private function checkAgentUpgradeLevel()
  402. {
  403. $nextAgentLevel = $this->getNextAgentLevel();
  404. if (count($nextAgentLevel)) {
  405. foreach ($nextAgentLevel as $level) {
  406. $checkLevel[$level->level] = $this->isMatchUpgradeLevelRule($level);
  407. // 不允许越级升级
  408. if (!$this->config->isUpgradeJump()) break;
  409. }
  410. $checkLevel = array_reverse($checkLevel, true);
  411. $canUpgradeLevel = array_search(true, $checkLevel);
  412. if ($canUpgradeLevel) {
  413. return $canUpgradeLevel;
  414. }
  415. }
  416. return 0;
  417. }
  418. /**
  419. * 分销商升级规则检查
  420. */
  421. public function isMatchUpgradeLevelRule($level)
  422. {
  423. foreach ($level->upgrade_rules as $name => $value) {
  424. $match[$name] = false;
  425. switch ($name) {
  426. case 'total_consume': // 用户消费金额
  427. $match[$name] = $this->user->$name >= $value;
  428. break;
  429. case 'child_user_count_all': // 团队用户人数
  430. case 'child_user_count_first': // 一级用户人数
  431. case 'child_user_count_second': // 二级用户人数
  432. case 'child_user_count_third': // 三级用户人数
  433. case 'child_order_money_first': // 一级分销订单金额
  434. case 'child_order_money_second': // 二级分销订单金额
  435. case 'child_order_money_third': // 三级分销订单金额
  436. case 'child_order_money_all': // 团队分销订单金额
  437. case 'child_order_count_first': // 一级分销订单数量
  438. case 'child_order_count_second': // 二级分销订单数量
  439. case 'child_order_count_third': // 三级分销订单数量
  440. case 'child_order_count_all': // 团队分销订单数量
  441. case 'child_agent_count_first': // 一级分销商人数
  442. case 'child_agent_count_second': // 二级分销商人数
  443. case 'child_agent_count_third': // 三级分销商人数
  444. case 'child_agent_count_all': // 团队分销商人数
  445. $match[$name] = $this->agent->$name >= $value;
  446. break;
  447. case 'child_agent_level_first': // 一级分销商等级统计
  448. case 'child_agent_level_all': // 团队分销商等级统计
  449. $match[$name] = true;
  450. if (count($value) > 0) {
  451. if (empty($this->agent->$name)) {
  452. $match[$name] = false;
  453. } else {
  454. foreach ($value as $k => $row) {
  455. if (!isset(($this->agent->$name)[$row['level']]) || ($this->agent->$name)[$row['level']] < $row['count']) {
  456. $match[$name] = false;
  457. break;
  458. }
  459. }
  460. }
  461. }
  462. break;
  463. }
  464. // ①满足任意一种条件:只要有一种符合立即返回可以升级状态
  465. if (!$level->upgrade_type && $match[$name]) {
  466. return true;
  467. break;
  468. }
  469. // ②满足所有条件:不满足任意一种条件立即返回不可升级状态
  470. if ($level->upgrade_type && !$match[$name]) {
  471. return false;
  472. break;
  473. }
  474. }
  475. // 循环完所有的 如果是①的情况则代表都不符合条件,如果是②则代表都符合条件 返回对应状态即可
  476. return boolval($level->upgrade_type);
  477. }
  478. }