4
0
Fork 0
master
panliang 2022-08-31 13:17:53 +08:00
commit d1bf6f0cc1
21 changed files with 1022 additions and 0 deletions

7
.gitignore vendored 100644
View File

@ -0,0 +1,7 @@
.DS_Store
phpunit.phar
/vendor
composer.phar
composer.lock
*.project
.idea/

60
README.md 100644
View File

@ -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 | - | 更新时间 |

33
composer.json 100644
View File

@ -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"
]
}
}
}

View File

@ -0,0 +1,70 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->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');
}
};

View File

@ -0,0 +1,3 @@
<?php
return [];

5
lang/en/user.php 100644
View File

@ -0,0 +1,5 @@
<?php
return [
];

View File

@ -0,0 +1,15 @@
<?php
return [
'labels' => [
'UserBalance' => '余额流水',
'user-balance' => '余额流水',
],
'fields' => [
'user_id' => '用户',
'cate' => '类别',
'amount' => '金额',
'description' => '描述',
'created_at' => '时间',
]
];

View File

@ -0,0 +1,27 @@
<?php
return [
'labels' => [
'User' => '用户管理',
'user' => '用户',
'users' => '用户',
],
'fields' => [
'id' => 'ID',
'name' => '姓名',
'gender' => '性别',
'phone' => '手机号',
'avatar' => '头像',
'balance' => '余额',
'profit' => 'e品额',
'inviter_id' => '邀请人',
'inviter' => [
'name' => '邀请人',
'phone' => '邀请人',
],
'invite_code' => '邀请码',
'username' => '用户名',
'password' => '密码',
'created_at' => '注册时间',
]
];

14
routes/admin.php 100644
View File

@ -0,0 +1,14 @@
<?php
namespace Peidikeji\User\Http\Admin;
use Illuminate\Support\Facades\Route;
Route::group([
'prefix' => 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');
});

28
routes/api.php 100644
View File

@ -0,0 +1,28 @@
<?php
namespace Peidikeji\User\Http\Api;
use Illuminate\Support\Facades\Route;
Route::group([
'middleware' => ['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']);
});
});

View File

@ -0,0 +1,48 @@
<?php
namespace Peidikeji\User\Enums;
use Dcat\Admin\Admin;
enum SocialiteType: string
{
case WxMini = 'wx-mini';
public static function options()
{
return [
self::WxMini->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 "<span class='label' style='background: $background;'>{$name}</span>";
}
public function dot()
{
$color = match ($this) {
static::WxMini => 'success',
};
$background = Admin::color()->get($color, $color);
$name = static::options()[$this->value] ?? '其它';
return "<i class='fa fa-circle' style='font-size: 13px;color: $background;'>&nbsp;&nbsp;{$name}</span>";
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Peidikeji\User\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Peidikeji\User\Models\User;
class UserRegister
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $user;
public function __construct(User $user)
{
$this->user = $user;
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Peidikeji\User\Filters;
use EloquentFilter\ModelFilter;
class UserFilter extends ModelFilter
{
public function key($key)
{
$this->where(function ($q) use ($key) {
$q->orWhere('phone', 'like', "%$key%")->orWhere('username', 'like', "%$key%");
});
}
}

View File

@ -0,0 +1,184 @@
<?php
namespace Peidikeji\User\Http\Admin;
use App\Admin\Renderable\BalanceLogTable;
use App\Models\BalanceLog;
use Dcat\Admin\Admin;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Grid\Filter;
use Dcat\Admin\Http\Controllers\AdminController;
use Dcat\Admin\Layout\Row;
use Dcat\Admin\Show;
use Dcat\Admin\Show\Tools;
use Dcat\Admin\Widgets\Card;
use Dcat\Admin\Widgets\Tab;
use Dcat\Admin\Widgets\Table;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Peidikeji\Merchant\Models\Merchant;
use Peidikeji\Order\Models\Order;
use Peidikeji\User\Models\User;
use Peidikeji\User\Models\UserSocialite;
use Illuminate\Support\Str;
use App\Admin\Actions\Grid\UserBalanceChange;
class UserController extends AdminController
{
protected $translation = 'dcat-admin-user::user';
public function list(Request $request)
{
$query = User::query();
if ($request->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 ? '<img src="'.$this->avatar.'" width="60" class="img-thumbnail" />' : '') . $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;
}
}

View File

@ -0,0 +1,221 @@
<?php
namespace Peidikeji\User\Http\Api;
use App\Exceptions\BizException;
use App\Http\Controllers\Controller;
use App\Models\Sms;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
use Overtrue\LaravelWeChat\EasyWeChat;
use Peidikeji\User\Enums\SocialiteType;
use Peidikeji\User\Events\UserRegister;
use Peidikeji\User\Models\User;
use Peidikeji\User\Models\UserSocialite;
class AuthController extends Controller
{
public function login(Request $request)
{
$request->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('修改成功');
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Peidikeji\User\Http\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Peidikeji\User\Http\Resources\UserResource;
class UserController extends Controller
{
public function profile()
{
$user = auth('api')->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('修改成功');
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Peidikeji\User\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Response;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $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' => ''];
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace Peidikeji\User\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Response;
class UserTinyResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $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' => ''];
}
}

122
src/Models/User.php 100644
View File

@ -0,0 +1,122 @@
<?php
namespace Peidikeji\User\Models;
use App\Models\BalanceLog;
use App\Models\CashOutLog;
use App\Models\UserAddress;
use App\Models\UserLike;
use EloquentFilter\Filterable;
use Laravel\Sanctum\HasApiTokens;
use Peidikeji\User\Filters\UserFilter;
use Peidikeji\Merchant\Models\Merchant;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Str;
use Peidikeji\Goods\Models\GoodsCart;
use Peidikeji\Order\Models\Order;
class User extends Authenticatable
{
use HasApiTokens;
use HasDateTimeFormatter;
use Filterable;
protected $fillable = ['username', 'password', 'avatar', 'balance', 'profit', 'invite_code', 'inviter_id', 'inviter_path', 'name', 'phone', 'vip_expired_at', 'gender'];
protected $dates = ['vip_expired_at'];
protected static function booted()
{
static::creating(function ($model) {
if (!$model->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');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Peidikeji\User\Models;
use Illuminate\Database\Eloquent\Model;
use Peidikeji\User\Enums\SocialiteType;
class UserSocialite extends Model
{
protected $fillable = ['data', 'openid', 'type', 'user_id'];
protected $casts = [
'data' => 'array',
'type' => SocialiteType::class
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Peidikeji\User;
use Illuminate\Support\ServiceProvider;
class UserServiceProvider extends ServiceProvider
{
public function register()
{
}
public function boot()
{
$this->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');
}
}