UserStringCode.php 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. <?php
  2. /**
  3. * 厦门云联储网络科技有限公司
  4. * https://www.baokuaiyun.com
  5. * Copyright (c) 2023 爆块云 All rights reserved.
  6. */
  7. namespace app\models;
  8. use Yii;
  9. use yii\behaviors\TimestampBehavior;
  10. use yii\db\ActiveRecord;
  11. use yii\db\Expression;
  12. use yii\helpers\ArrayHelper;
  13. use yii\helpers\Json;
  14. use app\constants\OptionSetting;
  15. /**
  16. * This is the model class for table "{{%user_string_code}}".
  17. *
  18. * @property integer $id
  19. * @property integer $store_id
  20. * @property integer $user_id
  21. * @property integer $parent_id
  22. * @property integer $parent_node
  23. * @property string $recommend_relation
  24. * @property integer $layer
  25. * @property string $recommend_relation_node
  26. * @property integer $layer_node
  27. * @property integer $team_num
  28. * @property integer $area_key
  29. * @property integer $layer_rank
  30. * @property integer $created_at
  31. * @property integer $updated_at
  32. */
  33. class UserStringCode extends \yii\db\ActiveRecord
  34. {
  35. /**
  36. * @inheritdoc
  37. */
  38. public static function tableName()
  39. {
  40. return '{{%user_string_code}}';
  41. }
  42. public function behaviors()
  43. {
  44. return [
  45. [
  46. 'class' => TimestampBehavior::class,
  47. 'attributes' => [
  48. ActiveRecord::EVENT_BEFORE_INSERT => ['created_at'],
  49. ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at']
  50. ]
  51. ]
  52. ];
  53. }
  54. /**
  55. * @inheritdoc
  56. */
  57. public function rules()
  58. {
  59. return [
  60. [['recommend_relation', 'recommend_relation_node'], 'string'],
  61. [
  62. [
  63. 'store_id', 'user_id', 'parent_id', 'layer', 'layer_node', 'created_at', 'updated_at', 'parent_node',
  64. 'team_num', 'area_key', 'layer_rank'
  65. ],
  66. 'integer'
  67. ],
  68. ];
  69. }
  70. /**
  71. * @inheritdoc
  72. */
  73. public function attributeLabels()
  74. {
  75. return [
  76. 'id' => 'ID',
  77. 'store_id' => '店铺id',
  78. 'user_id' => '用户id',
  79. 'parent_id' => '分销上级id',
  80. 'parent_node' => '父节点',
  81. 'recommend_relation_node' => '节点推荐关系',
  82. 'layer_node' => '节点层级',
  83. 'recommend_relation' => '分销推荐关系',
  84. 'layer' => '分销层级',
  85. 'area_key' => '区域Key',
  86. 'layer_rank' => '层级排位',
  87. 'team_num' => '团队人数',
  88. 'created_at' => '创建时间',
  89. 'updated_at' => '更新时间',
  90. ];
  91. }
  92. /**
  93. * 加入节点
  94. * @param $store_id
  95. * @param $user_id
  96. * @return bool
  97. */
  98. public static function joinNode($order)
  99. {
  100. $store_id = $order->store_id;
  101. $user_id = $order->user_id;
  102. $saas_user_id = $order->saas_id;
  103. // $share_setting = Option::get('share_basic_setting', $store_id);
  104. $share_setting = Option::get(OptionSetting::SHARE_STRING_CODE_DEFAULT_SETTING, $store_id, OptionSetting::SHARE_GROUP_NAME, '{}');
  105. $share_setting = $share_setting ? Json::decode($share_setting['value']) : [];
  106. if (!$share_setting) {
  107. //debug_log([__METHOD__, __LINE__, '加入串码节点:未开启分销设置'], "app_debug.log");
  108. return false;
  109. }
  110. // if (!is_array($share_setting['is_string_code']) || empty($share_setting['is_string_code']['value'])) {
  111. // //debug_log([__METHOD__, __LINE__, '加入串码节点:未开启串码节点功能'], "app_debug.log");
  112. // return false;
  113. // }
  114. // if (!is_array($share_setting['string_code_layer_max']) || empty($share_setting['string_code_layer_max']['value'])) {
  115. // //debug_log([__METHOD__, __LINE__, '加入串码节点:未设置串码最大层级'], "app_debug.log");
  116. // return false;
  117. // }
  118. // if (!is_array($share_setting['string_code_condition_coin']) || empty($share_setting['string_code_condition_coin']['value'])) {
  119. // //debug_log([__METHOD__, __LINE__, '加入串码节点:未设置贡献积分条件'], "app_debug.log");
  120. // return false;
  121. // }
  122. if (!is_array($share_setting['string_code_max_layer'])) {
  123. //debug_log([__METHOD__, __LINE__, '加入串码节点:未设置串码最大层级'], "app_debug.log");
  124. return false;
  125. }
  126. if (!is_array($share_setting['string_code_condition_value'])) {
  127. //debug_log([__METHOD__, __LINE__, '加入串码节点:未设置贡献积分条件'], "app_debug.log");
  128. return false;
  129. }
  130. if (!is_array($share_setting['string_code_root_user_id'])) {
  131. //debug_log([__METHOD__, __LINE__, '加入串码节点:未设置默认根节点'], strtolower(__CLASS__ . '.log'));
  132. return false;
  133. }
  134. // 判断是否满足条件
  135. $user = User::findOne(['id' => $user_id]);
  136. if ($user['total_coin'] < $share_setting['string_code_condition_value']) {
  137. //debug_log([__METHOD__, __LINE__, '加入串码节点:用户贡献积分不足'], "app_debug.log");
  138. return false;
  139. }
  140. $user_string_code = self::findOne(['user_id' => $user_id]);
  141. if ($user_string_code) {
  142. //debug_log([__METHOD__, __LINE__, "加入串码节点:用户:{$user_id}已加入串码节点"], "app_debug.log");
  143. return false;
  144. }
  145. // 锁定 root 节点
  146. if (!$user['parent_id']) {
  147. $parent_id = 73;
  148. } else {
  149. $parent_id = $user['parent_id'];
  150. }
  151. $parent_string_code = self::findOne(['user_id' => $parent_id]);
  152. list($real_parent_node, $layer_rank, $area_key) = self::getRealParentNodePlus($store_id, $user_id, $parent_id, $share_setting['string_code_max_layer']);
  153. if (empty($real_parent_node['recommend_relation_node'])) {
  154. $recommend_relation_node = ',' . $real_parent_node->user_id . ',';
  155. } else {
  156. $recommend_relation_node = $real_parent_node['recommend_relation_node'] . $real_parent_node->user_id . ',';
  157. }
  158. if (empty($parent_string_code['recommend_relation'])) {
  159. $recommend_relation = ',' . $parent_string_code->user_id . ',';
  160. } else {
  161. $recommend_relation = $real_parent_node['recommend_relation'] . $parent_string_code->user_id . ',';
  162. }
  163. // 保存节点数据
  164. $user_string_code = new self();
  165. $user_string_code->store_id = $store_id;
  166. $user_string_code->user_id = $user_id;
  167. $user_string_code->parent_id = $parent_id;
  168. $user_string_code->parent_node = $real_parent_node['user_id'];
  169. $user_string_code->recommend_relation_node = $recommend_relation_node;
  170. $user_string_code->layer_node = $real_parent_node['layer_node'] + 1;
  171. $user_string_code->recommend_relation = $recommend_relation;
  172. $user_string_code->layer = $parent_string_code['layer'] + 1;
  173. $user_string_code->area_key = $area_key;
  174. $user_string_code->layer_rank = $layer_rank;
  175. $user_string_code->save();
  176. // 保存用户区域关系
  177. // UserStringCodeArea::createUserStringCodeArea($store_id, $user_id, $user['parent_id'], $row, $col, $area_key);
  178. // 所有上级节点 团队人数 + 1
  179. self::setTeamCountInc($user_id);
  180. return true;
  181. }
  182. /**
  183. * 获取用户最后一个节点
  184. * 从左至右,从上至下
  185. * @param $store_id 店铺ID
  186. * @param $user_id 下单用户ID
  187. * @param $parent_id 下单用户上级ID
  188. * @param $max_layer 限制最大层数
  189. * @return array
  190. */
  191. public static function getRealParentNodePlus($store_id, $order_user_id, $parent_id, $max_layer)
  192. {
  193. // 针对父节点对应的区
  194. $real_area_key = 0;
  195. // 针对根节点对应的排序
  196. $real_layer_rank = 0;
  197. $parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $parent_id]);
  198. $children_list = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $parent_id]);
  199. // 未有子节点
  200. if (!$children_list) {
  201. //debug_log([__METHOD__, __LINE__, "未有子节点"], "app_debug.log");
  202. $real_parent_node = $parent_node;
  203. return [$real_parent_node, $real_area_key, $real_layer_rank];
  204. }
  205. // 先判断 最大层级下 第一列能否能占位
  206. $temp_parent_id = $parent_id;
  207. //debug_log([__METHOD__, __LINE__, "最大层数:{$max_layer}"], "app_debug.log");
  208. for ($i = 0; $i < $max_layer; $i++) {
  209. //debug_log([__METHOD__, __LINE__, "第1区循环:{$i},用户ID:{$temp_parent_id}"], "app_debug.log");
  210. $temp_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $temp_parent_id, 'area_key' => $real_area_key]);
  211. if (!$temp_node) {
  212. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $temp_parent_id]);
  213. return [$real_parent_node, $real_layer_rank, $real_area_key];
  214. } else {
  215. $temp_parent_id = $temp_node['user_id'];
  216. }
  217. }
  218. // 广度优先遍历
  219. $queue = [];
  220. $queue[] = $parent_node;
  221. while (!empty($queue)) {
  222. // 当前节点
  223. $current_node = array_shift($queue);
  224. // 当前层数
  225. $current_layer = $current_node['layer_node'] - $parent_node['layer_node'];
  226. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},当前层数:{$current_layer}"], "app_debug.log");
  227. // 判断用户第1区有无落点
  228. $parent_first_col_auth = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $current_node['user_id'], 'area_key' => 0]);
  229. if (!$parent_first_col_auth) {
  230. $real_area_key = 0;
  231. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},1区没人,落第1区"], "app_debug.log");
  232. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $current_node['user_id']]);
  233. $current_layer_rank = UserStringCode::find()
  234. ->where(['store_id' => $store_id, 'layer_node' => $real_parent_node['layer_node'] + 1])
  235. ->max('layer_rank');
  236. $real_layer_rank = $current_layer_rank + 1;
  237. return [$real_parent_node, $real_layer_rank, $real_area_key];
  238. }
  239. // 判断用户能否开第2区
  240. $parent_second_col_auth = self::checkSecondColumnAuthPlus($store_id, $order_user_id, $current_node['user_id'], $max_layer);
  241. if ($parent_second_col_auth) {
  242. // 判断第2区第一个有无占位
  243. $real_area_key = 1;
  244. $temp_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $current_node['user_id'], 'area_key' => $real_area_key]);
  245. if (!$temp_node) {
  246. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},可开第2区,落第2区"], "app_debug.log");
  247. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $current_node['user_id']]);
  248. $current_layer_rank = UserStringCode::find()
  249. ->where(['store_id' => $store_id, 'layer_node' => $real_parent_node['layer_node'] + 1])
  250. ->max('layer_rank');
  251. $real_layer_rank = $current_layer_rank + 1;
  252. return [$real_parent_node, $real_layer_rank, $real_area_key];
  253. }
  254. }
  255. // 判断用户能否开第3区
  256. $parent_multi_col_auth = self::checkMultiColumnAuth($store_id, $current_node['user_id']);
  257. if ($parent_multi_col_auth) {
  258. // 判断第3区第一个有无占位
  259. $real_area_key = 2;
  260. $temp_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $current_node['user_id'], 'area_key' => $real_area_key]);
  261. if (!$temp_node) {
  262. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},可开第3区,落第3区"], "app_debug.log");
  263. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $current_node['user_id']]);
  264. $current_layer_rank = UserStringCode::find()
  265. ->where(['store_id' => $store_id, 'layer_node' => $real_parent_node['layer_node'] + 1])
  266. ->max('layer_rank');
  267. $real_layer_rank = $current_layer_rank + 1;
  268. return [$real_parent_node, $real_layer_rank, $real_area_key];
  269. }
  270. }
  271. // 查询当前节点的子节点(假设最多有3个子节点情况,根据实际parent_id关联查询)
  272. $children_nodes = UserStringCode::find()->where(['store_id' => $store_id, 'parent_node' => $current_node['user_id']])->orderBy("area_key ASC")->all();
  273. if ($children_nodes) {
  274. foreach ($children_nodes as $node) {
  275. $queue[] = $node;
  276. }
  277. }
  278. }
  279. }
  280. public static function getRealParentNodePluss($store_id, $user_id, $max_layer)
  281. {
  282. // 针对父节点对应的区
  283. $real_area_key = 0;
  284. // 针对根节点对应的排序
  285. $real_layer_rank = 0;
  286. $parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $user_id]);
  287. $children_list = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $user_id]);
  288. // 未有子节点
  289. if (!$children_list) {
  290. //debug_log([__METHOD__, __LINE__, "未有子节点"], "app_debug.log");
  291. $real_parent_node = $parent_node;
  292. return [$real_parent_node, $real_area_key, $real_layer_rank];
  293. }
  294. // // 先判断 最大层级下 第一列能否能占位
  295. // $temp_parent_id = $user_id;
  296. // //debug_log([__METHOD__, __LINE__, "最大层数:{$max_layer}"], "app_debug.log");
  297. // for ($i = 0; $i < $max_layer; $i++) {
  298. // //debug_log([__METHOD__, __LINE__, "第1区循环:{$i},用户ID:{$temp_parent_id}"], "app_debug.log");
  299. // $temp_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $temp_parent_id, 'area_key' => $real_area_key]);
  300. // if (!$temp_node) {
  301. // $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $temp_parent_id]);
  302. // return [$real_parent_node, $real_layer_rank, $real_area_key];
  303. // } else {
  304. // $temp_parent_id = $temp_node['user_id'];
  305. // }
  306. // }
  307. // 广度优先遍历
  308. $queue = [];
  309. $queue[] = $parent_node;
  310. while (!empty($queue)) {
  311. // 当前节点
  312. $current_node = array_shift($queue);
  313. // 当前层数
  314. $current_layer = $current_node['layer_node'] - $parent_node['layer_node'];
  315. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},当前层数:{$current_layer}"], "app_debug.log");
  316. // 1 区按深度优先达到深度,再按广度遍历
  317. $parent_first_col_auth = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $current_node['user_id'], 'area_key' => 0]);
  318. if (!$parent_first_col_auth) {
  319. $real_area_key = 0;
  320. $temp_parent_id = $current_node['user_id'];
  321. //debug_log([__METHOD__, __LINE__, "最大层数:{$max_layer},当前层数:{$current_layer}"], "app_debug.log");
  322. for ($i = 0; $i < $max_layer - $current_layer; $i++) {
  323. //debug_log([__METHOD__, __LINE__, "第1区循环:{$i},用户ID:{$temp_parent_id}"], "app_debug.log");
  324. $temp_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $temp_parent_id, 'area_key' => $real_area_key]);
  325. if (!$temp_node) {
  326. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},深度遍历1区没人,落第1区"], "app_debug.log");
  327. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $temp_parent_id]);
  328. $current_layer_rank = UserStringCode::find()
  329. ->where(['store_id' => $store_id, 'layer_node' => $real_parent_node['layer_node'] + 1])
  330. ->max('layer_rank');
  331. $real_layer_rank = $current_layer_rank + 1;
  332. return [$real_parent_node, $real_layer_rank, $real_area_key];
  333. }
  334. $temp_parent_id = $temp_node['user_id'];
  335. }
  336. unset($temp_parent_id);
  337. }
  338. // 判断用户第1区有无落点
  339. // $parent_first_col_auth = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $current_node['user_id'], 'area_key' => 0]);
  340. // if (!$parent_first_col_auth) {
  341. // $real_area_key = 0;
  342. // //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},1区没人,落第1区"], "app_debug.log");
  343. // $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $current_node['user_id']]);
  344. // $current_layer_rank = UserStringCode::find()
  345. // ->where(['store_id' => $store_id, 'layer_node' => $real_parent_node['layer_node'] + 1])
  346. // ->max('layer_rank');
  347. // $real_layer_rank = $current_layer_rank + 1;
  348. // return [$real_parent_node, $real_layer_rank, $real_area_key];
  349. // }
  350. // 判断用户能否开第2区
  351. $parent_second_col_auth = self::checkSecondColumnAuthPlus($store_id, $current_node['user_id'], $max_layer);
  352. if ($parent_second_col_auth) {
  353. // 判断第2区第一个有无占位
  354. $real_area_key = 1;
  355. $temp_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $current_node['user_id'], 'area_key' => $real_area_key]);
  356. if (!$temp_node) {
  357. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},可开第2区,落第2区"], "app_debug.log");
  358. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $current_node['user_id']]);
  359. $current_layer_rank = UserStringCode::find()
  360. ->where(['store_id' => $store_id, 'layer_node' => $real_parent_node['layer_node'] + 1])
  361. ->max('layer_rank');
  362. $real_layer_rank = $current_layer_rank + 1;
  363. return [$real_parent_node, $real_layer_rank, $real_area_key];
  364. }
  365. }
  366. // 判断用户能否开第3区
  367. $parent_multi_col_auth = self::checkMultiColumnAuth($store_id, $current_node['user_id']);
  368. if ($parent_multi_col_auth) {
  369. // 判断第3区第一个有无占位
  370. $real_area_key = 2;
  371. $temp_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $current_node['user_id'], 'area_key' => $real_area_key]);
  372. if (!$temp_node) {
  373. //debug_log([__METHOD__, __LINE__, "当前节点ID:{$current_node['user_id']},可开第3区,落第3区"], "app_debug.log");
  374. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $current_node['user_id']]);
  375. $current_layer_rank = UserStringCode::find()
  376. ->where(['store_id' => $store_id, 'layer_node' => $real_parent_node['layer_node'] + 1])
  377. ->max('layer_rank');
  378. $real_layer_rank = $current_layer_rank + 1;
  379. return [$real_parent_node, $real_layer_rank, $real_area_key];
  380. }
  381. }
  382. // 查询当前节点的子节点(假设最多有3个子节点情况,根据实际parent_id关联查询)
  383. $children_nodes = UserStringCode::find()->where(['store_id' => $store_id, 'parent_node' => $current_node['user_id']])->orderBy("area_key ASC")->all();
  384. if ($children_nodes) {
  385. foreach ($children_nodes as $node) {
  386. $queue[] = $node;
  387. }
  388. }
  389. }
  390. }
  391. /**
  392. * 广度优先遍历
  393. * @param $store_id
  394. * @param $user_id
  395. * @return void
  396. */
  397. public static function breadthFirstTraversal($store_id, $user_id)
  398. {
  399. $queue = [];
  400. $root_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $user_id]);
  401. $queue[] = $root_node;
  402. while (!empty($queue)) {
  403. $current_node = array_shift($queue);
  404. // 在这里可以对当前节点进行操作,比如输出节点的一些关键信息,以下仅输出id作为示例
  405. echo "当前节点ID: " . $current_node['user_id'] . "\n";
  406. // 查询当前节点的子节点(假设最多有3个子节点情况,根据实际parent_id关联查询)
  407. $children_nodes = UserStringCode::find()->where(['store_id' => $store_id, 'parent_node' => $current_node['user_id']])->all();
  408. if ($children_nodes) {
  409. foreach ($children_nodes as $node) {
  410. $queue[] = $node;
  411. }
  412. }
  413. }
  414. }
  415. /**
  416. * 广度优先遍历并输出树形结构的函数
  417. * @param $store_id
  418. * @param $user_id
  419. * @return void
  420. */
  421. public static function breadthFirstTraversalAndPrintTree($store_id, $user_id)
  422. {
  423. $queue = [];
  424. $root_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $user_id]);
  425. $queue[] = $root_node;
  426. $levels = [];
  427. while (!empty($queue)) {
  428. $current_node = array_shift($queue);
  429. $level = $current_node['layer_node'];
  430. $pos = $current_node['layer_rank'];
  431. if (!isset($levels[$level])) {
  432. $levels[$level] = [];
  433. }
  434. $levels[$level][$pos] = $current_node['user_id'];
  435. $children_nodes = UserStringCode::find()->where(['store_id' => $store_id, 'parent_node' => $current_node['user_id']])->orderBy("area_key ASC")->all();
  436. if ($children_nodes) {
  437. foreach ($children_nodes as $node) {
  438. $queue[] = $node;
  439. }
  440. }
  441. }
  442. $maxLevelWidth = max(array_map('count', $levels));
  443. foreach ($levels as $level => $nodes) {
  444. $indent = str_repeat(' ', $level);
  445. $line = $indent;
  446. foreach ($nodes as $pos => $nodeVal) {
  447. $line.= $nodeVal;
  448. if ($pos < count($nodes) - 1) {
  449. $line.= ' ';
  450. }
  451. }
  452. $line.= str_repeat(' ', $maxLevelWidth - count($nodes));
  453. echo $line. "\n";
  454. }
  455. }
  456. /**
  457. * 创建 root 节点
  458. * @param $store_id
  459. * @param $user_id
  460. * @return bool
  461. */
  462. public static function createRootNode($store_id, $user_id)
  463. {
  464. $mode = self::findOne(['user_id' => $user_id]);
  465. if ($mode) {
  466. //debug_log([__METHOD__, __LINE__, "root 节点已存在"], "app_debug.log");
  467. return false;
  468. }
  469. $mode = new self();
  470. $mode->store_id = $store_id;
  471. $mode->user_id = $user_id;
  472. $mode->recommend_relation_node = "";
  473. $mode->recommend_relation = "";
  474. return $mode->save();
  475. }
  476. /**
  477. * 获取用户最后一个节点
  478. * 从左至右,从上至下
  479. * @param $store_id 店铺ID
  480. * @param $user_id 用户ID
  481. * @param $max_layer 限制最大层数
  482. * @return array
  483. */
  484. public static function getRealParentNode($store_id, $user_id, $max_layer)
  485. {
  486. $real_row = 0;
  487. $real_col = 0;
  488. $real_area_key = 0;
  489. $parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $user_id]);
  490. $children_list = UserStringCodeArea::findOne(['store_id' => $store_id, 'user_id' => $user_id]);
  491. // 未有子节点
  492. if (!$children_list) {
  493. $real_row++;
  494. $real_parent_node = $parent_node;
  495. return [$real_parent_node, $real_row, $real_col, $real_area_key];
  496. }
  497. // 先判断 最大层级下 第一列能否能占位
  498. for ($row = $real_row; $row <= $max_layer; $row++) {
  499. if ($row == 0) continue; // 0行0列是root
  500. $temp = UserStringCodeArea::findOne(['store_id' => $store_id, 'user_id' => $user_id, 'row' => $row, 'col' => $real_col, 'area_key' => $real_area_key]);
  501. if (!$temp) {
  502. $last_area = UserStringCodeArea::findOne(['store_id' => $store_id, 'user_id' => $user_id, 'row' => $row - 1, 'col' => $real_col, 'area_key' => $real_area_key]);
  503. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $last_area['child_id']]);
  504. $real_row = $row;
  505. return [$real_parent_node, $real_row, $real_col, $real_area_key];
  506. }
  507. }
  508. // 用户现有最大列数
  509. $current_max_cols = UserStringCodeArea::find()->where(['store_id' => $store_id, 'user_id' => $user_id])->max('col');
  510. // 循环判断第一列 用户满足 条件(有一位直推才可以开放)第二列
  511. // 再判断推荐用户下第1行能否点位
  512. // 先深度,后横向
  513. $rows = $max_layer;
  514. $cols = $current_max_cols + 1;
  515. for ($r = 0; $r <= $rows; $r++) {
  516. if ($r = 0) {
  517. // root 节点
  518. $temp_user_id = $user_id;
  519. $root_second_col_auth = self::checkSecondColumnAuth($store_id, $temp_user_id, $max_layer); // 判断用户能否开第2列
  520. if ($root_second_col_auth) {
  521. // 判断第2列第一个有无占位
  522. $root_second_node = UserStringCodeArea::findOne([
  523. 'store_id' => $store_id, 'user_id' => $user_id, 'row' => 1, 'col' => 1, 'area_key' => 1
  524. ]);
  525. if (!$root_second_node) {
  526. $real_row = 1;
  527. $real_col = 1;
  528. $real_parent_node = $parent_node;
  529. return [$real_parent_node, $real_row, $real_col, $real_area_key];
  530. }
  531. }
  532. // $root_multi_col_auth = self::checkMultiColumnAuth($store_id, $temp_user_id); // 判断用户能否开多列
  533. } else {
  534. // 子节点
  535. for ($c = 0; $c <= $cols; $c++) {
  536. $temp_area = UserStringCodeArea::findOne(['store_id' => $store_id, 'user_id' => $user_id, 'row' => $r, 'col' => $c]);
  537. if ($temp_area) {
  538. $real_parent_node = UserStringCode::findOne(['store_id' => $store_id, 'user_id' => $temp_area['child_id']]);
  539. $real_row = $r;
  540. $real_col = $c;
  541. $real_parent_node = $parent_node;
  542. return [$real_parent_node, $real_row, $real_col, $real_area_key];
  543. }
  544. }
  545. }
  546. }
  547. }
  548. /**
  549. * 判断用户是否能开第2列
  550. * @param $store_id
  551. * @param $user_id
  552. * @param $max_layer
  553. * @return bool
  554. */
  555. public static function checkSecondColumnAuth($store_id, $user_id, $max_layer)
  556. {
  557. $first_col_user_list = self::find()
  558. ->where(['store_id' => $store_id, 'user_id' => $user_id, 'col' => 1])
  559. ->andWhere(['<=', 'row', $max_layer])
  560. ->asArray()
  561. ->column('child_id');
  562. if (!$first_col_user_list) return false;
  563. $invite_user_ids = User::find()
  564. ->where(['store_id' => $store_id, 'parent_id' => $user_id])
  565. ->andWhere(['in', 'id', $first_col_user_list])
  566. ->asArray()
  567. ->column('id');
  568. if ($invite_user_ids) {
  569. return true;
  570. } else {
  571. return false;
  572. }
  573. }
  574. /**
  575. * 判断用户是否能开第2列
  576. * @param $store_id
  577. * @param $user_id
  578. * @param $max_layer
  579. * @return bool
  580. */
  581. public static function checkSecondColumnAuthPlus($store_id, $order_user_id, $user_id, $max_layer)
  582. {
  583. // $first_col_user_ids = [];
  584. // $temp_parent_id = $user_id;
  585. // for ($row = 0; $row < $max_layer; $row++) {
  586. // $temp_node = self::findOne(['store_id' => $store_id, 'parent_node' => $temp_parent_id, 'area_key' => 0]);
  587. // if ($temp_node) {
  588. // $temp_parent_id = $temp_node['user_id'];
  589. // $first_col_user_ids[] = $temp_node['user_id'];
  590. // }
  591. // }
  592. // if (!$first_col_user_ids) return false;
  593. // $invite_user_ids = User::find()
  594. // ->where(['store_id' => $store_id, 'parent_id' => $user_id])
  595. // ->andWhere(['in', 'id', $first_col_user_ids])
  596. // ->select('id')
  597. // ->column();
  598. $invite_user = User::find()
  599. ->where(['store_id' => $store_id, 'parent_id' => $user_id])
  600. ->andWhere(['>=', 'total_coin', 50])
  601. ->andWhere(['!=', 'id', $order_user_id])
  602. ->one();
  603. if ($invite_user) {
  604. return true;
  605. } else {
  606. return false;
  607. }
  608. }
  609. /**
  610. * 判断用户是否能开多列
  611. * 用户股东等级大于等于V7
  612. * @param $store_id
  613. * @param $user_id
  614. * @return bool
  615. */
  616. public static function checkMultiColumnAuth($store_id, $user_id)
  617. {
  618. $user_holder_level = ShareHolder::find()->alias('sh')
  619. ->where(['sh.store_id' => $store_id, 'sh.user_id' => $user_id, 'sh.is_delete' => 0])
  620. ->andWhere(['sh.store_id' => $store_id, 'shl.is_delete' => 0])
  621. ->leftJoin(['shl' => ShareHolderLevel::tableName()], 'sh.level_id=shl.id')
  622. ->select('sh.user_id, shl.level')
  623. ->asArray()
  624. ->one();
  625. if ($user_holder_level && $user_holder_level['level'] >= 9) {
  626. return true;
  627. } else {
  628. return false;
  629. }
  630. }
  631. /**
  632. * 获取用户最后一个节点
  633. * 从左至右,从上至下
  634. * @param $user_id 用户ID
  635. * @param $layer 查找层数
  636. * @param $max_layer 最大层数
  637. * @return UserStringCode
  638. */
  639. public static function getNodePositionByParentId($user_id, $layer, $max_layer)
  640. {
  641. $last_node = self::findOne(['user_id' => $user_id]);
  642. $children_list = self::find()->where(['parent_id' => $user_id, 'parent_node' => $user_id])
  643. ->andWhere(['>=', 'area_key', 0])
  644. ->orderBy('area_key asc')
  645. ->asArray()
  646. ->all();
  647. if ($children_list) {
  648. $layer++;
  649. if ($layer <= $max_layer) {
  650. // 从左至右,从上至下的区域开始找
  651. for ($area_key = 0; $area_key < $children_list[count($children_list) - 1]['area_key']; $area_key++) {
  652. $temp = self::getLastNodeByUserId($children_list[$area_key]['user_id'], $layer, $max_layer);
  653. if ($temp) {
  654. $last_node = $temp;
  655. }
  656. }
  657. } else {
  658. var_dump("layer > max_layer:大于逻辑");
  659. }
  660. }
  661. return $last_node;
  662. }
  663. /**
  664. * 指定用户上级所有节点团队人数 + 1
  665. * @param $user_id
  666. * @return int
  667. */
  668. public static function setTeamCountInc($user_id)
  669. {
  670. $parent_node_ids = self::getAllParentNodeIds($user_id);
  671. if ($parent_node_ids) {
  672. return self::updateAll(
  673. ['team_num' => new Expression('team_num + 1')],
  674. ['in', 'user_id', $parent_node_ids]
  675. );
  676. } else {
  677. return 0;
  678. }
  679. }
  680. /**
  681. * 获取节点用户最大可开通区域
  682. * @param $user_id
  683. * @return int
  684. */
  685. public static function getMaxAreaKeyByUserId($user_id)
  686. {
  687. $user_node = self::findOne(['user_id' => $user_id]);
  688. return $user_node['area_key'];
  689. }
  690. public static function getAllParentIds($user_id)
  691. {
  692. $relation = self::findOne(['user_id' => $user_id]);
  693. $recommon_relation = trim($relation['recommend_relation'], ',');
  694. if (!$recommon_relation) return [];
  695. return explode(',', $recommon_relation);
  696. }
  697. public static function getAllParentNodeIds($user_id)
  698. {
  699. $relation = self::findOne(['user_id' => $user_id]);
  700. $recommon_relation_node = trim($relation['recommend_relation_node'], ',');
  701. if (!$recommon_relation_node) return [];
  702. return explode(',', $recommon_relation_node);
  703. }
  704. public static function getChildNodeIdsByUserId($user_id, $node_key = '', $level = 0, $scope = 'all')
  705. {
  706. $user_node = self::find()->select(['layer_node'])->where(['user_id' => $user_id])->asArray()->one();
  707. $layer_node = (int)$user_node['layer_node'];
  708. $query = self::find()->select(['user_id'])->where(['like', 'recommend_relation_node', (',' . $user_id . ',')]);
  709. if ($node_key) $query->andWhere(['node_key' => $node_key]);
  710. //查询多N代全部子用户
  711. if ($level > 0 && $scope == 'all') $query->andWhere(['>=', 'layer_node', $level + $layer_node]);
  712. //查询指定代子用户
  713. if ($level > 0 && $scope == 'equal') $query->andWhere(['=', 'layer_node', $level + $layer_node]);
  714. return $query->asArray()->column();
  715. }
  716. /**
  717. * 查询用户指定部门的所有用户 ids
  718. * @param $user_id
  719. * @param $node_key
  720. * @return array
  721. */
  722. public static function getChildNodeIdsByAreaKey($store_id, $user_id, $area_key)
  723. {
  724. $child_ids = [];
  725. $queue = [];
  726. $root_node = UserStringCode::findOne(['store_id' => $store_id, 'parent_node' => $user_id, 'area_key' => $area_key]);
  727. $queue[] = $root_node;
  728. while (!empty($queue)) {
  729. $current_node = array_shift($queue);
  730. $children_nodes = UserStringCode::find()->where(['store_id' => $store_id, 'parent_node' => $current_node['user_id']])->all();
  731. if ($children_nodes) {
  732. foreach ($children_nodes as $node) {
  733. $queue[] = $node;
  734. }
  735. }
  736. $child_ids[] = $current_node['user_id'];
  737. }
  738. return $child_ids;
  739. }
  740. public static function getAllParentList($user_id)
  741. {
  742. $relation = self::findOne(['user_id' => $user_id]);
  743. $recommon_relation = trim($relation['recommend_relation'], ',');
  744. $relation_arr = explode(',', $recommon_relation);
  745. if (empty($relation_arr)) {
  746. return [];
  747. }
  748. $list = Share::find()->alias('s')->where(array('in', 's.user_id', $relation_arr))
  749. ->andWhere(['sl.is_delete' => 0])
  750. ->leftJoin(['sl' => ShareLevel::tableName()], 'sl.level=s.level')
  751. ->select('s.user_id, sl.order_layer, sl.point_reward')
  752. ->asArray()
  753. ->all();
  754. if (empty($list)) {
  755. return [];
  756. }
  757. $level_list = ArrayHelper::index($list, "user_id");
  758. $res = [];
  759. foreach ($relation_arr as $v) {
  760. $temp = [];
  761. $temp['user_id'] = $v;
  762. $temp['order_layer'] = $level_list[$v]['order_layer'] ?? 0;
  763. $temp['point_reward'] = $level_list[$v]['point_reward'] ?? 0;
  764. $temp['is_bonus'] = isset($level_list[$v]);
  765. $res[] = $temp;
  766. }
  767. return $res;
  768. }
  769. public static function getAllParentNodeList($user_id)
  770. {
  771. $relation = self::findOne(['user_id' => $user_id]);
  772. $recommon_relation_node = trim($relation['recommend_relation_node'], ',');
  773. $relation_arr = explode(',', $recommon_relation_node);
  774. if (empty($relation_arr)) {
  775. return [];
  776. }
  777. $list = Share::find()->alias('s')->where(array('in', 's.user_id', $relation_arr))
  778. ->andWhere(['sl.is_delete' => 0])
  779. ->leftJoin(['sl' => ShareLevel::tableName()], 'sl.level=s.level')
  780. ->select('s.user_id, sl.order_layer, sl.point_reward')
  781. ->asArray()
  782. ->all();
  783. if (empty($list)) {
  784. return [];
  785. }
  786. $level_list = ArrayHelper::index($list, "user_id");
  787. $res = [];
  788. foreach ($relation_arr as $v) {
  789. $temp = [];
  790. $temp['user_id'] = $v;
  791. $temp['order_layer'] = $level_list[$v]['order_layer'] ?? 0;
  792. $temp['point_reward'] = $level_list[$v]['point_reward'] ?? 0;
  793. $temp['is_bonus'] = isset($level_list[$v]);
  794. $res[] = $temp;
  795. }
  796. return $res;
  797. }
  798. public static function getRecommendRelationByUserId($user_id)
  799. {
  800. $res = "";
  801. $parentList = [];
  802. self::getParentList($user_id, $parentList);
  803. if ($parentList) {
  804. $res = "," . implode(',', array_reverse($parentList)) . ",";
  805. }
  806. return $res;
  807. }
  808. public static function getParentList($user_id, &$parentList)
  809. {
  810. $user = User::findOne(['id' => $user_id]);
  811. if ($user['parent_id']) {
  812. $parentList[] = $user['parent_id'];
  813. $parent = User::findOne(['id' => $user['parent_id']]);
  814. if ($parent) {
  815. return self::getParentList($parent['id'], $parentList);
  816. }
  817. }
  818. return $parentList;
  819. }
  820. public static function getLayerByUserId($user_id)
  821. {
  822. $res = 1;
  823. $parentList = [];
  824. self::getParentList($user_id, $parentList);
  825. if ($parentList) {
  826. $res += count($parentList);
  827. }
  828. return $res;
  829. }
  830. public static function createUserNode($store_id, $user_id)
  831. {
  832. $user_node = self::findOne(['user_id' => $user_id]);
  833. if (!$user_node) {
  834. $user = User::findOne(['id' => $user_id]);
  835. $user_node = new self();
  836. $user_node->store_id = $store_id;
  837. $user_node->user_id = $user_id;
  838. $user_node->parent_id = $user['parent_id'];
  839. $user_node->recommend_relation = self::getRecommendRelationByUserId($user_id);
  840. $user_node->layer = self::getLayerByUserId($user_id);
  841. $user_node->recommend_relation_node = "";
  842. $user_node->layer_node = 0;
  843. $user_node->created_at = time();
  844. if (!$user_node->save()) {
  845. //debug_log([__METHOD__, __LINE__, "用户:{$user_id},节点信息保存失败"], "app_debug.log");
  846. }
  847. }
  848. return $user_node;
  849. }
  850. public static function getChildNodeCount($user_id)
  851. {
  852. $node_count = self::find()->select('team_num')->where(['user_id' => $user_id])->scalar();
  853. return (int)$node_count;
  854. }
  855. /**
  856. * 获取用户节点团队业绩
  857. * @param $user_id
  858. * @return false|int|string|null
  859. */
  860. public static function getTeamNodeOrderPrice($user_id)
  861. {
  862. $child_ids = self::getChildNodeIdsByUserId($user_id);
  863. $team_ids = array_merge($child_ids, [$user_id]);
  864. return User::find()->select('SUM(`total_coin`) as node_order_price')->where(['in', 'id', $team_ids])->scalar();
  865. }
  866. /**
  867. * 获取用户部门列表
  868. * @param $user_id
  869. * @return array
  870. */
  871. public static function getUserDepartmentList($store_id, $user_id)
  872. {
  873. $user = User::findOne(['id' => $user_id]);
  874. $user_share_holder_level = ShareHolder::findOne(['user_id' => $user_id]);
  875. if ($user['total_coin'] < 50) {
  876. // 未消费大礼包只有A部门
  877. $area_key_highest = 0;
  878. } else {
  879. // 有消费则有A,B等多部门
  880. if ($user_share_holder_level['level_id'] < 9) {
  881. // 股东等级小于V7只有A,B两条线
  882. $area_key_highest = 1;
  883. } else {
  884. // 大于等于V7可开多线
  885. $area_key_highest = self::getLastNodeKeyByUserId($user_id) + 1;
  886. }
  887. }
  888. $res = [];
  889. for ($area_key = 0; $area_key <= $area_key_highest; $area_key++) {
  890. $last_user_id = self::getLastUserByDepartmentKey($store_id, $user_id, $area_key);
  891. $last_user = User::findOne(['id' => $last_user_id]);
  892. $department_name = $area_key + 1;
  893. $temp = [];
  894. $temp['id'] = $area_key;
  895. $temp['department_key'] = $area_key;
  896. $temp['user_id'] = $last_user_id;
  897. $temp['department_name'] = "{$department_name}-部门";
  898. $temp['nickname'] = $last_user['nickname'];
  899. $temp['mobile'] = $last_user['binding'];
  900. $res[] = $temp;
  901. }
  902. return $res;
  903. }
  904. /**
  905. * 获取指定用户最高部门 node_key
  906. * @param $user_id
  907. * @return int|mixed
  908. */
  909. public static function getLastNodeKeyByUserId($user_id)
  910. {
  911. $last_node_key = 0;
  912. $node_list = self::find()
  913. ->where(['parent_id' => $user_id])
  914. ->orderBy('node_key DESC')
  915. ->asArray()
  916. ->one();
  917. if ($node_list) {
  918. $last_node_key = $node_list['node_key'];
  919. }
  920. return $last_node_key;
  921. }
  922. /**
  923. * 获取指定用户的部门最后一个用户
  924. * @param $user_id
  925. * @param $department_name
  926. * @return int
  927. */
  928. public static function getLastUserByDepartmentKey($store_id, $user_id, $area_key)
  929. {
  930. $child_ids = self::getChildNodeIdsByAreaKey($store_id, $user_id, $area_key);
  931. return $child_ids[count($child_ids) - 1];
  932. }
  933. public static function getLastParentNodeLeader($user_id)
  934. {
  935. $user_node = self::findOne(['user_id' => $user_id]);
  936. if ($user_node->parent_id == $user_node->parent_node) {
  937. return self::findOne(['user_id' => $user_node->parent_id]);
  938. } else {
  939. return self::getLastParentNodeLeader($user_node->parent_node);
  940. }
  941. }
  942. /**
  943. * 获取用户区域列表
  944. * @param $user_id
  945. * @return void
  946. */
  947. public static function getAreaList($user_id)
  948. {
  949. }
  950. }