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

miaoxing / user / 13100248475

02 Feb 2025 03:42PM UTC coverage: 12.738% (+0.1%) from 12.605%
13100248475

push

github

semantic-release-bot
chore(release): publish

See CHANGELOG.md for more details.

20 of 80 branches covered (25.0%)

107 of 840 relevant lines covered (12.74%)

1.93 hits per line

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

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

3
namespace Miaoxing\User\Service;
4

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

10
/**
11
 * @property int $score 积分
12
 * @property string $money 账户余额
13
 * @property string $rechargeMoney 充值账户余额
14
 * @property string $source 用户来源
15
 * @property bool $isMobileVerified
16
 * @property string|null $id
17
 * @property string $appId
18
 * @property string $outId
19
 * @property int $adminType 管理员类型
20
 * @property string $groupId 用户组
21
 * @property bool $isAdmin
22
 * @property string $nickName
23
 * @property string $remarkName
24
 * @property string $username
25
 * @property string $name
26
 * @property string $email
27
 * @property string $mobile
28
 * @property string|null $mobileVerifiedAt 手机校验时间
29
 * @property string $phone
30
 * @property string $password
31
 * @property int $sex
32
 * @property string $country
33
 * @property string $province
34
 * @property string $city
35
 * @property string $district
36
 * @property string $address
37
 * @property string $signature
38
 * @property bool $isEnabled 是否启用
39
 * @property string $avatar
40
 * @property string|null $lastLoginAt
41
 * @property string|null $createdAt
42
 * @property string|null $updatedAt
43
 * @property string $createdBy
44
 * @property string $updatedBy
45
 * @property string|null $displayName
46
 */
47
class UserModel extends BaseUserModel
48
{
49
    public function __construct(array $options = [])
50
    {
51
        parent::__construct($options);
78✔
52
        $this->virtual = array_merge($this->virtual, [
78✔
53
            'isMobileVerified',
78✔
54
        ]);
78✔
55
    }
56

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

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

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

83
    public function afterSave()
84
    {
85
        parent::afterSave();
54✔
86
        $this->removeModelCache();
54✔
87
    }
88

89
    public function afterDestroy()
90
    {
91
        parent::afterDestroy();
12✔
92
        $this->removeModelCache();
12✔
93
    }
94

95
    /**
96
     * Record: 移动用户分组
97
     *
98
     * @param int $groupId
99
     * @return array
100
     */
101
    public function updateGroup($groupId)
102
    {
103
        $group = GroupModel::findOrInit($groupId);
6✔
104
        $ret = wei()->event->until('groupMove', [[$this['id']], $group]);
6✔
105
        if ($ret) {
6✔
106
            return $ret;
×
107
        }
108

109
        $this->save(['groupId' => $groupId]);
6✔
110

111
        return $this->suc();
6✔
112
    }
113

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

161
            $ret = wei()->verifyCode->check($data['mobile'], $data['verifyCode']);
×
162
            if (1 !== $ret['code']) {
×
163
                return $ret + ['verifyCodeErr' => true];
×
164
            }
165
        } else {
166
            $validator = wei()->validate([
×
167
                'data' => $data,
×
168
                'rules' => [
×
169
                    'email' => [
×
170
                        'required' => true,
×
171
                    ],
×
172
                ],
×
173
                'names' => [
×
174
                    'email' => '邮箱',
×
175
                ],
×
176
                'messages' => [
×
177
                    'email' => [
×
178
                        'required' => '请输入邮箱',
×
179
                    ],
×
180
                ],
×
181
            ]);
×
182
            if (!$validator->isValid()) {
×
183
                return ['code' => -1, 'message' => $validator->getFirstMessage()];
×
184
            }
185
        }
186

187
        // 2. 统一校验
188
        $validator = wei()->validate([
×
189
            'data' => $data,
×
190
            'rules' => [
×
191
                'email' => [
×
192
                    'required' => false,
×
193
                    'email' => true,
×
194
                    'notRecordExists' => ['user', 'email'],
×
195
                ],
×
196
                'password' => [
×
197
                    'minLength' => 6,
×
198
                ],
×
199
                'passwordConfirm' => [
×
200
                    'equalTo' => $data['password'],
×
201
                ],
×
202
            ],
×
203
            'names' => [
×
204
                'email' => '邮箱',
×
205
                'password' => '密码',
×
206
            ],
×
207
            'messages' => [
×
208
                'passwordConfirm' => [
×
209
                    'required' => '请再次输入密码',
×
210
                    'equalTo' => '两次输入的密码不相等',
×
211
                ],
×
212
            ],
×
213
        ]);
