UserPartitionRelationship.php 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. <?php
  2. namespace common\models\user;
  3. use addons\Agent\common\models\Agent;
  4. use common\enums\AddonsEnum;
  5. use common\enums\MemberLabelEnum;
  6. use common\enums\OrderStatusEnum;
  7. use common\enums\RabbitMqEnum;
  8. use common\enums\RedisKeyEnum;
  9. use common\enums\StatusEnum;
  10. use common\events\user\UserChangeRelationshipEvent;
  11. use common\helpers\Base64Helper;
  12. use common\helpers\DateHelper;
  13. use common\helpers\LockHelper;
  14. use common\logic\memberLabel\MemberLabelLogic;
  15. use common\models\base\BaseActiveRecord;
  16. use common\models\common\MallSetting;
  17. use common\models\order\Order;
  18. use common\models\statistics\UserStatisticsTotal;
  19. use PHPUnit\Util\Exception;
  20. use Yii;
  21. use common\helpers\ArrayHelper;
  22. use function Symfony\Component\String\s;
  23. /**
  24. * This is the model class for table "qimall_user_partition_relationship".
  25. *
  26. *
  27. * @property int $id
  28. * @property int $part 分片,片数
  29. * @property int $user_id 用户ID
  30. * @property string $tree 推荐关系树,【注意字符集!!!】
  31. * @property int $deep 层级
  32. * @property int $created_at
  33. * @property int $updated_at
  34. */
  35. class UserPartitionRelationship extends BaseActiveRecord
  36. {
  37. /**
  38. * {@inheritdoc}
  39. */
  40. public static function tableName()
  41. {
  42. return '{{%user_partition_relationship}}';
  43. }
  44. /**
  45. * {@inheritdoc}
  46. */
  47. public function rules()
  48. {
  49. return [
  50. [['part', 'user_id', 'deep', 'created_at', 'updated_at'], 'integer'],
  51. [['tree'], 'string', 'max' => 1000],
  52. ];
  53. }
  54. /**
  55. * {@inheritdoc}
  56. */
  57. public function attributeLabels()
  58. {
  59. return [
  60. 'id' => 'ID',
  61. 'part' => '分片,片数',
  62. 'user_id' => '用户ID',
  63. 'tree' => '推荐关系树,【注意字符集!!!】',
  64. 'deep' => '层级',
  65. 'created_at' => 'Created At',
  66. 'updated_at' => 'Updated At',
  67. ];
  68. }
  69. /**
  70. * @desc:保存数据
  71. * @Author: hua
  72. * @Date: 2021/11/29
  73. * @Time: 17:54
  74. * @Copyright: copyright (c) 2021 广东七件事集团
  75. * @param array $params
  76. * @return UserPartitionRelationship|false|null
  77. */
  78. public static function setData(array $params)
  79. {
  80. try {
  81. if (empty($params['user_id'])) throw new Exception('user_id不能为空');
  82. $model = self::findOne(['user_id' => $params['user_id']]);
  83. if (empty($model)) {
  84. $model = new self();
  85. $model->loadDefaultValues();
  86. }
  87. if (empty($params['parent_id'])) {
  88. $params['tree'] = Base64Helper::dec2Base64($params['user_id']);
  89. $params['deep'] = 0;
  90. $params['part'] = 1;
  91. if (!$model->load($params, '') || !$model->save()) throw new Exception($model->getErrorMessage());
  92. } else {
  93. self::bindParent($params['user_id'],$params['parent_id']);
  94. }
  95. return true;
  96. } catch (Exception $e) {
  97. self::$static_error = $e->getMessage();
  98. return false;
  99. }
  100. }
  101. public static function bindParent($user_id,$parent_id){
  102. Yii::$app->custom->logs("关系链绑定日志:". $user_id, $parent_id);
  103. //绑定上级,上级关系链,正在改动,阻塞等待锁释放
  104. $parent_tree_model = self::find()->where(['user_id' => $parent_id])->orderBy('part asc')->one();
  105. $tree_id_arr = explode(',', $parent_tree_model['tree']);
  106. $top_id = array_shift($tree_id_arr);
  107. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_USER_RELATIONSHIP , $top_id);
  108. $res = LockHelper::readLock($lock_key);//锁存在,阻塞
  109. if (!$res) throw new Exception('当前访问人数过多');
  110. $parent_list = self::find()->where(['user_id' => $parent_id])->orderBy('part desc')->all();
  111. $insert = [];
  112. $time = time();
  113. foreach ($parent_list as $k => $value) {
  114. if ($k == 0) {
  115. $code = Base64Helper::dec2Base64($user_id);
  116. $tree = explode(',', $value['tree']);
  117. if (count($tree) >= 150) {
  118. //超过tree的最大存储长度,新增加一条
  119. $insert[] = self::packData($code, $value['part'] + 1, $user_id, $value['deep'], $time);
  120. $insert[] = self::packData($value['tree'], $value['part'], $user_id, $value['deep'], $time);
  121. } else {
  122. $insert[] = self::packData($value['tree'] . ',' . $code, $value['part'], $user_id, $value['deep'], $time);
  123. }
  124. } else {
  125. $insert[] = self::packData($value['tree'], $value['part'], $user_id, $value['deep'], $time);
  126. }
  127. }
  128. UserPartitionRelationship::deleteAll(['user_id' => $user_id]);
  129. $field = ['tree', 'part', 'user_id', 'deep', 'created_at', 'updated_at'];
  130. if ($insert) {
  131. $insert = array_reverse($insert);
  132. $res = Yii::$app->db->createCommand()->batchInsert(self::tableName(), $field, $insert)->execute();
  133. if ($res == false) throw new Exception('保存关系链失败');
  134. Yii::$app->custom->logs("关系链绑定成功:". $user_id, $parent_id);
  135. }
  136. }
  137. protected static function packData($tree, $part, $user_id, $deep, $time)
  138. {
  139. return [$tree, $part, $user_id, $deep + 1, $time, $time];
  140. }
  141. /**
  142. *
  143. * @param array $where
  144. * @param string $select
  145. * @param array $with
  146. * @param int $is_array
  147. * @return array|User[]|\yii\db\ActiveRecord[]
  148. */
  149. public static function getUserRelationList(array $where, $select = '*', array $with = [], int $is_array = 0)
  150. {
  151. $query = self::find();
  152. self::paramPack(['where' => $where, 'with' => $with], $query);
  153. return $query->select($select)->asArray($is_array)->all();
  154. }
  155. /**
  156. * 获取自身和上级关系链
  157. * @param $user_id
  158. * @return array
  159. */
  160. public static function getTree($user_id)
  161. {
  162. $user_relation_list = self::lists(['where' => [['user_id' => $user_id]], 'order' => 'part asc']);
  163. $parent_id_tree = '';//最新一个分片的tree
  164. $all_parent_id_tree = '';//所有上级tree
  165. $all_parent_id_tree_arr = [];//所有上级id
  166. $deep = 0;
  167. foreach ($user_relation_list as $key => $value) {
  168. $all_parent_id_tree .= $value['tree'] . ',';
  169. $all_parent_id_tree_arr = array_merge($all_parent_id_tree_arr, explode(',', $value['tree']));
  170. if (!isset($user_relation_list[$key + 1])) {
  171. $parent_id_tree = $value['tree'];
  172. }
  173. $deep = $value['deep'];
  174. }
  175. $all_parent_id_tree = trim($all_parent_id_tree, ',');
  176. return [$parent_id_tree, $deep, $all_parent_id_tree, $all_parent_id_tree_arr];
  177. }
  178. /**
  179. * 查询下级数组
  180. * @param $user_id
  181. * @param int $deep
  182. * @return array
  183. */
  184. public static function getChildIdArr($user_id, $deep = 0, $start_time = 0, $end_time = 0)
  185. {
  186. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  187. $where[] = ['<>', 'user_id', $user_id];
  188. $where[] = ['like', 'tree', "{$tree},%", false];
  189. if (!empty($deep)) $where[] = ['deep' => $deep + $parent_deep];
  190. if (!empty($start_time)) {
  191. $where[] = ['>=', 'updated_at', $start_time];
  192. }
  193. if (!empty($end_time)) {
  194. $where[] = ['<=', 'updated_at', $end_time];
  195. }
  196. $list = self::getUserRelationList($where);
  197. return array_column($list, 'user_id');
  198. }
  199. /**
  200. * 查询下级数组
  201. * @param $user_id
  202. * @param int $deep
  203. * @return array
  204. */
  205. public static function getChildIdArrV2($user_id, $deep = 0)
  206. {
  207. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  208. $where[] = ['<>', 'user_id', $user_id];
  209. $where[] = ['like', 'tree', "{$tree},%", false];
  210. if (!empty($deep)) $where[] = ['deep' => $deep + $parent_deep];
  211. $list = self::getUserRelationList($where, 'user_id', [], 1);
  212. return array_column($list, 'user_id');
  213. }
  214. /**
  215. * 查询下级数组(包含自己)
  216. * @param $user_id
  217. * @param int $deep
  218. * @return array
  219. */
  220. public static function getChildList($user_id, $deep = 0)
  221. {
  222. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  223. $where[] = ['like', 'tree', "{$tree}%", false];
  224. if (!empty($deep)) $where[] = ['deep' => $deep + $parent_deep];
  225. return self::getUserRelationList($where, 'tree, user_id', [], 1);
  226. }
  227. public static function getChildIdArrByChunk($user_id, $offset = 0, $limit=1000)
  228. {
  229. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  230. $where[] = ['<>', 'user_id', $user_id];
  231. $where[] = ['like', 'tree', "{$tree},%", false];
  232. $query = self::find();
  233. self::paramPack(['where' => $where], $query);
  234. $list = $query->orderBy('id asc')->offset($offset)->limit($limit)->all();
  235. return array_column($list, 'user_id');
  236. }
  237. /**
  238. * 查询下级数组
  239. * @param $user_id
  240. * @param int $deep
  241. * @return array
  242. */
  243. public static function getChildIdArrOrderBy($user_id, $deep = 0)
  244. {
  245. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  246. $where[] = ['<>', 'user_id', $user_id];
  247. $where[] = ['like', 'tree', "{$tree},%", false];
  248. if (!empty($deep)) $where[] = ['deep' => $deep + $parent_deep];
  249. $query = self::find();
  250. self::paramPack(['where' => $where, 'with' => [],'order' => 'updated_at asc'], $query);
  251. $list = $query->select('*')->asArray(0)->all();
  252. return array_column($list, 'user_id');
  253. }
  254. /**
  255. * @desc:查询下级数组
  256. * @Author: hua
  257. * @Date: 2021/11/30
  258. * @Time: 15:10
  259. * @Copyright: copyright (c) 2021 广东七件事集团
  260. * @param $user_id
  261. * @param int $deep
  262. * @return bool|int|string|null
  263. */
  264. public static function getChildCounts($user_id, $deep = 0)
  265. {
  266. $model = self::find()->where(['user_id' => $user_id])->orderBy('part desc')->asArray()->one();
  267. $tree = $model['tree'];
  268. $parent_deep = $model['deep'];
  269. $query = self::find()->where(['<>', 'user_id', $user_id])->andWhere(['like', 'tree', "{$tree}%", false]);
  270. if (!empty($deep)) $query->andWhere(['deep' => $deep + $parent_deep]);
  271. return $query->count();
  272. }
  273. /**
  274. * 查找上级数组
  275. * @param $user_id
  276. * @return array|false|string[]
  277. */
  278. public static function getParentIdArr($user_id)
  279. {
  280. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  281. array_pop($all_tree_arr);
  282. $arr = [];
  283. foreach ($all_tree_arr as $code) {
  284. $arr[] = Base64Helper::Base642dec($code);
  285. }
  286. return $arr;
  287. }
  288. /**
  289. * 查询多个会员id直推上级ID数组
  290. * @param $user_id
  291. * @return array|false|string[]
  292. */
  293. public static function getDirectIdArr($user_id)
  294. {
  295. $list = User::lists(['where' => [['id' => $user_id]], 'id,parent_id']);
  296. return array_column($list, 'parent_id', 'id');
  297. }
  298. public static function getParentIds($user_id,$deep=0)
  299. {
  300. $user = User::getOne([['id' => $user_id]], true);
  301. switch ($deep){
  302. case 1:
  303. return $user['parent_id'];
  304. case 2:
  305. return [$user['parent_id'], $user['second_parent_id']];
  306. default:
  307. return [$user['parent_id'], $user['second_parent_id'], $user['third_parent_id']];
  308. }
  309. }
  310. //查询上级昵称数组
  311. public static function getParentsNickname($parent_id_arr)
  312. {
  313. $parent_list = User::lists(['where' => [['id' => $parent_id_arr]]]);
  314. return array_column($parent_list, 'nickname', 'id');
  315. }
  316. /**
  317. * @desc:绑定关系链,首次下单,首次付款
  318. * @Author: hua
  319. * @Date: 2021/11/12
  320. * @Time: 11:33
  321. * @Copyright: copyright (c) 2021 广东七件事集团
  322. * @param $user_id
  323. * @param $mall_id
  324. * @param $data
  325. */
  326. public static function bindRelationship($user_id, $mall_id)
  327. {
  328. $relation_setting = \Yii::$app->services->setting->mall->get($mall_id, 'user_relation_setting.relation_setting');
  329. $data = [];
  330. if (!empty($relation_setting)) $data = json_decode($relation_setting, true);
  331. $where = ['user_id' => $user_id, 'mall_id' => $mall_id,'store_id'=>0,'mch_id'=>0];
  332. $result = false;
  333. if (!empty($data['status'])&&!empty($data['become_child_way'])) {
  334. $where['pay_status'] = 1;
  335. if ($data['become_child_way'] == 2) $where['order_status'] = OrderStatusEnum::ACCOMPLISH;//首次完成订单
  336. $counts = Order::find()->where($where)->count();
  337. if ($counts == 1&&(in_array($data['become_child_way'],[2,3]))) {
  338. $user = User::findOne(['id' => $user_id, 'mall_id' => $mall_id]);
  339. if (!empty($user->temp_parent_id) && empty($user->parent_id) && $user->temp_parent_id != $user_id) {
  340. $params['mall_id'] = $mall_id;
  341. $params['user_id'] = $user_id;
  342. $params['parent_id'] = $user->temp_parent_id;
  343. $res = self::setData($params);
  344. if ($res == false) throw new Exception(self::getStaticError());
  345. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  346. array_pop($all_tree_arr);//自身
  347. array_pop($all_tree_arr);//直推上级
  348. $second_parent_id = array_pop($all_tree_arr);//间推上级
  349. $third_parent_id = array_pop($all_tree_arr);//
  350. $user->parent_id = $params['parent_id'];
  351. if($second_parent_id){
  352. $user->second_parent_id = Base64Helper::Base642dec($second_parent_id);
  353. }
  354. if($third_parent_id){
  355. $user->third_parent_id = Base64Helper::Base642dec($third_parent_id);
  356. }
  357. $res = $user->save();
  358. if ($res == false) throw new Exception($user->getErrorMessage());
  359. $result = true;
  360. }
  361. }
  362. }
  363. return $result;
  364. }
  365. /**
  366. * 我的客户
  367. * @param $params
  368. * @return mixed
  369. */
  370. public static function myCustomer($params, $config)
  371. {
  372. $children_id_arr = self::getChildIdArr($params['user_id'], $params['level']);
  373. $user_team_statistics = UserTeamStatistics::getOne([['user_id' => $params['user_id']]]);
  374. $data['user_direct_total'] = !empty($user_team_statistics['user_direct_total']) ? $user_team_statistics['user_direct_total'] : 0;
  375. $data['user_indirect_total'] = !empty($user_team_statistics['user_indirect_total']) ? $user_team_statistics['user_indirect_total'] : 0;
  376. $data['user_team_total'] = !empty($user_team_statistics['user_team_total']) ? $user_team_statistics['user_team_total'] : 0;
  377. $data['loss_user_direct_total'] = !empty($user_team_statistics['loss_user_direct_total']) ? $user_team_statistics['loss_user_direct_total'] : 0;
  378. $data['loss_user_indirect_total'] = !empty($user_team_statistics['loss_user_indirect_total']) ? $user_team_statistics['loss_user_indirect_total'] : 0;
  379. $data['loss_user_team_total'] = !empty($user_team_statistics['loss_user_team_total']) ? $user_team_statistics['loss_user_team_total'] : 0;
  380. $data['level1_zn'] = '好友人数';
  381. $data['level2_zn'] = '粉丝人数';
  382. $data['level_team_zn'] = '潜在粉丝';
  383. $data['level_team_agent_zn'] = '群组业绩';
  384. $data['parent_name'] = '平台';
  385. $data['parent_level_name'] = '平台方';
  386. $data['parent_avatar_url'] = '';
  387. if (!empty($params['user_custom_text'])) {
  388. $user_custom_text = $params['user_custom_text'];
  389. if (!empty($user_custom_text['direct_text'])) $data['level1_zn'] = $user_custom_text['direct_text'];
  390. if (!empty($user_custom_text['indirect_text'])) $data['level2_zn'] = $user_custom_text['indirect_text'];
  391. if (!empty($user_custom_text['team_text'])) $data['level_team_zn'] = $user_custom_text['team_text'];
  392. if (!empty($user_custom_text['team_agent_text'])) $data['level_team_agent_zn'] = $user_custom_text['team_agent_text'];
  393. }
  394. $data['is_show_indirect'] = !isset($user_custom_text['is_show_indirect']) || !empty($user_custom_text['is_show_indirect']);
  395. $data['is_show_team'] = !isset($user_custom_text['is_show_team']) || !empty($user_custom_text['is_show_team']);
  396. $data['is_show_team_agent'] = $user_custom_text['is_show_team_agent'] ?? 1;
  397. $user = User::getOne([['id' => $params['user_id']]], false, ['parent']);
  398. $parent = $user['parent'];
  399. if (!empty($parent)) {
  400. $data['parent_name'] = empty($parent->nickname) ? $parent->id : $parent->nickname;
  401. $data['parent_avatar_url'] = $parent->avatar_url;
  402. $data['parent_level_name'] = $parent->userLevel->name ?? '默认等级';
  403. }
  404. $time = time();
  405. $date = date('Y-m-d', $time);
  406. $start_time = strtotime($date . ' 00:00:00');
  407. $where = ['and', ['in', 'id', $children_id_arr], ['>=', 'created_at', $start_time], ['<=', 'created_at', $time]];
  408. $add_counts = User::find()->where($where)->count();
  409. $where = ['and', ['in', 'id', $children_id_arr], ['>=', 'last_login_at', $start_time], ['<=', 'last_login_at', $time]];
  410. $active_counts = User::find()->where($where)->count();
  411. //今日流失 修改成 沉睡客户: 90天未登陆
  412. $where = ['and', ['in', 'id', $children_id_arr], ['<=', 'last_login_at', strtotime($date . ' 23:59:59') - 90 * 24 * 3600]];
  413. $loss_counts = User::find()->where($where)->count();
  414. $where = ['and', ['in', 'user_id', $children_id_arr], ['>=', 'created_at', $start_time], ['<=', 'created_at', $time]];
  415. if (!empty($config) && $config['settle_type'] == 2) {
  416. $where[] = ['order_status' => [OrderStatusEnum::ACCOMPLISH]];
  417. } else {
  418. if ($config['is_sub_refund'] == 1) {
  419. $where[] = ['order_status' => [OrderStatusEnum::PAY, OrderStatusEnum::SING, OrderStatusEnum::SHIPMENTS, OrderStatusEnum::ACCOMPLISH]];
  420. }
  421. }
  422. $order_list = Order::find()->where($where)->select('id,user_id,pay_time,pay_money,goods_price,order_status')->asArray()->all();
  423. $total_price = 0;
  424. $order_uids = [];
  425. $userNums = []; // 用于记录用户购买次数
  426. foreach ($order_list as $v) {
  427. if ($v['pay_time']) {// 只有订单付款才计算订单人员
  428. if (!in_array($v['user_id'], $order_uids)) $order_uids[] = $v['user_id'];
  429. $total_price += $v['goods_price'];
  430. $userNums[$v['user_id']]++;
  431. }
  432. }
  433. $level_list = [
  434. 'add_counts' => !empty($add_counts) ? $add_counts : 0,//今日新增
  435. 'active_counts' => !empty($active_counts) ? $active_counts : 0,//今日活跃
  436. 'loss_counts' => !empty($loss_counts) ? $loss_counts : 0,//今日流失
  437. 'add_order_counts' => count($order_uids),//今日下单
  438. 'pay_order_counts' => count(array_filter($userNums, function ($v) {return $v == 1;})),//今日首购(修改为今日只下一单的用户,超过一单就不算)
  439. 'total_price' => !empty($total_price) ? bcadd($total_price,0,2) : 0,//新增业绩
  440. ];
  441. $level_list['total_price'] == \sprintf("%01.2f", $level_list['total_price']);
  442. $data['level_list'] = $level_list;
  443. return $data;
  444. }
  445. /**
  446. * 我的客户列表
  447. * @param $params
  448. * @return array|bool|mixed|void
  449. */
  450. public static function myChildrenList($params)
  451. {
  452. $is_hide_mobile = Yii::$app->services->setting->mall->get($params['mall_id'], 'user_operate.is_hide_mobile');
  453. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($params['user_id']);
  454. $where = [];
  455. // if (!empty($params['level'])) $where[] = ['deep' => $params['level'] + $parent_deep];
  456. // $orderBy = 'user_id desc';
  457. // if (!empty($params['sort'])) {
  458. // switch ($params['sort']) {
  459. // case 'new':
  460. // $orderBy = 'created_at desc';
  461. // break;
  462. // case 'old':
  463. // $orderBy = 'created_at asc';
  464. // break;
  465. // }
  466. // }
  467. // // 查询等级或者关键词
  468. // if (!empty($params['user_level']) or !empty($params['keyword'])) {
  469. // $query = User::find()->where(['mall_id' => $params['mall_id'], 'status' => StatusEnum::ENABLED]);
  470. // // 等级
  471. // if (!empty($params['user_level'])) {
  472. // $query->andWhere(['level' => $params['user_level']]);
  473. // }
  474. // // 关键词
  475. // if (!empty($params['keyword'])) {
  476. // $query->andWhere(['or',
  477. // ['like', 'id', $params['keyword']],
  478. // ['like', 'nickname', $params['keyword']],
  479. // ['like', 'mobile', $params['keyword']]
  480. // ]);
  481. // }
  482. // $user_ids = $query->select('id')->column();
  483. // }
  484. // isset($user_ids) && $where[] = ['user_id' => $user_ids];
  485. // // 根据查看的类型(今日新增、今日活跃、近期未访、今日下单、今日首购)获取用户id
  486. // if (!empty($params['type'])) {
  487. // $childrenIdsFind = self::find();
  488. // self::paramPack([
  489. // 'where' => $where,
  490. // 'select' => 'user_id'
  491. // ], $childrenIdsFind);
  492. // $userIdsByType = self::getMyChildrenUserIdsByType($childrenIdsFind->column(), $params['type']);
  493. // if (is_array($userIdsByType)) {
  494. // $where[] = ['user_id' => $userIdsByType];
  495. // }
  496. // }
  497. if (!empty($params['type'])) {
  498. $where[] = ['<>', 'user_id', $params['user_id']];
  499. $where[] = ['like', 'tree', "{$tree},%", false];
  500. if (!empty($params['level'])) $where[] = ['deep' => $params['level'] + $parent_deep];
  501. $childrenIdsFind = self::find();
  502. self::paramPack([
  503. 'where' => $where,
  504. 'select' => 'user_id'
  505. ], $childrenIdsFind);
  506. $userIdsByType = self::getMyChildrenUserIdsByType($childrenIdsFind->column(), $params['type']);
  507. $where = [];
  508. if (is_array($userIdsByType)) {
  509. $where[] = ['u.id' => $userIdsByType];
  510. }
  511. }
  512. $where[] = ['u.mall_id' => $params['mall_id']];
  513. $where[] = ['u.status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]];
  514. switch ($params['level']) {
  515. case 1:
  516. $where[] = ['u.parent_id' => $params['user_id']];
  517. break;
  518. case 2:
  519. $where[] = ['u.second_parent_id' => $params['user_id']];
  520. break;
  521. }
  522. if (!empty($params['user_level'])) $where[] = ['u.level' => $params['user_level']];
  523. if (!empty($params['keyword'])) $where[] = ['or', ['like', 'u.nickname', $params['keyword']], ['like', 'u.mobile', $params['keyword']], ['like', 'u.id', $params['keyword']]];
  524. $orderBy = 'u.id desc';
  525. if (!empty($params['sort_type'])) {
  526. if ($params['sort_type'] == 'created_at') {
  527. $order_desc = 'u.'.$params['sort_type'];
  528. } else {
  529. $order_desc = 'ua.'.$params['sort_type'];
  530. }
  531. }
  532. if (!empty($params['sort'])) {
  533. switch ($params['sort']) {
  534. case 'new':
  535. $orderBy = $order_desc . ' desc'.' , u.id desc';
  536. break;
  537. case 'old':
  538. $orderBy = $order_desc . ' asc'.' , u.id asc';
  539. break;
  540. }
  541. }
  542. $joins = [['LEFT JOIN', UserActionRecord::tableName() .' as ua', 'ua.user_id = u.id']];
  543. // print_r(['alias' => 'u', 'where' => $where, 'order' => $orderBy, 'select' => 'u.id,u.mobile,u.nickname,u.avatar_url,u.level,u.created_at,ua.last_pay_time']);exit;
  544. $list = User::listPage(['alias' => 'u', 'where' => $where, 'join' => $joins, 'order' => $orderBy, 'select' => 'u.id,u.mobile,u.nickname,u.avatar_url,u.level,u.created_at,ua.last_pay_at']);
  545. if (!empty($list['list'])) {
  546. $child_id_arr = array_column($list['list'], 'id');
  547. // $user_arr = User::lists(['where' => [['mall_id' => $params['mall_id']],['id' => $child_id_arr]], 'index' => 'id']);
  548. $user_wallet_arr = UserWallet::lists(['where' => [['user_id' => $child_id_arr]], 'index' => 'user_id', 'select' => 'user_id,total_commission']);
  549. $user_team_statistics_arr = UserTeamStatistics::lists(['where' => [['user_id' => $child_id_arr]], 'index' => 'user_id', 'select' => 'user_id,active_user_team_total,loss_user_team_total,user_team_order_total']);
  550. $user_statistics_total_arr = UserStatisticsTotal::lists(['where' => [['user_id' => $child_id_arr]], 'index' =>'user_id','select' => 'id,user_id,order_num,order_money,pay_money,pay_num,finish_order_num,finish_order_money,refund_finish_order_num,refund_finish_order_money']);
  551. // print_r($user_statistics_total_arr);exit;
  552. $user_level_arr = UserLevel::lists(['where' => [['mall_id' => $params['mall_id']], ['status' => StatusEnum::ENABLED]], 'index' => 'level', 'select' => 'level, name']);
  553. $team_data_config = \Yii::$app->services->setting->addons->get($params['mall_id'], AddonsEnum::ADDONS_NAME_TEAM_DATA, 'basic');
  554. foreach ($list['list'] as &$item) {
  555. if (!empty($team_data_config) && $team_data_config['settle_type'] == 2) {
  556. $order_num = $user_statistics_total_arr[$item['id']]['finish_order_num'] ?? 0;
  557. $order_money = $user_statistics_total_arr[$item['id']]['finish_order_money'] ?? 0;// 消费金额
  558. } else {
  559. $order_num = $user_statistics_total_arr[$item['id']]['pay_num'];
  560. $order_money = $user_statistics_total_arr[$item['id']]['pay_money'];
  561. if ($team_data_config['is_sub_refund']) {//渠道数据插件配置,开启扣除已售后订单数据
  562. $order_num = bcsub($user_statistics_total_arr[$item['id']]['pay_num'], $user_statistics_total_arr[$item['id']]['refund_finish_order_num']);
  563. $order_money = bcsub($user_statistics_total_arr[$item['id']]['pay_money'], $user_statistics_total_arr[$item['id']]['refund_finish_order_money'], 2);// 消费金额
  564. }
  565. }
  566. // 手机号
  567. $mobile = !empty($item['mobile']) ? $item['mobile'] : '';
  568. // 是否隐藏
  569. $mobile = (($is_hide_mobile == 1) && $mobile) ? substr_replace($mobile, '****', 3, 4) : $mobile;
  570. // $item['nickname'] = $user_arr[$item['id']]['nickname'] ?? '';
  571. // $item['avatar_url'] = $user_arr[$item['id']]['avatar_url'] ?? '';
  572. $item['mobile'] = $mobile;
  573. // $item['level'] = $user_arr[$item['id']]['level'] ?? 0;
  574. $item['level_name'] = $user_level_arr[$item['level']]['name'] ?? MallSetting::getDefaultLevelName($params['mall_id']);
  575. $item['created_at'] = date('Y-m-d H:i:s', $item['created_at']);
  576. $item['total_commission'] = $user_wallet_arr[$item['id']]['total_commission'] ?? 0;
  577. $item['active_user_team_total'] = $user_team_statistics_arr[$item['id']]['active_user_team_total'] ?? 0;
  578. $item['loss_user_team_total'] = $user_team_statistics_arr[$item['id']]['loss_user_team_total'] ?? 0;
  579. $item['user_team_order_total'] = $user_team_statistics_arr[$item['id']]['user_team_order_total'] ?? 0;
  580. $item['order_num'] = $order_num ?? 0;
  581. $item['order_money'] = $order_money ?? 0;// 消费金额
  582. $item['user_id'] = $item['id'];
  583. }
  584. }
  585. $data['is_hide_mobile'] = $is_hide_mobile;
  586. return $list;
  587. }
  588. /**
  589. * 我的客户的业绩订单列表
  590. * @param $params
  591. * @return array|bool|mixed|void
  592. */
  593. public static function myChildrenOrders($params)
  594. {
  595. $is_hide_mobile = Yii::$app->services->setting->mall->get($params['mall_id'], 'user_operate.is_hide_mobile');
  596. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($params['user_id']);
  597. $where[] = ['<>', 'user_id', $params['user_id']];
  598. $where[] = ['like', 'tree', "{$tree},%", false];
  599. if (!empty($params['level'])) $where[] = ['deep' => $params['level'] + $parent_deep];
  600. $userIdsFind = self::find();
  601. self::paramPack([
  602. 'where' => $where,
  603. 'select' => 'user_id'
  604. ], $userIdsFind);
  605. $userIds = $userIdsFind->column();
  606. $time = time();
  607. $date = date('Y-m-d', $time);
  608. $start_time = strtotime($date . ' 00:00:00');
  609. $list = Order::listPage([
  610. 'select' => 'id, order_no, user_id, pay_time, pay_money, goods_price, shipping_money',
  611. 'where' => [
  612. ['in', 'user_id', $userIds],
  613. ['>=', 'created_at', $start_time],
  614. ['<=', 'created_at', $time],
  615. ['=', 'pay_status', OrderStatusEnum::PAY]
  616. ],
  617. 'with' => [
  618. 'user' => function ($query) {
  619. $query->select('id, nickname, avatar_url, mobile, level');
  620. }
  621. ]
  622. ]);
  623. if (!empty($list['list'])) {
  624. $user_level_arr = UserLevel::lists(['where' => [['mall_id' => $params['mall_id']], ['status' => StatusEnum::ENABLED]], 'index' => 'level', 'select' => 'level, name']);
  625. foreach ($list['list'] as &$item) {
  626. if (!empty($item['user'])) {
  627. // 手机号
  628. $mobile = $item['user']['mobile'];
  629. // 是否隐藏
  630. $mobile = (($is_hide_mobile == 1) && $mobile) ? substr_replace($mobile, '****', 3, 4) : $mobile;
  631. $item['user']['mobile'] = $mobile;
  632. $item['user']['level_name'] = $user_level_arr[$item['user']['level']] ?? MallSetting::getDefaultLevelName($params['mall_id']);
  633. }
  634. $item['pay_time_text'] = $item['pay_time'] ? date('Y-m-d H:i:s', $item['pay_time']) : '';
  635. }
  636. }
  637. return $list;
  638. }
  639. /**
  640. * 根据类型返回用户id
  641. *
  642. * @param array $children_id_arr
  643. * @param string $type
  644. *
  645. * @return array|null
  646. */
  647. public static function getMyChildrenUserIdsByType(array $children_id_arr, string $type)
  648. {
  649. if (empty($children_id_arr)) {
  650. return [];
  651. }
  652. $time = time();
  653. $date = date('Y-m-d', $time);
  654. $start_time = strtotime($date . ' 00:00:00');
  655. switch ($type) {
  656. case 'today_new': // 今日新增
  657. $where = ['and', ['in', 'id', $children_id_arr], ['>=', 'created_at', $start_time], ['<=', 'created_at', $time]];
  658. return User::find()->where($where)->select('id')->column();
  659. case 'today_active': // 今日活跃
  660. $where = ['and', ['in', 'id', $children_id_arr], ['>=', 'last_login_at', $start_time], ['<=', 'last_login_at', $time]];
  661. return User::find()->where($where)->select('id')->column();
  662. case 'recent_inactive': // 近期未访
  663. //今日流失 修改成 沉睡客户: 90天未登陆
  664. $where = ['and', ['in', 'id', $children_id_arr], ['<=', 'last_login_at', strtotime($date . ' 23:59:59') - 90 * 24 * 3600]];
  665. return User::find()->where($where)->select('id')->column();
  666. case 'today_orders': // 今日下单
  667. $where = ['and', ['in', 'user_id', $children_id_arr], ['>=', 'created_at', $start_time], ['<=', 'created_at', $time], ['=', 'pay_status', OrderStatusEnum::PAY]];
  668. return Order::find()->where($where)->select('DISTINCT(user_id)')->column();
  669. case 'today_acquisitions': // 今日首购
  670. $where = ['and', ['in', 'user_id', $children_id_arr], ['>=', 'created_at', $start_time], ['<=', 'created_at', $time], ['=', 'pay_status', OrderStatusEnum::PAY]];
  671. $orders = Order::find()->groupBy('user_id')->where($where)->select('user_id, count(*) count')->having('count = 1')->asArray()->all();
  672. return array_column($orders, 'user_id');
  673. default:
  674. return null;
  675. }
  676. }
  677. public static function changeRelationship($params)
  678. {
  679. \Yii::$app->db->close();
  680. $t = \Yii::$app->db->beginTransaction();
  681. $stock_locks = [];
  682. try {
  683. $user_parent_model = User::findOne($params['parent_id']);
  684. if (empty($user_parent_model->is_inviter)) throw new Exception('该推荐人没有推广资格');
  685. $user_model = User::findOne($params['user_id']);
  686. if (empty($params['parent_id'])) throw new Exception('请选择推荐人');
  687. $arr['before_id'] = $params['before_parent_id'] = $user_model->parent_id;//修改前
  688. $arr['parent_id'] = $params['after_parent_id'] = $params['parent_id'];//修改后
  689. $user_model->parent_id = $params['parent_id'];
  690. //加锁
  691. foreach ($arr as $id) {
  692. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_USER_RELATIONSHIP , $id);
  693. $identification = uniqid();
  694. $stock_locks[$lock_key] = $identification;
  695. if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new \Exception('修改关系,当前访问人数过多');
  696. }
  697. list($code, $old_user_relation_ship, $old_all_tree, $old_deep, $old_tree) = self::getUserRelationshipData($params['user_id']);//查询当前用户的的tree信息
  698. list($tree, $parent_deep, $parent_all_tree, $parent_all_tree_arr) = self::getTree($params['parent_id']);//查询上级的tree信息
  699. $parent_all_tree_arr[] = $code;
  700. $new_all_tree = $parent_all_tree . ',' . $code;
  701. $insert_tree_arr = self::sliceArr($parent_all_tree_arr);//切分后新的关系链数组
  702. self::saveTree($insert_tree_arr, $old_user_relation_ship, $params['user_id'], $parent_deep, 1);
  703. $user_model->parent_id = $params['parent_id'];
  704. $user_model->second_parent_id = $user_parent_model->parent_id ?? 0;
  705. $user_model->third_parent_id = $user_parent_model->second_parent_id ?? 0;
  706. $res = $user_model->save();
  707. if ($res == false) throw new \Exception('维护当前用户的user表关系链错误');
  708. $model_log = UserRelationshipLog::setData(['user_id' => $params['user_id'], 'before_id' => $arr['before_id'], 'after_id' => $params['parent_id']]);
  709. if ($model_log === false) throw new Exception(UserRelationshipLog::getStaticError());
  710. //所有团队部分修改
  711. $i = 1;
  712. $page_size = 1000;
  713. $new_deep = $parent_deep + 1;
  714. while ($child_list = self::find()->where(['like', 'tree', $old_tree . ',%', false])
  715. ->andWhere(['<>','user_id',$params['user_id']])
  716. ->select('user_id')
  717. ->offset(($i - 1) * $page_size)->groupBy('user_id')->limit($page_size)->all()) {
  718. foreach ($child_list as $item) {
  719. $user_id = $item['user_id'];
  720. list($code_, $old_child_relation_ship, $child_all_tree, $child_deep, $child_tree_) = self::getUserRelationshipData($user_id);//查询当前用户的的tree信息
  721. $child_all_tree = str_replace($old_all_tree, $new_all_tree, $child_all_tree);//关系链替换
  722. $tree_arr = explode(',', $child_all_tree);//转成数组
  723. $insert_tree_arr = self::sliceArr($tree_arr);//按150切分
  724. $cha_deep = $child_deep - $old_deep;
  725. self::saveTree($insert_tree_arr, $old_child_relation_ship, $user_id, $new_deep, $cha_deep);
  726. //大于3层的,不需要维护user表
  727. if ($cha_deep <= 3) {
  728. self::stickUser($tree_arr,$user_id);
  729. }
  730. }
  731. $i++;
  732. }
  733. $t->commit();
  734. LockHelper::batchUlock($stock_locks);
  735. // 关系链修改完成触发修改关系链事件
  736. $event = new UserChangeRelationshipEvent($user_model->mall_id);
  737. Yii::$app->services->events->dispatch($event, $params, $event->listeners);
  738. UserPartitionRelationship::statisticsUserTeamData($new_all_tree, $old_all_tree,$user_model->mall_id);
  739. // Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_ORDER, RabbitMqEnum::ORDER_QUEUE,
  740. // ['new_all_tree' => $new_all_tree,'new_all_tree' => $old_all_tree, 'user_id' => $user_model->id], UserTeamProfitStatistics::class, 'userChangeRelationship');
  741. } catch (\Exception $e) {
  742. $t->rollBack();
  743. LockHelper::batchUlock($stock_locks);
  744. var_dump('修改关系的错误信息:' . $e->getMessage() . $e->getFile() . $e->getLine());
  745. return false;
  746. }
  747. }
  748. //修改成平台
  749. public static function changeRelationshipToPlatform($params)
  750. {
  751. \Yii::$app->db->close();
  752. $t = \Yii::$app->db->beginTransaction();
  753. $stock_locks = [];
  754. try {
  755. $user_model = User::findOne($params['user_id']);
  756. $arr['before_id'] = $params['before_parent_id'] = $user_model->parent_id;//修改前
  757. $arr['parent_id'] = $params['after_parent_id'] = 0;//修改后
  758. $user_model->parent_id = 0;
  759. //加锁
  760. foreach ($arr as $id) {
  761. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_USER_RELATIONSHIP , $id);
  762. $identification = uniqid();
  763. $stock_locks[$lock_key] = $identification;
  764. if (!LockHelper::lock($lock_key, $identification, 30, 60)) throw new \Exception('修改关系,当前访问人数过多');
  765. }
  766. list($code, $old_user_relation_ship, $old_all_tree, $old_deep, $old_tree) = self::getUserRelationshipData($params['user_id']);//查询当前用户的的tree信息
  767. $parent_deep = 0;
  768. $parent_all_tree_arr = [];
  769. $parent_all_tree_arr[] = $code;
  770. $new_all_tree = $code;
  771. $insert_tree_arr = self::sliceArr($parent_all_tree_arr);//切分后新的关系链数组
  772. self::saveTree($insert_tree_arr, $old_user_relation_ship, $params['user_id'], $parent_deep, 0);
  773. $user_model->parent_id = 0;
  774. $user_model->second_parent_id = 0;
  775. $user_model->third_parent_id = 0;
  776. $res = $user_model->save();
  777. if ($res == false) throw new \Exception('维护当前用户的user表关系链错误');
  778. $model_log = UserRelationshipLog::setData(['user_id' => $params['user_id'], 'before_id' => $arr['before_id'], 'after_id' => 0]);
  779. if ($model_log === false) throw new Exception(UserRelationshipLog::getStaticError());
  780. //所有团队部分修改
  781. $i = 1;
  782. $page_size = 1000;
  783. while ($child_list = self::find()->where(['like', 'tree', $old_tree . ',%', false])
  784. ->andWhere(['<>','user_id',$params['user_id']])
  785. ->select('user_id')
  786. ->offset(($i - 1) * $page_size)->groupBy('user_id')->orderBy('user_id')->limit($page_size)->all()) {
  787. foreach ($child_list as $item) {
  788. $user_id = $item['user_id'];
  789. list($code_, $old_child_relation_ship, $child_all_tree, $child_deep, $child_tree_) = self::getUserRelationshipData($user_id);//查询当前用户的的tree信息
  790. $child_all_tree = str_replace($old_all_tree, $new_all_tree, $child_all_tree);//关系链替换
  791. $tree_arr = explode(',', $child_all_tree);//转成数组
  792. $insert_tree_arr = self::sliceArr($tree_arr);//按150切分
  793. $cha_deep = $child_deep - $old_deep;
  794. self::saveTree($insert_tree_arr, $old_child_relation_ship, $user_id, 0, $cha_deep);
  795. //大于3层的,不需要维护user表
  796. if ($cha_deep <= 3) {
  797. self::stickUser($tree_arr,$user_id);
  798. }
  799. }
  800. $i++;
  801. }
  802. $t->commit();
  803. LockHelper::batchUlock($stock_locks);
  804. // 关系链修改完成触发修改关系链事件
  805. $event = new UserChangeRelationshipEvent($user_model->mall_id);
  806. Yii::$app->services->events->dispatch($event, $params, $event->listeners);
  807. UserPartitionRelationship::statisticsUserTeamData($new_all_tree, $old_all_tree,$user_model->mall_id);
  808. // Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_ORDER, RabbitMqEnum::ORDER_QUEUE,
  809. // ['new_all_tree' => '','old_all_tree' => $old_all_tree, 'user_id' => $user_model->id], UserTeamProfitStatistics::class, 'userChangeRelationship');
  810. } catch (\Exception $e) {
  811. $t->rollBack();
  812. LockHelper::batchUlock($stock_locks);
  813. var_dump('修改关系的错误信息:' . $e->getMessage() . $e->getFile() . $e->getLine());
  814. return false;
  815. }
  816. }
  817. public static function stickUser(&$tree_arr,$user_id){
  818. array_pop($tree_arr);
  819. $parent_id_code = array_pop($tree_arr);
  820. $second_parent_id_code = array_pop($tree_arr);
  821. $third_parent_id_code = array_pop($tree_arr);
  822. $fields =[];
  823. $fields['parent_id'] = !empty($parent_id_code) ? Base64Helper::Base642dec($parent_id_code) : 0;
  824. $fields['second_parent_id'] = !empty($second_parent_id_code) ? Base64Helper::Base642dec($second_parent_id_code) : 0;
  825. $fields['third_parent_id'] = !empty($third_parent_id_code) ? Base64Helper::Base642dec($third_parent_id_code) : 0;
  826. $res = User::updateAll($fields,['id' => $user_id]);
  827. if ($res === false) throw new \Exception(User::getStaticError());
  828. }
  829. /**
  830. * @desc:关系链切分,150个为一组
  831. * @Author: hua
  832. * @Date: 2021/12/1
  833. * @Time: 9:28
  834. * @Copyright: copyright (c) 2021 广东七件事集团
  835. * @param $arr
  836. * @param int $part
  837. * @return array
  838. */
  839. public static function sliceArr($arr, $part = 1)
  840. {
  841. $insert_tree_arr = [];
  842. while ($part_tree = array_slice($arr, ($part - 1) * 150, 150)) {
  843. $insert_tree_arr[$part] = implode(',', $part_tree);
  844. $part++;
  845. }
  846. return $insert_tree_arr;
  847. }
  848. /**
  849. * @desc:把切分后的关系链数组保存
  850. * @Author: hua
  851. * @Date: 2021/12/1
  852. * @Time: 9:28
  853. * @Copyright: copyright (c) 2021 广东七件事集团
  854. * @param $insert_tree_arr
  855. * @param $old_relation_ship
  856. * @param $user_id
  857. * @param $new_deep
  858. * @param $cha_deep
  859. */
  860. protected static function saveTree($insert_tree_arr, $old_relation_ship, $user_id, $new_deep, $cha_deep)
  861. {
  862. foreach ($insert_tree_arr as $part => $tree) {
  863. if (isset($old_relation_ship[$part])) {
  864. $obj = $old_relation_ship[$part];
  865. $obj->tree = $tree;
  866. $obj->deep = $new_deep + $cha_deep;
  867. $obj->save();
  868. unset($old_relation_ship[$part]);
  869. } else {
  870. $obj = new self();
  871. $obj->part = $part;
  872. $obj->user_id = $user_id;
  873. $obj->tree = $tree;
  874. $obj->deep = $new_deep + $cha_deep;
  875. $obj->save();
  876. }
  877. }
  878. foreach ($old_relation_ship as $val) {
  879. UserPartitionRelationship::deleteAll(['id' => $val['id']]);
  880. }
  881. }
  882. protected static function getUserRelationshipData($user_id)
  883. {
  884. $condition = [
  885. 'where' => [
  886. ['user_id' => $user_id]
  887. ],
  888. 'order' => 'part asc'
  889. ];
  890. $old_user_relation_ship_list = self::lists($condition, false);//查询当前用户的关系链列表
  891. $code = Base64Helper::dec2Base64($user_id);
  892. $old_user_relation_ship = [];//旧的关系链数组,以分区为key
  893. $old_all_tree = '';//旧的关系链,不分区
  894. $old_deep = 0;//旧的深度
  895. $old_tree = '';//旧的最后一个分区存储的关系链
  896. foreach ($old_user_relation_ship_list as $item) {
  897. $old_user_relation_ship[$item['part']] = $item;
  898. $old_deep = $item['deep'];
  899. $old_all_tree .= $item['tree'] . ',';
  900. $old_tree = $item['tree'];
  901. }
  902. $old_all_tree = trim($old_all_tree, ',');//旧的tree
  903. return [$code, $old_user_relation_ship, $old_all_tree, $old_deep, $old_tree];
  904. }
  905. /**
  906. * @param int $type 1:下级 2:上级
  907. * @param int $deep 层级
  908. * @param int $page
  909. * @return array ['user_id'=>bind_time] count:10
  910. */
  911. public static function getRelationship($user_id, $page = 1, $keyword = '', $search_type = '', $type, $deep = 0)
  912. {
  913. $limit = 10;
  914. if ($type == 1) {
  915. $user_relationship = self::find()->where(['user_id' => $user_id])->orderBy('part desc')->one();
  916. if (empty($user_relationship)) return [];
  917. $tree = $user_relationship->tree;
  918. //找出分片最大一个的tree,like查询10个下级
  919. $model = self::find()->alias('upr')
  920. ->where(['like', 'upr.tree', "{$tree},%", false])
  921. ->andWhere(['<>', 'upr.user_id', $user_id])
  922. ->select(['upr.user_id', 'upr.updated_at']);
  923. if (!empty($deep)) $model->andWhere(['=', 'upr.deep', $deep + $user_relationship->deep]);
  924. //如果有关键字筛选
  925. if ($keyword !== '') {
  926. $model->join('left join', User::tableName() . ' as u', 'u.id = upr.user_id');
  927. if ($search_type == 1) $model->andWhere(['=', 'u.id', $keyword]);
  928. if ($search_type == 2) $model->andWhere(['=', 'u.nickname', $keyword]);
  929. if ($search_type == 3) $model->andWhere(['=', 'u.mobile', $keyword]);
  930. if ($search_type == 4) $model->andWhere(['=', 'u.level', $keyword]);
  931. }
  932. $count = $model->count();
  933. $list = $model->offset(($page - 1) * $limit)->limit($limit)->all();
  934. } else {
  935. $model = self::find();
  936. $user_relationship = $model->where(['user_id' => $user_id])->select(['user_id', 'tree'])->all();
  937. if (empty($user_relationship)) return [];
  938. $parent_ids = [];
  939. switch ($deep){
  940. case 0:
  941. foreach ($user_relationship as $value) {
  942. $tree = explode(',', $value['tree']);
  943. array_pop($tree);
  944. $parent_ids = array_merge($parent_ids, array_map([Base64Helper::class, 'Base642dec'], $tree));
  945. }
  946. break;
  947. case 1:
  948. $parent_ids[]=self::getParentIds($user_id,$deep);
  949. break;
  950. case 2:
  951. list($parent_id, $second_parent_id) = self::getParentIds($user_id,$deep);
  952. $parent_ids[]=$parent_id;
  953. $parent_ids[]=$second_parent_id;
  954. break;
  955. case 3:
  956. $parent_ids = self::getParentIdArr($user_id);
  957. break;
  958. }
  959. if ($keyword) {
  960. //var_dump($keyword);
  961. if ($search_type == 1) {
  962. if (in_array($keyword, $parent_ids)) {
  963. $model = User::find()->where(['id' => $keyword]);
  964. } else {
  965. return ['count' => 0, 'list' => []];
  966. }
  967. } else {
  968. if ($search_type == 2) $search_keywork = 'nickname';
  969. if ($search_type == 3) $search_keywork = 'mobile';
  970. $model = User::find()->where(["$search_keywork" => $keyword, 'id' => $parent_ids]);
  971. }
  972. $count = $model->count();
  973. $parent_ids = $model->select('id')->column();
  974. } else {
  975. $parent_ids = array_unique($parent_ids);
  976. $count = count($parent_ids);
  977. }
  978. $ids = array_slice($parent_ids, ($page - 1) * $limit, $limit);
  979. $list = self::find()->where(['user_id' => $ids])->select(['user_id', 'updated_at'])->all();
  980. }
  981. $data = [];
  982. foreach ($list as $val) {
  983. $data[$val['user_id']] = $val['updated_at'];
  984. }
  985. return ['count' => $count, 'list' => array_column($list, 'updated_at', 'user_id')];
  986. }
  987. /**
  988. * @desc:分段查询
  989. * @Author: hua
  990. * @Date: 2021/12/4
  991. * @Time: 11:37
  992. * @Copyright: copyright (c) 2021 广东七件事集团
  993. * @param $offset
  994. * @param $limit
  995. * @param $tree
  996. * @param array $params
  997. * @return false|mixed
  998. */
  999. public static function batchGetTeamData($offset, $limit, $tree, $params = [])
  1000. {
  1001. $params['where'][] = ['like', 'tree', "{$tree},%", false];
  1002. $params['offset'] = ($offset - 1) * $limit;
  1003. $params['limit'] = $limit;
  1004. $params['order'] = 'id desc';
  1005. return self::lists($params);
  1006. }
  1007. /**
  1008. * @desc:修改关系链后,统计直推、间推、团队会员数据
  1009. * @Author: hua
  1010. * @Date: 2021/12/6
  1011. * @Time: 9:25
  1012. * @Copyright: copyright (c) 2021 广东七件事集团
  1013. * @param $new_tree
  1014. * @param $old_tree
  1015. */
  1016. public static function statisticsUserTeamData($new_tree, $old_tree,$mall_id)
  1017. {
  1018. $new_ids = explode(',', $new_tree);
  1019. $old_ids = explode(',', $old_tree);
  1020. array_pop($new_ids);//把自身去掉
  1021. array_pop($old_ids);//把自身去掉
  1022. $ids = $new_ids;
  1023. if (!empty($old_ids)) $ids = array_merge($new_ids, $old_ids);
  1024. if (!empty($ids)) {
  1025. $userIds = [];
  1026. foreach ($ids as $user_id) {
  1027. $user_id = Base64Helper::Base642dec($user_id);
  1028. $userIds[]=$user_id;
  1029. self::userTeamData($user_id,$mall_id);
  1030. }
  1031. \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK, RabbitMqEnum::TASK_QUEUE, [
  1032. 'user_id'=>$userIds,
  1033. 'mall_id'=>$mall_id,
  1034. 'cate_ids'=>[MemberLabelEnum::POPULARIZE,MemberLabelEnum::LEAD]
  1035. ], MemberLabelLogic::class, 'executeSyncCateId5Or6');
  1036. }
  1037. }
  1038. /**
  1039. * @desc:统计直推、间推、团队会员数据
  1040. * @Author: hua
  1041. * @Date: 2021/12/6
  1042. * @Time: 10:40
  1043. * @Copyright: copyright (c) 2021 广东七件事集团
  1044. * @param $user_id
  1045. * @return int
  1046. */
  1047. public static function userTeamData($user_id,$mall_id)
  1048. {
  1049. $team_user_counts = self::getChildCounts($user_id);// 团队
  1050. $direct_user_counts = self::getChildCounts($user_id, 1);// 直推
  1051. $indirect_user_counts = self::getChildCounts($user_id, 2);// 间推
  1052. $userTeamStatistics = UserTeamStatistics::findOne(['user_id'=>$user_id,'mall_id'=>$mall_id,'status'=>StatusEnum::ENABLED]);
  1053. if(empty($userTeamStatistics)){
  1054. $userTeamStatistics = new UserTeamStatistics();
  1055. $userTeamStatistics->loadDefaultValues();
  1056. $userTeamStatistics->mall_id = $mall_id;
  1057. $userTeamStatistics->user_id = $user_id;
  1058. }
  1059. $userTeamStatistics->user_team_total = $team_user_counts;
  1060. $userTeamStatistics->user_direct_total = $direct_user_counts;
  1061. $userTeamStatistics->user_indirect_total = $indirect_user_counts;
  1062. return $userTeamStatistics->save();
  1063. }
  1064. /**
  1065. * @notes:分页获取下级,并进行组合
  1066. * @param $user_id
  1067. * @param $deep
  1068. * @param $limit
  1069. * @param $offset
  1070. * @return array[]
  1071. * @author: lun
  1072. * @Time: 2022/12/13 16:15
  1073. */
  1074. public static function getChildIdMake($user_id, $deep = 0, $limit = 10, $offset = 1)
  1075. {
  1076. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  1077. $where[] = ['like', 'tree', "{$tree}%", false];
  1078. if (!empty($deep)) $where[] = ['between','deep', $parent_deep, ($deep + $parent_deep)];
  1079. $select = "id,part,user_id,tree,deep,created_at";
  1080. $order = 'deep asc,id asc';
  1081. $params = compact('where','select','order');
  1082. $params['offset'] = ($offset - 1) * $limit;
  1083. $params['limit'] = $limit;
  1084. $list = self::lists($params);
  1085. $user_ids = array_column($list, 'user_id');
  1086. // 获取这些用户的上级id
  1087. $parent_arr = User::find()->select('id,nickname,mobile,parent_id')->where(['id' => $user_ids, 'status' => StatusEnum::ENABLED])->indexBy('id')->asArray()->all();
  1088. foreach ($list as $key => $item){
  1089. $list[$key]['pid'] = $parent_arr[$item['user_id']]['parent_id'];
  1090. $list[$key]['nickname'] = $parent_arr[$item['user_id']]['nickname'];
  1091. $list[$key]['mobile'] = $parent_arr[$item['user_id']]['mobile'];
  1092. }
  1093. return ['list' => $list];
  1094. }
  1095. /**
  1096. * @notes:关系链树组合
  1097. * @param $list
  1098. * @param $pid
  1099. * @return array
  1100. * @author: lun
  1101. * @Time: 2022/12/13 16:14
  1102. */
  1103. public static function listToTree($list, $pid = 0)
  1104. {
  1105. $arr = array_filter($list,function ($v) use ($pid){
  1106. if($pid == $v['pid']){
  1107. return true;
  1108. }
  1109. return false;
  1110. });
  1111. $arr = array_values($arr);
  1112. $res = array_map(function ($v) use ($list){
  1113. $v['children'] = self::listToTree($list,$v['user_id']);
  1114. return $v;
  1115. },$arr);
  1116. return $res;
  1117. }
  1118. /**
  1119. * @Description: 获取用户是否需要绑定上级
  1120. * @Author: zsan
  1121. * @DateTime 2023-05-22 17:37:59
  1122. * @param integer $mall_id
  1123. * @param integer $user_id
  1124. * @return integer
  1125. */
  1126. public static function getIsNeedPopups($mall_id, $user_id = 0)
  1127. {
  1128. $is_bind_popups = 0;
  1129. try{
  1130. if ($user_id == 0) throw new \Exception('not need pop-ups 1');
  1131. $relation_setting = \Yii::$app->services->setting->mall->get($mall_id, 'user_relation_setting.relation_setting');
  1132. $data = [];
  1133. if (!empty($relation_setting)) $data = json_decode($relation_setting, true);
  1134. if (empty($data['status'])) throw new \Exception('not need pop-ups 2');
  1135. if (empty($data['become_child_way'])) throw new \Exception('not need pop-ups 3');
  1136. if (!in_array($data['become_child_way'], [5])) throw new \Exception('not need pop-ups 4');
  1137. $user = User::findOne(['id' => $user_id, 'mall_id' => $mall_id]);
  1138. if ($user->parent_id > 0) throw new \Exception('not need pop-ups 5');
  1139. $is_bind_popups = 1;
  1140. }catch(\Exception $e){
  1141. $is_bind_popups = 0;
  1142. }finally{
  1143. return $is_bind_popups;
  1144. }
  1145. }
  1146. /**
  1147. * @Description: 通过邀请码绑定用户
  1148. * @Author: zsan
  1149. * @DateTime 2023-05-22 17:37:04
  1150. * @param integer $mall_id
  1151. * @param integer $user_id
  1152. * @param string $code
  1153. * @return boolean
  1154. */
  1155. public static function bindUserByCode($mall_id, $user_id, $code)
  1156. {
  1157. try {
  1158. $user = User::findOne(['id' => $user_id, 'mall_id' => $mall_id]);
  1159. if (!$user) throw new \Exception('id参数错误');
  1160. if ($user->parent_id > 0) throw new \Exception('该用户已有上级');
  1161. $parentCode = UserInviteCode::findOne(['mall_id' => $mall_id, 'code' => $code]);
  1162. if (!$parentCode) throw new \Exception('邀请码错误');
  1163. if ($parentCode->user_id == $user_id) throw new \Exception('不能绑定自己为自己的上级');
  1164. if ($parentCode->user->is_inviter <= 0) throw new \Exception('该邀请码用户无推广资格');
  1165. // 因为邀请码绑定上级时,会出现两个人互相绑定,所以这里需要判断
  1166. $hasUserBind = self::hasReverseRelation($user_id, $parentCode->user_id);
  1167. if ($hasUserBind) throw new \Exception('该用户已在需要绑定的关系链中,请勿重复操作');
  1168. $params['mall_id'] = $mall_id;
  1169. $params['user_id'] = $user_id;
  1170. $params['parent_id'] = $parentCode->user_id;
  1171. $res = self::setData($params);
  1172. if ($res == false) throw new Exception(self::getStaticError());
  1173. list($tree, $parent_deep, $all_tree, $all_tree_arr) = self::getTree($user_id);
  1174. array_pop($all_tree_arr); //自身
  1175. array_pop($all_tree_arr); //直推上级
  1176. $second_parent_id = array_pop($all_tree_arr); //间推上级
  1177. $third_parent_id = array_pop($all_tree_arr); //
  1178. $user->parent_id = $params['parent_id'];
  1179. if ($second_parent_id) {
  1180. $user->second_parent_id = Base64Helper::Base642dec($second_parent_id);
  1181. }
  1182. if ($third_parent_id) {
  1183. $user->third_parent_id = Base64Helper::Base642dec($third_parent_id);
  1184. }
  1185. $user->temp_parent_id > 0 && $user->temp_parent_id = 0;
  1186. $res = $user->save();
  1187. if ($res == false) throw new Exception($user->getErrorMessage());
  1188. return $res;
  1189. } catch (\Exception $e) {
  1190. self::setStaticError($e->getMessage());
  1191. return false;
  1192. }
  1193. }
  1194. /**
  1195. * 判断当前用户要绑定的上下级是否已绑定过自己
  1196. * @param $user_id
  1197. * @param $parent_id
  1198. * @return bool
  1199. */
  1200. public static function hasReverseRelation($user_id, $parent_id): bool
  1201. {
  1202. // 获取当前需要绑定上级所有上级关系链id
  1203. $parentIds = self::getParentIdArr($parent_id);
  1204. // 获取当前需要绑定上级所有下级关系链id
  1205. $childrenIds = self::getChildIdArr($user_id);
  1206. $userIds = array_unique(array_merge($parentIds, $childrenIds));
  1207. return in_array($user_id, $userIds);
  1208. }
  1209. public static function fixDeep(){
  1210. $i = 1;
  1211. $page_size = 1000;
  1212. while ($list = User::find()
  1213. ->where(['status'=>1])
  1214. ->orderBy('id desc')
  1215. ->offset(($i - 1) * $page_size)->limit($page_size)->all()) {
  1216. foreach ($list as $item) {
  1217. list($tree, $deep, $all_tree, $all_tree_arr) = self::getTree($item['id']);
  1218. $t_deep = count($all_tree_arr)-1;
  1219. if($t_deep<0) $t_deep = 0;
  1220. if($t_deep!= $deep){
  1221. $res = UserPartitionRelationship::updateAll(['deep'=>$t_deep],['user_id'=>$item['id']]);
  1222. var_dump('user_id'.$item['id'].':::'.$res);
  1223. }
  1224. }
  1225. $i++;
  1226. }
  1227. }
  1228. /**
  1229. * 返回某个用户id是不是当前$userId这个团队的
  1230. *
  1231. * @param int $userId
  1232. * @param int $targetUserId
  1233. *
  1234. * @return bool
  1235. */
  1236. public static function isMyTeam(int $userId, int $targetUserId): bool
  1237. {
  1238. // 获取tree
  1239. $myTree = self::find()
  1240. ->where(['user_id' => $userId])
  1241. ->select('tree')
  1242. ->scalar();
  1243. $myTree = explode(',', $myTree) ?: [];
  1244. if (in_array(Base64Helper::dec2Base64($targetUserId), $myTree)) {
  1245. return true;
  1246. }
  1247. $targetTree = self::find()
  1248. ->where(['user_id' => $targetUserId])
  1249. ->select('tree')
  1250. ->scalar();
  1251. $targetTree = explode(',', $targetTree) ?: [];
  1252. return in_array(Base64Helper::dec2Base64($userId), $targetTree);
  1253. }
  1254. }