UserService   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 156
c 2
b 1
f 0
dl 0
loc 278
rs 10
wmc 25

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getUserByClientId() 0 7 2
A getAuthClients() 0 12 3
A setPasswordResetToken() 0 12 4
A getUserByResetToken() 0 10 4
B createUserAfterInitData() 0 135 5
A getToken() 0 18 2
A createUser() 0 24 3
A getUserByUsernameOrEmail() 0 6 2
1
<?php
2
3
namespace app\core\services;
4
5
use app\core\exceptions\InternalException;
6
use app\core\exceptions\InvalidArgumentException;
7
use app\core\models\Account;
8
use app\core\models\AuthClient;
9
use app\core\models\Category;
10
use app\core\models\User;
11
use app\core\requests\JoinRequest;
12
use app\core\types\AccountType;
13
use app\core\types\AuthClientType;
14
use app\core\types\ColorType;
15
use app\core\types\TransactionType;
16
use app\core\types\UserStatus;
17
use Exception;
18
use sizeg\jwt\Jwt;
19
use Yii;
20
use yii\db\ActiveRecord;
21
use yii\db\Exception as DBException;
22
use yii\helpers\ArrayHelper;
23
use yiier\helpers\ModelHelper;
24
use yiier\helpers\Setup;
25
26
class UserService
27
{
28
    /**
29
     * @param JoinRequest $request
30
     * @return User
31
     * @throws InternalException|\Throwable
32
     */
33
    public function createUser(JoinRequest $request): User
34
    {
35
        $transaction = Yii::$app->db->beginTransaction();
36
        $user = new User();
37
        try {
38
            $user->username = $request->username;
39
            $user->email = $request->email;
40
            $user->base_currency_code = $request->base_currency_code;
41
            $user->setPassword($request->password);
42
            $user->generateAuthKey();
43
            if (!$user->save()) {
44
                throw new DBException(Setup::errorMessage($user->firstErrors));
45
            }
46
            $this->createUserAfterInitData($user);
47
            $transaction->commit();
48
        } catch (Exception $e) {
49
            $transaction->rollBack();
50
            Yii::error(
51
                ['request_id' => Yii::$app->requestId->id, $user->attributes, $user->errors, (string)$e],
52
                __FUNCTION__
53
            );
54
            throw new InternalException($e->getMessage());
55
        }
56
        return $user;
57
    }
58
59
60
    /**
61
     * @return string
62
     * @throws \Throwable
63
     */
64
    public function getToken(): string
65
    {
66
        /** @var Jwt $jwt */
67
        $jwt = Yii::$app->jwt;
68
        if (!$jwt->key) {
69
            throw new InternalException(t('app', 'The JWT secret must be configured first.'));
70
        }
71
        $signer = $jwt->getSigner('HS256');
72
        $key = $jwt->getKey();
73
        $time = time();
74
        return (string)$jwt->getBuilder()
75
            ->issuedBy(params('appUrl'))
0 ignored issues
show
Bug introduced by
It seems like params('appUrl') can also be of type yii\web\Session; however, parameter $issuer of Lcobucci\JWT\Builder::issuedBy() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

75
            ->issuedBy(/** @scrutinizer ignore-type */ params('appUrl'))
Loading history...
76
            ->identifiedBy(Yii::$app->name, true)
77
            ->issuedAt($time)
78
            ->expiresAt($time + 3600 * 72)
79
            ->withClaim('username', \user('username'))
80
            ->withClaim('id', \user('id'))
81
            ->getToken($signer, $key);
82
    }
83
84
85
    /**
86
     * @param string $value
87
     * @return User|ActiveRecord|null
88
     */
89
    public static function getUserByUsernameOrEmail(string $value)
90
    {
91
        $condition = strpos($value, '@') ? ['email' => $value] : ['username' => $value];
92
        return User::find()->where(['status' => UserStatus::ACTIVE])
0 ignored issues
show
Bug Best Practice introduced by
The expression return app\core\models\U...here($condition)->one() also could return the type array which is incompatible with the documented return type app\core\models\User|null|yii\db\ActiveRecord.
Loading history...
93
            ->andWhere($condition)
94
            ->one();
95
    }
96
97
    /**
98
     * @param User $user
99
     * @return bool
100
     * @throws \yii\base\Exception
101
     */
102
    public function setPasswordResetToken(User $user)
103
    {
104
        if (!$user) {
0 ignored issues
show
introduced by
$user is of type app\core\models\User, thus it always evaluated to true.
Loading history...
105
            return false;
106
        }
107
108
        if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
109
            $user->generatePasswordResetToken();
110
        }
111
112
        if (!$user->save()) {
113
            return false;
114
        }
115
    }