×
214
        if (!$validator->isValid()) {
×
215
            return ['code' => -7, 'message' => $validator->getFirstMessage()];
×
216
        }
217

218
        if ($data['mobile']) {
×
219
            $user = wei()->user()->mobileVerified()->find(['mobile' => $data['mobile']]);
×
220
            if ($user) {
×
221
                return ['code' => -8, 'message' => '手机号码已存在'];
×
222
            }
223
        }
224

225
        $ret = $this->event->until('userRegisterValidate', [$this]);
×
226
        if ($ret) {
×
227
            return $ret;
×
228
        }
229

230
        // 3. 保存到数据库
231
        $this->setPlainPassword($data['password']);
×
232

233
        if ($data['mobile']) {
×
234
            $this->setMobileVerified();
×
235
        }
236

237
        $this->save([
×
238
            'email' => (string) $data['email'],
×
239
            'mobile' => (string) $data['mobile'],
×
240
            'username' => (string) $data['username'],
×
241
            'source' => isset($data['source']) ? $data['source'] : '',
×
242
        ]);
×
243

244
        return ['code' => 1, 'message' => '注册成功'];
×
245
    }
246

247
    public function isMobileExists($mobile)
248
    {
249
        return (bool) wei()->userModel()
×
250
            ->where('mobile', $mobile)
×
251
            ->where('id', '!=', $this->id)
×
252
            ->fetchColumn();
×
253
    }
254

255
    /**
256
     * QueryBuilder: 查询手机号码验证过
257
     *
258
     * @return $this
259
     */
260
    public function mobileVerified()
261
    {
262
        return $this->whereNotNull('mobileVerifiedAt');
18✔
263
    }
264

265
    /**
266
     * @param bool $verified
267
     * @return $this
268
     */
269
    public function setMobileVerified($verified = true)
270
    {
271
        $this->mobileVerifiedAt = $verified ? Time::now() : null;
24✔
272
        return $this;
24✔
273
    }
274

275
    public function updateMobileIfVerified($save = true, $req = null)
276
    {
277
        $req || $req = $this->req;
×
278

279
        // 未校验,或者是输入了新手机,需要校验
280
        if (
281
            !$this->isMobileVerified()
×
282
            || $this['mobile'] != $req['mobile']
×
283
        ) {
284
            $ret = $this->checkMobile($req['mobile']);
×
285
            if (1 !== $ret['code']) {
×
286
                return $ret;
×
287
            }
288

289
            if (!$req['verifyCode']) {
×
290
                return $this->err('验证码不能为空');
×
291
            }
292

293
            $ret = wei()->verifyCode->check($req['mobile'], $req['verifyCode']);
×
294
            if (1 !== $ret['code']) {
×
295
                return $ret + ['verifyCodeErr' => true];
×
296
            }
297
        }
298

299
        if ($this['mobile'] == $req['mobile']) {
×
300
            return $this->suc(['changed' => false]);
×
301
        }
302

303
        $this['mobile'] = $req['mobile'];
×
304
        $this->setMobileVerified();
×
305
        if ($save) {
×
306
            $this->save();
×
307
        }
308
        return $this->suc(['changed' => true]);
×
309
    }
310

311
    /**
312
     * Repo: 记录用户操作日志
313
     *
314
     * @param string $action
315
     * @param array $data
316
     * @return $this
317
     */
318
    public function log($action, array $data)
319
    {
320
        /** @phpstan-ignore-next-line */
321
        $user = User::cur();
×
322
        $app = wei()->app;
×
323

324
        if (isset($data['param']) && is_array($data['param'])) {
×
325
            $data['param'] = json_encode($data['param'], \JSON_UNESCAPED_UNICODE);
×
326
        }
327

328
        if (isset($data['ret']) && is_array($data['ret'])) {
×
329
            $data['ret'] = json_encode($data['ret'], \JSON_UNESCAPED_UNICODE);
×
330
        }
331

332
        wei()->db->insert('userLogs', $data + [
×
333
                'appId' => $app->getId(),
×
334
                'userId' => $user->id,
×
335
                'nickName' => $user->nickName,
×
336
                'page' => $app->getControllerAction(),
×
337
                'action' => $action,
×
338
                'createTime' => date('Y-m-d H:i:s'),
×
339
            ]);
×
340

341
        return $this;
×
342
    }
343

344
    public function getTags()
345
    {
346
        $userTags = wei()->userTag->getAll();
×
347
        $tags = [];
×
348
        $relations = wei()->userTagsUserModel()->asc('id')->findAll(['user_id' => $this['id']]);
×
349
        foreach ($relations as $relation) {
×
350
            $tags[] = $userTags[$relation->tagId];
×
351
        }
352
        return $tags;
×
353
    }
