Agent.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. <?php
  2. namespace addons\shopro\service\commission;
  3. use addons\shopro\service\commission\Config;
  4. use app\admin\model\shopro\user\User as UserModel;
  5. use app\admin\model\shopro\commission\Agent as AgentModel;
  6. use app\admin\model\shopro\commission\Level as LevelModel;
  7. use app\admin\model\shopro\commission\Log as LogModel;
  8. use app\admin\model\shopro\Share as ShareModel;
  9. /**
  10. * 分销商业务
  11. */
  12. class Agent
  13. {
  14. public $user; // 商城用户
  15. public $agent; // 分销商
  16. public $config; // 分销设置
  17. public $parentUserId;
  18. public $nextAgentTeam;
  19. public $nextUserTeam;
  20. /**
  21. * 构造函数
  22. *
  23. * @param mixed $user 用户ID/用户对象
  24. */
  25. public function __construct($user)
  26. {
  27. if (is_numeric($user)) {
  28. $this->user = UserModel::get($user);
  29. } else {
  30. $this->user = UserModel::get($user->id);
  31. }
  32. if (!empty($this->user->id)) {
  33. $this->agent = AgentModel::with(['level_info'])->find($this->user->id);
  34. }
  35. $this->config = new Config();
  36. }
  37. /**
  38. * 获取分销商实时状态
  39. */
  40. public function getAgentStatus($autoCreate = false)
  41. {
  42. if (empty($this->agent)) {
  43. // 自动创建分销商
  44. if ($autoCreate) {
  45. return $this->createNewAgent();
  46. }
  47. return NULL;
  48. }
  49. return $this->agent->status;
  50. }
  51. /**
  52. * 获取分销商可参与状态 正常和冻结都可正常浏览并统计业绩
  53. */
  54. public function isAgentAvaliable()
  55. {
  56. $status = $this->getAgentStatus();
  57. if (in_array($status, [AgentModel::AGENT_STATUS_NORMAL, AgentModel::AGENT_STATUS_FREEZE])) {
  58. return true;
  59. }
  60. return false;
  61. }
  62. /**
  63. * 获取分销商等级
  64. */
  65. public function getAgentLevel()
  66. {
  67. if (empty($this->agent)) {
  68. return 0;
  69. }
  70. if (empty($this->agent->level_info)) {
  71. return 1;
  72. }
  73. return $this->agent->level_info->level;
  74. }
  75. /**
  76. * 分销商升级是否锁定
  77. */
  78. public function getAgentUpgradeLock()
  79. {
  80. if (empty($this->agent)) {
  81. return true;
  82. }
  83. if ($this->agent->upgrade_lock == AgentModel::UPGRADE_LOCK_OPEN) {
  84. return true;
  85. }
  86. return false;
  87. }
  88. /**
  89. * 实时获取上级推荐人
  90. */
  91. public function getParentUserId()
  92. {
  93. if (empty($this->parentUserId)) {
  94. $this->parentUserId = 0;
  95. $parent_user_id = $this->user->parent_user_id;
  96. // 未直接绑定分销商,从分享记录查找最近的分销商
  97. if ($parent_user_id === NULL) {
  98. $shareLog = ShareModel::hasWhere(
  99. 'agent',
  100. function ($query) {
  101. return $query->where('status', 'in', [AgentModel::AGENT_STATUS_NORMAL, AgentModel::AGENT_STATUS_FREEZE]);
  102. }
  103. )->where('Share.user_id', $this->user->id)->order('id desc')->find();
  104. if ($shareLog) {
  105. $parent_user_id = $shareLog['share_id'];
  106. }
  107. }
  108. // 再次检查上级分销商是否可用
  109. if ($parent_user_id > 0) {
  110. $parentUser = UserModel::where('id', $parent_user_id)->find();
  111. $parentAgent = AgentModel::avaliable()->where(['user_id' => $parent_user_id])->find();
  112. if ($parentUser && $parentAgent) {
  113. $this->parentUserId = $parentAgent->user_id;
  114. }
  115. }
  116. }
  117. return $this->parentUserId;
  118. }
  119. /**
  120. * 创建分销商
  121. */
  122. public function createNewAgent($event = '', $applyInfo = [])
  123. {
  124. // 已经是分销商
  125. if (!empty($this->agent)) {
  126. return $this->getAgentStatus();
  127. }
  128. $agentStatus = AgentModel::AGENT_STATUS_NULL;
  129. $condition = $this->config->getBecomeAgentEvent();
  130. $check = false; // 是否满足条件
  131. $needAgentApplyForm = $this->config->isAgentApplyForm();
  132. if ($event !== '' && $condition['type'] !== $event) {
  133. return $agentStatus;
  134. }
  135. switch ($condition['type']) {
  136. case 'apply': // 直接自助申请
  137. $check = true;
  138. $needAgentApplyForm = true;
  139. break;
  140. case 'goods': // 需购买指定产品
  141. $isBuy = \app\admin\model\shopro\order\Order::hasWhere('items', function ($query) use ($condition) {
  142. return $query->where('goods_id', 'in', $condition['value'])->where('refund_status', 0);
  143. })->where('Order.user_id', $this->user->id)->paid()->find();
  144. if ($isBuy) $check = true;
  145. break;
  146. case 'consume': // 消费累计
  147. if ($this->user->total_consume >= $condition['value']) {
  148. $check = true;
  149. }
  150. break;
  151. case 'user': // 新会员注册
  152. $check = true;
  153. $needAgentApplyForm = false;
  154. break;
  155. }
  156. // 可以成为分销商 检查系统设置
  157. if ($check) {
  158. // 需后台审核
  159. if ($this->config->isAgentCheck()) {
  160. $agentStatus = AgentModel::AGENT_STATUS_PENDING;
  161. } else {
  162. $agentStatus = AgentModel::AGENT_STATUS_NORMAL;
  163. }
  164. // 需要提交资料
  165. if ($needAgentApplyForm && empty($applyInfo)) {
  166. $agentStatus = AgentModel::AGENT_STATUS_NEEDINFO; // 需要主动提交资料,暂时不加分销商信息
  167. }
  168. }
  169. // 可以直接添加分销商信息
  170. if ($agentStatus === AgentModel::AGENT_STATUS_NORMAL || $agentStatus === AgentModel::AGENT_STATUS_PENDING) {
  171. AgentModel::create([
  172. 'user_id' => $this->user->id,
  173. 'level' => 1, // 默认分销商等级
  174. 'status' => $agentStatus,
  175. 'apply_info' => $applyInfo,
  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_1 = count($nextUserTeam);
  276. // 二级用户人数 = 一级分销商的一级用户人数
  277. $this->agent->child_user_count_2 = array_sum(array_column($nextAgentTeam, 'child_user_count_1'));
  278. // 团队用户人数 = 一级用户人数 + 一级用户的团队用户人数
  279. $this->agent->child_user_count_all = $this->agent->child_user_count_1 + array_sum(array_column($nextAgentTeam, 'child_user_count_all'));
  280. // 一级分销商人数
  281. $this->agent->child_agent_count_1 = count($nextAgentTeam);
  282. // 二级分销商人数 = 一级分销商的一级分销商人数
  283. $this->agent->child_agent_count_2 = array_sum(array_column($nextAgentTeam, 'child_agent_count_1'));
  284. // 团队分销商人数 = 一级分销商人数 + 一级分销商的团队分销商人数
  285. $this->agent->child_agent_count_all = $this->agent->child_agent_count_1 + array_sum(array_column($nextAgentTeam, 'child_agent_count_all'));
  286. // 二级分销订单金额 = 一级分销商的一级分销订单金额 + 一级分销商的自购订单金额
  287. $this->agent->child_order_money_2 = array_sum(array_column($nextAgentTeam, 'child_order_money_1')) + array_sum(array_column($nextAgentTeam, 'child_order_money_0'));
  288. // 团队分销订单金额 = 自购分销订单金额 + 一级分销订单金额 + 一级所有分销商的团队分销订单总金额
  289. $this->agent->child_order_money_all = $this->agent->child_order_money_0 + $this->agent->child_order_money_1 + array_sum(array_column($nextAgentTeam, 'child_order_money_all'));
  290. // 二级分销订单数量 = 一级分销商的一级分销订单数量 + 一级分销商的自购订单数量
  291. $this->agent->child_order_count_2 = array_sum(array_column($nextAgentTeam, 'child_order_count_1')) + array_sum(array_column($nextAgentTeam, 'child_order_count_0'));
  292. // 团队分销订单数量 = 自购分销订单数量 + 一级分销订单数量 + 一级所有分销商的团队分销订单总数量
  293. $this->agent->child_order_count_all = $this->agent->child_order_count_0 + $this->agent->child_order_count_1 + array_sum(array_column($nextAgentTeam, 'child_order_count_all'));
  294. // 一级分销商等级统计
  295. $child_agent_level_1 = array_count_values(array_column($nextAgentTeam, 'level'));
  296. ksort($child_agent_level_1);
  297. $this->agent->child_agent_level_1 = $child_agent_level_1;
  298. // 团队分销商等级统计 = 一级分销商等级 + 一级分销商的团队分销商等级
  299. $child_agent_level_all = $this->childAgentLevelCount(array_column($nextAgentTeam, 'child_agent_level_all'), $this->agent->child_agent_level_1);
  300. ksort($child_agent_level_all);
  301. $this->agent->child_agent_level_all = $child_agent_level_all;
  302. $this->agent->save();
  303. // 分销商自动升级
  304. if (!$this->getAgentUpgradeLock() && $upgrade) {
  305. $canUpgradeLevel = $this->checkAgentUpgradeLevel();
  306. if ($canUpgradeLevel) {
  307. if ($this->config->isUpgradeCheck()) {
  308. $this->agent->level_status = $canUpgradeLevel;
  309. } else {
  310. $this->agent->level = $canUpgradeLevel;
  311. LogModel::add($this->user->id, 'agent', ['type' => 'level', 'level' => LevelModel::find($canUpgradeLevel)]);
  312. }
  313. $this->agent->save();
  314. }
  315. }
  316. \think\Log::info('统计分销商业绩[ID=' . $this->user->id . '&nickname=' . $this->user->nickname . '] ---Ended');
  317. }
  318. $parentUserId = $this->getParentUserId();
  319. if ($parentUserId) {
  320. $this->createAsyncAgentUpgrade($parentUserId);
  321. }
  322. }
  323. /**
  324. * 统计团队分销商等级排布
  325. */
  326. private function childAgentLevelCount($childAgentLevelArray, $childAgentLevel1Array)
  327. {
  328. $childAgentLevelCount = [];
  329. foreach ($childAgentLevelArray as &$agentLevel) {
  330. if (!empty($agentLevel)) {
  331. $agentLevel = json_decode($agentLevel, true);
  332. array_walk($agentLevel, function ($count, $level) use (&$childAgentLevelCount) {
  333. if (isset($childAgentLevelCount[$level])) {
  334. $childAgentLevelCount[$level] += $count;
  335. } else {
  336. $childAgentLevelCount[$level] = $count;
  337. }
  338. });
  339. }
  340. }
  341. array_walk($childAgentLevel1Array, function ($count, $level) use (&$childAgentLevelCount) {
  342. if (isset($childAgentLevelCount[$level])) {
  343. $childAgentLevelCount[$level] += $count;
  344. } else {
  345. $childAgentLevelCount[$level] = $count;
  346. }
  347. });
  348. return $childAgentLevelCount;
  349. }
  350. /**
  351. * 获取下级分销商团队
  352. */
  353. public function getNextAgentTeam()
  354. {
  355. if (!$this->isAgentAvaliable()) {
  356. return [];
  357. }
  358. if (empty($this->nextAgentTeam)) {
  359. $this->nextAgentTeam = AgentModel::hasWhere('user', function ($query) {
  360. return $query->where('parent_user_id', $this->user->id);
  361. })->column('user_id, Agent.level, child_user_count_1, child_user_count_all,child_agent_count_1, child_agent_count_all, child_order_money_0, child_order_money_1, child_order_money_all, child_order_count_0, child_order_count_1, child_order_count_all, child_agent_level_1, child_agent_level_all');
  362. }
  363. return $this->nextAgentTeam;
  364. }
  365. /**
  366. * 获取下级直推团队用户
  367. */
  368. public function getNextUserTeam()
  369. {
  370. if (!$this->isAgentAvaliable()) {
  371. return [];
  372. }
  373. if (empty($this->nextUserTeam)) {
  374. $this->nextUserTeam = UserModel::where(['parent_user_id' => $this->user->id, 'status' => 'normal'])->column('id');
  375. }
  376. return $this->nextUserTeam;
  377. }
  378. /**
  379. * 获取可升级的分销商等级
  380. */
  381. private function getNextAgentLevel()
  382. {
  383. $nextAgentLevel = [];
  384. $agentLevel = $this->getAgentLevel();
  385. if ($agentLevel) {
  386. $nextAgentLevel = LevelModel::where('level', '>', $agentLevel)->order('level asc')->select();
  387. }
  388. return $nextAgentLevel;
  389. }
  390. /**
  391. * 比对当前分销商条件是否满足升级规则
  392. */
  393. private function checkAgentUpgradeLevel()
  394. {
  395. $nextAgentLevel = $this->getNextAgentLevel();
  396. if (count($nextAgentLevel)) {
  397. foreach ($nextAgentLevel as $level) {
  398. $checkLevel[$level->level] = $this->isMatchUpgradeLevelRule($level);
  399. // 不允许越级升级
  400. if (!$this->config->isUpgradeJump()) break;
  401. }
  402. $checkLevel = array_reverse($checkLevel, true);
  403. $canUpgradeLevel = array_search(true, $checkLevel);
  404. if ($canUpgradeLevel) {
  405. return $canUpgradeLevel;
  406. }
  407. }
  408. return 0;
  409. }
  410. /**
  411. * 分销商升级规则检查
  412. */
  413. public function isMatchUpgradeLevelRule($level)
  414. {
  415. foreach ($level->upgrade_rules as $name => $value) {
  416. $match[$name] = false;
  417. switch ($name) {
  418. case 'total_consume': // 用户消费金额
  419. $match[$name] = $this->user->$name >= $value;
  420. break;
  421. case 'child_user_count_all': // 团队用户人数
  422. case 'child_user_count_1': // 一级用户人数
  423. case 'child_user_count_2': // 二级用户人数
  424. case 'child_order_money_0': // 自购分销订单金额
  425. case 'child_order_money_1': // 一级分销订单金额
  426. case 'child_order_money_2': // 二级分销订单金额
  427. case 'child_order_money_all': // 团队分销订单金额
  428. case 'child_order_count_0': // 自购分销订单数量
  429. case 'child_order_count_1': // 一级分销订单数量
  430. case 'child_order_count_2': // 二级分销订单数量
  431. case 'child_order_count_all': // 团队分销订单数量
  432. case 'child_agent_count_1': // 一级分销商人数
  433. case 'child_agent_count_2': // 二级分销商人数
  434. case 'child_agent_count_all': // 团队分销商人数
  435. $match[$name] = $this->agent->$name >= $value;
  436. break;
  437. case 'child_agent_level_1': // 一级分销商等级统计
  438. case 'child_agent_level_all': // 团队分销商等级统计
  439. $match[$name] = true;
  440. if (count($value) > 0) {
  441. if (empty($this->agent->$name)) {
  442. $match[$name] = false;
  443. } else {
  444. foreach ($value as $k => $row) {
  445. if (!isset(($this->agent->$name)[$row['level']]) || ($this->agent->$name)[$row['level']] < $row['count']) {
  446. $match[$name] = false;
  447. break;
  448. }
  449. }
  450. }
  451. }
  452. break;
  453. }
  454. // ①满足任意一种条件:只要有一种符合立即返回可以升级状态
  455. if (!$level->upgrade_type && $match[$name]) {
  456. return true;
  457. break;
  458. }
  459. // ②满足所有条件:不满足任意一种条件立即返回不可升级状态
  460. if ($level->upgrade_type && !$match[$name]) {
  461. return false;
  462. break;
  463. }
  464. }
  465. // 循环完所有的 如果是①的情况则代表都不符合条件,如果是②则代表都符合条件 返回对应状态即可
  466. return boolval($level->upgrade_type);
  467. }
  468. }