116
117
    /**
118
     * @param User $user
119
     * @throws DBException
120
     * @throws \app\core\exceptions\InvalidArgumentException
121
     */
122
    public function createUserAfterInitData(User $user): void
123
    {
124
        try {
125
            $account = new Account();
126
            $account->setAttributes([
127
                'name' => Yii::t('app', 'General Account'),
128
                'type' => AccountType::getName(AccountType::GENERAL_ACCOUNT),
129
                'user_id' => $user->id,
130
                'currency_balance' => 0,
131
                'default' => (bool)Account::DEFAULT,
132
                'currency_code' => $user->base_currency_code
133
            ]);
134
            if (!$account->save()) {
135
                throw new DBException('Init Account fail ' . Setup::errorMessage($account->firstErrors));
136
            }
137
            $items = [
138
                [
139
                    'name' => Yii::t('app', 'Food and drink'),
140
                    'color' => ColorType::RED,
141
                    'icon_name' => 'food',
142
                    'transaction_type' => TransactionType::EXPENSE,
143
                    'default' => Category::NOT_DEFAULT
144
                ],
145
                [
146
                    'name' => Yii::t('app', 'Home life'),
147
                    'color' => ColorType::ORANGE,
148
                    'icon_name' => 'home',
149
                    'transaction_type' => TransactionType::EXPENSE,
150
                    'default' => Category::NOT_DEFAULT
151
                ],
152
                [
153
                    'name' => Yii::t('app', 'Traffic'),
154
                    'color' => ColorType::BLUE,
155
                    'icon_name' => 'bus',
156
                    'transaction_type' => TransactionType::EXPENSE,
157
                    'default' => Category::NOT_DEFAULT
158
                ],
159
                [
160
                    'name' => Yii::t('app', 'Recreation'),
161
                    'color' => ColorType::VOLCANO,
162
                    'icon_name' => 'game',
163
                    'transaction_type' => TransactionType::EXPENSE,
164
                    'default' => Category::NOT_DEFAULT
165
                ],
166
                [
167
                    'name' => Yii::t('app', 'Health care'),
168
                    'color' => ColorType::GREEN,
169
                    'icon_name' => 'medicine-chest',
170
                    'transaction_type' => TransactionType::EXPENSE,
171
                    'default' => Category::NOT_DEFAULT
172
                ],
173
                [
174
                    'name' => Yii::t('app', 'Clothes'),
175
                    'color' => ColorType::PURPLE,
176
                    'icon_name' => 'clothes',
177
                    'transaction_type' => TransactionType::EXPENSE,
178
                    'default' => Category::NOT_DEFAULT
179
                ],
180
                [
181
                    'name' => Yii::t('app', 'Cultural education'),
182
                    'color' => ColorType::CYAN,
183
                    'icon_name' => 'education',
184
                    'transaction_type' => TransactionType::EXPENSE,
185
                    'default' => Category::NOT_DEFAULT
186
                ],
187
                [
188
                    'name' => Yii::t('app', 'Investment expenditure'),
189
                    'color' => ColorType::GOLD,
190
                    'icon_name' => 'investment',
191
                    'transaction_type' => TransactionType::EXPENSE,
192
                    'default' => Category::NOT_DEFAULT
193
                ],
194
                [
195
                    'name' => Yii::t('app', 'Childcare'),
196
                    'color' => ColorType::LIME,
197
                    'icon_name' => 'baby',
198
                    'transaction_type' => TransactionType::EXPENSE,
199
                    'default' => Category::NOT_DEFAULT
200
                ],
201
                [
202
                    'name' => Yii::t('app', 'Other expenses'),
203
                    'color' => ColorType::GEEK_BLUE,
204
                    'icon_name' => 'expenses',
205
                    'transaction_type' => TransactionType::EXPENSE,
206
                    'default' => Account::DEFAULT,
207
                ],
208
                [
209
                    'name' => Yii::t('app', 'Work income'),
210
                    'color' => ColorType::BLUE,
211
                    'icon_name' => 'work',
212
                    'transaction_type' => TransactionType::INCOME,
213
                    'default' => Category::NOT_DEFAULT
214
                ],
215
                [
216
                    'name' => Yii::t('app', 'Investment income'),
217
                    'color' => ColorType::GOLD,
218
                    'icon_name' => 'investment',
219
                    'transaction_type' => TransactionType::INCOME,
220
                    'default' => Category::NOT_DEFAULT
221
                ],
222
                [
223
                    'name' => Yii::t('app', 'Other income'),
224
                    'color' => ColorType::MAGENTA,
225
                    'icon_name' => 'income',
226
                    'transaction_type' => TransactionType::INCOME,
227
                    'default' => Category::DEFAULT,
228
                ],
229
                [
230
                    'name' => Yii::t('app', 'Transfer'),
231
                    'color' => ColorType::GREEN,
232
                    'icon_name' => 'transfer',
233
                    'transaction_type' => TransactionType::TRANSFER,
234
                    'default' => Category::NOT_DEFAULT,
235
                ],
236
                [
237
                    'name' => Yii::t('app', 'Adjust Balance'),
238
                    'color' => ColorType::BLUE,
239
                    'icon_name' => 'adjust',
240
                    'transaction_type' => TransactionType::ADJUST,
241
                    'default' => Category::NOT_DEFAULT,
242
                ],
243
            ];
244
            $time = date('Y-m-d H:i:s');
245
            $rows = [];
246
            foreach ($items as $key => $value) {
247
                $rows[$key] = $value;
248
                $rows[$key]['user_id'] = $user->id;
249
                $rows[$key]['created_at'] = $time;
250
                $rows[$key]['updated_at'] = $time;
251
            }
252
            if (!ModelHelper::saveAll(Category::tableName(), $rows)) {
253
                throw new DBException('Init Category fail');
254
            }
255
        } catch (Exception $e) {
256
            throw $e;
257
        }
258
    }