354

355
    public function getIsMobileVerifiedAttribute()
356
    {
357
        return (bool) $this->mobileVerifiedAt;
36✔
358
    }
359

360
    public function setIsMobileVerifiedAttribute()
361
    {
362
        // do nothing
363
    }
×
364

365
    /**
366
     * @return string
367
     */
368
    public function getAvatarAttribute()
369
    {
370
        return (isset($this->attributes['avatar']) && $this->attributes['avatar']) ?
42✔
371
            $this->attributes['avatar'] : $this->user->defaultAvatar;
42✔
372
    }
373

374
    /**
375
     * QueryBuilder:
376
     *
377
     * @return \Miaoxing\Plugin\Service\UserModel
378
     */
379
    public function valid()
380
    {
381
        return $this->where(['isValid' => 1]);
×
382
    }
383

384
    /**
385
     * Record: 检查指定的手机号码能否绑定当前用户
386
     *
387
     * @param string $mobile
388
     * @return Ret
389
     * @svc
390
     */
391
    protected function checkMobile(string $mobile)
392
    {
393
        if (!$mobile) {
18✔
394
            return err('手机不能为空');
×
395
        }
396

397
        // 1. 检查是否已存在认证该手机号码的用户
398
        $mobileUser = static::new()->mobileVerified()->findBy('mobile', $mobile);
18✔
399
        if ($mobileUser && $mobileUser['id'] != $this['id']) {
18✔
400
            return err('已存在认证该手机号码的用户');
12✔
401
        }
402

403
        // 2. 提供接口供外部检查手机号
404
        $ret = $this->event->until('userCheckMobile', [$this, $mobile]);
12✔
405
        if ($ret) {
12✔
406
            return $ret;
×
407
        }
408

409
        return suc('手机号码可以绑定');
12✔
410
    }
411

412
    /**
413
     * Record: 绑定手机
414
     *
415
     * @param array|\ArrayAccess $data
416
     * @return array
417
     * @svc
418
     */
419
    protected function bindMobile($data)
420
    {
421
        // 1. 校验数据
422
        $ret = $this->checkMobile($data['mobile']);
×
423
        if (1 !== $ret['code']) {
×
424
            return $ret;
×
425
        }
426

427
        // 2. 校验验证码
428
        $ret = wei()->verifyCode->check($data['mobile'], $data['verifyCode']);
×
429
        if (1 !== $ret['code']) {
×
430
            return $ret + ['verifyCodeErr' => true];
×
431
        }
432

433
        // 3. 记录手机信息
434
        $this['mobile'] = $data['mobile'];
×
435
        $this->setMobileVerified();
×
436

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

439
        $this->save();
×
440

441
        return $this->suc('绑定成功');
×
442
    }
443

444
    /**
445
     * Record: 更新当前用户资料
446
     *
447
     * @param array|\ArrayAccess $data
448
     * @return array
449
     * @svc
450
     */
451
    protected function updateData($data)
452
    {
453
        $isMobileVerified = $this->isMobileVerified();
×
454

455
        $validator = wei()->validate([
×
456
            'data' => $data,
×
457
            'rules' => [
×
458
                'mobile' => [
×
459
                    'required' => !$isMobileVerified,
×
460
                    'mobileCn' => true,
×
461
                ],
×
462
                'name' => [
×
463
                    'required' => false,
×
464
                ],
×
465
                'address' => [
×
466
                    'required' => false,
×
467
                    'minLength' => 3,
×
468
                ],
×
469
            ],
×
470
            'names' => [
×
471
                'mobile' => '手机号码',
×
472
                'name' => '姓名',
×
473
                'address' => '详细地址',
×
474
            ],
×
475
        ]);
×
476
        if (!$validator->isValid()) {
×
477
            return $this->err($validator->getFirstMessage());
×
478
        }
479

480
        if (!$isMobileVerified) {
×
481
            // 手机号未认证时,检查手机号,根据配置检查是否重复
482
            if (wei()->user->checkMobileUnique && $this->isMobileExists($data['mobile'])) {
×
483
                return $this->err('手机号码已存在');
×
484
            }
485
            $this['mobile'] = $data['mobile'];
×
486
        }
487

488
        $result = $this->event->until('preUserUpdate', [$data, $this]);
×
489
        if ($result) {
×
490
            return $result;
×
491
        }
492

493
        $this->save([
×
494
            'name' => $data['name'],
×
495
            'address' => $data['address'],
×
496
        ]);
×
497

498
        return $this->suc();
×
499
    }
500
}
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