User.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. <?php
  2. /**
  3. * 厦门云联储网络科技有限公司
  4. * https://www.baokuaiyun.com
  5. * Copyright (c) 2023 爆块云 All rights reserved.
  6. */
  7. namespace app\models;
  8. use yii\db\ActiveRecord;
  9. use yii\db\ActiveQuery;
  10. use yii\web\IdentityInterface;
  11. use yii\behaviors\TimestampBehavior;
  12. use app\librarys\shareTree\behaviors\ClosureTableBehavior;
  13. use app\models\userTreeQuery\UserQuery;
  14. use app\librarys\shareTree\behaviors\OldClosureTableBehavior;
  15. use app\models\userTreeQuery\OldUserQuery;
  16. use app\modules\admin\models\pospal\PospalForm;
  17. use app\models\ShareHolderLevel;
  18. use app\models\SaasUser;
  19. use app\models\ShareHolder;
  20. use app\utils\Share\BonusPool;
  21. /**
  22. * Class User
  23. * @package app\modules\common\models
  24. *
  25. * @property integer $id
  26. * @property integer $type
  27. * @property string $username
  28. * @property string $password
  29. * @property string $auth_key
  30. * @property string $access_token
  31. * @property integer $is_delete
  32. * @property string $wechat_open_id
  33. * @property string $wechat_union_id
  34. * @property string $nickname
  35. * @property string $avatar_url
  36. * @property integer $store_id
  37. * @property integer $mch_id
  38. * @property integer $is_distributor
  39. * @property integer $parent_id
  40. * @property integer $time
  41. * @property string $total_price
  42. * @property string $price
  43. * @property integer $is_clerk
  44. * @property integer $we7_uid
  45. * @property integer $shop_id
  46. * @property integer $level
  47. * @property string $integral
  48. * @property string $total_integral
  49. * @property string $coin
  50. * @property string $total_coin
  51. * @property string $money
  52. * @property string $contact_way
  53. * @property string $comments
  54. * @property string $binding
  55. * @property string $wechat_platform_open_id
  56. * @property integer $platform
  57. * @property integer $blacklist
  58. * @property integer $is_admin
  59. * @property integer $parent_user_id
  60. * @property string $appcid
  61. * @property integer $is_delivery
  62. * @property integer $delivery_office
  63. * @property string $alipay_open_id
  64. * @property integer $is_saas_clerk
  65. * @property string $created_at
  66. * @property string $updated_at
  67. * @property string $bytedance_open_id
  68. * @property integer $is_holder
  69. * @property string $wechat_app_open_id
  70. * @property string $user_remark
  71. * @property integer $old_parent_id
  72. * @property integer $adapay_user_huifu_id
  73. * @property string $remark_name
  74. * @property integer $is_student
  75. * @property string $ali_openId
  76. * @property string $user_level_expires_ext
  77. * @property string $user_level_expires
  78. * @property string $purchase_money
  79. * @property integer $is_cloud_inventory
  80. * @property integer $cloud_inventory_level
  81. */
  82. class User extends ActiveRecord implements IdentityInterface
  83. {
  84. //从银豹同步时设置为1
  85. public $yinbaoSync = 0;
  86. /**
  87. * 用户类型
  88. */
  89. CONST USER_TYPE_ADMIN = 0;
  90. CONST USER_TYPE_NORMAL = 1;
  91. public static $valid_user_type = [
  92. self::USER_TYPE_ADMIN,
  93. self::USER_TYPE_NORMAL
  94. ];
  95. /**
  96. * 是否分销商
  97. */
  98. CONST IS_DISTRIBUTOR = 1;
  99. CONST NOT_DISTRIBUTOR = 0;
  100. CONST APPLY_DISTRIBUTOR = 2;
  101. public static $valid_distributor_type = [
  102. self::IS_DISTRIBUTOR,
  103. self::NOT_DISTRIBUTOR,
  104. self::APPLY_DISTRIBUTOR
  105. ];
  106. /**
  107. * 是否核销员
  108. */
  109. CONST IS_CLERK = 1;
  110. CONST NOT_CLERK = 0;
  111. public static $valid_clerk_type = [
  112. self::IS_CLERK,
  113. self::NOT_CLERK
  114. ];
  115. /**
  116. * 用户来源平台:微信小程序
  117. */
  118. CONST USER_FROM_WECHAT = 0;
  119. /**
  120. * 用户来源平台:支付宝
  121. */
  122. CONST USER_FROM_ALIPAY = 1;
  123. /**
  124. * 用户来源平台:公众号
  125. */
  126. CONST USER_FROM_OFFICIAL_ACCOUNTS = 2;
  127. /**
  128. * 用户来源平台:app微信
  129. */
  130. CONST USER_FROM_APP = 3;
  131. /**
  132. * 用户来源平台:手机号注册
  133. */
  134. CONST USER_FROM_PHONE = 4;
  135. /**
  136. * 用户来源平台: 抖音
  137. */
  138. CONST USER_FROM_BYTEDANCE = 5;
  139. /**
  140. * 用户来源平台: 收银台添加
  141. */
  142. CONST USER_FROM_CASHIER = 6;
  143. /**
  144. * 用户来源平台: 后台添加
  145. */
  146. CONST USER_FROM_OPERATOR_BACK = 8;
  147. public static $valid_user_platform_type = [
  148. self::USER_FROM_WECHAT,
  149. self::USER_FROM_ALIPAY,
  150. self::USER_FROM_OFFICIAL_ACCOUNTS,
  151. self::USER_FROM_APP,
  152. self::USER_FROM_PHONE,
  153. ];
  154. /**
  155. * 是否在黑名单
  156. */
  157. CONST USER_IN_BLACKLIST = 1;
  158. CONST USER_NOT_IN_BLACKLIST = 0;
  159. public static $valid_user_blacklist_type = [
  160. self::USER_IN_BLACKLIST,
  161. self::USER_NOT_IN_BLACKLIST
  162. ];
  163. /**
  164. * 是否删除
  165. */
  166. CONST USER_IS_DELETE = 1;
  167. CONST USER_NOT_DELETE = 0;
  168. public static $valid_user_delete_type = [
  169. self::USER_IS_DELETE,
  170. self::USER_NOT_DELETE
  171. ];
  172. /**
  173. * 是否配送员
  174. */
  175. CONST IS_DELIVERY = 1;
  176. CONST NOT_DELIVERY = 0;
  177. public static $valid_delivery_type = [
  178. self::IS_DELIVERY,
  179. self::NOT_DELIVERY
  180. ];
  181. /**
  182. * 骑手是否在线
  183. */
  184. CONST DELIVERY_ONLINE = 1;
  185. CONST DELIVERY_OFFLINE = 0;
  186. public static $valid_delivery_status_type = [
  187. self::DELIVERY_ONLINE,
  188. self::DELIVERY_OFFLINE
  189. ];
  190. /**
  191. * 用户充值类型 1:积分 2:余额
  192. */
  193. CONST RECHARGE_INTEGRAL = 1;
  194. CONST RECHARGE_MONEY = 2;
  195. public static $valid_recharge_type = [
  196. self::RECHARGE_INTEGRAL,
  197. self::RECHARGE_MONEY
  198. ];
  199. /**
  200. * 用户充值增加或减少 1:增加 2:减少
  201. */
  202. CONST RECHARGE_ADD = 1;
  203. CONST RECHARGE_REDUCE = 2;
  204. public static $valid_recharge_status = [
  205. self::RECHARGE_ADD,
  206. self::RECHARGE_ADD
  207. ];
  208. /**
  209. * @return string
  210. */
  211. public static function tableName()
  212. {
  213. return '{{%user}}';
  214. }
  215. public function behaviors()
  216. {
  217. return [
  218. [
  219. // 自动更新创建和更新时间
  220. 'class' => TimestampBehavior::class
  221. ],
  222. 'treePath' => [
  223. 'class' => ClosureTableBehavior::class,
  224. 'treePathModelClass' => UserTreePath::class,
  225. 'ownerParentIdAttribute' => 'parent_id',
  226. ],
  227. 'oldTreePath' => [
  228. 'class' => OldClosureTableBehavior::class,
  229. 'treePathModelClass' => OldUserTreePath::class,
  230. 'ownerParentIdAttribute' => 'old_parent_id',
  231. ]
  232. ];
  233. }
  234. //会员到期时间格式
  235. //user_level_expires_ext:[['level' => 1, 'expires_in' => 到期时间戳], ['level' => 2, 'expires_in' => 到期时间戳]]
  236. public function rules()
  237. {
  238. return [
  239. [['id', 'platform', 'type', 'is_delete', 'store_id', 'is_distributor', 'parent_id', 'old_parent_id', 'time', 'is_clerk',
  240. 'we7_uid', 'shop_id', 'level', 'is_delivery', 'delivery_office', 'is_saas_clerk','is_admin', 'is_holder'],
  241. 'integer'],
  242. [['total_price', 'price', 'money', 'integral', 'total_integral','free_money','total_free_money', 'user_level_expires','global_money_total','global_money'], 'number'],
  243. [['avatar_url', 'binding', 'alipay_open_id', 'bytedance_open_id', 'user_level_expires_ext'], 'string'],
  244. [['username', 'password', 'auth_key', 'access_token', 'wechat_open_id', 'wechat_union_id', 'nickname',
  245. 'contact_way', 'comments', 'wechat_app_open_id', 'remark_name', 'ali_openId'], 'string', 'max' => 255],
  246. [['access_token', 'binding', 'user_remark'], 'string', 'max' => 60],
  247. [['username', 'access_token'], 'required'],
  248. [['access_token', 'create_at', 'updated_at', 'adapay_user_huifu_id'], 'safe'],
  249. [['money_month', 'money_month_used'], 'safe'],
  250. ];
  251. }
  252. public function attributeLabels()
  253. {
  254. return [
  255. 'id' => 'ID',
  256. 'type' => '用户类型:0=管理员,1=普通用户',
  257. 'username' => 'Username',
  258. 'password' => 'Password',
  259. 'auth_key' => 'Auth Key',
  260. 'access_token' => 'Access Token',
  261. 'is_delete' => 'Is Delete',
  262. 'wechat_open_id' => '微信openid',
  263. 'wechat_union_id' => '微信用户union id',
  264. 'nickname' => '昵称',
  265. 'avatar_url' => '头像url',
  266. 'store_id' => '商城id',
  267. 'mch_id' => '配送员绑定商户id',
  268. 'is_distributor' => '是否是分销商 0--不是 1--是 2--申请中',
  269. 'parent_id' => '父级ID',
  270. 'time' => '成为分销商的时间',
  271. 'total_price' => '累计佣金',
  272. 'price' => '可提现佣金',
  273. 'is_clerk' => '是否是核销员 0--不是 1--是',
  274. 'we7_uid' => '微擎账户id',
  275. 'shop_id' => 'Shop ID',
  276. 'level' => '会员等级',
  277. 'integral' => '用户当前积分',
  278. 'total_integral' => '用户总积分',
  279. 'coin' => '用户当前贡献积分',
  280. 'total_coin' => '用户总贡献积分',
  281. 'money' => '余额',
  282. 'contact_way' => '联系方式',
  283. 'comments' => '备注',
  284. 'binding' => '授权手机号',
  285. 'wechat_platform_open_id' => '微信公众号openid',
  286. 'platform' => '小程序平台 微信:wx,支付宝:my(0、微信小程序,1、微信公众号2、APP端)',
  287. 'blacklist' => '黑名单 0.否 | 1.是',
  288. 'parent_user_id' => '可能成为上级的ID',
  289. 'appcid' => 'app唯一标识',
  290. 'is_admin' => '是否为手机端管理员',
  291. 'is_delivery'=>'是否是配送员 0--不是 1--是',
  292. 'delivery_office'=>'骑手是否在线(0、在线,1离线)',
  293. 'alipay_open_id' => '支付宝user_id',
  294. 'is_saas_clerk' => '是否为saas版核销员',
  295. 'created_at' => '创建时间',
  296. 'updated_at' => '更新时间',
  297. 'wechat_app_open_id' => '微信app端openid',
  298. 'old_parent_id' => '旧父级ID',
  299. 'user_remark' => '用户备注',
  300. 'remark_name' => '备注名',
  301. 'is_student' => '是否通过学生认证:0=否;1=是;',
  302. 'ali_openId' => '支付宝新openid',
  303. 'user_level_expires_ext' => '用户等级到期时间备注',
  304. 'user_level_expires' => '用户等级到期时间'
  305. ];
  306. }
  307. public function beforeSave($insert)
  308. {
  309. // 创建用户自动赋值access_token
  310. if (parent::beforeSave($insert)) {
  311. if ($this->isNewRecord) {
  312. $this->access_token = \Yii::$app->security->generateRandomString();
  313. }
  314. return true;
  315. }
  316. return false;
  317. }
  318. public function afterSave($insert, $changedAttributes)
  319. {
  320. parent::afterSave($insert, $changedAttributes);
  321. if ($insert && ($this->type == 1) && ($this->yinbaoSync == 0)) {
  322. (new PospalForm(['store_id' => $this->store_id]))->afterUserSave($this);
  323. }
  324. if($insert && $this->alipay_open_id){
  325. UserCoupon::updateAll(['user_id' => $this->id], ['alipay_open_id' => $this->alipay_open_id]);
  326. }
  327. $changeIntegral = isset($changedAttributes['integral']) && ($this->integral != $changedAttributes['integral']);
  328. $changeMoney = isset($changedAttributes['money']) && ($this->money != $changedAttributes['money']);
  329. if(!$insert && ($changeIntegral || $changeMoney)){
  330. \Yii::error([__METHOD__, $insert, $changedAttributes]);
  331. (new \app\modules\admin\models\alipay\Card(['store_id' => $this->store_id]))->cardUpdateByOpenid($this->alipay_open_id);
  332. }
  333. if($insert){
  334. (new \app\utils\OrderUtil())->userAutoBecomeShare($this->id);
  335. // 赠送优惠券
  336. \app\utils\AutoSendCoupon::send($this->id, CouponAutoSend::EVENT_NEWUSER, $this->store_id, 0, CouponAutoSend::EXT_REG);
  337. // 自动注册成为股东
  338. $share_level_list = ShareHolderLevel::find()->where([ 'store_id' => $this->store_id, 'is_delete' => 0])->all();
  339. // 查找是否有设置消费金额为0的等级条件
  340. $currentLevel = null;
  341. foreach ($share_level_list as $k => $v) {
  342. $share_level_list_condition = json_decode($v->condition, true);
  343. if (!isset($share_level_list_condition['self'])) {
  344. continue;
  345. }
  346. $shareholder = $share_level_list_condition['self'];
  347. if ($shareholder['is_open'] && $shareholder['value']['price'] === '0') {
  348. $currentLevel = $v;
  349. break;
  350. }
  351. }
  352. if ($currentLevel) {
  353. $saas_user = SaasUser::findOne(['mobile' => $this->binding]);
  354. $shareHolderModel = new ShareHolder();
  355. $shareHolderModel->level_id = $currentLevel->id;
  356. $shareHolderModel->store_id = $this->store_id;
  357. $shareHolderModel->user_id = $this->id;
  358. $shareHolderModel->name = $saas_user->name;
  359. $shareHolderModel->address = '';
  360. $shareHolderModel->mobile = $saas_user->mobile;
  361. $shareHolderModel->province = 0;
  362. $shareHolderModel->city = 0;
  363. $shareHolderModel->district = 0;
  364. $shareHolderModel->province_name = '';
  365. $shareHolderModel->city_name = '';
  366. $shareHolderModel->district_name = '';
  367. $shareHolderModel->status = 1;
  368. $shareHolderModel->audit_time = time();
  369. if ($shareHolderModel->save()) {
  370. // BonusPool::checkChildHolderUpdateLevel($this->id, $currentLevel->id);
  371. BonusPool::ShareHolderLevelJob($shareHolderModel->store_id, 0, $shareHolderModel->user_id);
  372. if ((int)$currentLevel->member_level > (int)$this->level) {
  373. $this->level = $currentLevel->member_level;
  374. }
  375. $this->is_holder = 1;
  376. $this->save();
  377. }
  378. }
  379. }
  380. if ($this->parent_id > 0 && !cache_lock(['ShareLevelJob', $this->store_id, $this->parent_id], 30)) {
  381. \queue_push(new \app\jobs\ShareLevelJob(['store_id' => $this->store_id, 'user_id' => $this->parent_id]), 30, 1);
  382. }
  383. if (isset($changedAttributes['level'])) {
  384. $user = User::findOne($this->id);
  385. $level = Level::findOne(['level' => $this->level, 'store_id' => $this->store_id, 'is_delete' => 0]);
  386. $result = self::handleUserLevelExpires($level, $user);
  387. $user->user_level_expires_ext = json_encode($result['user_level_expires_ext'], JSON_UNESCAPED_UNICODE);
  388. $user->user_level_expires = $result['expires_in'];
  389. $user->save();
  390. }
  391. }
  392. public static function find(): UserQuery
  393. {
  394. return new UserQuery(get_called_class());
  395. }
  396. public static function findOld(): OldUserQuery
  397. {
  398. return new OldUserQuery(get_called_class());
  399. }
  400. /**
  401. * @return ActiveQuery
  402. */
  403. public function getTreePathOwner(): ActiveQuery
  404. {
  405. return $this->hasOne(UserTreePath::class, ['parent_id' => 'id', 'child_id' => 'id']);
  406. }
  407. /**
  408. * @return ActiveQuery
  409. */
  410. public function getOldTreePathOwner(): ActiveQuery
  411. {
  412. return $this->hasOne(OldUserTreePath::class, ['parent_id' => 'id', 'child_id' => 'id']);
  413. }
  414. /**
  415. * @return ActiveQuery
  416. */
  417. public function getTreePathsChild(): ActiveQuery
  418. {
  419. return $this->hasMany(UserTreePath::class, ['child_id' => 'id']);
  420. }
  421. /**
  422. * @return ActiveQuery
  423. */
  424. public function getOldTreePathsChild(): ActiveQuery
  425. {
  426. return $this->hasMany(OldUserTreePath::class, ['child_id' => 'id']);
  427. }
  428. /**
  429. * @return ActiveQuery
  430. */
  431. public function getTreePathsNearestParent(): ActiveQuery
  432. {
  433. return $this->hasMany(UserTreePath::class, ['nearest_parent_id' => 'id']);
  434. }
  435. /**
  436. * @return ActiveQuery
  437. */
  438. public function getOldTreePathsNearestParent(): ActiveQuery
  439. {
  440. return $this->hasMany(OldUserTreePath::class, ['nearest_parent_id' => 'id']);
  441. }
  442. /**
  443. * @return ActiveQuery
  444. */
  445. public function getTreePathsParent(): ActiveQuery
  446. {
  447. return $this->hasMany(UserTreePath::class, ['parent_id' => 'id']);
  448. }
  449. /**
  450. * @return ActiveQuery
  451. */
  452. public function getOldTreePathsParent(): ActiveQuery
  453. {
  454. return $this->hasMany(OldUserTreePath::class, ['parent_id' => 'id']);
  455. }
  456. /**
  457. * {@inheritdoc}
  458. */
  459. public static function findIdentity($id)
  460. {
  461. return static::findOne($id);
  462. }
  463. /**
  464. * {@inheritdoc}
  465. */
  466. public static function findIdentityByAccessToken($token, $type = null)
  467. {
  468. return static::findOne(['access_token' => $token]);
  469. }
  470. /**
  471. * {@inheritdoc}
  472. */
  473. public function getId()
  474. {
  475. return $this->id;
  476. }
  477. /**
  478. * {@inheritdoc}
  479. */
  480. public function getAuthKey()
  481. {
  482. }
  483. /**
  484. * {@inheritdoc}
  485. */
  486. public function validateAuthKey($authKey)
  487. {
  488. }
  489. /**
  490. * 刷新token
  491. * @return bool
  492. */
  493. public function refreshToken()
  494. {
  495. $this->access_token = \Yii::$app->security->generateRandomString();
  496. return $this->save();
  497. }
  498. public static function handleUserLevelExpires($level, $user) {
  499. if (!$level || !$user) {
  500. return ['user_level_expires_ext' => [], 'expires_in' => 0];
  501. }
  502. // 会员等级新增有效期时间
  503. $expires_in = 0;
  504. //购买会员有限期 0为永久
  505. if ($level->expires_in > 0) {
  506. $expires_in = strtotime('+' . $level->expires_in . ' day');
  507. if ($level->expires_type) {
  508. $expires_in = strtotime('+' . $level->expires_in . ' year');
  509. }
  510. }
  511. $user_level_expires_ext = [['level' => $user->level, 'expires_in' => $expires_in]];
  512. if ($user->user_level_expires_ext) {
  513. $user_level_expires_ext = json_decode($user->user_level_expires_ext, true);
  514. //给新的加上 排序(按等级) 并且 去除已经过期的 作为新的一个json
  515. array_push($user_level_expires_ext, ['level' => $user->level, 'expires_in' => $expires_in]);
  516. //排序
  517. $last_names = array_column($user_level_expires_ext, 'level');
  518. array_multisort($last_names, SORT_DESC, $user_level_expires_ext);
  519. //去除已经过期的
  520. foreach ($user_level_expires_ext as $user_level_index => $user_level_item) {
  521. // 去除已经过期的
  522. if ($user_level_item['expires_in'] > 0 && $user_level_item['expires_in'] <= time()) {
  523. unset($user_level_expires_ext[$user_level_index]);
  524. }
  525. }
  526. $user_level_expires_ext = array_values($user_level_expires_ext);
  527. }
  528. return ['user_level_expires_ext' => $user_level_expires_ext, 'expires_in' => $expires_in];
  529. }
  530. public static function findUserByStore($store_id,$mobile,$nick_name,$avatar){
  531. $user = User::find()->where(['store_id'=>$store_id,'binding'=>$mobile])->one();
  532. if (!empty($user)){
  533. return $user->id;
  534. }
  535. $user = new User();
  536. $user->type = User::USER_TYPE_NORMAL;
  537. $user->binding = $mobile;
  538. $user->nickname = $nick_name;
  539. $user->avatar_url = $avatar;
  540. $user->username = \Yii::$app->security->generateRandomString();
  541. $user->password = \Yii::$app->security->generatePasswordHash(\Yii::$app->security->generateRandomString(), 5);
  542. $user->auth_key = \Yii::$app->security->generateRandomString();
  543. $user->access_token = \Yii::$app->security->generateRandomString();
  544. $user->is_delete = User::USER_NOT_DELETE;
  545. $user->store_id = $store_id;
  546. $user->parent_id = 0;
  547. $user->old_parent_id = 0;
  548. $user->platform = User::USER_FROM_OPERATOR_BACK;
  549. if (!$user->save()) {
  550. return 0;
  551. }
  552. return $user->id;
  553. }
  554. }