User.php 108 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541
  1. <?php
  2. namespace common\models\user;
  3. use addons\Agent\common\models\Agent;
  4. use addons\Boss\common\models\AddonsBoss;
  5. use addons\Chanjet\common\global_func\Request;
  6. use addons\Chanjet\common\models\AddonsChanjetRequest;
  7. use addons\Coupon\common\enums\AddonsCouponEnum;
  8. use addons\Coupon\common\models\AddonsCoupon;
  9. use addons\Coupon\common\models\AddonsCouponUserRelate;
  10. use addons\Distribution\common\models\Distribution;
  11. use addons\FunTown\common\helper\GameAsyncHelper;
  12. use addons\Redpack\common\models\RedpackWallet;
  13. use addons\SecondaryCard\common\models\SecondaryCardOrder;
  14. use addons\Store\common\models\StoreCouponUserRelate;
  15. use common\components\export\CsvTool;
  16. use common\enums\AddonsChanjetRequestEnum;
  17. use common\enums\AddonsEnum;
  18. use common\enums\DownloadEnum;
  19. use common\enums\MemberLabelEnum;
  20. use common\enums\OrderStatusEnum;
  21. use common\enums\RabbitMqEnum;
  22. use common\enums\StatusEnum;
  23. use common\enums\UserWalletEnum;
  24. use common\events\user\UserLoginEvent;
  25. use common\events\user\UserRegisterEvent;
  26. use common\events\user\UserUpdateEvent;
  27. use common\helpers\ArrayHelper;
  28. use common\helpers\Base64Helper;
  29. use common\helpers\csv\CsvImportForm;
  30. use common\helpers\sms\Sms;
  31. use common\models\api\AccessToken;
  32. use common\models\base\BaseActiveRecord;
  33. use common\models\common\ChangeMobileLog;
  34. use common\models\common\Download;
  35. use common\models\goods\GoodsCateRelate;
  36. use common\models\mall\MallAddons;
  37. use common\models\order\Order;
  38. use common\models\common\BuyingPointDay;
  39. use common\models\common\MallSetting;
  40. use common\models\statistics\UserAchievementStatisticsTotal;
  41. use common\models\user\UserLevelExpired;
  42. use common\models\user\UserDigital;
  43. use common\models\user\DigitalLog;
  44. use common\models\wechat\WechatSubscribe;
  45. use services\common\UserWalletService;
  46. use Yii;
  47. use yii\db\Exception;
  48. use yii\web\IdentityInterface;
  49. /**
  50. * This is the model class for table "{{%user}}".
  51. *
  52. * @property int $id
  53. * @property int $base64_code user_id的64进制
  54. * @property int $mall_id 商城ID
  55. * @property int $mch_id 商户ID
  56. * @property int $store_id 门店id
  57. * @property string $username 用户名
  58. * @property string $password 密码
  59. * @property string $transaction_password 交易密码
  60. * @property string $area_code 手机区号
  61. * @property string $mobile 手机号
  62. * @property string $nickname 昵称
  63. * @property string $avatar_url 头像
  64. * @property string $platform 平台名字:WXAPP、WECHAT、MP-WX
  65. * @property int $temp_parent_id 临时父级
  66. * @property int $parent_id 第一父级ID(直推人id)
  67. * @property int $second_parent_id 第二推荐人id
  68. * @property int $third_parent_id 第三推荐人id
  69. * @property int $junior_at 成为下级时间
  70. * @property int $created_at 添加时间
  71. * @property int $last_login_at 最后登录时间
  72. * @property int $upgrade_level_at 升级时间
  73. * @property int $inviter_at 邀请时间
  74. * @property string $login_ip 用户登录ip
  75. * @property int $is_inviter 是否是邀请者
  76. * @property int $birthday 生日
  77. * @property int $status 状态[-1:删除;0:禁用;1启用]
  78. * @property int $level 会员等级,-1为普通会员,对应jxmall_mall_member_level表level字段
  79. * @property int $source 用户来源1分享首页2分享海报3分享商品4分享内容5分享视频6分享资讯7分享名片
  80. * @property string $device_type 登录设备
  81. * @property string $device_mac 登录设备MAC地址
  82. */
  83. class User extends BaseActiveRecord implements IdentityInterface
  84. {
  85. //微信小程序
  86. const PLATFORM_MP_WX = 'mp-wx';
  87. //支付宝小程序
  88. const PLATFORM_MP_ALI = 'mp-ali';
  89. //百度小程序
  90. const PLATFORM_MP_BD = 'mp-bd';
  91. //头条小程序
  92. const PLATFORM_MP_TT = 'mp-tt';
  93. //公众号
  94. const PLATFORM_WECHAT = 'wechat';
  95. //微信视频小店
  96. const PLATFORM_WECHAT_VIDEO_SHOP = 'wechat-video-shop';
  97. //h5
  98. const PLATFORM_H5 = 'h5';
  99. //app
  100. const PLATFORM_APP = 'app';
  101. const PLATFORM_XIAOE = 'xiaoe';
  102. const PLATFORM_SHEN_QUAN_YONG_USER_SYNC = 'shen-quan-yong-user-sync';
  103. /** @var string 用户来源1分享首页2分享海报3分享商品4分享内容5分享视频6分享资讯7分享名片 */
  104. const SOURCE_SHARE_INDEX = 1;
  105. const SOURCE_SHARE_POSTER = 2;
  106. const SOURCE_SHARE_GOODS = 3;
  107. const SOURCE_SHARE_CONTENT = 4;
  108. const SOURCE_SHARE_VIDEO = 5;
  109. const SOURCE_SHARE_NEWS = 6;
  110. const SOURCE_SHARE_CARD = 7;
  111. const SOURCE_SHARE_O2O = 8;
  112. const SOURCE_ADD_PLATFORM = 9;
  113. const SOURCE_DIY_PAGE = 10;
  114. const SOURCE_GROUP_BUY_ACTIVE_GOODS = 11;
  115. const SOURCE_BARGAIN_ACTIVE_ORDER = 12;
  116. const SOURCE_BARGAIN_ACTIVE = 13;
  117. const SOURCE_SCORE_MALL = 14;
  118. const SOURCE_LUCKY_GROUP_GOODS = 15;
  119. const SOURCE_SHARE_REDPACK = 16;//分享海报得红包
  120. const SOURCE_SAlARY = 17;
  121. const SOURCE_VALET_ORDER = 18; // 代客下单
  122. const SOURCE_GROUP_BUY_COPY_ACTIVE_GOODS = 19;
  123. const SOURCE_WECHAT_VIDEO_SHOP = 20;
  124. const SOURCE_MATERIAL = 21;
  125. const SOURCE_FUN_TOWN = 22;
  126. const SOURCE_GROUP_BUY_REBATE_ACTIVE_GOODS = 31;
  127. const SOURCE_DOUYIN_CPS_GOODS_DETAIL = 30;
  128. const SOURCE_AIYUNUO = 32;
  129. const SOURCE_VALET_ORDER_DEFAULT_USER = 40; // 代客下单默认下单预览用户
  130. const SOURCE_DOUBLE_COPY_VIRTUAL_USER = 50;
  131. const SOURCE_HEALTH_EXAMINATION = 66; //分享朋友圈素材
  132. const SOURCE_XIAOE = 67; //小鹅通
  133. const SOURCE_SHEN_QUAN_YONG_USER_SYNC = 68; // 申泉泳酒业用户同步
  134. public static $sources = [
  135. self::SOURCE_SHARE_INDEX => "分享首页",
  136. self::SOURCE_SHARE_POSTER => "分享海报",
  137. self::SOURCE_SHARE_GOODS => "分享商品",
  138. self::SOURCE_SHARE_CONTENT => "分享内容",
  139. self::SOURCE_SHARE_VIDEO => "分享视频",
  140. self::SOURCE_SHARE_NEWS => "分享资讯",
  141. self::SOURCE_SHARE_CARD => "分享名片",
  142. self::SOURCE_SHARE_O2O => "分享O2O门店",
  143. self::SOURCE_ADD_PLATFORM => "后台添加",
  144. self::SOURCE_DIY_PAGE => "自定义页面",// 装修页
  145. self::SOURCE_GROUP_BUY_ACTIVE_GOODS => "分享拼团活动",// 装修页
  146. self::SOURCE_BARGAIN_ACTIVE_ORDER => "分享我的砍价活动",// 装修页
  147. self::SOURCE_BARGAIN_ACTIVE => "分享砍价活动",// 装修页
  148. self::SOURCE_SCORE_MALL => "分享积分商品",// 装修页
  149. self::SOURCE_LUCKY_GROUP_GOODS => "分享幸运拼团商品",// 装修页
  150. self::SOURCE_SAlARY => "薪资插件注册",// 装修页
  151. self::SOURCE_VALET_ORDER => "代客下单",// 装修页
  152. self::SOURCE_WECHAT_VIDEO_SHOP => "微信视频小店",// 装修页
  153. self::SOURCE_MATERIAL => "朋友圈素材",// 装修页
  154. self::SOURCE_FUN_TOWN => "好玩小镇",// 装修页
  155. self::SOURCE_AIYUNUO => "爱优诺",// 装修页
  156. self::SOURCE_VALET_ORDER_DEFAULT_USER => "代客下单默认用户",// 装修页
  157. self::SOURCE_DOUBLE_COPY_VIRTUAL_USER => "二二复制虚拟用户",// 装修页
  158. self::SOURCE_HEALTH_EXAMINATION => "健康驿站",// 装修页
  159. self::SOURCE_XIAOE => "小鹅通",
  160. self::SOURCE_SHEN_QUAN_YONG_USER_SYNC => "申泉泳酒业用户同步",
  161. ];
  162. const TYPE_WECHAT = 'wechat';
  163. const TYPE_MP_WX = 'mp-wx';
  164. const TYPE_H5 = 'h5';
  165. const TYPE_APP = 'app';
  166. const TYPE_MANUAL = 'manual';
  167. const TYPE_FACEBOOK_APP = 'facebook_app';
  168. const TYPE_WECHAT_VIDEO_SHOP = 'wechat-video-shop';
  169. const TYPE_OTHER = 'other';
  170. const TYPE_NAME_LIST = array(
  171. self::TYPE_WECHAT => '微信公众号',
  172. self::TYPE_MP_WX => '小程序',
  173. self::TYPE_H5 => 'H5',
  174. self::TYPE_APP => 'APP',
  175. self::TYPE_MANUAL => '手动导入',
  176. self::TYPE_FACEBOOK_APP => 'Facebook-APP',
  177. self::TYPE_WECHAT_VIDEO_SHOP => '微信视频小店',
  178. self::TYPE_OTHER => '其他',
  179. self::PLATFORM_MP_ALI => '支付宝小程序',
  180. self::PLATFORM_XIAOE => '小鹅通',
  181. self::PLATFORM_SHEN_QUAN_YONG_USER_SYNC => '申泉泳酒业用户同步',
  182. );
  183. public static $head_list = [
  184. '0' => [
  185. 'field' => 'serial_number',
  186. 'field_name' => '序号',
  187. ],
  188. '1' => [
  189. 'field' => 'nickname',
  190. 'field_name' => '昵称',
  191. ],
  192. '2' => [
  193. 'field' => 'mobile',
  194. 'field_name' => '绑定手机号',
  195. ],
  196. '3' => [
  197. 'field' => 'invite_mobile',
  198. 'field_name' => '上级推荐人手机号',
  199. ],
  200. '4' => [
  201. 'field' => 'balance',
  202. 'field_name' => '余额',
  203. ],
  204. '5' => [
  205. 'field' => 'score',
  206. 'field_name' => '积分',
  207. ],
  208. '6' => [
  209. 'field' => 'commission',
  210. 'field_name' => '佣金',
  211. ],
  212. '7' => [
  213. 'field' => 'level',
  214. 'field_name' => '权重',
  215. ],
  216. ];
  217. public static $max_count = 5000;
  218. public static $column_num = 7;
  219. /**
  220. * {@inheritdoc}
  221. */
  222. public static function tableName()
  223. {
  224. return '{{%user}}';
  225. }
  226. /**
  227. * {@inheritdoc}
  228. */
  229. public function rules()
  230. {
  231. return [
  232. [['mall_id', 'mch_id', 'store_id', 'mobile', 'area_code', 'temp_parent_id', 'parent_id', 'second_parent_id', 'third_parent_id', 'junior_at', 'created_at', 'last_login_at', 'birthday', 'upgrade_level_at', 'inviter_at', 'is_inviter', 'status', 'level', 'source'], 'integer'],
  233. [['username', 'nickname', 'device_type', 'device_mac'], 'string', 'max' => 64],
  234. [['nickname', 'device_type', 'device_mac'], 'safe'],
  235. [['password', 'transaction_password'], 'string', 'max' => 128],
  236. // [['mobile'], 'match', 'pattern' => '/^1\d{10}$/', 'message' => '请输入正确的手机号码'],
  237. [['avatar_url'], 'string', 'max' => 255],
  238. [['platform'], 'string', 'max' => 45],
  239. [['login_ip'], 'string', 'max' => 15],
  240. [['base64_code'], 'string', 'max' => 10],
  241. ];
  242. }
  243. /**
  244. * {@inheritdoc}
  245. */
  246. public function attributeLabels()
  247. {
  248. return [
  249. 'id' => 'ID',
  250. 'base64_code' => 'user_id的64进制',
  251. 'mall_id' => '商城ID',
  252. 'mch_id' => '商户ID',
  253. 'store_id' => '门店id',
  254. 'username' => '用户名',
  255. 'password' => '密码',
  256. 'transaction_password' => '交易密码',
  257. 'area_code' => '手机区号',
  258. 'mobile' => '手机号',
  259. 'nickname' => '昵称',
  260. 'avatar_url' => '头像',
  261. 'platform' => '平台名字:app:微信授权、WECHAT:公众号、MP-WX:小程序',
  262. 'temp_parent_id' => '临时父级',
  263. 'parent_id' => '第一父级ID(直推人id)',
  264. 'second_parent_id' => '第二推荐人id',
  265. 'third_parent_id' => '第三推荐人id',
  266. 'junior_at' => '成为下级时间',
  267. 'created_at' => '添加时间',
  268. 'last_login_at' => '最后登录时间',
  269. 'upgrade_level_at' => '升级时间',
  270. 'inviter_at' => '邀请时间',
  271. 'login_ip' => '用户登录ip',
  272. 'is_inviter' => '是否是邀请者',
  273. 'status' => '状态[-1:删除;0:禁用;1启用]',
  274. 'level' => '会员等级,-1为普通会员,对应jxmall_mall_member_level表level字段',
  275. 'source' => '用户来源1分享首页2分享海报3分享商品4分享内容5分享视频6分享资讯7分享名片',
  276. 'device_type' => '登录设备',
  277. 'device_mac' => '登录设备MAC地址',
  278. ];
  279. }
  280. /**
  281. * 关联标签
  282. * @Author: lun
  283. * @DateTime: 2022/3/14 0014 10:36
  284. * @Copyright: copyright (c) 2021 广东七件事集团
  285. * @return \yii\db\ActiveQuery
  286. */
  287. public function getUserLabel()
  288. {
  289. return $this->hasMany(UserLabel::class, ['id' => 'label_id'])
  290. ->select('id,name,color')
  291. ->where(['status' => StatusEnum::ENABLED])
  292. ->via('userLabelRelationship');
  293. }
  294. /**
  295. * 用户标签关联
  296. * @Author: lun
  297. * @DateTime: 2022/3/14 0014 10:36
  298. * @Copyright: copyright (c) 2021 广东七件事集团
  299. * @return \yii\db\ActiveQuery
  300. */
  301. public function getUserLabelRelationship()
  302. {
  303. return $this->hasMany(UserLabelRelationship::class, ['user_id' => 'id'])->select('label_id,user_id');
  304. }
  305. /**
  306. * 获取上级 - 关联
  307. * @desc
  308. * @return \yii\db\ActiveQuery
  309. */
  310. public function getParentCustom()
  311. {
  312. return $this->hasOne(User::class, ['id' => 'parent_id'])->select('id,username,mobile,nickname,parent_id,avatar_url');
  313. }
  314. /**
  315. * @return \yii\db\ActiveQuery
  316. */
  317. public function getUserFreezeCommission()
  318. {
  319. return $this->hasOne(UserFreezeCommission::class, ['user_id' => 'id']);
  320. }
  321. public function getUserAchievementStatisticsTotal()
  322. {
  323. return $this->hasOne(UserAchievementStatisticsTotal::class, ['user_id' => 'id']);
  324. }
  325. /**
  326. * 保存数据
  327. * @param array $params
  328. * @return bool|User
  329. * @throws \yii\db\Exception
  330. */
  331. public static function setData(array $params)
  332. {
  333. $t = Yii::$app->db->beginTransaction();
  334. try {
  335. $model = self::saveDataMainFunc($params);
  336. $t->commit();
  337. UserInviteCode::setData([
  338. 'user_id' => $model->id,
  339. 'mall_id' =>$params['mall_id']
  340. ]);
  341. return $model;
  342. } catch (Exception $e) {
  343. $t->rollBack();
  344. self::$static_error = $e->getMessage();
  345. Yii::error('注册用户报错:' . $e->getFile().',第'.$e->getLine().'行,原因:'.$e->getMessage());
  346. return false;
  347. }
  348. }
  349. /**
  350. * 用户注册主体方法
  351. * @param array $params
  352. * @return User
  353. * @throws Exception
  354. */
  355. public static function saveDataMainFunc(array $params)
  356. {
  357. $is_close_register = MallSetting::conf($params['mall_id'],'user.is_close_register');
  358. if ($is_close_register == 1) throw new Exception('站点维护中,暂时无法注册');
  359. if (!empty($params['id'])) {
  360. $model = self::findOne(['id' => $params['id'], 'status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]);
  361. if (empty($model)) throw new Exception('服务不存在或已删除');
  362. } else {
  363. $model = new self();
  364. $model->loadDefaultValues();
  365. }
  366. $parent_id = $second_parent_id =$third_parent_id =0;
  367. //传推荐人手机号码
  368. $relation_setting = \Yii::$app->services->setting->mall->get($params['mall_id'], 'user_relation_setting.relation_setting');
  369. if (!empty($relation_setting)) $relation_setting = json_decode($relation_setting, true);
  370. $relation_setting_status = 1;
  371. if(empty($relation_setting['status'])) {
  372. $relation_setting_status = 0;
  373. }
  374. if (!empty($params['invite_mobile'])){
  375. $is_invite_register = MallSetting::conf($params['mall_id'],'user.is_invite_register');
  376. $parent = User::findOne(['mall_id' => $params['mall_id'], 'mobile' => $params['invite_mobile'],'is_inviter'=>1, 'status' => 1]);
  377. if(!empty($relation_setting['become_child_way'])&&$relation_setting['become_child_way']==4){
  378. if(empty($parent)) throw new Exception('填写的推荐人手机号不存在');
  379. }
  380. if($is_invite_register && empty($parent)) throw new Exception('填写的推荐人手机号不存在');
  381. }else{
  382. //传推荐人会员id
  383. if (!empty($params['parent_id']) && $params['parent_id'] != -1){
  384. $parent = User::findOne(['id'=>$params['parent_id'],'mall_id' => $params['mall_id'],'is_inviter'=>1]);
  385. }
  386. }
  387. if ($relation_setting_status==1&&!empty($parent)){
  388. if($params['mall_id']!=$parent['mall_id']) throw new Exception('商城交叉');
  389. $parent_id = $parent->id??0;
  390. $second_parent_id = $parent->parent_id??0;
  391. $third_parent_id = $parent->second_parent_id??0;
  392. }
  393. $params['parent_id'] = 0;
  394. if(!empty($params['platform'])&&$params['platform']!= User::TYPE_MANUAL){
  395. //非后台手动添加的会员,需受关系设置配置影响
  396. if(!empty($relation_setting['become_child_way'])){
  397. switch ($relation_setting['become_child_way']){
  398. case 1:
  399. case 4:
  400. case 5:
  401. $params['parent_id'] = $parent_id;
  402. $params['second_parent_id'] =$second_parent_id;
  403. $params['third_parent_id'] = $third_parent_id;
  404. break;
  405. case 2:
  406. case 3:
  407. $params['temp_parent_id'] = $parent_id;
  408. $parent_id = 0;
  409. $params['parent_id'] = $parent_id;
  410. break;
  411. }
  412. }
  413. // if (!empty($relation_setting['become_child_way']) && $relation_setting['become_child_way'] == 1) {
  414. // $params['parent_id'] = $parent_id;
  415. // $params['second_parent_id'] =$second_parent_id;
  416. // $params['third_parent_id'] = $third_parent_id;
  417. // } else {
  418. // $params['temp_parent_id'] = $parent_id;
  419. // $parent_id = 0;
  420. // $params['parent_id'] = $parent_id;
  421. // }
  422. }else{
  423. $params['parent_id'] = $parent_id;
  424. $params['second_parent_id'] =$second_parent_id;
  425. $params['third_parent_id'] = $third_parent_id;
  426. }
  427. if(!empty($relation_setting['status'])&&!empty($relation_setting['get_power_way'])&&$relation_setting['get_power_way']==1) $params['is_inviter'] = 1;
  428. if (!$model->load($params, '') || !$model->save()) throw new Exception($model->getErrorMessage());
  429. $model->base64_code = Base64Helper::dec2Base64($model->id);
  430. $res = $model->save();
  431. if ($res === false) throw new Exception('转码失败');
  432. $params['user_id'] = $model->id;
  433. $res = UserWallet::setData($params);
  434. if ($res === false) throw new Exception($model->getErrorMessage());
  435. $param = [
  436. 'user_id' => $model->id,
  437. 'parent_id' => $parent_id,
  438. ];
  439. Yii::$app->custom->logs("关系链绑定数据", json_encode($param));
  440. $res = UserPartitionRelationship::setData($param);
  441. if ($res === false) throw new Exception(UserPartitionRelationship::getStaticError());
  442. return $model;
  443. }
  444. /**
  445. * 没有事务的注册用户方法
  446. * @param array $params
  447. * @return User
  448. * @throws Exception
  449. */
  450. public static function setDataByNoTransaction(array $params)
  451. {
  452. $model = self::saveDataMainFunc($params);
  453. UserInviteCode::setData([
  454. 'user_id' => $model->id,
  455. 'mall_id' =>$params['mall_id']
  456. ]);
  457. return $model;
  458. }
  459. /**
  460. * @param array $where
  461. * @param string $select
  462. * @param array $with
  463. * @param int $is_array
  464. * @return array|User|\yii\db\ActiveRecord|null
  465. */
  466. public static function getUser(array $where, $select = '*', array $with = [], int $is_array = 0)
  467. {
  468. $default_cond = [['<>', 'status', StatusEnum::DELETE]];
  469. $where = array_merge($default_cond, $where);
  470. $query = self::find();
  471. self::paramPack(['where' => $where, 'with' => $with], $query);
  472. return $query->select($select)->asArray($is_array)->one();
  473. }
  474. /**
  475. * @param array $where
  476. * @param string $select
  477. * @param array $with
  478. * @param int $is_array
  479. * @return array|User[]|\yii\db\ActiveRecord[]
  480. */
  481. public static function getUserList(array $where, $select = '*', array $with = [], int $is_array = 0)
  482. {
  483. $default_cond = [['<>', 'status', StatusEnum::DELETE]];
  484. $where = array_merge($default_cond, $where);
  485. $query = self::find();
  486. self::paramPack(['where' => $where, 'with' => $with], $query);
  487. return $query->select($select)->asArray($is_array)->all();
  488. }
  489. /**
  490. * @param int|string $id
  491. * @return User|IdentityInterface|null
  492. */
  493. public static function findIdentity($id)
  494. {
  495. $userData = self::findOne($id);
  496. if (!empty($userData) && $userData->status == StatusEnum::ENABLED) {
  497. return $userData;
  498. }
  499. return null;
  500. }
  501. /**
  502. * @param mixed $token
  503. * @param null $type
  504. * @return User|IdentityInterface|null
  505. */
  506. public static function findIdentityByAccessToken($token, $type = null)
  507. {
  508. return static::findOne(['access_token' => $token]);
  509. }
  510. /**
  511. * @return int|string
  512. */
  513. public function getId()
  514. {
  515. return $this->id;
  516. }
  517. /**
  518. * @return string|null
  519. */
  520. public function getAuthKey()
  521. {
  522. return $this->auth_key;
  523. }
  524. /**
  525. * @param string $authKey
  526. * @return bool|null
  527. */
  528. public function validateAuthKey($authKey)
  529. {
  530. return $this->getAuthKey() === $authKey;
  531. }
  532. /**
  533. * 关联用户钱包
  534. * @return \yii\db\ActiveQuery
  535. */
  536. public function getUserWallet()
  537. {
  538. return $this->hasOne(UserWallet::class, ['user_id' => 'id']);
  539. }
  540. /**
  541. * 关联会员等级
  542. * @return \yii\db\ActiveQuery
  543. */
  544. public function getUserLevel()
  545. {
  546. return $this->hasOne(UserLevel::class, ['level' => 'level'])->andWhere(['status' => StatusEnum::ENABLED]);
  547. }
  548. public function getUserLevelTwo()
  549. {
  550. return $this->hasOne(UserLevel::class, ['level' => 'level', 'mall_id' => 'mall_id'])->andWhere(['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]);
  551. }
  552. public function getUserDigital()
  553. {
  554. return $this->hasOne(UserDigital::class, ['user_id' => 'id'])->andWhere(['status' => StatusEnum::ENABLED]);
  555. }
  556. /**
  557. * 关联会员渠道信息
  558. * @return \yii\db\ActiveQuery
  559. */
  560. public function getUserTeamStatistics()
  561. {
  562. return $this->hasOne(UserTeamStatistics::class, ['user_id' => 'id']);
  563. }
  564. public function getParent()
  565. {
  566. return $this->hasOne(User::class, ['id' => 'parent_id']);
  567. }
  568. /**
  569. * 关联买点
  570. */
  571. public function getBuyingPoint() {
  572. return $this->hasMany(BuyingPointDay::class, ['user_id' => 'id'])
  573. ->where(['in', 'page_type', ['goods_detail', 'group_buy', 'bargain']])
  574. ->select(['sum(browse_num) browse_num', 'user_id', 'item_id'])
  575. ->asArray()
  576. ->groupBy(['item_id']);
  577. }
  578. /**
  579. * 畅捷通请求
  580. *
  581. * @return \yii\db\ActiveQuery
  582. */
  583. public function getAddonsChanjetRequest()
  584. {
  585. return $this
  586. ->hasOne(AddonsChanjetRequest::class, ['table_id' => 'id'])
  587. ->orderBy('updated_at desc')
  588. ->select('id, table_id, request_status, updated_at')
  589. ->where([
  590. 'table' => self::tableName(),
  591. 'path' => [Request::PATHS['PARTNER_CREATE_BATCH'], Request::PATHS['PARTNER_UPDATE']]
  592. ])
  593. ->andWhere(['>', 'request_status', 0]);
  594. }
  595. /**
  596. * 管理后台会员详情
  597. * @param $mall_id
  598. * @param $user_id
  599. * @return array
  600. */
  601. public static function getUserDetail($mall_id, $user_id)
  602. {
  603. $user = self::getOne([['id'=>$user_id]],true,['parent']);
  604. $user_level = UserLevel::findOne(['level' => $user['level'], 'mall_id'=>$mall_id,'status' => [StatusEnum::ENABLED,StatusEnum::DISABLED]]);
  605. $area = UserExtra::findOne(['user_id' => $user_id]);
  606. $user_address = UserAddress::findOne(['user_id' => $user_id, 'is_default' => 1, 'status' => StatusEnum::ENABLED]);
  607. if (!empty($area)) $area = $area->toArray();
  608. $user['source'] = (self::$sources)[$user['source']];
  609. $user['area']['province'] = '';
  610. $user['area']['city'] = '';
  611. $user['area']['area'] = '';
  612. $user['remark'] = '';
  613. $user['all_platform'] = UserLoginLog::getAllPlatform($user_id);
  614. $user['birthday'] = '';
  615. $user['wechat'] = '';
  616. $user['sex'] = '';
  617. $user['id_card'] = '';
  618. $user['sex_text'] = '';
  619. $user['team'] = [];
  620. if (!empty($area)) {
  621. $user['area']['province'] = $area['province'];
  622. $user['area']['city'] = $area['city'];
  623. $user['area']['area'] = $area['area'];
  624. $user['remark'] = $area['remark'];
  625. $user['birthday'] = $area['birthday'];
  626. $user['wechat'] = $area['wechat'];
  627. $user['sex'] = $area['sex'];
  628. $user['id_card'] = $area['id_card'];
  629. $user['sex_text'] = !empty(UserExtra::SEX_ARR[$area['sex']]) ? UserExtra::SEX_ARR[$area['sex']] : '';
  630. }
  631. $user['level'] = !empty($user_level['name']) ? $user_level['name'] : MallSetting::getDefaultLevelName($mall_id);
  632. $user['address'] = !empty($user_address) ? $user_address['province'].$user_address['city'].$user_address['district'].$user_address['town'].$user_address['detail'] : '';
  633. $user['platform'] = !empty(self::TYPE_NAME_LIST[$user['platform']]) ? self::TYPE_NAME_LIST[$user['platform']] : '';
  634. $user['buy_power'] = [];
  635. $parent = $user['parent'];
  636. $user['parent_nickname'] = empty($parent) ? '' : ($parent['nickname']?$parent['nickname']:$parent['id']);
  637. $user['interest'] = [];
  638. //$user['tag'] = UserTagRelate::getUserTagList($mall_id, $user_id);
  639. $where[] = ['=','user_id',$user_id];
  640. $where[] = ['=','mall_id',$mall_id];
  641. $where[] = ['=','status',StatusEnum::ENABLED];
  642. $with = ['userLabel' => function($qurey){
  643. $qurey->select('name,id,color');
  644. }];
  645. $user['tag'] = UserLabelRelationship::lists(['where' =>$where,'with' => $with,'select' => 'label_id,status,id'],true);
  646. if (!empty($user['tag'])) {
  647. // 过滤到标签中 userLabel为 null的数据
  648. $user['tag'] = array_filter($user['tag'], function ($item) {
  649. return !empty($item['userLabel']);
  650. });
  651. }
  652. $user_team_statistics = UserTeamStatistics::findOne(['user_id' => $user_id]);
  653. if (!empty($user_team_statistics)) {
  654. $user['team'] = [
  655. 'team_count' => $user_team_statistics['user_team_total'],
  656. 'first' => $user_team_statistics['user_direct_total'],
  657. 'second' => $user_team_statistics['user_indirect_total']
  658. ];
  659. $user['buy_power'] = [
  660. 'total_price' => !empty($user_team_statistics['pay_order_price_total']) ? $user_team_statistics['pay_order_price_total'] : '0.00',
  661. 'total_orders' => !empty($user_team_statistics['pay_order_total']) ? $user_team_statistics['pay_order_total'] : 0,
  662. 'unit_price' => !empty($user_team_statistics['order_total']) ? bcdiv($user_team_statistics['order_price_total'] / $user_team_statistics['order_total'], 1, 2) : '0.00',
  663. 'last_time' => !empty($order['created_at']) ? date('Y-m-d H:i:s', $order['created_at']) : '-',
  664. 'total_refund_price' => !empty($user_team_statistics['order_refund_price_total']) ? $user_team_statistics['order_refund_price_total'] : '0.00',
  665. 'total_refund_orders' => !empty($user_team_statistics['order_refund_total']) ? $user_team_statistics['order_refund_total'] : 0,
  666. ];
  667. }
  668. $user['user_promote_identity'] = self::getUserPromoteIdentity($user_id,$mall_id);
  669. $user['user_property'] = User::property($mall_id, $user_id);
  670. $user['user_level_list'] = UserLevel::getListIndexByLevelColumn($mall_id);
  671. //会员期限
  672. $user['user_level_expired'] = '-';
  673. if($user_level && isset($user_level['is_open_expired']) && $user_level['is_open_expired']){
  674. $user_level_expired = UserLevelExpired::findOne(['user_id'=> $user_id,'level'=>$user_level['level'],'status'=>StatusEnum::ENABLED]);
  675. if($user_level_expired){
  676. $user['user_level_expired'] = date('Y-m-d H:i:s', $user_level_expired['expired_at']);
  677. }
  678. }
  679. // 判断是否显示我的客户
  680. if (MallAddons::isInstall($mall_id, 'TeamData')) $user['is_show_my_customer'] = 1;
  681. $cateIdMap = MemberLabelEnum::getMap();
  682. $userMemberLabelModel = UserMemberLabel::findOne(['user_id'=>$user_id]);
  683. $memberLabelList = [];
  684. if(!empty($userMemberLabelModel)){
  685. $memberLabelIds = [];
  686. foreach ($cateIdMap as $cateId=>$val){
  687. $memberLabelIds[]=$userMemberLabelModel['cate_id'.$cateId];
  688. }
  689. $memberLabelList = MemberLabel::lists(['where'=>[['id'=>$memberLabelIds]],'select'=>'id,name,color']);
  690. }
  691. $user['userMemberLabel'] = $memberLabelList;
  692. $user['invite_code'] = UserInviteCode::find()->where(['and', ['mall_id' => $mall_id], ['user_id' => $user_id]])->select('code')->scalar() ?? '';
  693. $user_info = UserInfo::findOne(['mall_id'=>$mall_id,'user_id'=>$user['id'],'platform'=>User::PLATFORM_WECHAT,'status'=>StatusEnum::ENABLED]);
  694. $is_follow = 0;
  695. if(!empty($user_info)){
  696. $wechat_subscribe =WechatSubscribe::findOne(['mall_id'=>$mall_id,'from_user_name'=>$user_info['openid']]);
  697. if(!empty($wechat_subscribe)&&$wechat_subscribe['subscribe_status']==1){
  698. $is_follow = '已关注';
  699. }
  700. }
  701. $user['is_follow'] = $is_follow;
  702. return $user;
  703. }
  704. /**
  705. * 通过h5登录
  706. * @param $params
  707. * @return array|bool|User|\yii\db\ActiveRecord
  708. */
  709. public static function loginByH5($params)
  710. {
  711. try {
  712. $where[] = ['mall_id' => $params['mall_id']];
  713. $where[] = ['status' => StatusEnum::ENABLED];
  714. //$where[] = ['or', ['username' => $params['username']], ['mobile' => $params['username']]];
  715. $where[] = ['mobile' => $params['username']];
  716. $user = User::getUser($where);
  717. if (empty($user)) throw new Exception('账号不存在');
  718. if (empty($params['password']) && empty($params['captcha'])) throw new Exception('必须选择一种登录方式');
  719. if (!empty($params['password'])) {
  720. if (!\Yii::$app->getSecurity()->validatePassword(trim($params['password']), $user->password)) throw new Exception('密码错误');
  721. } else {
  722. if (YII_ENV == 'prod') {
  723. if (!Sms::checkValidateCode($params['username'], $params['captcha'],Sms::SMS_TYPE_USER_LOGIN)) throw new Exception('验证码不正确');
  724. }
  725. }
  726. $user->last_login_at = time();
  727. $user->login_ip = ArrayHelper::get_client_ip();
  728. $res = $user->save();
  729. if ($res === false) throw new Exception(User::getStaticError());
  730. return $user;
  731. } catch (\Exception $e) {
  732. self::$static_error = $e->getMessage();
  733. return false;
  734. }
  735. }
  736. /**
  737. * 获取推荐人列表
  738. * @param $params
  739. * @return array|bool|mixed|void
  740. */
  741. public static function getCanBindInviter($params)
  742. {
  743. $parent_id = 0;
  744. if(empty($params['inviter_id'])&&empty($params['keyword'])){
  745. }else{
  746. $query = self::find()->where(['mall_id'=>$params['mall_id']]);
  747. if (!empty($params['inviter_id'])) $query->andWhere(['id'=>$params['inviter_id']]);
  748. if (!empty($params['keyword'])) $query->andWhere(['mobile'=>$params['keyword']]);
  749. $user =$query->one();
  750. if(!empty($user)){
  751. list($tree,$parent_deep,$all_tree, $all_tree_arr) = UserPartitionRelationship::getTree($user['id']);
  752. $parent_ids = [];
  753. foreach ($all_tree_arr as $item){
  754. $parent_ids[]= Base64Helper::Base642dec($item);
  755. }
  756. if(!in_array($params['user_id'],$parent_ids))$parent_id = $user['id'];
  757. }
  758. }
  759. $user_list = User::listPage(['where' => [['mall_id' => $params['mall_id']], ['in', 'id', $parent_id], ['is_inviter' => 1]]]);
  760. if (!empty($user_list['list'])) {
  761. $user_level_arr = UserLevel::getUserLevelArr($params['mall_id']);
  762. foreach ($user_list['list'] as &$v) {
  763. $v['level_name'] = !empty($user_level_arr[$v['level']]) ? $user_level_arr[$v['level']] : MallSetting::getDefaultLevelName($params['mall_id']);;
  764. }
  765. }
  766. return $user_list;
  767. }
  768. /**
  769. * 修改推荐人,加锁
  770. * @param $params
  771. * @throws \Exception
  772. */
  773. public static function updateParentByLock($params)
  774. {
  775. if(!empty($params['parent_id'])){
  776. $user_model = User::findOne($params['user_id']);
  777. if($user_model->parent_id==$params['parent_id']){
  778. throw new \Exception('原上级与要修改的一样');
  779. }
  780. \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK_CHANGE_RELATIONSHIP, RabbitMqEnum::CHANGE_RELATIONSHIP_QUEUE, $params, UserPartitionRelationship::class, 'changeRelationship');
  781. }else{
  782. $user_model = User::findOne($params['user_id']);
  783. if($user_model->parent_id==0){
  784. throw new \Exception('上级已经是平台');
  785. }
  786. \Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK_CHANGE_RELATIONSHIP, RabbitMqEnum::CHANGE_RELATIONSHIP_QUEUE, $params, UserPartitionRelationship::class, 'changeRelationshipToPlatform');
  787. }
  788. return true;
  789. }
  790. /**
  791. * 批量导入会员
  792. * @param $params
  793. * @return array|bool
  794. */
  795. public static function importUsers($params)
  796. {
  797. $t = Yii::$app->db->beginTransaction();
  798. try {
  799. self::isCanImport($params['mall_id']);
  800. $csvImport = new CsvImportForm();
  801. $data = $csvImport->setDataForm($params)
  802. ->setMaxCount(self::$max_count)
  803. ->setImportFieldConfig(self::$head_list)
  804. ->setColumnNum(self::$column_num)
  805. ->readCsvData();
  806. if (!$data) throw new \Exception(CsvImportForm::getStaticError());
  807. $success_counts = $fail_counts = 0;
  808. $counts = count($data);
  809. $fail_arr = [];
  810. $mobile_user_id_arr = [];
  811. $success_user_id_arr = [];
  812. $merge_arr = [];
  813. foreach ($data as $item) {
  814. if(!empty($item['balance'])){
  815. $item['total_balance'] = $item['balance'];
  816. }else{
  817. $item['balance'] = 0;
  818. $item['total_balance'] = 0;
  819. }
  820. if(!empty($item['score'])){
  821. $item['total_score'] = $item['score'];
  822. }else{
  823. $item['score'] = 0;
  824. $item['total_score'] = 0;
  825. }
  826. if(!empty($item['commission'])){
  827. $item['total_commission'] = $item['commission'];
  828. }else{
  829. $item['commission'] = 0;
  830. $item['total_commission'] = 0;
  831. }
  832. $param = $item;
  833. $param['platform'] = User::TYPE_MANUAL;
  834. $param['mall_id'] = $params['mall_id'];
  835. $param['source'] = User::SOURCE_ADD_PLATFORM;
  836. $password = substr($param['mobile'],-6,6);
  837. $param['username'] = $param['mobile'];
  838. $param['password'] = Yii::$app->getSecurity()->generatePasswordHash($password, 4);
  839. $merge_arr[] = $param;
  840. }
  841. // 数组组装
  842. $user_merge = ArrayHelper::buildTree($merge_arr,'mobile', 'invite_mobile', 'child');
  843. self::batchAddUsers('import', $params['mall_id'], $user_merge, $success_counts, $fail_arr, $fail_counts, $success_user_id_arr,$mobile_user_id_arr);
  844. $param['counts'] = $counts;
  845. $param['success_counts'] = $success_counts;
  846. $param['fail_counts'] = $fail_counts;
  847. $param['status'] = 1;
  848. $user_import_log_model = UserImportLog::setData($param);
  849. if (!$user_import_log_model) throw new \Exception(UserImportLog::getStaticError());
  850. if (!empty($fail_arr)) {
  851. $res = UserImportFailLog::setData($fail_arr, $user_import_log_model->id);
  852. if (!$res) throw new \Exception(UserImportFailLog::getStaticError());
  853. }
  854. $t->commit();
  855. //触发余额+佣金+积分日志
  856. self::importUserLog($merge_arr, $mobile_user_id_arr);
  857. // 触发注册成功事件,事务提交完成后触发
  858. foreach ($success_user_id_arr as $user_id){
  859. $event = new UserRegisterEvent($params['mall_id']);
  860. $data = ['user_id' => $user_id, 'platform' => User::TYPE_MANUAL];
  861. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  862. }
  863. return ['counts' => $counts, 'success_counts' => $success_counts, 'fail_counts' => $fail_counts];
  864. } catch (\Exception $e) {
  865. self::$static_error = $e->getMessage();
  866. $t->rollBack();
  867. return false;
  868. }
  869. }
  870. /**
  871. * 触发余额+佣金+积分日志
  872. * @param array $data 导入用户信息数据
  873. * @param array $mobile_user_id_arr 用户对于user_id
  874. * @return void
  875. * @throws \Exception
  876. */
  877. public static function importUserLog(array $data, array $mobile_user_id_arr)
  878. {
  879. $insert_wallet = [];//插入数据
  880. $mobile_list = array_keys($mobile_user_id_arr);
  881. foreach ($data as $val) {
  882. if (!in_array($val['mobile'], $mobile_list)) {
  883. continue;
  884. }
  885. $user_id = $mobile_user_id_arr[$val['mobile']];
  886. $insert_wallet_item = [
  887. 'mall_id' => $val['mall_id'],
  888. 'user_id' => $user_id,
  889. 'is_frozen' => false,
  890. 'source_table_id' => $user_id,
  891. 'from_type' => UserWalletEnum::RECHARGE,
  892. 'capital_type' => UserWalletEnum::IMPORT_RECHARGE_PLATFORM,
  893. ];
  894. if (!empty($val['balance'])) {//余额
  895. $insert_wallet_item['wallet_type'] = 'balance';
  896. $insert_wallet_item['money'] = $val['balance'];
  897. $insert_wallet_item['source_table'] = 'balance_log';
  898. $insert_wallet_item['desc'] = '后台批量导入余额';
  899. $insert_wallet[] = $insert_wallet_item;
  900. }
  901. if (!empty($val['score'])) {//积分
  902. $insert_wallet_item['wallet_type'] = 'score';
  903. $insert_wallet_item['money'] = $val['score'];
  904. $insert_wallet_item['source_table'] = 'score_log';
  905. $insert_wallet_item['desc'] = '后台批量导入积分';
  906. $insert_wallet[] = $insert_wallet_item;
  907. }
  908. if (!empty($val['commission'])) {//佣金
  909. $insert_wallet_item['wallet_type'] = 'commission';
  910. $insert_wallet_item['money'] = $val['commission'];
  911. $insert_wallet_item['source_table'] = 'commission_log';
  912. $insert_wallet_item['desc'] = '后台批量导入佣金';
  913. $insert_wallet[] = $insert_wallet_item;
  914. }
  915. }
  916. if ($insert_wallet) UserWalletService::batchHandleByQueue($insert_wallet);
  917. }
  918. public static function importUsersSync($params)
  919. {
  920. $t = Yii::$app->db->beginTransaction();
  921. try {
  922. self::isCanImport($params['mall_id']);
  923. $basePath = \Yii::$app->basePath;
  924. if (empty($_FILES) || !isset($_FILES['file'])) throw new \Exception('请上传csv文件');
  925. $fileName = $_FILES['file']['name'];
  926. $tmpName = $_FILES['file']['tmp_name'];
  927. $basePath = $basePath . '/web/uploads/';
  928. if (!is_dir($basePath))mkdir($basePath, 0775, true);
  929. $ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
  930. if ($ext != 'csv') throw new \Exception('请上传csv文件');
  931. $uploadFile = $basePath . 'ADMIN-' . date('Y-m-d His') . '.' . $ext;
  932. //上传
  933. move_uploaded_file($tmpName, $uploadFile);
  934. $success_counts = $fail_counts = 0;
  935. $counts = 0;
  936. $param['counts'] = $counts;
  937. $param['success_counts'] = $success_counts;
  938. $param['fail_counts'] = $fail_counts;
  939. $param['upload_file'] = $uploadFile;
  940. $param['mall_id'] = $params['mall_id'];
  941. $user_import_log_model = UserImportLog::setData($param);
  942. if (!$user_import_log_model) throw new \Exception(UserImportLog::getStaticError());
  943. $t->commit();
  944. $params = ['id'=>$user_import_log_model['id']];
  945. Yii::$app->services->rabbitMq->push(RabbitMqEnum::EXCHANGE_TASK_IMPORT_USER, RabbitMqEnum::IMPORT_USER, $params, self::class, 'syncImportUser');
  946. return ['counts' => $counts, 'success_counts' => $success_counts, 'fail_counts' => $fail_counts];
  947. } catch (\Exception $e) {
  948. self::$static_error = $e->getMessage();
  949. $t->rollBack();
  950. return false;
  951. }
  952. }
  953. public static function isCanImport($mall_id){
  954. $time = strtotime('2023-03-17 23:59:59');
  955. $res = UserImportLog::find()->where(['mall_id'=>$mall_id,'status'=>3])->andWhere(['>=','created_at',$time])->one();
  956. if(!empty($res)) throw new \Exception('等上一次导入执行完再导入');
  957. }
  958. //异步执行
  959. public static function syncImportUser($params){
  960. if(empty($params['id'])) return false;
  961. $model = UserImportLog::findOne(['id' => $params['id']]);
  962. if (!empty($model['upload_file'])) {
  963. $upload_file = $model['upload_file'];
  964. $mall_id = $model['mall_id'];
  965. $cvsFile = fopen($upload_file, 'r'); //开始读取csv文件数据
  966. $fail_arr = [];
  967. $success_counts = $fail_counts =$counts = $i = 0;
  968. $counts = 0;
  969. $success_user_id_arr = [];
  970. $data = [];// 初始化数据
  971. $mobile_user_id_arr = [];
  972. while ($fileData = fgetcsv($cvsFile)) {
  973. $i++;
  974. if ($i == 1) continue;//过滤表头
  975. $counts++;
  976. $mobile = 0;
  977. $commission = 0;
  978. $score = 0;
  979. $balance = 0;
  980. $nickname = '';
  981. $invite_mobile = '';
  982. $level = 0;
  983. foreach ($fileData as $k => &$val) {
  984. $val = mb_convert_encoding(trim($val), "UTF-8", "GBK");
  985. switch ($k) {
  986. case 1:
  987. $nickname = !empty($val)?$val:'';
  988. break;
  989. case 2:
  990. $mobile = !empty($val)?trim($val):0;
  991. break;
  992. case 3:
  993. $invite_mobile = !empty($val)?$val:0;
  994. break;
  995. case 4:
  996. $balance = !empty($val)?$val:0;
  997. break;
  998. case 5:
  999. $score = !empty($val)?$val:0;
  1000. break;
  1001. case 6:
  1002. $commission = !empty($val)?$val:0;
  1003. break;
  1004. case 7:
  1005. $level = !empty($val) ? trim($val) : 0;
  1006. break;
  1007. }
  1008. }
  1009. $param['mall_id'] = $mall_id;
  1010. $param['mobile'] = $mobile;
  1011. $param['commission'] = $commission;
  1012. $param['total_commission'] = $commission;
  1013. $param['score'] = $score ?? 0;
  1014. $param['total_score'] = $score;
  1015. $param['balance'] = $balance;
  1016. $param['total_balance'] = $balance;
  1017. $param['invite_mobile'] = $invite_mobile;
  1018. $param['platform'] = User::TYPE_MANUAL;
  1019. $param['nickname'] = $nickname;
  1020. $param['source'] = User::SOURCE_ADD_PLATFORM;
  1021. $password = substr($param['mobile'], -6, 6);
  1022. $param['username'] = $param['mobile'];
  1023. $param['level'] = $level;
  1024. $param['password'] = Yii::$app->getSecurity()->generatePasswordHash($password, 4);
  1025. $data[] = $param;
  1026. }
  1027. // 数组组装
  1028. $user_merge = ArrayHelper::buildTree($data, 'mobile', 'invite_mobile', 'child');
  1029. self::batchAddUsers('syncImport', $mall_id, $user_merge, $success_counts, $fail_arr, $fail_counts, $success_user_id_arr, $mobile_user_id_arr);
  1030. $model->counts = $counts;
  1031. $model->success_counts = $success_counts;
  1032. $model->fail_counts = $fail_counts;
  1033. $model->status = 1;
  1034. if (!$model->save()) throw new \Exception($model->getErrorMessage());
  1035. //触发余额+佣金+积分日志
  1036. self::importUserLog($data, $mobile_user_id_arr);
  1037. if (!empty($fail_arr)) {
  1038. $res = UserImportFailLog::setData($fail_arr, $model->id);
  1039. if (!$res) throw new \Exception(UserImportFailLog::getStaticError());
  1040. }
  1041. }
  1042. }
  1043. /**
  1044. * 批量插入用户
  1045. * @Author: lun
  1046. * @DateTime: 2023/8/3 0003 15:16
  1047. * @Copyright: copyright (c) 2021 广东七件事集团
  1048. * @param $mall_id
  1049. * @param $users
  1050. * @param int $success_counts
  1051. * @param array $fail_arr
  1052. * @param int $fail_counts
  1053. * @throws Exception
  1054. */
  1055. public static function batchAddUsers($type, $mall_id, $users, &$success_counts = 0, &$fail_arr = [], &$fail_counts = 0, &$success_user_id_arr = [],&$mobile_user_id_arr = [])
  1056. {
  1057. // 因为后面佣金等异步提交会增加金额所以前期数据要清除
  1058. $money_list = ['score', 'total_score', 'balance', 'total_balance', 'commission', 'total_commission'];
  1059. foreach ($users as $user) {
  1060. $fail_params = $user;
  1061. unset($fail_params['child']);// 移除下级
  1062. // 将手机号验证改为国内及香港手机号验证
  1063. $combinedPattern = '/^(?:1[3-9]\d{9}|(?:\+852|00852)?\d{8})$/';
  1064. if (!preg_match($combinedPattern, $user['mobile'])) {
  1065. $fail_params['reason'] = '手机号码错误';
  1066. list($fail_arr, $fail_counts) = UserImportFailLog::packFailArr($fail_params, $fail_arr, $fail_counts);
  1067. } else {
  1068. foreach ($money_list as $item) {
  1069. unset($user[$item]);
  1070. }
  1071. $where = [];
  1072. $where[] = ['mall_id' => $mall_id];
  1073. $where[] = ['mobile' => $user['mobile']];
  1074. if (!empty(User::getUser($where))) {
  1075. $fail_params['reason'] = '手机号码已存在';
  1076. list($fail_arr, $fail_counts) = UserImportFailLog::packFailArr($fail_params, $fail_arr, $fail_counts);
  1077. } else {
  1078. // 保存用户
  1079. // 用户权重导入(判断权重是否存在)
  1080. if (!empty($user['level'])) {
  1081. $exists = UserLevel::findOne(['mall_id' => $mall_id, 'level' => $user['level'], 'status' => [StatusEnum::DISABLED, StatusEnum::ENABLED]]);
  1082. if (!$exists) {
  1083. $fail_params['reason'] = '用户权重不存在';
  1084. list($fail_arr, $fail_counts) = UserImportFailLog::packFailArr($fail_params, $fail_arr, $fail_counts);
  1085. continue;
  1086. }
  1087. }
  1088. $res = User::setData($user);
  1089. if ($res) {
  1090. $success_user_id_arr[] = $res['id'];
  1091. $mobile_user_id_arr[$user['mobile']] = $res['id'];
  1092. $success_counts++;
  1093. if ($type == 'syncImport') {// 异步处理触发注册事件
  1094. $event = new UserRegisterEvent($mall_id);
  1095. $data = ['user_id' => $res['id'], 'platform' => User::TYPE_MANUAL];
  1096. Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1097. }
  1098. } else {
  1099. $fail_params['reason'] = User::getStaticError();
  1100. list($fail_arr, $fail_counts) = UserImportFailLog::packFailArr($fail_params, $fail_arr, $fail_counts);
  1101. }
  1102. }
  1103. }
  1104. // 子级用户处理
  1105. if (!empty($user['child'])) {
  1106. self::batchAddUsers($type, $mall_id, $user['child'], $success_counts, $fail_arr, $fail_counts, $success_user_id_arr, $mobile_user_id_arr);
  1107. }
  1108. }
  1109. }
  1110. /**
  1111. * 搜索用户
  1112. * @param $keywork
  1113. * @param $mall_id
  1114. */
  1115. public static function searchUser($params)
  1116. {
  1117. $query = self::find()->alias('u')
  1118. ->where(['u.status' => StatusEnum::ENABLED, 'u.mall_id' => $params['mall_id']]);
  1119. //默认不存在no_inviter字段,o2o门店插件添加技师时搜索用户传入参数,不限制需要邀请资格
  1120. if (empty($params['no_inviter'])) {
  1121. $query->andWhere(['u.is_inviter' => 1]);
  1122. }
  1123. if (isset($params['keyword']) && !empty($params['keyword'])) {
  1124. $query->andWhere(['or', ['like', 'nickname', $params['keyword']], ['like', 'mobile', $params['keyword']], ['like', 'id', $params['keyword']]]);
  1125. }
  1126. $list = $query->limit(20)->select('u.id,u.nickname,u.mobile')->asArray()->all();
  1127. foreach ($list as &$item) {
  1128. if (empty($item['nickname'])) {
  1129. $item['nickname'] = $item['mobile'] . "(id:{$item['id']})";
  1130. }
  1131. }
  1132. return $list;
  1133. }
  1134. /**
  1135. * @desc:公众号绑定手机号
  1136. * @Author: hua
  1137. * @Date: 2021/10/22
  1138. * @Time: 15:01
  1139. * @Copyright: copyright (c) 2021 广东七件事集团
  1140. * @param $params
  1141. * @param int $is_check_code
  1142. * @return array|false
  1143. */
  1144. public function bind($params, $is_check_code = 0)
  1145. {
  1146. try {
  1147. $mall_setting = Yii::$app->services->setting->mall->get($params['mall_id'], 'user');
  1148. $check_sms_code_value = $mall_setting['check_sms_code'] ?? 1;
  1149. //手机验证码验证
  1150. if (YII_ENV == 'prod' && $check_sms_code_value) {
  1151. //短信验证码
  1152. if (!Sms::checkValidateCode($params['mobile'], $params['captcha'],Sms::SMS_TYPE_MOBILE_BIND)) throw new Exception('验证码不正确');
  1153. }
  1154. $where = [['mobile' => $params['mobile']], ['mall_id' => $params['mall_id']], ['status' => [StatusEnum::DISABLED, StatusEnum::ENABLED]]];
  1155. $user = User::getOne($where);
  1156. $openid = \Yii::$app->request->headers->get('openid');
  1157. $where = [];
  1158. $where['openid'] = $openid;
  1159. $where['mall_id'] = $params['mall_id'];
  1160. $where['status'] = StatusEnum::ENABLED;
  1161. if (!empty($params["platform"])) $where['platform'] = $params["platform"];
  1162. $user_info = UserInfo::findOne($where);
  1163. if (!empty($user)) {
  1164. if (!empty($user_info)) {
  1165. $res = UserInfo::findOne(['mall_id' => $params['mall_id'], 'user_id' => $user->id,'platform'=>$user_info->platform,'status'=>StatusEnum::ENABLED]);
  1166. if (!empty($res)) throw new Exception('该手机号码已经被注册');
  1167. $user_info->user_id = $user->id;
  1168. $user_info->save();
  1169. return \Yii::$app->services->apiAccessToken->getAccessToken($user, 'api');
  1170. }
  1171. throw new Exception('手机号已被使用');
  1172. } else {
  1173. //小鹅通特殊操作
  1174. if ($user_info && $user_info->user_id) {
  1175. $params['id'] = $user_info->user_id;
  1176. $user = User::getOne([['id' => $user_info->user_id], ['status' => [StatusEnum::DISABLED, StatusEnum::ENABLED]]]);
  1177. if ($user && $user->parent_id) {
  1178. $params['parent_id'] = $user->parent_id;
  1179. }
  1180. }
  1181. $password = substr($params['mobile'],-6,6);
  1182. $params['username'] = $params['mobile'];
  1183. $params['password'] = \Yii::$app->getSecurity()->generatePasswordHash($password);
  1184. $params['last_login_at'] = time();
  1185. if (!empty($user_info)) {
  1186. $platform_data = json_decode($user_info->platform_data, true);
  1187. $params['nickname'] = $params['avatar_url'] = '';
  1188. if(!empty($platform_data['nickname'])&&!empty($platform_data['headimgurl'])){
  1189. $params['nickname'] = (string)$platform_data['nickname'];
  1190. $params['avatar_url'] = $platform_data['headimgurl'];
  1191. }
  1192. if(!empty($platform_data['nickName'])&&!empty($platform_data['avatarUrl'])){
  1193. $params['nickname'] = (string)$platform_data['nickName'];
  1194. $params['avatar_url'] = $platform_data['avatarUrl'];
  1195. }
  1196. $params['platform'] = $user_info->platform??'';
  1197. }
  1198. $user = User::setData($params);
  1199. if ($user == false) throw new Exception(User::getStaticError());
  1200. // 公众号授权手机触发注册事件
  1201. $event = new UserRegisterEvent($user->mall_id);
  1202. $data = ['user_id' => $user->id, 'platform' => $user->platform];
  1203. \Yii::$app->services->events->dispatch($event, $data, $event->listeners);
  1204. if (!empty($user_info)) {
  1205. $user_info->user_id = $user->id;
  1206. $user_info->save();
  1207. }
  1208. \Yii::$app->user->login($user);
  1209. // 触发登录成功事件,事务提交完成后触发
  1210. $event = new UserLoginEvent($user->mall_id);
  1211. \Yii::$app->services->events->dispatch($event, ['mall_id' => $user->mall_id, 'nickname' => $user->nickname, 'user_id' => $user->id, 'platform' => $user->platform, 'ip' => ArrayHelper::get_client_ip()], $event->listeners);
  1212. }
  1213. return \Yii::$app->services->apiAccessToken->getAccessToken($user, 'api');
  1214. } catch (\Exception $e) {
  1215. self::$static_error = $e->getMessage();
  1216. return false;
  1217. }
  1218. }
  1219. /**
  1220. * @desc:修改个人资料
  1221. * @Author: hua
  1222. * @Date: 2021/10/28
  1223. * @Time: 9:56
  1224. * @Copyright: copyright (c) 2021 广东七件事集团
  1225. * @param array $params
  1226. * @return User|false
  1227. * @throws \yii\db\Exception
  1228. */
  1229. public static function change(array $params)
  1230. {
  1231. $t = Yii::$app->db->beginTransaction();
  1232. try {
  1233. $operator = '';
  1234. if (!empty($params['id'])) {
  1235. $model = self::findOne(['id' => $params['id'], 'status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]]);
  1236. if (empty($model)) throw new Exception('服务不存在或已删除');
  1237. $old_mobile = $model['mobile'];
  1238. $operator = $model['id'];
  1239. } else {
  1240. $model = new self();
  1241. $model->loadDefaultValues();
  1242. }
  1243. if (!empty($params['birthday'])) $params['birthday'] = strtotime($params['birthday'] . ' 00:00:00');
  1244. if (!$model->load($params, '') || !$model->save()) throw new Exception($model->getErrorMessage());
  1245. if(!empty($old_mobile)&&!empty($params['mobile'])&&$params['mobile'] != $old_mobile){
  1246. $param['new_mobile'] = $params['mobile'];
  1247. $param['old_mobile'] = $old_mobile;
  1248. $param['operator'] = (string)$operator;
  1249. $param['user_id'] = $params['id'];
  1250. $param['mall_id'] = $params['mall_id'];
  1251. $param['type'] = 1;
  1252. ChangeMobileLog::setData($param);
  1253. }
  1254. $t->commit();
  1255. return $model;
  1256. } catch (Exception $e) {
  1257. $t->rollBack();
  1258. self::$static_error = $e->getMessage();
  1259. return false;
  1260. }
  1261. }
  1262. /**
  1263. * 异步导出至下载中心
  1264. * @Author zhenjianhua
  1265. * @DateTime 2021-11-12 16:01:25
  1266. * @copyright: Copyright (c) 2020 广东七件事集团
  1267. * @param array $data
  1268. * @return void
  1269. */
  1270. public static function exportHandleOld(array $data)
  1271. {
  1272. date_default_timezone_set('PRC');
  1273. $download_id = $data['download_id'] ?? 0;
  1274. $download = Download::findOne(['id' => $download_id]);
  1275. if($download){
  1276. try{
  1277. $params = json_decode($download->params,true);
  1278. $filename = $download->name;
  1279. $type = $download->type;
  1280. $mall_id = $params['mall_id'];
  1281. $model = self::find();
  1282. $model->where(['mall_id'=>$params['mall_id']]);
  1283. if (!empty($params['register_start'])) {
  1284. $model->andWhere(['>=', 'created_at', strtotime($params['register_start'])]);
  1285. }
  1286. if (!empty($params['register_end'])) {
  1287. $model->andWhere(['<=', 'created_at', strtotime($params['register_end'])]);
  1288. }
  1289. $model->with(['parent','userLevel'=>function($query)use($mall_id){
  1290. $query->where(['mall_id'=>$mall_id]);
  1291. },'userWallet']);
  1292. $fields = $params['fields'];
  1293. $fields_arr = self::fieldsList();
  1294. $have_select_field_arr = [];
  1295. foreach ($fields as $value){
  1296. $have_select_field_arr[]=$fields_arr[$value];
  1297. }
  1298. $csv = new CsvTool();
  1299. $csv->filename = $filename;
  1300. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  1301. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  1302. $csv->header = $have_select_field_arr;
  1303. foreach ($model->batch() as $users) {
  1304. // $users 是一个包含100条或小于100条用户表数据的数组
  1305. empty($csv->header) && $csv->header =array_keys($users[0]);
  1306. $csv->data = array_map(function($user)use($fields){
  1307. $row = [];
  1308. foreach ($fields as $field){
  1309. if($field=='platform'){
  1310. $temp = self::TYPE_NAME_LIST;
  1311. if(!empty($user['platform'])&&!empty($temp[$user['platform']])){
  1312. $row[] = $temp[$user['platform']]??'';
  1313. }else{
  1314. $row[] = $temp[$user['platform']] = '';
  1315. }
  1316. }else if($field=='parent_mobile'){
  1317. $row[] = $user['parent']['mobile']??'';
  1318. }else if($field=='level'){
  1319. $row[] = $user['userLevel']['name']??'默认等级';
  1320. }else if($field=='created_at'){
  1321. $row[] = !empty($user['created_at'])?date('Y-m-d H:i:s',$user['created_at']):'';
  1322. }else if(in_array($field,['commission', 'balance', 'score'])){
  1323. $wallet = $user->userWallet;
  1324. $row[] = $wallet[$field] ?? 0;
  1325. }else{
  1326. if((isset($user[$field]))) $row[]=$user[$field]??'';
  1327. }
  1328. }
  1329. return $row;
  1330. },$users);
  1331. $csv->export();
  1332. }
  1333. $url = $csv->upload($params['mall_id'],$csv->relative_filename);
  1334. $download->status = 2;
  1335. $download->url = $url;
  1336. $download->num = $csv->line_num;
  1337. $download->size = $csv->file_size;
  1338. $res = $download->save();
  1339. return true;
  1340. }catch(Exception $e){
  1341. $download->status = 3;
  1342. self::setStaticError($e->getMessage());
  1343. return false;
  1344. }
  1345. }
  1346. }
  1347. public static function exportHandle(array $data)
  1348. {
  1349. date_default_timezone_set('PRC');
  1350. $download_id = $data['download_id'] ?? 0;
  1351. $download = Download::findOne(['id' => $download_id]);
  1352. if($download){
  1353. try{
  1354. $params = json_decode($download->params,true);
  1355. $isChanjetExport = !empty($params['is_chanjet_addons']);
  1356. $filename = $download->name;
  1357. $type = $download->type;
  1358. $mall_id = $params['mall_id'];
  1359. $fields = $params['fields'];
  1360. $fields_arr = $isChanjetExport ? self::chanjetFieldsList() : self::fieldsList();
  1361. $have_select_field_arr = [];
  1362. foreach ($fields as $value){
  1363. $have_select_field_arr[]=$fields_arr[$value];
  1364. }
  1365. $csv = new CsvTool();
  1366. $csv->filename = $filename;
  1367. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  1368. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  1369. $csv->header = $have_select_field_arr;
  1370. $select = 'u.id,u.id as user_id,nickname,username,avatar_url,mobile,level,u.status,platform,u.is_inviter,
  1371. balance,commission,u.source,score,u.created_at,u.parent_id';
  1372. $with = [];
  1373. // 畅捷通导出
  1374. if ($isChanjetExport) {
  1375. $with[] = 'addonsChanjetRequest';
  1376. $select .= ',u.updated_at,uw.frozen_commission';
  1377. $user_level_arr = UserLevel::getUserLevelArr($mall_id);
  1378. }
  1379. $query = User::find()
  1380. ->alias('u')
  1381. ->with($with)
  1382. ->leftJoin(UserWallet::tableName() . 'uw', 'uw.user_id = u.id')
  1383. ->where(['u.mall_id'=>$params['mall_id']])
  1384. ->andWhere(['u.status'=>[StatusEnum::ENABLED,StatusEnum::DISABLED]]);
  1385. // 用户条件筛选搜索
  1386. // 根据用户id精准查找
  1387. if (!empty($params['user_id'])){
  1388. $query->andWhere(['u.id'=>$params['user_id']]);
  1389. }
  1390. //根据会员标签进行搜索
  1391. if (!empty($params['memberLabelIds'])){
  1392. $cateIdArr = [];
  1393. foreach ($params['memberLabelIds'] as $value){
  1394. $temp_arr = explode('_', $value);
  1395. $cateIdArr[$temp_arr[0]][] = $temp_arr[1];
  1396. }
  1397. foreach ($cateIdArr as $cate_id => $memberLabelIds) {
  1398. $query->andWhere(['uml.'.'cate_id' . $cate_id=>$memberLabelIds]);
  1399. }
  1400. $query->leftJoin(UserMemberLabel::tableName() . 'uml', 'uml.user_id = u.id');
  1401. }
  1402. // 根据用户昵称或手机号码查找
  1403. if (!empty($params['nickname_or_mobile'])){
  1404. $query->andWhere([
  1405. 'OR',
  1406. ['like', 'u.nickname', $params['nickname_or_mobile']],
  1407. ['like', 'u.mobile', $params['nickname_or_mobile']],
  1408. ]);
  1409. }
  1410. // 查找上级推荐人
  1411. if (!empty($params['parent_id'])){
  1412. $query->andWhere(['u.parent_id'=>$params['parent_id']]);
  1413. }
  1414. // 查找注册来源渠道
  1415. if (!empty($params['source']) && $params['source'] != 'all') {
  1416. $query->andWhere(['u.platform'=>$params['source']]);
  1417. }
  1418. // 根据用户渠道查找
  1419. if (isset($params['channel']) && $params['channel'] != 'all'){
  1420. $query->andWhere(['u.platform'=>$params['channel']]);
  1421. }
  1422. // 查找注册时间
  1423. if (!empty($params['register_start'])) {
  1424. $query->andWhere(['>=', 'u.created_at', strtotime($params['register_start'])]);
  1425. }
  1426. if (!empty($params['register_end'])) {
  1427. $query->andWhere(['<=', 'u.created_at', strtotime($params['register_end'])]);
  1428. }
  1429. // 最后更新时间
  1430. if (!empty($params['update_start'])) {
  1431. $query->andWhere(['>=', 'u.updated_at', strtotime($params['update_start'])]);
  1432. }
  1433. if (!empty($params['update_end'])) {
  1434. $query->andWhere(['<=', 'u.updated_at', strtotime($params['update_end'])]);
  1435. }
  1436. // 查找余额范围
  1437. if (!empty($params['balance_start'])) {
  1438. $query->andWhere(['>', 'uw.balance', $params['balance_start']]);
  1439. }
  1440. if (!empty($params['balance_end'])) {
  1441. $query->andWhere(['<', 'uw.balance', $params['balance_end']]);
  1442. }
  1443. // 查找积分范围
  1444. if (!empty($params['score_start'])) {
  1445. $query->andWhere(['>', 'uw.score', $params['score_start']]);
  1446. }
  1447. if (!empty($params['score_end'])) {
  1448. $query->andWhere(['<', 'uw.score', $params['score_end']]);
  1449. }
  1450. // 查找佣金范围
  1451. if (!empty($params['commission_start']) && !empty($params['commission_end'])){
  1452. $query->andWhere(['between', 'uw.total_commission', $params['commission_start'], $params['commission_end']]);
  1453. }
  1454. if (!empty($params['commission_start']) && empty($params['commission_end'])){
  1455. $query->andWhere(['>', 'uw.total_commission', $params['commission_start']]);
  1456. }
  1457. if (empty($params['commission_start']) && !empty($params['commission_end'])){
  1458. $query->andWhere(['<', 'uw.total_commission', $params['commission_end']]);
  1459. }
  1460. // 黑名单
  1461. if (!empty($params['black'])) {
  1462. $black = $params['black'] == 2 ? 1 : 0;
  1463. $query->andWhere(['=', 'u.status', $black]);
  1464. }
  1465. //添加等级查询
  1466. $levels = UserLevel::getListIndexByLevelColumn($params['mall_id']);
  1467. $data['levels'] = $levels;
  1468. if (isset($params['level']) && $params['level'] !== ''){
  1469. $query->andWhere(['=', 'u.level', $params['level']]);
  1470. }
  1471. //根据会员标签进行搜索
  1472. if (!empty($params['lableValue'])){
  1473. $user_label_where = ['mall_id' =>$params['mall_id'],'status' => StatusEnum::ENABLED,'label_id' => $params['lableValue']];
  1474. $user_ids = UserLabelRelationship::find()->select("user_id")->where($user_label_where)->asArray()->column();
  1475. $query->andWhere([ 'u.id'=>$user_ids]);
  1476. }
  1477. // 查找上级推荐人手机号
  1478. if (isset($params['p_mobile']) && $params['p_mobile'] !== '') {
  1479. $query->andWhere([
  1480. 'in',
  1481. 'u.parent_id',
  1482. User::find()
  1483. ->where([
  1484. 'mall_id' => $mall_id,
  1485. 'status' => [StatusEnum::ENABLED,StatusEnum::DISABLED]
  1486. ])
  1487. ->andWhere(['like', 'mobile', $params['p_mobile']])
  1488. ->select('id')
  1489. ->column()
  1490. ]);
  1491. }
  1492. $with = ['parent','userLevel'=>function($qu)use($mall_id){
  1493. $qu->where(['mall_id'=>$mall_id, 'status' => StatusEnum::ENABLED]);
  1494. }];
  1495. //导出选择了数字币 增加对应查询
  1496. if (in_array('digital', $params['fields'], true)) {
  1497. $with['userDigital'] = function($query) {
  1498. $query->select(['user_id', 'digital']);
  1499. };
  1500. }
  1501. $query->orderBy('u.id desc')->select($select)
  1502. ->with($with);
  1503. $i = 1;
  1504. $page_size = 1000;
  1505. $data_list = [];
  1506. while ($users = $query
  1507. ->offset(($i - 1) * $page_size)->groupBy('user_id')->limit($page_size)->asArray()->all()) {
  1508. // $users 是一个包含100条或小于100条用户表数据的数组
  1509. empty($csv->header) && $csv->header =array_keys($users[0]);
  1510. if((in_array('redpack',$fields)||in_array('frozen_redpack',$fields))&&MallAddons::isInstall($mall_id,'Redpack')){
  1511. $user_id_arr = array_column($users,'id');
  1512. $redpack_wallet_list = RedpackWallet::lists(['where'=>[['user_id'=>$user_id_arr],['status'=>StatusEnum::ENABLED]],'index'=>'user_id']);
  1513. }
  1514. foreach($users as $user){
  1515. $row = [];
  1516. foreach ($fields as $field){
  1517. if($field=='platform'){
  1518. $temp = self::TYPE_NAME_LIST;
  1519. if(!empty($user['platform'])&&!empty($temp[$user['platform']])){
  1520. $row[] = $temp[$user['platform']]??'';
  1521. }else{
  1522. $row[] = $temp[$user['platform']] = '';
  1523. }
  1524. }else if($field=='parent_mobile'){
  1525. $row[] = $user['parent']['mobile']??'';
  1526. }else if($field=='level'){
  1527. $row[] = $user['userLevel']['name'] ?? MallSetting::getDefaultLevelName($mall_id);
  1528. }else if($field=='created_at'){
  1529. $row[] = !empty($user['created_at'])?date('Y-m-d H:i:s',$user['created_at']):'';
  1530. }else if(in_array($field,['commission', 'balance', 'score'])){
  1531. $row[] =$user[$field]??0;
  1532. }else if(in_array($field,['redpack', 'frozen_redpack'])){
  1533. if(isset($redpack_wallet_list)&&isset($redpack_wallet_list[$user['id']])){
  1534. $row[] = $redpack_wallet_list[$user['id']][$field];
  1535. }else{
  1536. $row[] = 0;
  1537. }
  1538. }else if ($field === 'chanjet_updated_at') {
  1539. if (isset($user['addonsChanjetRequest']['updated_at'])) {
  1540. $row[] = date('Y-m-d H:i:s', $user['addonsChanjetRequest']['updated_at']);
  1541. }else {
  1542. $row[] = '';
  1543. }
  1544. }else if ($field === 'chanjet_request_status') {
  1545. if (isset($user['addonsChanjetRequest']['request_status'])) {
  1546. $row[] = AddonsChanjetRequestEnum::getMap()[$user['addonsChanjetRequest']['request_status']] ?? '';
  1547. }else {
  1548. $row[] = '';
  1549. }
  1550. }else if ($field === 'level_name') {
  1551. $row[] = empty($user_level_arr[$user['level']]) ? MallSetting::getDefaultLevelName($mall_id) : $user_level_arr[$user['level']];
  1552. }else if ($field === 'nickname') {//会员昵称太多特殊符号,发现有+这个符号加上某些特殊符号一起导出会导致文件乱码,现在把+替换成空格导出
  1553. $row[] = $user['nickname'] ? str_replace('+', ' ', $user['nickname']) : '';
  1554. }else if ($field === 'digital') {
  1555. $user_digital = $user['userDigital'] ?? [];
  1556. $row[] = $user_digital['digital'] ?? 0;
  1557. }
  1558. else{
  1559. if((isset($user[$field]))) $row[]=$user[$field]??'';
  1560. }
  1561. }
  1562. $data_list[]=$row;
  1563. }
  1564. $i++;
  1565. }
  1566. $csv->data = $data_list;
  1567. $csv->export();
  1568. $url = $csv->upload($params['mall_id'],$csv->relative_filename);
  1569. $download->status = 2;
  1570. $download->url = $url;
  1571. $download->num = $csv->line_num;
  1572. $download->size = $csv->file_size;
  1573. $res = $download->save();
  1574. return true;
  1575. }catch(Exception $e){
  1576. $download->status = 3;
  1577. self::setStaticError($e->getMessage());
  1578. return false;
  1579. }
  1580. }
  1581. }
  1582. public static function fieldsList()
  1583. {
  1584. return [
  1585. 'platform' => '所属平台',
  1586. 'id' => '用户ID',
  1587. 'username' => '真实姓名',
  1588. 'nickname' => '昵称',
  1589. 'mobile' => '绑定手机号',
  1590. 'level' => '会员等级',
  1591. 'created_at' => '加入时间',
  1592. 'parent_mobile' => '上级手机号码',
  1593. 'commission'=> '佣金',
  1594. 'balance' => '余额',
  1595. 'score' => '积分',
  1596. 'redpack' => '激活红包',
  1597. 'frozen_redpack' => '冻结红包',
  1598. 'digital' => '数字币',
  1599. ];
  1600. }
  1601. public static function chanjetFieldsList()
  1602. {
  1603. return [
  1604. 'id' => '会员ID',
  1605. 'username' => '会员',
  1606. 'mobile' => '手机号',
  1607. 'level_name' => '会员等级',
  1608. 'balance' => '余额',
  1609. 'frozen_commission'=> '佣金',
  1610. 'created_at' => '加入时间',
  1611. 'updated_at' => '最后更新时间',
  1612. 'chanjet_updated_at' => '最后推送时间',
  1613. 'chanjet_request_status' => '推送状态',
  1614. ];
  1615. }
  1616. /**
  1617. * @desc:推广身份
  1618. * @Author: hua
  1619. * @Date: 2021/11/22
  1620. * @Time: 9:20
  1621. * @Copyright: copyright (c) 2021 广东七件事集团
  1622. * @param $user_id
  1623. * @param $mall_id
  1624. * @return array
  1625. */
  1626. public static function getUserPromoteIdentity($user_id, $mall_id)
  1627. {
  1628. $data = [];
  1629. $addons_list = MallAddons::getEnableAddonsName($mall_id);
  1630. $arr = [
  1631. [
  1632. 'addons'=>AddonsEnum::ADDONS_NAME_DISTRIBUTION,
  1633. 'name'=>'分销商等级',
  1634. 'model'=>Distribution::class
  1635. ],
  1636. [
  1637. 'addons'=>AddonsEnum::ADDONS_NAME_AGENT,
  1638. 'name'=>'渠道等级',
  1639. 'model'=>Agent::class
  1640. ],
  1641. [
  1642. 'addons'=>AddonsEnum::ADDONS_NAME_BOSS,
  1643. 'name'=>'股东等级',
  1644. 'model'=>AddonsBoss::class
  1645. ],
  1646. [
  1647. 'addons'=>AddonsEnum::ADDONS_NAME_BOSS_WEIGHT,
  1648. 'name'=>'股东等级',
  1649. 'model'=>\addons\BossWeight\common\models\AddonsBoss::class
  1650. ]
  1651. ];
  1652. $where = [];
  1653. $where[]=['user_id'=>$user_id];
  1654. $where[]=['mall_id'=>$mall_id];
  1655. $where[]=['<>','status',StatusEnum::DELETE];
  1656. foreach ($arr as $item){
  1657. if(!in_array($item['addons'],$addons_list)) continue;
  1658. if($item['addons']==AddonsEnum::ADDONS_NAME_BOSS_WEIGHT){
  1659. $with_key = 'bossLevel';
  1660. }else{
  1661. $with_key = lcfirst($item['addons']).'Level';
  1662. }
  1663. $result = $item['model']::getOne($where,true,[$with_key=>function($query)use($mall_id){
  1664. $query->where(['mall_id'=>$mall_id])->andWhere(['<>','status',StatusEnum::DELETE]);
  1665. }]);
  1666. if (!empty($result)) {
  1667. $data[] = [
  1668. 'name' => $item['name'],
  1669. 'level_name' => $result[$with_key]['name']??'默认等级',
  1670. 'time' => !empty($result['upgrade_level_at']) ? date('Y-m-d H:i:s', $result['upgrade_level_at']) : '-'
  1671. ];
  1672. }
  1673. }
  1674. return $data;
  1675. }
  1676. /**
  1677. * 用户资产
  1678. * @Author: lun
  1679. * @DateTime: 2021/11/23 0023 19:12
  1680. * @Copyright: copyright (c) 2021 广东七件事集团
  1681. * @param $mall_id
  1682. * @param $user_id
  1683. */
  1684. public static function property($mall_id, $user_id)
  1685. {
  1686. // 查询用户资产
  1687. $user_wallet = UserWallet::findOne(['mall_id' => $mall_id, 'user_id' => $user_id, 'status' => StatusEnum::ENABLED]);
  1688. // 余额
  1689. $balance_name = Yii::$app->services->setting->mall->get($mall_id,'balance.balance_name');
  1690. $data[] = [
  1691. 'englists' => 'balance',
  1692. 'name' => $balance_name,
  1693. 'list' => [['name' => '可用'.$balance_name, 'val' => $user_wallet->balance], ['name' => '已使用', 'val' => $user_wallet->total_use_balance], ['name' => '已提现', 'val' => $user_wallet->balance_withdrawal]],
  1694. ];
  1695. // 积分
  1696. $score_name = Yii::$app->services->setting->mall->get($mall_id,'score.score_name');
  1697. $data[] =[
  1698. 'englists' => 'score',
  1699. 'name' => $score_name,
  1700. 'list' => [['name' => '可用' . $score_name, 'val' => $user_wallet->score], ['name' => '已使用' . $score_name, 'val' => $user_wallet->total_use_score, '转余额' => '0']],// TODO 转余额功能暂未开发
  1701. ];
  1702. // 佣金
  1703. $data[] =[
  1704. 'englists' => 'commission',
  1705. 'name' => '佣金',
  1706. 'list' => [['name' => '当前佣金', 'val' => $user_wallet->commission], ['name' => '待结算', 'val' => $user_wallet->frozen_commission], ['name' => '已提现', 'val' => $user_wallet->commission_withdrawal]],
  1707. ];
  1708. // 红包拓客
  1709. $is_install_redpack = MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_REDPACK);
  1710. if($is_install_redpack){
  1711. // 红包拓客
  1712. $user_redpack = RedpackWallet::getOne([['mall_id' => $mall_id,'user_id' => $user_id,'status' => StatusEnum::ENABLED]],true);
  1713. $data[] =[
  1714. 'englists' => 'redpack',
  1715. 'name' => '红包',
  1716. 'list' => [['name' => '当前可用激活红包', 'val' => $user_redpack['redpack'] ?? 0], ['name' => '可用冻结红包', 'val' => $user_redpack['frozen_redpack'] ?? 0], ['name' => '已使用激活红包', 'val' => $user_redpack['total_use_redpack'] ?? 0],['name' => '已使用冻结红包', 'val' => $user_redpack['total_use_frozen_redpack'] ?? 0]],
  1717. ];
  1718. }
  1719. // 优惠券
  1720. $is_install_coupon = MallAddons::isInstall($mall_id, 'Coupon');
  1721. if ($is_install_coupon) {
  1722. //统计已经使用
  1723. $coupon_where = "acu.status = ".StatusEnum::ENABLED." and acu.user_id = ".$user_id;
  1724. $coupon_where .= " and acu.giving_status <> 2";
  1725. $coupon_where .= " and acu.coupon_status = 2";
  1726. $coupon = AddonsCouponUserRelate::find()
  1727. ->alias("acu")
  1728. ->leftJoin(AddonsCoupon::tableName() . ' as ac', 'ac.id = acu.coupon_id')
  1729. ->select('count(acu.id) num , acu.is_use')
  1730. ->where($coupon_where)
  1731. ->groupBy('acu.is_use')
  1732. ->asArray()
  1733. ->all();
  1734. // 统计门店已经使用
  1735. // $overdue_where = "end_time <".time()." and status = ".StatusEnum::ENABLED." and user_id = ".$user_id;
  1736. $overdue_where = ['user_id' => $user_id, 'coupon_status' => AddonsCouponEnum::STATUS_EXPIRED];
  1737. $overdue_coupon = AddonsCouponUserRelate::find()->where($overdue_where)->count();
  1738. // 统计已失效
  1739. $failure_where = ['user_id' => $user_id, 'coupon_status' => AddonsCouponEnum::STATUS_FAILURE];
  1740. $failure_coupon = AddonsCouponUserRelate::find()->where($failure_where)->count();
  1741. $couponCount = [];
  1742. foreach ($coupon as $v) $couponCount[$v['is_use']] = $v['num'];
  1743. //统计已经过期(判断插件是否安装)
  1744. if (MallAddons::isInstall($mall_id, AddonsEnum::ADDONS_NAME_STORE)) {
  1745. $storeCoupon = StoreCouponUserRelate::find()
  1746. ->alias("acu")
  1747. ->leftJoin(AddonsCoupon::tableName() . ' as ac', 'ac.id = acu.coupon_id')
  1748. ->select(['sum(if(acu.is_use = 0, 1, 0)) as num', 'sum(if(acu.is_use = 1, 1, 0)) as used',
  1749. 'sum(if(acu.coupon_status = 3, 1, 0)) as expire', 'sum(if(acu.coupon_status = 4, 1, 0)) as lose'])
  1750. ->where($coupon_where)
  1751. ->andWhere(['from_type' => 1])
  1752. ->asArray()
  1753. ->one();
  1754. } else {
  1755. $storeCoupon = [
  1756. 'num' => 0,
  1757. 'used' => 0,
  1758. 'expire' => 0,
  1759. 'lose' => 0
  1760. ];
  1761. }
  1762. $data[] =[
  1763. 'englists' => 'coupon',
  1764. 'name' => '优惠券',
  1765. 'list' => [
  1766. ['name' => '可使用', 'val' => ($couponCount['0'] + $storeCoupon['num']) ?? 0],
  1767. ['name' => '已使用', 'val' => ($couponCount['1'] + $storeCoupon['used']) ?? 0],
  1768. ['name' => '已过期', 'val' => ($overdue_coupon + $storeCoupon['expire'])],
  1769. ['name' => '已失效', 'val' => ($failure_coupon + $storeCoupon['lose'])],
  1770. ],
  1771. ];
  1772. }
  1773. // 次卡
  1774. $is_install_secondary_card = MallAddons::isInstall($mall_id, 'SecondaryCard');
  1775. if ($is_install_secondary_card) {
  1776. $query = SecondaryCardOrder::find()->where(['mall_id'=>$mall_id,'user_id'=>$user_id,'status'=>StatusEnum::ENABLED])->andWhere(['>','open_time',0]);
  1777. $can_query = clone $query;
  1778. $can_use = $can_query->andWhere(['=','order_status',0])->andWhere(['>','verifiable_times',0])->count();
  1779. $use_query = clone $query;
  1780. $use = $use_query->andWhere(['=','verifiable_times',0])->count();
  1781. $exp_query = clone $query;
  1782. $exp = $exp_query->andWhere(['order_status'=>2])->count();
  1783. $data[] =[
  1784. 'englists' => 'secondary_card',
  1785. 'name' => '次卡',
  1786. 'list' => [['name' => '可使用', 'val' => $can_use ?? 0], ['name' => '已使用', 'val' => $use ?? 0], ['name' => '已过期', 'val' => $exp ?? 0]],// TODO 已过期优惠券暂无开发
  1787. ];
  1788. }
  1789. //数字币
  1790. $digital_is_enable = \Yii::$app->services->setting->mall->get($mall_id, 'digital.is_enable');
  1791. if ($digital_is_enable) {
  1792. $digital = UserDigital::getOne([['mall_id' => $mall_id, 'user_id' => $user_id, 'status' => StatusEnum::ENABLED]], true);
  1793. $digital_use = DigitalLog::find()->where([
  1794. 'mall_id' => $mall_id,
  1795. 'user_id' => $user_id,
  1796. 'status' => StatusEnum::ENABLED,
  1797. 'change_type' => 'sub',
  1798. 'from_type' => 'transfer',
  1799. 'capital_type' => 'transfer_give',
  1800. 'send_status' => DigitalLog::SEND_STATUS_NORMAL
  1801. ])
  1802. ->sum('change_num');
  1803. $data[] =[
  1804. 'englists' => 'digital',
  1805. 'name' => '数字币',
  1806. 'list' => [['name' => '可使用', 'val' => $digital['digital'] ?? 0], ['name' => '已使用', 'val' => $digital_use ?? 0], ['name' => '待结算', 'val' => $digital['frozen_digital'] ?? 0]],
  1807. ];
  1808. }
  1809. return $data;
  1810. }
  1811. /**
  1812. * 分片查询
  1813. * @Author: lun
  1814. * @DateTime: 2021/12/4 0004 10:21
  1815. * @Copyright: copyright (c) 2021 广东七件事集团
  1816. * @param $params
  1817. * @param $user_id
  1818. * @param $offset
  1819. * @param $limit
  1820. * @return bool|mixed
  1821. */
  1822. public static function batchGetUsers($params, $part, $limit){
  1823. $params['offset'] = ($part-1)*$limit;
  1824. $params['limit'] = $limit;
  1825. return self::lists($params);
  1826. }
  1827. /**
  1828. * @desc:订单支付,获取发展资格
  1829. * @Author: hua
  1830. * @Date: 2021/12/6
  1831. * @Time: 15:48
  1832. * @Copyright: copyright (c) 2021 广东七件事集团
  1833. * @param $mall_id
  1834. * @param $user_id
  1835. * @param $order
  1836. * @return bool
  1837. */
  1838. public static function getInviterByPay($mall_id, $user_id, $order){
  1839. $relation_setting = \Yii::$app->services->setting->mall->get($mall_id, 'user_relation_setting.relation_setting');
  1840. $is_inviter = 1;
  1841. if (!empty($relation_setting)) {
  1842. $relation_setting = json_decode($relation_setting, true);
  1843. //付款后
  1844. if (!empty($relation_setting['status']) && !empty($relation_setting['buy_compute_way']) && $relation_setting['buy_compute_way'] == 1) {
  1845. $user = User::findOne(['id' => $user_id]);
  1846. if ($user->is_inviter) return true;
  1847. $where = [
  1848. 'mall_id' => $mall_id,
  1849. 'user_id' => $user_id,
  1850. 'pay_status' => 1,
  1851. 'order_status' => [OrderStatusEnum::PAY,
  1852. OrderStatusEnum::SHIPMENTS, OrderStatusEnum::SING, OrderStatusEnum::ACCOMPLISH]
  1853. ];
  1854. if (!empty($relation_setting['get_power_way']) && $relation_setting['get_power_way'] == 3) {
  1855. User::getIsInviterByOr($user, $where, $order, $is_inviter, $relation_setting);
  1856. }
  1857. if (!empty($relation_setting['get_power_way']) && $relation_setting['get_power_way'] == 4) {
  1858. //与
  1859. User::getIsInviterByAnd($user, $where, $order, $is_inviter, $relation_setting);
  1860. }
  1861. }
  1862. }
  1863. }
  1864. /**
  1865. * @desc:订单完成,获取发展资格
  1866. * @Author: hua
  1867. * @Date: 2021/12/6
  1868. * @Time: 15:49
  1869. * @Copyright: copyright (c) 2021 广东七件事集团
  1870. * @param $mall_id
  1871. * @param $user_id
  1872. * @param $order
  1873. * @return bool
  1874. */
  1875. public static function getInviterByOrderFinish($mall_id, $user_id, $order){
  1876. $relation_setting = \Yii::$app->services->setting->mall->get($mall_id, 'user_relation_setting', 'relation_setting');
  1877. $is_inviter = 1;
  1878. if (!empty($relation_setting['relation_setting'])) {
  1879. $relation_setting = json_decode($relation_setting['relation_setting'], true);
  1880. //订单完成
  1881. if (!empty($relation_setting['status']) && !empty($relation_setting['buy_compute_way']) && $relation_setting['buy_compute_way'] == 2) {
  1882. $user = User::findOne(['id' => $user_id]);
  1883. if ($user->is_inviter) return true;
  1884. $where = [
  1885. 'mall_id' => $mall_id,
  1886. 'user_id' => $user_id,
  1887. 'pay_status' => 1,
  1888. 'order_status' => [ OrderStatusEnum::ACCOMPLISH]
  1889. ];
  1890. if (!empty($relation_setting['get_power_way']) && $relation_setting['get_power_way'] == 3) {
  1891. User::getIsInviterByOr($user, $where, $order, $is_inviter, $relation_setting);
  1892. }
  1893. if (!empty($relation_setting['get_power_way']) && $relation_setting['get_power_way'] == 4) {
  1894. //与
  1895. User::getIsInviterByAnd($user, $where, $order, $is_inviter, $relation_setting);
  1896. }
  1897. }
  1898. }
  1899. }
  1900. /**
  1901. * @desc:或
  1902. * @Author: hua
  1903. * @Date: 2021/12/6
  1904. * @Time: 15:49
  1905. * @Copyright: copyright (c) 2021 广东七件事集团
  1906. * @param $user
  1907. * @param $where
  1908. * @param $order
  1909. * @param $is_inviter
  1910. * @param $relation_setting
  1911. * @return bool
  1912. */
  1913. private static function getIsInviterByOr($user,$where,$order,$is_inviter,$relation_setting){
  1914. //或
  1915. //消费次数
  1916. if(!empty($relation_setting['buy_num_selected'])){
  1917. //消费次数
  1918. $counts = Order::find()->where($where)->count();
  1919. if ($counts >= $relation_setting['buy_num']){
  1920. $user->is_inviter = $is_inviter;
  1921. $user->save();
  1922. return true;
  1923. }
  1924. }
  1925. //消费金额
  1926. if(!empty($relation_setting['buy_price_selected'])){
  1927. $total_pay_money = Order::find()->where($where)->sum('pay_money');
  1928. if ($total_pay_money >= $relation_setting['buy_price']){
  1929. $user->is_inviter = $is_inviter;
  1930. $user->save();
  1931. return true;
  1932. }
  1933. }
  1934. //购买商品
  1935. if(!empty($relation_setting['buy_goods_selected'])){
  1936. $res = self::buyGoodsSelected($relation_setting,$order);
  1937. if($res){
  1938. $user->is_inviter = $is_inviter;
  1939. $user->save();
  1940. }
  1941. }
  1942. }
  1943. /**
  1944. * @desc:与
  1945. * @Author: hua
  1946. * @Date: 2021/12/6
  1947. * @Time: 15:49
  1948. * @Copyright: copyright (c) 2021 广东七件事集团
  1949. * @param $user
  1950. * @param $where
  1951. * @param $order
  1952. * @param $is_inviter
  1953. * @param $relation_setting
  1954. * @return false
  1955. */
  1956. private static function getIsInviterByAnd($user,$where,$order,$is_inviter,$relation_setting){
  1957. if(!empty($relation_setting['buy_num_selected'])){
  1958. //消费次数
  1959. $counts = Order::find()->where($where)->count();
  1960. if ($counts < $relation_setting['buy_num']) return false;
  1961. }
  1962. //消费金额
  1963. if(!empty($relation_setting['buy_price_selected'])){
  1964. $total_pay_money = Order::find()->where($where)->sum('pay_money');
  1965. if ($total_pay_money < $relation_setting['buy_price']) return false;
  1966. }
  1967. //购买商品
  1968. if($relation_setting['buy_goods_selected']){
  1969. $res = self::buyGoodsSelected($relation_setting,$order);
  1970. if($res==false) return false;
  1971. }
  1972. $user->is_inviter = $is_inviter;
  1973. $user->save();
  1974. return true;
  1975. }
  1976. /**
  1977. * @desc:购买商品
  1978. * @Author: hua
  1979. * @Date: 2021/12/6
  1980. * @Time: 15:49
  1981. * @Copyright: copyright (c) 2021 广东七件事集团
  1982. * @param $relation_setting
  1983. * @param $user
  1984. * @param $is_inviter
  1985. * @param $order
  1986. * @return bool
  1987. */
  1988. private static function buyGoodsSelected($relation_setting,$order){
  1989. if($relation_setting['buy_goods_way']==1){
  1990. return true;
  1991. }
  1992. if($relation_setting['buy_goods_way']==2){
  1993. $goods_id_arr = array_column($order['detail'],'goods_id');
  1994. $setting_goods_id_arr = $relation_setting['goods_ids'] ?? [];
  1995. if(array_intersect($goods_id_arr,$setting_goods_id_arr)){
  1996. return true;
  1997. }
  1998. }
  1999. if($relation_setting['buy_goods_way']==3){
  2000. $goods_id_arr = array_column($order['detail'],'goods_id');
  2001. $goods_cate_list = GoodsCateRelate::findAll(['goods_id'=>$goods_id_arr]);
  2002. $cate_id_arr = array_column($goods_cate_list,'cate_id');
  2003. $setting_cate_ids = $relation_setting['cat_ids'] ?? [];
  2004. if(array_intersect($cate_id_arr,$setting_cate_ids)){
  2005. return true;
  2006. }
  2007. }
  2008. return false;
  2009. }
  2010. /**
  2011. * 导出渠道人数
  2012. */
  2013. public static function exportTeamSizeHandle(array $data) {
  2014. $download_id = $data['download_id'] ?? 0;
  2015. $download = Download::findOne(['id' => $download_id]);
  2016. if ($download) {
  2017. try {
  2018. $params = json_decode($download->params,true);
  2019. $filename = $download->name;
  2020. $type = $download->type;
  2021. $where['where'][] = ['=', 'u.mall_id', $params['mall_id']];
  2022. $where['where'][] = ['=', 'u.status', StatusEnum::ENABLED];
  2023. if (isset($params['level'])) $where['where'][] = ['=', 'u.level', $params['level']];
  2024. if (isset($params['user_id']) && !empty($params['user_id'])) $where['where'][] = ['=', 'u.id', $params['user_id']];
  2025. if (isset($params['start_time']) && !empty($params['start_time']) && isset($params['end_time']) && !empty($params['end_time'])) {
  2026. $where['where'][] = ['>=', 'u.created_at', strtotime($params['start_time'])];
  2027. $where['where'][] = ['<=', 'u.created_at', strtotime($params['end_time'])];
  2028. }
  2029. if (isset($params['keyword']) && !empty($params['keyword'])) {
  2030. $where['where'][] = [
  2031. 'or',
  2032. ['like', 'u.nickname', $params['keyword']],
  2033. ['like', 'u.mobile', $params['keyword']]
  2034. ];
  2035. }
  2036. $where['alias'] = 'u';
  2037. $where['select'] = ['u.id', 'u.nickname', 'u.mobile', 'u.level level_name', 'uts.user_team_total', 'uts.user_direct_total', 'uts.user_indirect_total', 'uts.team_finish_order_total','uts.team_finish_order_price_total',];
  2038. $where['order'] = 'u.id asc';
  2039. $where['join'] = [['LEFT JOIN', UserTeamStatistics::tableName() .' as uts', 'u.id=uts.user_id']];
  2040. $model = self::find();
  2041. self::paramPack($where, $model);
  2042. $levels = UserLevel::getListIndexByLevelColumn($params['mall_id'], [StatusEnum::ENABLED, StatusEnum::DISABLED]);
  2043. $list = [];
  2044. foreach ($model->asArray()->each(500) as $l) {
  2045. $l['level_name'] = $levels[$l['level_name']] ?? MallSetting::getDefaultLevelName($download->mall_id);
  2046. $l['user_team_total'] = $l['user_team_total'] ?? 0;
  2047. $l['user_direct_total'] = $l['user_direct_total'] ?? 0;
  2048. $l['user_indirect_total'] = $l['user_indirect_total'] ?? 0;
  2049. $l['team_finish_order_total'] = $l['team_finish_order_total'] ?? 0;
  2050. $l['team_finish_order_price_total'] = $l['team_finish_order_price_total'] ?? 0;
  2051. $list[] = $l;
  2052. }
  2053. if (!empty($list)) {
  2054. $csv = new CsvTool();
  2055. $csv->filename = $filename;
  2056. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  2057. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  2058. $csv->header = ['用户ID', '昵称', '手机号','等级','渠道人数', '好友人数', '粉丝人数', '渠道订单数', '团队总业绩'];
  2059. $csv->data = $list;
  2060. $csv->export();
  2061. $csv->updateDown($download, $params['mall_id']);
  2062. }
  2063. } catch (\Exception $e) {
  2064. $download->status = 3;
  2065. $res = $download->save();
  2066. self::setStaticError($e->getMessage());
  2067. return false;
  2068. }
  2069. }
  2070. }
  2071. /**
  2072. * @param $params
  2073. * @return true
  2074. * @throws Exception
  2075. */
  2076. public static function updateMobile($params){
  2077. //检验新的手机号
  2078. $user = User::findOne(['id'=>$params['user_id'],'mall_id'=>$params['mall_id'],'status'=>[StatusEnum::ENABLED,StatusEnum::DISABLED]]);
  2079. if(empty($user)) throw new Exception('会员不存在');
  2080. if($params['mobile'] == $user->mobile) throw new \Exception('手机号与原手机号一致');
  2081. // 如果有安装 好玩小镇那么就需要判断是否能使用当前号码
  2082. if (MallAddons::isInstall($params['mall_id'], AddonsEnum::ADDONS_NAME_FUN_TOWN)) {
  2083. GameAsyncHelper::validMobile($params['mall_id'], $params['mobile']);
  2084. }
  2085. $old_mobile = $user->mobile;
  2086. $where[] = ['mall_id' => $params['mall_id']];
  2087. $where[] = ['mobile' => $params['mobile']];
  2088. $where[] = ['status' => [StatusEnum::ENABLED,StatusEnum::DISABLED]];
  2089. if (User::getUser($where)) throw new \Exception( '手机号码已注册');
  2090. //验证码
  2091. $user->mobile = $params['mobile'];
  2092. $user->username = $params['mobile'];
  2093. $res = $user->save();
  2094. if(empty($res)) throw new \Exception('修改失败');
  2095. $params['new_mobile'] = $params['mobile'];
  2096. $params['old_mobile'] = $old_mobile;
  2097. $params['type'] = 1;
  2098. ChangeMobileLog::setData($params);
  2099. // 触发用户信息修改事件
  2100. $event = new UserUpdateEvent($params['mall_id']);
  2101. Yii::$app->services->events->dispatch(
  2102. $event,
  2103. [
  2104. 'user_id'=>$params['user_id'],
  2105. 'mall_id' => $params['mall_id'],
  2106. 'old_mobile' => $old_mobile,
  2107. 'is_admin' => true,
  2108. 'operator_user_id' => $params['operator_user_id']
  2109. ],
  2110. $event->listeners
  2111. );
  2112. return true;
  2113. }
  2114. public static function getInviterMsg($mallId, $allowInviter)
  2115. {
  2116. $inviter_msg = [];
  2117. $config = Yii::$app->services->setting->mall->get($mallId, 'poster_setting.is_create_poster');
  2118. if (!empty($config)) {
  2119. $setting = json_decode($config, true);
  2120. if (!empty($setting['value']) && $setting['value'] == 2) {// 无推广资格,并且不允许推广
  2121. $allowInviter = 0;
  2122. $relation_setting = \Yii::$app->services->setting->mall->get($mallId, 'user_relation_setting.relation_setting');
  2123. if (!empty($relation_setting)) {
  2124. $relation_setting = json_decode($relation_setting, true);
  2125. // get_power_way【1=无条件,3=或,4=与】
  2126. switch ($relation_setting['get_power_way']) {
  2127. case 3:
  2128. $inviter_msg[] = "需满足以下任意条件即可推广!";
  2129. break;
  2130. case 4:
  2131. $inviter_msg[] = "需满足以下所有条件即可推广!";
  2132. break;
  2133. default:
  2134. $inviter_msg[] = "您暂无推广资格";
  2135. break;
  2136. }
  2137. if ($relation_setting['buy_num_selected'] == 1) $inviter_msg[] = "消费次数:{$relation_setting['buy_num']}次";
  2138. if ($relation_setting['buy_price_selected'] == 1) $inviter_msg[] = "消费金额:{$relation_setting['buy_price']}元";
  2139. // 1=任意商品,2=指定商品,3=指定商品分类
  2140. switch ($relation_setting['buy_goods_way']) {
  2141. case 1:
  2142. $inviter_msg[] = "购买商城任意商品";
  2143. break;
  2144. case 2:
  2145. $goods_msg = '';
  2146. if($relation_setting['goods_list']){
  2147. foreach ($relation_setting['goods_list'] as $goods) {
  2148. $goods_msg .= "【{$goods['name']}】";
  2149. }
  2150. }
  2151. $inviter_msg[] = "购买:{$goods_msg}商品";
  2152. break;
  2153. case 3:
  2154. $cat_msg = '';
  2155. foreach ($relation_setting['cat_list'] as $cat) {
  2156. $cat_msg .= "【{$cat['label']}】";
  2157. }
  2158. $inviter_msg[] = "购买:【{$cat_msg}】分类商品";
  2159. break;
  2160. }
  2161. }
  2162. }
  2163. }
  2164. return [$allowInviter, $inviter_msg];
  2165. }
  2166. //发送公众号消息
  2167. // $send_message_data = ['user_id'=>$user_id, 'mall_id' => $data['mall_id']],];
  2168. public static function teamAddSendMessage($send_message_data){
  2169. $mall_id = $send_message_data['mall_id'];
  2170. $user_id = $send_message_data['user_id'];
  2171. $parent_id_arr = UserPartitionRelationship::getParentIdArr($user_id);
  2172. if(empty($parent_id_arr))return false;
  2173. foreach ($parent_id_arr as $parent_id){
  2174. $user_info = UserInfo::findOne(['mall_id' => $mall_id, 'user_id' => $parent_id, 'status' => StatusEnum::ENABLED,'platform'=>'wechat']);
  2175. if (empty($user_info)) return false;
  2176. $h5 = \Yii::$app->services->setting->mall->get($mall_id, 'basic.web_url');
  2177. if (empty($h5)) return false;
  2178. $mall = Mall::findOne(['id'=>$mall_id]);
  2179. if(empty($mall)) return false;
  2180. $h5 = substr($h5, 0, strpos($h5, '?'));
  2181. $url = $h5.'plugins/public/promotion/client';
  2182. WechatHelper::sendTemplateMessage($mall_id,'team_add',$user_info['openid'],$url,[]);
  2183. }
  2184. }
  2185. public static function withdrawalSuccessSendMessage($send_message_data){
  2186. $mall_id = $send_message_data['mall_id'];
  2187. $user_id = $send_message_data['user_id'];
  2188. $type = $send_message_data['type'];
  2189. $user_info = UserInfo::findOne(['mall_id' => $mall_id, 'user_id' => $user_id, 'status' => StatusEnum::ENABLED,'platform'=>'wechat']);
  2190. if (empty($user_info)) return false;
  2191. $h5 = \Yii::$app->services->setting->mall->get($mall_id, 'basic.web_url');
  2192. if (empty($h5)) return false;
  2193. $mall = Mall::findOne(['id'=>$mall_id]);
  2194. if(empty($mall)) return false;
  2195. $h5 = substr($h5, 0, strpos($h5, '?'));
  2196. $url = $h5.'plugins/pages/assets/balance/list?type=2';
  2197. WechatHelper::sendTemplateMessage($mall_id,'withdrawal_success',$user_info['openid'],$url,[]);
  2198. }
  2199. public static function addBlacklist($params){
  2200. $t = null;
  2201. try {
  2202. $user_ids = explode(',', $params['user_id']);
  2203. $list = User::lists(['where'=>[['in', 'id',$user_ids]]],false);
  2204. $isEnable = !empty($params['status']) && $params['status']==StatusEnum::ENABLED;
  2205. if ($isEnable) {
  2206. $delLogList = UserDelLog::find()
  2207. ->where([
  2208. 'user_id' => $user_ids,
  2209. 'is_recovered' => StatusEnum::DISABLED,
  2210. 'mall_id' => $params['mall_id']
  2211. ])
  2212. ->select('user_id, old_mobile')
  2213. ->indexBy('user_id')
  2214. ->asArray()
  2215. ->all();
  2216. $registeredMobiles = self::find()
  2217. ->select('mobile')
  2218. ->where([
  2219. 'mobile' => array_column($delLogList, 'old_mobile'),
  2220. 'status' => StatusEnum::ENABLED,
  2221. 'mall_id' => $params['mall_id']
  2222. ])
  2223. ->column();
  2224. }
  2225. $t = Yii::$app->db->beginTransaction();
  2226. foreach ($list as $user){
  2227. // 查询手机号有没有被注册,注册了则不能恢复
  2228. if ($isEnable && isset($delLogList[$user->id])) {
  2229. if (in_array($delLogList[$user->id]['old_mobile'], $registeredMobiles)) {
  2230. throw new \Exception('该用户手机号已被占用,不允许恢复!');
  2231. }
  2232. // 是否是被注销删除的用户,如果是则恢复
  2233. self::recoveryUser($user);
  2234. }
  2235. $user->status = $params['status'];
  2236. $user->save();
  2237. }
  2238. if(empty($params['status'])){
  2239. self::disableAccessToken($user_ids);
  2240. }
  2241. $t->commit();
  2242. return true;
  2243. }catch (\Exception $e){
  2244. isset($t) && $t->rollBack();
  2245. throw new \Exception($e->getMessage());
  2246. }
  2247. }
  2248. /**
  2249. * 检查是否被注销,如果被注销将其恢复
  2250. *
  2251. * @param $user
  2252. *
  2253. * @return void
  2254. * @throws \Exception
  2255. */
  2256. protected static function recoveryUser($user)
  2257. {
  2258. $user_del_log = UserDelLog::find()->where(['user_id' => $user->id, 'is_recovered' => StatusEnum::DISABLED])->one();
  2259. if (!$user_del_log) {
  2260. return;
  2261. }
  2262. $user_del_log->is_recovered = StatusEnum::ENABLED;
  2263. $user_del_log->recover_time = time();
  2264. $user_del_log->save();
  2265. if ($user->username == $user_del_log->new_mobile) {
  2266. $user->username = $user_del_log->old_mobile;
  2267. }
  2268. $user->mobile = $user_del_log->old_mobile;
  2269. UserInfo::updateAll(['status'=>StatusEnum::ENABLED],['user_id'=>$user->id]);
  2270. }
  2271. protected static function disableAccessToken($user_ids)
  2272. {
  2273. $access_token_list = AccessToken::lists(['where'=>[
  2274. ['user_id'=>$user_ids],
  2275. ['group'=>'api']
  2276. ],
  2277. 'index'=>'user_id'
  2278. ]);
  2279. foreach ($access_token_list as $item){
  2280. $access_token = $item['access_token'];
  2281. Yii::$app->cache->delete(Yii::$app->services->apiAccessToken->getCacheKey($access_token));
  2282. }
  2283. AccessToken::updateAll(['status'=>StatusEnum::DISABLED],['user_id'=>$user_ids,'group'=>'api']);
  2284. return true;
  2285. }
  2286. /**
  2287. * @param integer $mall_id
  2288. * @param string $keyword
  2289. * @return static
  2290. */
  2291. public static function getUserModelByKeyword(int $mall_id, string $keyword, string $field = 'id')
  2292. {
  2293. return static::find()
  2294. ->select($field)
  2295. ->where(['mall_id' => $mall_id])
  2296. ->andWhere([
  2297. 'or',
  2298. ['like', 'nickname', $keyword],
  2299. ['like', 'mobile', $keyword],
  2300. ]);
  2301. }
  2302. /**
  2303. * @param integer $mall_id
  2304. * @param string $keyword
  2305. * @param string $field
  2306. * @return array
  2307. */
  2308. public static function getUserIdsByKeyword(int $mall_id, string $keyword)
  2309. {
  2310. return static::getUserModelByKeyword(
  2311. $mall_id, $keyword, 'id'
  2312. )->column();
  2313. }
  2314. /**
  2315. * 更改用户登录设备
  2316. *
  2317. * @param integer $id
  2318. * @param string $device_type
  2319. * @param string $device_mac
  2320. * @return int
  2321. */
  2322. public static function updateLoginDevice(int $id, string $device_type, string $device_mac)
  2323. {
  2324. return static::updateAll(['device_type' => $device_type, 'device_mac' => $device_mac], ['id' => $id]);
  2325. }
  2326. /**
  2327. * 获取用户父级
  2328. *
  2329. * @param integer $id
  2330. * @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
  2331. */
  2332. public static function getUserParents(int $id)
  2333. {
  2334. return static::find()
  2335. ->select(['parent_id', 'second_parent_id', 'third_parent_id'])
  2336. ->where(['id' => $id, 'status' => StatusEnum::ENABLED])
  2337. ->one();
  2338. }
  2339. /**
  2340. * @param int $mallId
  2341. * @param int $userId
  2342. *
  2343. * @return false|int|string|null
  2344. */
  2345. public static function getUserMobile(int $mallId, int $userId)
  2346. {
  2347. return static::find()
  2348. ->where([
  2349. 'status' => [StatusEnum::DISABLED, StatusEnum::ENABLED],
  2350. 'mall_id' => $mallId,
  2351. 'id' => $userId
  2352. ])
  2353. ->select('mobile')
  2354. ->scalar() ?: '';
  2355. }
  2356. public function getUserLevelExpired()
  2357. {
  2358. return $this
  2359. ->hasMany(UserLevelExpired::class, ['user_id' => 'id'])
  2360. ->where(['status' => StatusEnum::ENABLED])
  2361. ->select('user_id, level, expired_at');
  2362. }
  2363. public static function searchWhereIds(int $mall_id, $keyword)
  2364. {
  2365. return self::find()->where(['mall_id' => $mall_id])
  2366. ->andWhere(['status' => [StatusEnum::ENABLED, StatusEnum::DISABLED]])
  2367. ->andWhere(['or', ['like', 'nickname', $keyword], ['like', 'mobile', $keyword], ['like', 'id', $keyword]])->select('id')->column();
  2368. }
  2369. /**
  2370. * 获取团队中对应层级有多少人数
  2371. * @param $user_id //会员id
  2372. * @param $level //查询层级
  2373. * @return false|int|string|null
  2374. */
  2375. public static function getTeamlevelCount($user_id,$level){
  2376. list($tree,$parent_deep,$all_tree, $all_tree_arr) = UserPartitionRelationship::getTree($user_id);
  2377. $count = (new \yii\db\Query())
  2378. ->select(['COUNT(DISTINCT u.id) as count'])
  2379. ->from(['u' => 'qimall_user'])
  2380. ->innerJoin(['upr' => 'qimall_user_partition_relationship'], 'u.id = upr.user_id')
  2381. ->where(['u.level' => $level,'u.status'=>StatusEnum::ENABLED])
  2382. ->andWhere(['like', 'upr.tree', "{$tree},%", false])
  2383. ->scalar();
  2384. return $count;
  2385. }
  2386. }