259
260
    public function getAuthClients()
261
    {
262
        $data = [];
263
        if ($items = AuthClient::find()->where(['user_id' => Yii::$app->user->id])->all()) {
264
            $items = ArrayHelper::index($items, 'type');
265
266
            foreach (AuthClientType::names() as $key => $value) {
267
                $data[$value] = $items[$key];
268
            }
269
        }
270
271
        return $data;
272
    }
273
274
    /**
275
     * @param string $token
276
     * @return User|array|ActiveRecord|null
277
     * @throws InvalidArgumentException
278
     */
279
    public function getUserByResetToken(string $token)
280
    {
281
        if (empty($token) || !is_string($token)) {
282
            throw new InvalidArgumentException('Token 验证失败,请重新操作。');
283
        }
284
285
        if (!$user = User::findByPasswordResetToken($token)) {
286
            throw new InvalidArgumentException('链接无效或者已失效,请重新操作。');
287
        }
288
        return $user;
289
    }
290
291
    /**
292
     * @param int $type
293
     * @param string $clientId
294
     * @return User
295
     * @throws Exception
296
     */
297
    public function getUserByClientId(int $type, string $clientId): User
298
    {
299
        /** @var AuthClient $model */
300
        if ($model = AuthClient::find()->where(['type' => $type, 'client_id' => $clientId])->one()) {
301
            return $model->user;
302
        }
303
        throw new Exception('您还未绑定账号,请先访问「个人设置」中的「账号绑定」进行绑定账号,然后才能快速记账。');
304
    }
305
}
306