From d1bf6f0cc1a962dbb52350c3806b9ac537ba3441 Mon Sep 17 00:00:00 2001 From: panliang <1163816051@qq.com> Date: Wed, 31 Aug 2022 13:17:53 +0800 Subject: [PATCH] init --- .gitignore | 7 + README.md | 60 +++++ composer.json | 33 +++ .../2022_08_11_110611_create_users_table.php | 70 ++++++ lang/en/user-balance.php | 3 + lang/en/user.php | 5 + lang/zh_CN/user-balance.php | 15 ++ lang/zh_CN/user.php | 27 +++ routes/admin.php | 14 ++ routes/api.php | 28 +++ src/Enums/SocialiteType.php | 48 ++++ src/Events/UserRegister.php | 20 ++ src/Filters/UserFilter.php | 15 ++ src/Http/Admin/UserController.php | 184 +++++++++++++++ src/Http/Api/AuthController.php | 221 ++++++++++++++++++ src/Http/Api/UserController.php | 40 ++++ src/Http/Resources/UserResource.php | 32 +++ src/Http/Resources/UserTinyResource.php | 30 +++ src/Models/User.php | 122 ++++++++++ src/Models/UserSocialite.php | 21 ++ src/UserServiceProvider.php | 27 +++ 21 files changed, 1022 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 composer.json create mode 100644 database/2022_08_11_110611_create_users_table.php create mode 100644 lang/en/user-balance.php create mode 100644 lang/en/user.php create mode 100644 lang/zh_CN/user-balance.php create mode 100644 lang/zh_CN/user.php create mode 100644 routes/admin.php create mode 100644 routes/api.php create mode 100644 src/Enums/SocialiteType.php create mode 100644 src/Events/UserRegister.php create mode 100644 src/Filters/UserFilter.php create mode 100644 src/Http/Admin/UserController.php create mode 100644 src/Http/Api/AuthController.php create mode 100644 src/Http/Api/UserController.php create mode 100644 src/Http/Resources/UserResource.php create mode 100644 src/Http/Resources/UserTinyResource.php create mode 100644 src/Models/User.php create mode 100644 src/Models/UserSocialite.php create mode 100644 src/UserServiceProvider.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d4b362 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +phpunit.phar +/vendor +composer.phar +composer.lock +*.project +.idea/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3ee2cf --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Dcat Admin Extension + +用户管理 + +## 安装 + +- `composer config repositories.peidikeji/dcat-admin-user git git@gitee.com:paddy_technology/dcat-admin-user.git` +- `composer require peidikeji/dcat-admin-user:dev-develop` +- `php artisan vendor:publish --tag=dcat-admin-user-migrations` + +## 事件 + +- 用户注册成功: `Peidikeji\User\Events\UserRegister` + +## 数据表 + +### 用户表: users + +| column | type | nullable | default | comment | +| - | - | - | - | - | +| id | bigint | not null | - | 主键 | +| username | varchar(191) | null | - | 用户名 | +| phone | varchar(191) | null | - | 手机号 | +| name | varchar(191) | null | - | 昵称 | +| avatar | varchar(191) | null | - | 头像 | +| balance | decimal(12, 2) | not null | 0 | 余额 | +| invite_code | varchar(191) | not null | - | 邀请码 | +| inviter_id | bigint | null | - | 邀请人 | +| inviter_path | varchart(191) | null | - | 所有的上级邀请人(-1-2-3-) | +| created_at | timestamp | null | - | 创建时间 | +| updated_at | timestamp | null | - | 更新时间 | + +### 第三方登录信息: user_socialites + +| column | type | nullable | default | comment | +| - | - | - | - | - | +| id | bigint | not null | - | 主键 | +| user_id | bigint | not null | - | 外键关联 users.id | +| type | varchar(191) | not null | - | 类型(SocialiteType) | +| openid | varchar(191) | not null | - | 第三方唯一凭证 | +| unionid | varchar(191) | null | - | 第三方唯一凭证 | +| data | json | null | - | 第三方数据 | +| created_at | timestamp | null | - | 创建时间 | +| updated_at | timestamp | null | - | 更新时间 | + +### 用户余额变动记录: user_balance_logs + +| column | type | nullable | default | comment | +| - | - | - | - | - | +| id | bigint | not null | - | 主键 | +| user_id | bigint | not null | - | 外键关联 users.id | +| cate | varchar(191) | not null | - | 类别 | +| description | varchar(191) | not null | - | 描述 | +| amount | decimal(12, 2) | not null | - | 变动数量(正数为增加, 负数为减少) | +| balance | decimal(12, 2) | not null | - | 变动后的余额 | +| remarks | varchart(191) | null | - | 备注 | +| source_type | varchart(191) | null | - | 来源(多态关联) | +| source_id | bigint | null | - | 来源(多态关联) | +| created_at | timestamp | null | - | 创建时间 | +| updated_at | timestamp | null | - | 更新时间 | diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8120d5c --- /dev/null +++ b/composer.json @@ -0,0 +1,33 @@ +{ + "name": "peidikeji/dcat-admin-user", + "alias": "用户管理", + "description": "用户管理", + "type": "library", + "keywords": ["dcat-admin", "extension", "user"], + "homepage": "https://gitee.com/paddy_technology/dcat-admin-user", + "license": "MIT", + "authors": [ + { + "name": "panliang", + "email": "1163816051@qq.com" + } + ], + "require": { + "php": ">=7.1.0", + "peidikeji/dcat-admin": "*", + "tucker-eric/eloquentfilter": "^3.1", + "laravel/framework": "^9.0" + }, + "autoload": { + "psr-4": { + "Peidikeji\\User\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Peidikeji\\User\\UserServiceProvider" + ] + } + } +} diff --git a/database/2022_08_11_110611_create_users_table.php b/database/2022_08_11_110611_create_users_table.php new file mode 100644 index 0000000..7066be5 --- /dev/null +++ b/database/2022_08_11_110611_create_users_table.php @@ -0,0 +1,70 @@ +id(); + $table->string('username'); + $table->string('password')->nullable(); + $table->string('phone')->nullable(); + $table->string('name')->nullable(); + $table->string('avatar')->nullable(); + $table->decimal('balance', 12, 2)->default(0)->comment('余额'); + $table->string('invite_code')->comment('邀请码'); + $table->unsignedBigInteger('inviter_id')->nullable()->comment('邀请人'); + $table->string('inviter_path')->nullable()->comment('所有上级邀请人'); + $table->timestamps(); + + $table->comment('用户表'); + }); + + Schema::create('user_socialites', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('user_id'); + $table->string('type'); + $table->string('openid'); + $table->string('unionid')->nullable(); + $table->json('data')->nullable(); + $table->timestamps(); + + $table->comment('用户-第三方登录'); + }); + + Schema::create('user_balance_logs', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('user_id'); + $table->string('cate')->comment('类别'); + $table->string('description')->comment('描述'); + $table->decimal('amount', 12, 2)->comment('变动数量, 正数为增加, 负数为减少'); + $table->decimal('balance', 12, 2)->comment('变更后的余额'); + $table->string('remarks')->nullable()->comment('备注'); + $table->nullableMorphs('source'); + $table->timestamps(); + + $table->comment('用户-余额流水记录'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('user_balance_logs'); + Schema::dropIfExists('user_socialites'); + Schema::dropIfExists('users'); + } +}; diff --git a/lang/en/user-balance.php b/lang/en/user-balance.php new file mode 100644 index 0000000..0b67a5f --- /dev/null +++ b/lang/en/user-balance.php @@ -0,0 +1,3 @@ + [ + 'UserBalance' => '余额流水', + 'user-balance' => '余额流水', + ], + 'fields' => [ + 'user_id' => '用户', + 'cate' => '类别', + 'amount' => '金额', + 'description' => '描述', + 'created_at' => '时间', + ] +]; diff --git a/lang/zh_CN/user.php b/lang/zh_CN/user.php new file mode 100644 index 0000000..0a79a2e --- /dev/null +++ b/lang/zh_CN/user.php @@ -0,0 +1,27 @@ + [ + 'User' => '用户管理', + 'user' => '用户', + 'users' => '用户', + ], + 'fields' => [ + 'id' => 'ID', + 'name' => '姓名', + 'gender' => '性别', + 'phone' => '手机号', + 'avatar' => '头像', + 'balance' => '余额', + 'profit' => 'e品额', + 'inviter_id' => '邀请人', + 'inviter' => [ + 'name' => '邀请人', + 'phone' => '邀请人', + ], + 'invite_code' => '邀请码', + 'username' => '用户名', + 'password' => '密码', + 'created_at' => '注册时间', + ] +]; diff --git a/routes/admin.php b/routes/admin.php new file mode 100644 index 0000000..44fa555 --- /dev/null +++ b/routes/admin.php @@ -0,0 +1,14 @@ + config('admin.route.prefix'), + 'middleware' => config('admin.route.middleware'), +], function () { + Route::get('api/user', [UserController::class, 'list'])->name('dcat.admin.api.users'); + + Route::resource('users', UserController::class)->names('dcat.admin.users'); +}); diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..fd82249 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,28 @@ + ['api'], + 'prefix' => 'api', +], function () { + Route::group(['prefix' => 'auth'], function () { + Route::post('login', [AuthController::class, 'login']); + Route::post('register', [AuthController::class, 'register']); + + Route::post('login-by-sms', [AuthController::class, 'loginBySms']); + + Route::post('login-by-wxmini', [AuthController::class, 'loginByWxMini']); + Route::post('wx-bind-phone', [AuthController::class, 'wxbindPhone']); + + Route::post('reset', [AuthController::class, 'reset']); + Route::post('reset-pwd', [AuthController::class, 'resetPwd']); + }); + + Route::group(['prefix' => 'user', 'middleware' => ['auth:api']], function () { + Route::get('profile', [UserController::class, 'profile']); + Route::put('profile', [UserController::class, 'update']); + }); +}); diff --git a/src/Enums/SocialiteType.php b/src/Enums/SocialiteType.php new file mode 100644 index 0000000..e2e5772 --- /dev/null +++ b/src/Enums/SocialiteType.php @@ -0,0 +1,48 @@ +value => '微信小程序', + ]; + } + + public function text() + { + return data_get(self::options(), $this->value); + } + + public function label() + { + $color = match ($this) { + static::WxMini => 'success', + }; + + $background = Admin::color()->get($color, $color); + + $name = static::options()[$this->value] ?? '其它'; + + return "{$name}"; + } + + public function dot() + { + $color = match ($this) { + static::WxMini => 'success', + }; + + $background = Admin::color()->get($color, $color); + + $name = static::options()[$this->value] ?? '其它'; + + return "  {$name}"; + } +} diff --git a/src/Events/UserRegister.php b/src/Events/UserRegister.php new file mode 100644 index 0000000..a4f1cdf --- /dev/null +++ b/src/Events/UserRegister.php @@ -0,0 +1,20 @@ +user = $user; + } +} diff --git a/src/Filters/UserFilter.php b/src/Filters/UserFilter.php new file mode 100644 index 0000000..6194426 --- /dev/null +++ b/src/Filters/UserFilter.php @@ -0,0 +1,15 @@ +where(function ($q) use ($key) { + $q->orWhere('phone', 'like', "%$key%")->orWhere('username', 'like', "%$key%"); + }); + } +} diff --git a/src/Http/Admin/UserController.php b/src/Http/Admin/UserController.php new file mode 100644 index 0000000..3c5e10e --- /dev/null +++ b/src/Http/Admin/UserController.php @@ -0,0 +1,184 @@ +filled('q')) { + $search = '%' . $request->input('q'). '%'; + $query->where(fn($q) => $q->where('phone', 'like', $search)->orWhere('name', 'like', $search)); + } + + if ($request->filled('none_merchant') || $request->filled('amp;none_merchant')) { + $query->whereDoesntHave('merchants'); + } + + $query->select(['id', 'phone as text']); + + if ($request->filled('_paginate')) { + $list = $query->paginate(); + } else { + $list = $query->get(); + } + return $list; + } + + protected function grid() + { + return Grid::make(User::with(['inviter']), function (Grid $grid) { + $grid->model()->sort(); + + $grid->disableRowSelector(); + + $grid->column('id'); + $grid->column('name')->display(function () { + return ($this->avatar ? '' : '') . $this->name; + }); + $grid->column('phone'); + $grid->column('balance')->modal(__('dcat-admin-user::user.fields.balance') . '变更记录', fn() => BalanceLogTable::make(['type' => BalanceLog::TYPE_BALANCE, 'subject_id' => $this->id, 'subject_type' => 'user'])); + $grid->column('profit')->modal(__('dcat-admin-user::user.fields.profit') . '变更记录', fn() => BalanceLogTable::make(['type' => BalanceLog::TYPE_PROFIT, 'subject_id' => $this->id, 'subject_type' => 'user'])); + $grid->column('inviter.phone'); + $grid->column('created_at'); + + $user = Admin::user(); + $grid->showCreateButton($user->can('dcat.admin.users.create')); + $grid->showViewButton($user->can('dcat.admin.users.show')); + $grid->showEditButton($user->can('dcat.admin.users.edit')); + $grid->showDeleteButton($user->can('dcat.admin.users.destroy')); + $grid->actions(function (Grid\Displayers\Actions $actions) use ($user) { + //余额变动 + if($user->can('dcat.admin.users.change_balance')){ + $actions->append(new UserBalanceChange()); + } + }); + + $grid->filter(function (Filter $filter) { + $filter->panel(); + $filter->where('name', function ($q) { + $search = '%'.$this->input.'%'; + $q->where(fn($q) => $q->where('username', 'like', $search)->orWhere('name', 'like', $search)->orWhere('phone', 'like', $search)); + }, '搜索')->placeholder('姓名/手机号')->width(3); + }); + }); + } + + protected function form() + { + return Form::make(new User(), function (Form $form) { + $uniquePhone = Rule::unique('users', 'phone'); + if ($form->isEditing()) { + $uniquePhone->ignore($form->model()->id); + } + $form->text('phone')->rules([$uniquePhone])->required(); + $form->text('name')->required(); + $form->image('avatar')->autoUpload()->saveFullUrl()->move('user/avatar'); + $form->select('inviter_id')->model(User::class, 'id', 'name')->ajax('api/user?_paginate=1'); + $form->hidden('inviter_path'); + $form->hidden('invite_code'); + + $form->saving(function (Form $form) { + $form->inviter_path = $form->inviter_id ? User::where('id', $form->inviter_id)->value('inviter_path') . $form->inviter_id.'-' : null; + if (!$form->invite_code) { + do { + $invite_code = strtoupper(Str::random(6)); + } while(User::where('invite_code', $invite_code)->exists()); + $form->invite_code = $invite_code; + } + }); + + $form->deleting(function (Form $form) { + $data = $form->model()->toArray(); + $ids = array_column($data, 'id'); + if (Merchant::whereIn('user_id', $ids)->exists()) { + return $form->response()->error('已经关联店铺, 无法删除'); + } + if (Order::whereIn('user_id', $ids)->exists()) { + return $form->response()->error('已经关联订单, 请先删除订单'); + } + // 删除用户的登录信息 + UserSocialite::whereIn('user_id', $ids)->delete(); + foreach($ids as $id) { + $current = User::findOrFail($id); + // 删除用户的邀请信息 + $users = User::where('inviter_path', 'like', '%-'.$id.'-%')->get(); + foreach($users as $user) { + $user->inviter_path = str_replace('-'.$id.'-', '-', $user->inviter_path); + $ids = explode('-', $user->inviter_path); + $user->inviter_id = count($ids) > 2 ? $ids[count($ids) - 2] : null; + $user->save(); + } + } + }); + }); + } + + protected function detail($id) + { + $info = User::with(['inviter', 'inviteUsers', 'socialites'])->findOrFail($id); + $show = Show::make($info); + $show->field('name'); + $show->field('gender'); + $show->field('phone'); + $show->field('avatar')->image('', 64); + $show->field('inviter.phone'); + $show->field('invite_code'); + $show->field('balance'); + $show->field('profit'); + $show->field('created_at'); + $show->tools(function (Tools $tools) { + $tools->disableBack(); + $tools->disableList(false); + }); + + $tab = new Tab(); + + $keys = ['id', 'username', 'name', 'phone', 'created_at']; + $inviteUsers = $info->inviteUsers->map(function ($item) { + return [$item->id, $item->username, $item->name, $item->phone, $item->created_at->format('Y-m-d H:i:s')]; + }); + $headers = array_map(fn($v) => __($this->translation . '.fields.' . $v), $keys); + $table = new Table($headers, $inviteUsers, ['table-hover']); + $tab->add('邀请用户', $table); + + $keys = ['type', 'openid', 'created_at']; + $socialites = $info->socialites->map(function ($item) { + return [$item->type->text(), $item->openid, $item->created_at->format('Y-m-d H:i:s')]; + }); + $table = new Table($keys, $socialites, ['table-hover']); + $tab->add('绑定账户', $table); + + $row = new Row(); + $row->column(6, $show); + $row->column(6, (new Card('', $tab))); + return $row; + } +} diff --git a/src/Http/Api/AuthController.php b/src/Http/Api/AuthController.php new file mode 100644 index 0000000..c1d07d0 --- /dev/null +++ b/src/Http/Api/AuthController.php @@ -0,0 +1,221 @@ +validate([ + 'username' => 'required', + 'password' => 'required', + ]); + + $user = User::filter(['key' => $request->input('username')])->first(); + if (!$user) { + return $this->error('用户名或密码错误'); + } + if (!Hash::check($request->input('password'), $user->password)) { + return $this->error('用户名或密码错误1'); + } + + return $this->attemptUser($user); + } + + public function loginBySms(Request $request) + { + $request->validate([ + 'phone' => 'required', + 'code' => 'required', + ]); + $phone = $request->input('phone'); + + $result = Sms::checkCode('login', $phone, $request->input('code')); + if (!$result) { + return $this->error('验证码不正确或已过期'); + } + + $user = User::where('phone', $phone)->first(); + if (!$user) { + $user = $this->createUser(['phone' => $phone], $request->input('invite_code')); + } + + return $this->attemptUser($user); + } + + /** + * 微信小程序登录 + * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html + */ + public function loginByWxMini(Request $request) + { + $request->validate([ + 'code' => 'required', + ]); + $app = EasyWeChat::miniApp(); + $response = $app->getClient()->get('/sns/jscode2session', [ + 'appid' => $app->getAccount()->getAppId(), + 'secret' => $app->getAccount()->getSecret(), + 'js_code' => $request->input('code'), + 'grant_type' => 'authorization_code', + ]); + $result = $response->toArray(); + $openid = data_get($result, 'openid'); + if (!$openid) { + return $this->error('未获取到 openid'); + } + $unionid = data_get($result, 'unionid'); + $socialite = UserSocialite::query()->updateOrCreate([ + 'openid' => $openid, + 'type' => SocialiteType::WxMini, + ], [ + 'data' => $result, + 'unionid' => $unionid, + ]); + if ($socialite->user) { + return $this->attemptUser($socialite->user); + } + + return $this->json(['openid' => $openid]); + } + + /** + * 微信小程序获取手机号 + * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-info/phone-number/getPhoneNumber.html + */ + public function wxbindPhone(Request $request) + { + $request->validate([ + 'openid' => 'required', + 'code' => 'required', + ]); + $response = EasyWeChat::miniApp()->getClient()->postJson('/wxa/business/getuserphonenumber', ['code' => $request->input('code')]); + $result = $response->toArray(); + if (data_get($result, 'errcode') !== 0) { + return $this->error(data_get($result, 'errmsg')); + } + $phone = data_get($result, 'phone_info.purePhoneNumber'); + if (!$phone) { + return $this->error('未获取到手机号'); + } + + $user = User::where('phone', $phone)->first(); + if (!$user) { + $user = $this->createUser(['phone' => $phone], $request->input('invite_code')); + } elseif ($user->socialites()->where('type', SocialiteType::WxMini)->exists()) { + return $this->error('手机号已经绑定过了'); + } + + $openid = $request->input('openid'); + UserSocialite::updateOrCreate([ + 'openid' => $openid, + 'type' => SocialiteType::WxMini, + ], [ + 'user_id' => $user->id, + ]); + + return $this->attemptUser($user); + } + + public function register(Request $request) + { + $request->validate([ + 'phone' => 'required', + 'code' => 'required', + ]); + $phone = $request->input('phone'); + + if (User::where('phone', $phone)->exists()) { + return $this->error('用户已经注册'); + } + + $result = Sms::checkCode('register', $phone, $request->input('code')); + if (!$result) { + return $this->error('验证码不正确或已过期'); + } + + $user = $this->createUser(['phone' => $phone], $request->input('invite_code')); + + return $this->attemptUser($user); + } + + public function reset(Request $request) + { + $request->validate([ + 'phone' => 'required', + 'code' => 'required', + 'password' => ['required', Password::min(6)], + ]); + $phone = $request->input('phone'); + + $result = Sms::checkCode('reset', $phone, $request->input('code')); + if (!$result) { + return $this->error('验证码不正确或已过期'); + } + + $user = User::where('phone', $phone)->first(); + if (!$user) { + return $this->error('用户不存在'); + } + + $user->update([ + 'password' => Hash::make($request->input('password')), + ]); + + return $this->success('密码重置成功'); + } + + protected function attemptUser(User $user, $name = 'api') + { + $token = $user->createToken($name)->plainTextToken; + return $this->json(['token' => $token, 'id' => $user->id]); + } + + protected function createUser($attributes = [], $invite_code = null) + { + if ($invite_code) { + $inviterId = User::where('invite_code', $invite_code)->value('id'); + if (!$inviterId) { + throw new BizException('邀请码错误'); + } + $attributes['inviter_id'] = $inviterId; + } + $user = User::create($attributes); + + event(new UserRegister($user)); + + return $user; + } + + public function resetPwd(Request $request) + { + $input = $request->validate([ + 'password' => 'required|current_password:api', + 'new_password' => 'required', + ], [ + 'password.current_password' => '密码错误', + ]); + $user = auth('api')->user(); + if (!$user || !Hash::check($input['password'], $user->password)) { + throw new BizException('密码错误'); + } + + $user->password = bcrypt($input['new_password']); + $user->save(); + + $user->tokens()->delete(); + return $this->success('修改成功'); + } +} diff --git a/src/Http/Api/UserController.php b/src/Http/Api/UserController.php new file mode 100644 index 0000000..8627834 --- /dev/null +++ b/src/Http/Api/UserController.php @@ -0,0 +1,40 @@ +user(); + + return UserResource::make($user); + } + + public function update(Request $request) + { + $update = $request->validate([ + 'name' => 'string', + 'avatar' => 'string', + 'gender' => 'string', + 'password' => 'confirmed', + ]); + $user = auth('api')->user(); + + if ($request->filled('password')) { + $validatePassword = !is_null($user->password); + if ($validatePassword) { + $request->validate(['password_old' => 'current_password:api'], ['password_old.current_password' => '原密码错误']); + } + $update['password'] = Hash::make($request->input('password')); + } + + $user->update($update); + return $this->success('修改成功'); + } +} diff --git a/src/Http/Resources/UserResource.php b/src/Http/Resources/UserResource.php new file mode 100644 index 0000000..9ac7c8c --- /dev/null +++ b/src/Http/Resources/UserResource.php @@ -0,0 +1,32 @@ + $this->id, + 'name' => $this->name, + 'username' => $this->username, + 'avatar' => $this->avatar, + 'balance' => $this->balance, + 'profit' => $this->profit, + 'invite_code' => $this->invite_code, + 'inviter_id' => $this->inviter_id, + 'phone' => $this->phone, + 'gender' => $this->gender, + 'vip_expired_at' => $this->vip_expired_at?->timestamp, + 'created_at' => $this->created_at?->timestamp, + ]; + } + + public function with($request) + { + return ['code' => Response::HTTP_OK, 'message' => '']; + } +} diff --git a/src/Http/Resources/UserTinyResource.php b/src/Http/Resources/UserTinyResource.php new file mode 100644 index 0000000..f9c09ee --- /dev/null +++ b/src/Http/Resources/UserTinyResource.php @@ -0,0 +1,30 @@ + $this->id, + 'name' => $this->name, + 'username' => $this->username, + 'avatar' => $this->avatar, + 'invite_code' => $this->invite_code, + 'inviter_id' => $this->inviter_id, + 'phone' => $this->phone ? substr_replace($this->phone, '****', 3, 4) : $this->phone, + 'created_at' => $this->created_at?->timestamp, + 'is_vip' => $this->isVip(), + 'vip_expired_at' => $this->vip_expired_at?->timestamp, + ]; + } + + public function with($request) + { + return ['code' => Response::HTTP_OK, 'message' => '']; + } +} diff --git a/src/Models/User.php b/src/Models/User.php new file mode 100644 index 0000000..76ec9d0 --- /dev/null +++ b/src/Models/User.php @@ -0,0 +1,122 @@ +invite_code) { + do { + $invite_code = strtoupper(Str::random(6)); + } while(User::where('invite_code', $invite_code)->exists()); + $model->invite_code = $invite_code; + } + if ($model->inviter_id && !$model->inviter_path) { + $inviter_path = User::where('id', $model->inviter_id)->value('inviter_path') ?: '-'; + $model->inviter_path = $inviter_path . $model->inviter_id . '-'; + } + }); + } + + public function modelFilter() + { + return UserFilter::class; + } + + public function inviter() + { + return $this->belongsTo(static::class, 'inviter_id'); + } + + public function inviteUsers() + { + return $this->hasMany(static::class, 'inviter_id')->sort(); + } + + public function socialites() + { + return $this->hasMany(UserSocialite::class, 'user_id'); + } + + public function balanceLogs() + { + return $this->morphMany(BalanceLog::class, 'subject'); + } + + public function cashOutLogs() + { + return $this->hasMany(CashOutLog::class, 'user_id'); + } + + public function merchants() + { + return $this->hasMany(Merchant::class, 'user_id'); + } + + public function scopeSort($q) + { + return $q->latest('id'); + } + + public function isVip(): bool + { + return $this->vip_expired_at && $this->vip_expired_at->gt(now()) ? true : false; + } + + public function getSubPhone() + { + // 18223350967 => 0967 + return $this->phone ? substr($this->phone, 7, 4) : ''; + } + + public function getHidePhone() + { + // 18223350967 => 182****0967 + return $this->phone ? substr_replace($this->phone, '****', 3, 4) : ''; + } + + public function addresses(){ + return $this->hasMany(UserAddress::class, 'user_id'); + } + + // 购物车 + public function carts() + { + return $this->hasMany(GoodsCart::class, 'user_id'); + } + + // 订单 + public function orders() + { + return $this->hasMany(Order::class, 'user_id'); + } + + public function likes() + { + return $this->hasMany(UserLike::class, 'user_id'); + } +} diff --git a/src/Models/UserSocialite.php b/src/Models/UserSocialite.php new file mode 100644 index 0000000..2151f89 --- /dev/null +++ b/src/Models/UserSocialite.php @@ -0,0 +1,21 @@ + 'array', + 'type' => SocialiteType::class + ]; + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } +} diff --git a/src/UserServiceProvider.php b/src/UserServiceProvider.php new file mode 100644 index 0000000..b520209 --- /dev/null +++ b/src/UserServiceProvider.php @@ -0,0 +1,27 @@ +loadRoutesFrom(__DIR__.'/../routes/admin.php'); + $this->loadRoutesFrom(__DIR__.'/../routes/api.php'); + + // $this->loadMigrationsFrom(__DIR__.'/../database/'); + + $this->publishes([ + __DIR__.'/../database/' => database_path('migrations') + ], 'dcat-admin-user-migrations'); + + $this->loadTranslationsFrom(__DIR__.'/../lang', 'dcat-admin-user'); + } +}