UserTeamStatisticsLogic.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. <?php
  2. namespace common\logic\statistics;
  3. use common\enums\RabbitMqEnum;
  4. use common\enums\RedisKeyEnum;
  5. use common\enums\StatusEnum;
  6. use common\helpers\FormatHelper;
  7. use common\models\statistics\UserStatisticsTotal;
  8. use common\models\statistics\UserTeamStatistics;
  9. use common\models\user\User;
  10. use common\models\user\UserPartitionRelationship;
  11. use common\traits\ErrorTrait;
  12. use Yii;
  13. use yii\db\Exception;
  14. use yii\db\Expression;
  15. /**
  16. * 用户团队数据统计
  17. * @Author lun
  18. * @DateTime 2021-11-16 19:07:32
  19. * @copyright: Copyright (c) 2020 广东七件事集团
  20. */
  21. class UserTeamStatisticsLogic{
  22. use ErrorTrait;
  23. /**
  24. * 订单创建统计
  25. * @Author: lun
  26. * @DateTime: 2021/11/20 0020 15:47
  27. * @Copyright: copyright (c) 2021 广东七件事集团
  28. * @param null $params
  29. */
  30. public function execute($mall_id)
  31. {
  32. // 订单创建统计处理
  33. Yii::$app->db->close();
  34. try {
  35. // 初始化分片参数
  36. $part = 1;
  37. $limit = 1000;
  38. $select = "id,mall_id";
  39. $part_where = [['=','mall_id',$mall_id], ['=','status',StatusEnum::ENABLED]];
  40. $params = ['where' => $part_where, 'select' => $select];
  41. while ($part_data = User::batchGetUsers($params, $part, $limit)){// 查询user表的
  42. $ids = implode(',', array_column($part_data,'id'));
  43. if ($ids) {
  44. $data = ['mall_id' => $mall_id, 'part' => $part, 'ids' => $ids];
  45. // Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, $data, self::class, 'async_handle');
  46. self::async_handle($data);
  47. }
  48. $part ++;
  49. }
  50. return true;
  51. } catch (\Exception $e) {
  52. $exception = FormatHelper::exception($e, "用户团队数据-每日统计");
  53. echo $exception;
  54. Yii::error($exception);
  55. $this->setError($exception);
  56. return false;
  57. }
  58. }
  59. /**
  60. * 异步处理单个分片数据
  61. * @Author: lun
  62. * @DateTime: 2021/12/6 0006 10:25
  63. * @Copyright: copyright (c) 2021 广东七件事集团
  64. * @param null $params
  65. */
  66. public static function async_handle($params = null) {
  67. try {
  68. $t1 = time();
  69. $ids = explode(',', $params['ids']);
  70. $mall_id = $params['mall_id'];
  71. $has_parent_list = User::find()->select('parent_id')->where(['parent_id'=>$ids])->groupBy('parent_id')->asArray()->all();
  72. if (empty($has_parent_list)) return true;
  73. // 用户数据统计查询字段
  74. $statistics_field = "sum(pay_num) pay_num,sum(pay_money) pay_money";
  75. /** 活跃用户:七天内有登录 **/
  76. /** 新增会员:七天内注册的用户 **/
  77. $seven_day = strtotime('-7 days');
  78. /** 流失用户:30天内没登录过 **/
  79. $thirty_day = strtotime('-30 days');
  80. // 条件初始化
  81. $where = ['=', 'status', StatusEnum::ENABLED];
  82. foreach ($has_parent_list as $k => $user){
  83. // 更新sql语句初始化
  84. $insert = "INSERT INTO `qimall_user_team_statistics` (`mall_id`, `user_id`, `active_user_team_total`, `loss_user_team_total`, `user_team_order_total`, `user_team_order_price_total`,
  85. `active_user_direct_total`, `loss_user_direct_total`, `new_user_direct_total`, `user_direct_order_total`, `user_direct_order_price_total`,
  86. `active_user_indirect_total`, `loss_user_indirect_total`, `user_indirect_order_total`, `user_indirect_order_price_total`, `created_at`, `updated_at`) VALUES (";
  87. $update = "UPDATE `qimall_user_team_statistics` SET ";
  88. $is_insert = $is_update = false;
  89. $user_id = $user['parent_id'];
  90. // 参数初始化
  91. $data = [
  92. 'mall_id' => $mall_id,
  93. 'user_id' => $user_id,
  94. 'active_user_team_total' => 0,
  95. 'loss_user_team_total' => 0,
  96. 'user_team_order_total' => 0,
  97. 'user_team_order_price_total' => 0,
  98. 'active_user_direct_total' => 0,
  99. 'loss_user_direct_total' => 0,
  100. 'new_user_direct_total' => 0,
  101. 'user_direct_order_total' => 0,
  102. 'user_direct_order_price_total' => 0,
  103. 'active_user_indirect_total' => 0,
  104. 'loss_user_indirect_total' => 0,
  105. 'user_indirect_order_total' => 0,
  106. 'user_indirect_order_price_total' => 0,
  107. ];
  108. // 分片获取用户团队数据
  109. $team_offset = 1;$team_limit = 1000;
  110. list($tree, $self_deep, $all_tree, $all_tree_arr) = UserPartitionRelationship::getTree($user_id);// 获取本人当前tree数据
  111. $team_params = [];
  112. while ($team_data = UserPartitionRelationship::batchGetTeamData($team_offset, $team_limit, $tree, $team_params)){// 查询关系链表
  113. $team_uids = $direct_uids = $indirect_uids = [];
  114. foreach ($team_data as $item) {
  115. // 获取该用户的所有下级
  116. $team_uids[] = $item['user_id'];// 团队用户
  117. if ($self_deep + 1 == $item['deep']) $direct_uids[] = $item['user_id'];// 直推用户
  118. if ($self_deep + 2 == $item['deep']) $indirect_uids[] = $item['user_id'];// 间推用户
  119. }
  120. // $last_primary_id = $item['id'];
  121. // $team_params['where'][] = ['>','id',$last_primary_id];
  122. $team_offset++;
  123. /** 团队用户数据查询 - 开始 **/
  124. // sqi = `user_team_total`, `active_user_team_total`, `loss_user_team_total`, `user_team_order_total`, `user_team_order_price_total`,
  125. if (!empty($team_uids)) {
  126. // 团队活跃会员总数
  127. $data['active_user_team_total'] += User::find()->select("id")->andWhere($where)->andWhere(['in','id',$team_uids])->andWhere(['>=', 'last_login_at', $seven_day])->count();
  128. // 团队流失用户
  129. $data['loss_user_team_total'] += User::find()->select("id")->andWhere($where)->andWhere(['in','id',$team_uids])->andWhere(['<=', 'last_login_at', $thirty_day])->count();
  130. // 团队会员订单统计
  131. $team_order = UserStatisticsTotal::find()->select($statistics_field)->andWhere($where)->andWhere(['=','mall_id',$mall_id])->andWhere(['in','user_id',$team_uids])->asArray()->one();
  132. $data['user_team_order_total'] += $team_order['pay_num'] ?? 0;// 团队会员订单总数
  133. $data['user_team_order_price_total'] += $team_order['pay_money'] ?? 0;// 团队会员订单总额
  134. }
  135. /** 团队用户数据查询 - 结束 **/
  136. /** 直推用户数据查询 - 开始 **/
  137. // sqi = `user_direct_total`, `active_user_direct_total`, `loss_user_direct_total`, `new_user_direct_total`, `user_direct_order_total`, `user_direct_order_price_total`,
  138. if (!empty($direct_uids)) {
  139. // 直推活跃会员总数
  140. $data['active_user_direct_total'] += User::find()->select("id")->andWhere($where)->andWhere(['in','id',$direct_uids])->andWhere(['>=', 'last_login_at', $seven_day])->count();
  141. // 直推流失用户总数
  142. $data['loss_user_direct_total'] += User::find()->select("id")->andWhere($where)->andWhere(['in','id',$direct_uids])->andWhere(['<=', 'last_login_at', $thirty_day])->count();
  143. // 直推近七天新增用户 - 仅直推才有
  144. $data['new_user_direct_total'] += User::find()->select("id")->andWhere($where)->andWhere(['in','id',$direct_uids])->andWhere(['>=', 'created_at', $seven_day])->count();
  145. // 直推会员订单统计
  146. $direct_order = UserStatisticsTotal::find()->select($statistics_field)->andWhere(['=','mall_id',$mall_id])->andWhere(['in','user_id',$direct_uids])->andWhere($where)->asArray()->one();
  147. $data['user_direct_order_total'] += $direct_order['pay_num'] ?? 0;// 直推会员订单总数
  148. $data['user_direct_order_price_total'] += $direct_order['pay_money'] ?? 0;// 直推会员订单总额
  149. }
  150. /** 直推用户数据查询 - 结束 **/
  151. /** 间推用户数据查询 - 开始 **/
  152. // sqi = `active_user_indirect_total`, `loss_user_indirect_total`, `user_indirect_order_total`, `user_indirect_order_price_total`,
  153. if (!empty($indirect_uids)) {
  154. // 间推活跃会员总数
  155. $data['active_user_indirect_total'] += User::find()->select("id")->andWhere($where)->andWhere(['in','id',$indirect_uids])->andWhere(['>=', 'last_login_at', $seven_day])->count();
  156. // 间推流失会员总数
  157. $data['loss_user_indirect_total'] += User::find()->select("id")->andWhere($where)->andWhere(['in','id',$indirect_uids])->andWhere(['<=', 'last_login_at', $thirty_day])->count();
  158. // 间推会员订单统计
  159. $indirect_order = UserStatisticsTotal::find()->select($statistics_field)->andWhere(['=','mall_id',$mall_id])->andWhere(['in','user_id',$indirect_uids])->andWhere($where)->asArray()->one();
  160. $data['user_indirect_order_total'] += $indirect_order['pay_num'] ?? 0;// 间推会员订单总数
  161. $data['user_indirect_order_price_total'] += $indirect_order['pay_money'] ?? 0;// 间推会员订单总额
  162. }
  163. /** 间推用户数据查询 - 结束 **/
  164. }
  165. // 判断是否已有用户数据
  166. $model = UserTeamStatistics::findOne(['mall_id' => $mall_id, 'user_id' => $user_id, 'status' => StatusEnum::ENABLED]);
  167. foreach ($data as $key => $val) {
  168. if (empty($model)) {
  169. $insert.= $val . ",";
  170. $is_insert = true;
  171. } else {
  172. if ($key == 'mall_id' || $key == 'user_id') continue;
  173. $update.= $key . "=" . $val . ",";
  174. $is_update = true;
  175. }
  176. }
  177. $insert = rtrim($insert, ',') . ")";
  178. $is_update && $update = rtrim($update, ',') . " where id=" . $model->id;
  179. // 批量执行sql语句
  180. $connection = Yii::$app->db;
  181. $trans = $connection->beginTransaction();
  182. try {
  183. $is_insert && $connection->createCommand($insert)->execute();
  184. $is_update && $connection->createCommand($update)->execute();
  185. $trans->commit();
  186. } catch(\Exception $e){// 如果有一条查询失败,则会抛出异常
  187. self::setStaticError($e->getMessage());
  188. $trans->rollBack();
  189. }
  190. }
  191. $t2 = time();
  192. echo "part={$params['part']},时间:" .($t2-$t1). ",执行成功".PHP_EOL;
  193. } catch (\Exception $e) {
  194. $exception = FormatHelper::exception($e, "part={$params['part']},mall={$params['mall_id']},用户团队数据-每日统计");
  195. echo $exception;
  196. Yii::error($exception);
  197. }
  198. }
  199. /**
  200. * 用户上级订单统计维护
  201. * $params = ['user_id' => 12219, 'pay_money' => 100.25];
  202. * @Author: lun
  203. * @DateTime: 2021/12/10 0010 16:56
  204. * @Copyright: copyright (c) 2021 广东七件事集团
  205. * @param null $params
  206. * @return bool
  207. */
  208. public static function incBatchParentDataByUid($params = null)
  209. {
  210. $trans = Yii::$app->db->beginTransaction();
  211. try {
  212. if (empty($params))throw new Exception('传参有误');
  213. $user_id = $params['user_id'];$time = time();
  214. $pay_money = $params['pay_money'];
  215. $original_order_money = $params['original_order_money'];
  216. // 查询此用户是否有上级
  217. $parent_list = UserPartitionRelationship::getParentIdArr($user_id);// 获取本人当前tree数据
  218. if (empty($parent_list)) {
  219. $trans->commit();
  220. return true;
  221. }
  222. $mall_id = User::find()->select('mall_id')->where(['id'=>$user_id])->scalar();
  223. $parent_list = array_reverse($parent_list);// 数组倒叙
  224. $field = "id,mall_id,user_id,user_team_order_total,user_direct_order_total,user_indirect_order_total,user_team_order_price_total,user_direct_order_price_total,user_indirect_order_price_total," .
  225. "user_direct_pay_selling_price, user_indirect_pay_selling_price, team_pay_selling_price";
  226. foreach ($parent_list as $key => $pid) {
  227. $model = UserTeamStatistics::find()->select($field)->where(['user_id' => $pid, 'status' => StatusEnum::ENABLED])->one();
  228. $emptyModel = empty($model);
  229. if ($emptyModel) {
  230. $model = new UserTeamStatistics();
  231. $model->mall_id = $mall_id;
  232. $model->user_id = $pid;
  233. $model->loadDefaultValues();
  234. } else {
  235. if ($key > 1) $inc_uid[] = $pid;
  236. }
  237. // 新增用户或直推间推上级
  238. if ($emptyModel || in_array($key,[0,1])) {
  239. // 上级团队
  240. $model->user_team_order_total = $model->user_team_order_total + 1;
  241. $model->user_team_order_price_total = $model->user_team_order_price_total + $pay_money;
  242. $model->team_pay_selling_price = $model->team_pay_selling_price + $original_order_money;
  243. if ($key == 0) {// 直推上级
  244. $model->user_direct_order_total = $model->user_direct_order_total + 1;
  245. $model->user_direct_order_price_total = $model->user_direct_order_price_total + $pay_money;
  246. $model->user_direct_pay_selling_price = $model->user_direct_pay_selling_price + $original_order_money;
  247. } elseif ($key == 1) {// 间推上级
  248. $model->user_indirect_order_total = $model->user_indirect_order_total + 1;
  249. $model->user_indirect_order_price_total = $model->user_indirect_order_price_total + $pay_money;
  250. $model->user_indirect_pay_selling_price = $model->user_indirect_pay_selling_price + $original_order_money;
  251. }
  252. if (!$model->save()) throw new Exception($model->getErrorMessage());
  253. }
  254. }
  255. // 批量修改数据 - 除开直推间推的所有需要修改的上级用户
  256. if (!empty($inc_uid)) {
  257. $res = UserTeamStatistics::updateAll(
  258. [
  259. 'user_team_order_total' => new Expression('user_team_order_total+1'),
  260. 'user_team_order_price_total' => new Expression('user_team_order_price_total+' . $pay_money),
  261. 'team_pay_selling_price' => new Expression('team_pay_selling_price+' . $original_order_money),
  262. 'updated_at' => $time
  263. ],
  264. ['user_id' => $inc_uid]
  265. );
  266. if ($res == false) throw new Exception(UserTeamStatistics::getStaticError());
  267. }
  268. $trans->commit();
  269. return true;
  270. } catch (\Exception $e) {
  271. $trans->rollBack();
  272. $exception = FormatHelper::exception($e, "用户上级订单数据维护");
  273. echo $exception;
  274. return false;
  275. }
  276. }
  277. /**
  278. * 用户上级订单统计维护
  279. * $params = ['user_id' => 12219, 'pay_money' => 100.25];
  280. * @Author: lun
  281. * @DateTime: 2021/12/10 0010 16:56
  282. * @Copyright: copyright (c) 2021 广东七件事集团
  283. * @param null $params
  284. * @return bool
  285. */
  286. public static function decBatchParentDataByUid($params = null)
  287. {
  288. $trans = Yii::$app->db->beginTransaction();
  289. try {
  290. if (empty($params) || empty($params['dec_update_field']))throw new Exception('传参有误');
  291. $user_id = $params['user_id'];
  292. $time = time();
  293. // 查询此用户是否有上级
  294. $parent_list = UserPartitionRelationship::getParentIdArr($user_id);// 获取本人当前tree数据
  295. if (empty($parent_list)) {
  296. $trans->commit();
  297. return true;
  298. }
  299. // 批量修改数据
  300. if (!empty($parent_list)) {
  301. $updateData = [];
  302. foreach ($params['dec_update_field'] as $key => $val) {
  303. $updateData[$key] = new Expression($key . '-' . $val);
  304. }
  305. if ($updateData) {
  306. $updateData['updated_at'] = $time;
  307. $res = UserTeamStatistics::updateAll($updateData, ['user_id' => $parent_list]);
  308. if ($res == false) throw new Exception(UserTeamStatistics::getStaticError());
  309. }
  310. }
  311. $trans->commit();
  312. return true;
  313. } catch (\Exception $e) {
  314. $trans->rollBack();
  315. $exception = FormatHelper::exception($e, "用户上级订单数据维护:decBatchParentDataByUid");
  316. echo $exception;
  317. return false;
  318. }
  319. }
  320. /**
  321. * @notes:完成订单增加上级业绩处理
  322. * @param $params
  323. * @return bool
  324. * @author: lun
  325. * @Time: 2022/11/7 17:17
  326. */
  327. public static function incBatchParentFinishOrderByUid($params = null)
  328. {
  329. $trans = Yii::$app->db->beginTransaction();
  330. try {
  331. if (empty($params))throw new Exception('传参有误');
  332. $user_id = $params['user_id'];$time = time();
  333. $pay_money = $params['pay_money'];
  334. $selling_price = $params['selling_price'];
  335. // 查询此用户是否有上级
  336. $parent_list = UserPartitionRelationship::getParentIdArr($user_id);// 获取本人当前tree数据
  337. if (empty($parent_list)) {
  338. $trans->commit();
  339. return true;
  340. }
  341. $parent_list = array_reverse($parent_list);// 数组倒叙
  342. /* 新增直推和间推的已完成的售价总额 */
  343. // 直推
  344. if (isset($parent_list[0])) {
  345. UserTeamStatistics::updateAllCounters(['user_direct_finish_selling_price' => $selling_price], ['user_id' => $parent_list[0]]);
  346. }
  347. // 间推
  348. if (isset($parent_list[1])) {
  349. UserTeamStatistics::updateAllCounters(['user_indirect_finish_selling_price' => $selling_price], ['user_id' => $parent_list[1]]);
  350. }
  351. // 批量修改数据 - 除开直推间推的所有需要修改的上级用户
  352. if (!empty($parent_list)) {
  353. $res = UserTeamStatistics::updateAll(
  354. [
  355. 'team_finish_order_total' => new Expression('team_finish_order_total+1'),
  356. 'team_finish_order_price_total' => new Expression('team_finish_order_price_total+' . $pay_money),
  357. 'team_finish_selling_price' => new Expression('team_finish_selling_price+' . $selling_price),
  358. 'updated_at' => $time
  359. ],
  360. ['user_id' => $parent_list]
  361. );
  362. if ($res == false) throw new Exception(UserTeamStatistics::getStaticError());
  363. }
  364. $trans->commit();
  365. return true;
  366. } catch (\Exception $e) {
  367. $trans->rollBack();
  368. $exception = FormatHelper::exception($e, "用户上级订单数据维护");
  369. echo $exception;
  370. return false;
  371. }
  372. }
  373. /**
  374. * @notes:老用户数据数据完成团队数据修改
  375. * @param $params
  376. * @return bool
  377. * @author: lun
  378. * @Time: 2022/11/8 13:45
  379. */
  380. public static function countOldUserTeamAchievement($params)
  381. {
  382. $mall_id = $params['mall_id'];
  383. $redis = Yii::$app->redis;
  384. $cacheKey = RedisKeyEnum::suffix(UserTeamStatistics::COUNT_FINISH_ORDER_KEY, 'mall_id_' . $mall_id);
  385. try {
  386. // 防止多次执行
  387. $checkCount = $redis->get($cacheKey);
  388. if ($checkCount && $checkCount == 1) return true;
  389. $redis->set($cacheKey, 1);
  390. $time = time();
  391. $part = 1;
  392. $limit = 1000;
  393. while ($list = UserTeamStatistics::find()->where(['mall_id' => $mall_id, 'status' => StatusEnum::ENABLED])->select('user_id')->orderBy('id asc')
  394. ->offset(($part - 1) * $limit)->limit($limit)->all()) {
  395. foreach ($list as $item) {
  396. // 获取当前用户的下级团队
  397. $team_offset = 1;$team_limit = 1000;
  398. $team_finish_order_total = 0;// 团队完成订单数量
  399. $team_finish_order_price_total = 0;// 团队完成订单总额
  400. list($tree, $self_deep, $all_tree, $all_tree_arr) = UserPartitionRelationship::getTree($item['user_id']);// 获取本人当前tree数据
  401. while ($team_data = UserPartitionRelationship::batchGetTeamData($team_offset, $team_limit, $tree)) {// 查询关系链表
  402. $team_uids = array_column($team_data, 'user_id');
  403. $finish_order_num = UserStatisticsTotal::find()->where(['user_id' => $team_uids, 'status' => StatusEnum::ENABLED])->sum('finish_order_num') ?? 0;
  404. $finish_order_money = UserStatisticsTotal::find()->where(['user_id' => $team_uids, 'status' => StatusEnum::ENABLED])->sum('finish_order_money') ?? 0;
  405. $team_finish_order_total += $finish_order_num;
  406. $team_finish_order_price_total += $finish_order_money;
  407. $team_offset++;
  408. }
  409. if (empty($team_finish_order_total)) continue;
  410. UserTeamStatistics::updateAll([
  411. 'team_finish_order_total' => $team_finish_order_total,
  412. 'team_finish_order_price_total' => $team_finish_order_price_total,
  413. 'updated_at' => $time,
  414. ],['user_id' => $item['user_id'], 'status' => StatusEnum::ENABLED]);
  415. }
  416. $part ++;
  417. }
  418. echo '执行成功';
  419. return true;
  420. } catch (\Exception $e) {
  421. $redis->set($cacheKey, 0);// 失败解锁
  422. self::setStaticError($e->getMessage());
  423. echo '执行失败:'.$e->getMessage();
  424. return false;
  425. }
  426. }
  427. }