• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

miaoxing / user / 3699122540

pending completion
3699122540

push

github

semantic-release-bot
chore(release): publish

14 of 22 branches covered (63.64%)

66 of 626 relevant lines covered (10.54%)

1.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

18.42
/src/Service/UserModel.php
1
<?php
2

3
namespace Miaoxing\User\Service;
4

5
use Miaoxing\Admin\Service\Group;
6
use Miaoxing\Admin\Service\GroupModel;
7
use Miaoxing\Plugin\Service\Ret;
8
use Miaoxing\Plugin\Service\UserModel as BaseUserModel;
9
use Wei\Time;
10

11
/**
12
 * @property GroupModel $group
13
 */
14
class UserModel extends BaseUserModel
15
{
16
    /**
17
     * 省市是否锁定(第三方平台不可更改)
18
     */
19
    public const STATUS_REGION_LOCKED = 3;
20

21
    /**
22
     * 当前记录是否为新创建的
23
     *
24
     * @var bool
25
     */
26
    protected $isCreated = false;
27

28
    /**
29
     * @var Group
30
     * @deprecated
31
     */
32
    protected $group;
33

34
    protected $attributes = [
35
        'sex' => 1,
36
        'groupId' => 0,
37
    ];
38

39
    /**
40
     * @var \Miaoxing\User\Service\UserProfile
41
     * @deprecated
42
     */
43
    protected $profile;
44

45
    public function __construct(array $options = [])
46
    {
47
        parent::__construct($options);
39✔
48
        $this->virtual = array_merge($this->virtual, [
39✔
49
            'isMobileVerified',
39✔
50
        ]);
26✔
51
    }
13✔
52

53
    public function group()
54
    {
55
        return $this->hasOne(wei()->groupModel(), 'id', 'groupId');
×
56
    }
57

58
    public function getBackendDisplayName()
59
    {
60
        if ($this['name'] && $this['nickName']) {
×
61
            return $this['name'] . '(' . $this['nickName'] . ')';
×
62
        } elseif ($this['name']) {
×
63
            return $this['name'];
×
64
        } else {
65
            return $this['nickName'];
×
66
        }
67
    }
68

69
    /**
70
     * Repo: 根据用户编号,从缓存中获取用户名
71
     *
72
     * @param int $id
73
     * @return string
74
     */
75
    public function getDisplayNameByIdFromCache($id)
76
    {
77
        return wei()->arrayCache->remember('nickName' . $id, function () use ($id) {
78
            $user = wei()->user()->find(['id' => $id]);
×
79

80
            return $user ? $user->getNickName() : '';
×
81
        });
×
82
    }
83

84
    public function afterCreate()
85
    {
86
        parent::afterCreate();
27✔
87

88
        $this->isCreated = true;
27✔
89

90
//        TODO queue
91
//        if (wei()->has('queue')) {
92
//            wei()->queue->push(UserCreate::class, ['id' => $this['id']]);
93
//        }
94
    }
9✔
95

96
    public function afterSave()
97
    {
98
        parent::afterSave();
27✔
99
        $this->removeModelCache();
27✔
100
    }
9✔
101

102
    public function afterDestroy()
103
    {
104
        parent::afterDestroy();
6✔
105
        $this->removeModelCache();
6✔
106
    }
2✔
107

108
    /**
109
     * Record: 移动用户分组
110
     *
111
     * @param int $groupId
112
     * @return array
113
     */
114
    public function updateGroup($groupId)
115
    {
116
        $group = wei()->groupModel()->findOrInit($groupId);
3✔
117
        $ret = wei()->event->until('groupMove', [[$this['id']], $group]);
3✔
118
        if ($ret) {
3✔
119
            return $ret;
×
120
        }
121

122
        $this->save(['groupId' => $groupId]);
3✔
123

124
        return $this->suc();
3✔
125
    }
126

127
    /**
128
     * Record: 创建一个新用户
129
     *
130
     * wei()->user()->register([
131
     *     'email' => 'xx', // 可选
132
     *     'username' => 'xx',
133
     *     'password' => 'xx',
134
     *     'passwordAgain' => 'xx,
135
     *     'source' => 1, // 来源,可选
136
     * ]);
137
     *
138
     * @param array $data
139
     * @return array
140
     * @todo 太多validate,需简化
141
     */
142
    public function register($data)
143
    {
144
        // 1. 校验额外数据
145
        if (isset($data['mobile'])) {
×
146
            $validator = wei()->validate([
×
147
                'data' => $data,
×
148
                'rules' => [
149
                    'mobile' => [
150
                        'required' => true,
151
                        'mobileCn' => true,
152
                    ],
153
                    'verifyCode' => [
154
                        'required' => true,
155
                    ],
156
                ],
157
                'names' => [
158
                    'mobile' => '手机号码',
159
                    'verifyCode' => '验证码',
160
                ],
161
                'messages' => [
162
                    'mobile' => [
163
                        'required' => '请输入手机号码',
164
                    ],
165
                    'verifyCode' => [
166
                        'required' => '请输入验证码',
167
                    ],
168
                ],
169
            ]);
170
            if (!$validator->isValid()) {
×
171
                return ['code' => -1, 'message' => $validator->getFirstMessage()];
×
172
            }
173

174
            $ret = wei()->verifyCode->check($data['mobile'], $data['verifyCode']);
×
175
            if (1 !== $ret['code']) {
×
176
                return $ret + ['verifyCodeErr' => true];
×
177
            }
178
        } else {
179
            $validator = wei()->validate([
×
180
                'data' => $data,
×
181
                'rules' => [
182
                    'email' => [
183
                        'required' => true,
184
                    ],
185
                ],
186
                'names' => [
187
                    'email' => '邮箱',
188
                ],
189
                'messages' => [
190
                    'email' => [
191
                        'required' => '请输入邮箱',
192
                    ],
193
                ],
194
            ]);
195
            if (!$validator->isValid()) {
×
196
                return ['code' => -1, 'message' => $validator->getFirstMessage()];
×
197
            }
198
        }
199

200
        // 2. 统一校验
201
        $validator = wei()->validate([
×
202
            'data' => $data,
×
203
            'rules' => [
204
                'email' => [
205
                    'required' => false,
206
                    'email' => true,
207
                    'notRecordExists' => ['user', 'email'],
208
                ],
209
                'password' => [
210
                    'minLength' => 6,
211
                ],
212
                'passwordConfirm' => [
213
                    'equalTo' => $data['password'],
×
214
                ],
215
            ],
216
            'names' => [
217
                'email' => '邮箱',
218
                'password' => '密码',
219
            ],
220
            'messages' => [
221
                'passwordConfirm' => [
222
                    'required' => '请再次输入密码',
223
                    'equalTo' => '两次输入的密码不相等',
224
                ],
225
            ],
226
        ]);
227
        if (!$validator->isValid()) {
×
228
            return ['code' => -7, 'message' => $validator->getFirstMessage()];
×
229
        }
230

231
        if ($data['mobile']) {
×
232
            $user = wei()->user()->mobileVerified()->find(['mobile' => $data['mobile']]);
×
233
            if ($user) {
×
234
                return ['code' => -8, 'message' => '手机号码已存在'];
×
235
            }
236
        }
237

238
        $ret = $this->event->until('userRegisterValidate', [$this]);
×
239
        if ($ret) {
×
240
            return $ret;
×
241
        }
242

243
        // 3. 保存到数据库
244
        $this->setPlainPassword($data['password']);
×
245

246
        if ($data['mobile']) {
×
247
            $this->setMobileVerified();
×
248
        }
249

250
        $this->save([
×
251
            'email' => (string) $data['email'],
×
252
            'mobile' => (string) $data['mobile'],
×
253
            'username' => (string) $data['username'],
×
254
            'source' => isset($data['source']) ? $data['source'] : '',
×
255
        ]);
256

257
        return ['code' => 1, 'message' => '注册成功'];
×
258
    }
259

260
    public function isMobileExists($mobile)
261
    {
262
        return (bool) wei()->userModel()
×
263
            ->where('mobile', $mobile)
×
264
            ->where('id', '!=', $this->id)
×
265
            ->fetchColumn();
×
266
    }
267

268
    /**
269
     * QueryBuilder: 查询手机号码验证过
270
     *
271
     * @return $this
272
     */
273
    public function mobileVerified()
274
    {
275
        return $this->where('mobile_verified_at', '!=', '0000-00-00 00:00:00');
9✔
276
    }
277

278
    /**
279
     * @param bool $verified
280
     * @return $this
281
     */
282
    public function setMobileVerified($verified = true)
283
    {
284
        $this->mobileVerifiedAt = $verified ? Time::now() : null;
12✔
285
        return $this;
12✔
286
    }
287

288
    public function updateMobileIfVerified($save = true, $req = null)
289
    {
290
        $req || $req = $this->req;
×
291

292
        // 未校验,或者是输入了新手机,需要校验
293
        if (
294
            !$this->isMobileVerified()
×
295
            || $this['mobile'] != $req['mobile']
×
296
        ) {
297
            $ret = $this->checkMobile($req['mobile']);
×
298
            if (1 !== $ret['code']) {
×
299
                return $ret;
×
300
            }
301

302
            if (!$req['verifyCode']) {
×
303
                return $this->err('验证码不能为空');
×
304
            }
305

306
            $ret = wei()->verifyCode->check($req['mobile'], $req['verifyCode']);
×
307
            if (1 !== $ret['code']) {
×
308
                return $ret + ['verifyCodeErr' => true];
×
309
            }
310
        }
311

312
        if ($this['mobile'] == $req['mobile']) {
×
313
            return $this->suc(['changed' => false]);
×
314
        }
315

316
        $this['mobile'] = $req['mobile'];
×
317
        $this->setMobileVerified();
×
318
        if ($save) {
×
319
            $this->save();
×
320
        }
321
        return $this->suc(['changed' => true]);
×
322
    }
323

324
    /**
325
     * Repo: 记录用户操作日志
326
     *
327
     * @param string $action
328
     * @param array $data
329
     * @return $this
330
     */
331
    public function log($action, array $data)
332
    {
333
        /** @phpstan-ignore-next-line */
334
        $user = User::cur();
×
335
        $app = wei()->app;
×
336

337
        if (isset($data['param']) && is_array($data['param'])) {
×
338
            $data['param'] = json_encode($data['param'], \JSON_UNESCAPED_UNICODE);
×
339
        }
340

341
        if (isset($data['ret']) && is_array($data['ret'])) {
×
342
            $data['ret'] = json_encode($data['ret'], \JSON_UNESCAPED_UNICODE);
×
343
        }
344

345
        wei()->db->insert('userLogs', $data + [
×
346
                'appId' => $app->getId(),
×
347
                'userId' => $user->id,
×
348
                'nickName' => $user->nickName,
×
349
                'page' => $app->getControllerAction(),
×
350
                'action' => $action,
×
351
                'createTime' => date('Y-m-d H:i:s'),
×
352
            ]);
353

354
        return $this;
×
355
    }
356

357
    public function getTags()
358
    {
359
        $userTags = wei()->userTag->getAll();
×
360
        $tags = [];
×
361
        $relations = wei()->userTagsUserModel()->asc('id')->findAll(['user_id' => $this['id']]);
×
362
        foreach ($relations as $relation) {
×
363
            $tags[] = $userTags[$relation->tagId];
×
364
        }
365
        return $tags;
×
366
    }
367

368
    public function getIsMobileVerifiedAttribute()
369
    {
370
        return (bool) $this->mobileVerifiedAt;
18✔
371
    }
372

373
    public function setIsMobileVerifiedAttribute()
374
    {
375
        // do nothing
376
    }
6✔
377

378
    public function getAvatarAttribute()
379
    {
380
        return (isset($this->attributes['avatar']) && $this->attributes['avatar']) ?
21✔
381
            $this->attributes['avatar'] : $this->user->defaultAvatar;
21✔
382
    }
383

384
    /**
385
     * Record: 检查当前记录是否刚创建
386
     *
387
     * @return bool
388
     */
389
    public function isCreated()
390
    {
391
        return $this->isCreated;
×
392
    }
393

394
    /**
395
     * Record: 获取昵称等可供展示的名称
396
     *
397
     * @return string
398
     * @deprecated use ->displayName
399
     */
400
    public function getNickName()
401
    {
402
        return $this->displayName;
×
403
    }
404

405
    /**
406
     * Record: 获取用户头像,没有设置头像则使用默认头像
407
     *
408
     * @return string
409
     * @deprecated
410
     */
411
    public function getHeadImg()
412
    {
413
        return $this['headImg'];
×
414
    }
415

416
    /**
417
     * Record: 指定用户是否为管理员
418
     *
419
     * @return bool
420
     * @deprecated 使用 $this->isAdmin
421
     */
422
    public function isAdmin()
423
    {
424
        return (bool) $this->isAdmin;
×
425
    }
426

427
    /**
428
     * QueryBuilder:
429
     *
430
     * @return \Miaoxing\Plugin\Service\UserModel
431
     */
432
    public function valid()
433
    {
434
        return $this->where(['isValid' => 1]);
×
435
    }
436

437
    /**
438
     * @deprecated
439
     */
440
    public function getProfile()
441
    {
442
        $this->profile || $this->profile = wei()->userProfile()->findOrInit(['userId' => $this['id']]);
×
443

444
        return $this->profile;
×
445
    }
446

447
    /**
448
     * Record: 获取用户的分组对象
449
     *
450
     * @return Group
451
     * @deprecated
452
     */
453
    public function getGroup()
454
    {
455
        $this->group || $this->group = wei()->group()->findOrInitById($this['groupId'], ['name' => '未分组']);
×
456

457
        return $this->group;
×
458
    }
459

460
    /**
461
     * Record: 检查指定的手机号码能否绑定当前用户
462
     *
463
     * @param string $mobile
464
     * @return Ret
465
     * @svc
466
     */
467
    protected function checkMobile(string $mobile)
468
    {
469
        if (!$mobile) {
9✔
470
            return err('手机不能为空');
×
471
        }
472

473
        // 1. 检查是否已存在认证该手机号码的用户
474
        $mobileUser = wei()->userModel()->mobileVerified()->findBy('mobile', $mobile);
9✔
475
        if ($mobileUser && $mobileUser['id'] != $this['id']) {
9✔
476
            return err('已存在认证该手机号码的用户');
6✔
477
        }
478

479
        // 2. 提供接口供外部检查手机号
480
        $ret = $this->event->until('userCheckMobile', [$this, $mobile]);
6✔
481
        if ($ret) {
6✔
482
            return $ret;
×
483
        }
484

485
        return suc('手机号码可以绑定');
6✔
486
    }
487

488
    /**
489
     * Record: 绑定手机
490
     *
491
     * @param array|\ArrayAccess $data
492
     * @return array
493
     * @svc
494
     */
495
    protected function bindMobile($data)
496
    {
497
        // 1. 校验数据
498
        $ret = $this->checkMobile($data['mobile']);
×
499
        if (1 !== $ret['code']) {
×
500
            return $ret;
×
501
        }
502

503
        // 2. 校验验证码
504
        $ret = wei()->verifyCode->check($data['mobile'], $data['verifyCode']);
×
505
        if (1 !== $ret['code']) {
×
506
            return $ret + ['verifyCodeErr' => true];
×
507
        }
508

509
        // 3. 记录手机信息
510
        $this['mobile'] = $data['mobile'];
×
511
        $this->setMobileVerified();
×
512

513
        $this->event->trigger('preUserMobileVerify', [$data, $this]);
×
514

515
        $this->save();
×
516

517
        return $this->suc('绑定成功');
×
518
    }
519

520
    /**
521
     * Record: 更新当前用户资料
522
     *
523
     * @param array|\ArrayAccess $data
524
     * @return array
525
     * @svc
526
     */
527
    protected function updateData($data)
528
    {
529
        $isMobileVerified = $this->isMobileVerified();
×
530

531
        $validator = wei()->validate([
×
532
            'data' => $data,
×
533
            'rules' => [
534
                'mobile' => [
535
                    'required' => !$isMobileVerified,
×
536
                    'mobileCn' => true,
537
                ],
538
                'name' => [
539
                    'required' => false,
540
                ],
541
                'address' => [
542
                    'required' => false,
543
                    'minLength' => 3,
544
                ],
545
            ],
546
            'names' => [
547
                'mobile' => '手机号码',
548
                'name' => '姓名',
549
                'address' => '详细地址',
550
            ],
551
        ]);
552
        if (!$validator->isValid()) {
×
553
            return $this->err($validator->getFirstMessage());
×
554
        }
555

556
        if (!$isMobileVerified) {
×
557
            // 手机号未认证时,检查手机号,根据配置检查是否重复
558
            if (wei()->user->checkMobileUnique && $this->isMobileExists($data['mobile'])) {
×
559
                return $this->err('手机号码已存在');
×
560
            }
561
            $this['mobile'] = $data['mobile'];
×
562
        }
563

564
        $result = $this->event->until('preUserUpdate', [$data, $this]);
×
565
        if ($result) {
×
566
            return $result;
×
567
        }
568

569
        $this->save([
×
570
            'name' => $data['name'],
×
571
            'address' => $data['address'],
×
572
        ]);
573

574
        return $this->suc();
×
575
    }
576
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc