user
parent
cf537656bd
commit
bdce01ac8e
16
README.md
16
README.md
|
|
@ -106,3 +106,19 @@ $menus = [
|
||||||
| source_id | bigint | null | - | 来源(多态关联) |
|
| source_id | bigint | null | - | 来源(多态关联) |
|
||||||
| created_at | timestamp | null | - | 创建时间 |
|
| created_at | timestamp | null | - | 创建时间 |
|
||||||
| updated_at | timestamp | null | - | 更新时间 |
|
| updated_at | timestamp | null | - | 更新时间 |
|
||||||
|
|
||||||
|
## 收货地址: user_address
|
||||||
|
|
||||||
|
| column | type | nullable | default | comment |
|
||||||
|
| - | - | - | - | - |
|
||||||
|
| id | bigint | not null | - | 主键 |
|
||||||
|
| user_id | bigint | not null | - | 外键关联 users.id |
|
||||||
|
| contact_name | varchar(191) | not null | - | 联系人 |
|
||||||
|
| phone | varchar(191) | not null | - | 电话 |
|
||||||
|
| address | varchar(191) | not null | - | 地址 |
|
||||||
|
| province_id | bigint | null | - | 所属地区 |
|
||||||
|
| city_id | bigint | null | - | 所属地区 |
|
||||||
|
| area_id | bigint | null | - | 所属地区 |
|
||||||
|
| is_default | int | not null | 0 | 默认地址 |
|
||||||
|
| created_at | timestamp | null | - | 创建时间 |
|
||||||
|
| updated_at | timestamp | null | - | 更新时间 |
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?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('user_withdraw', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('user_id')->comment('申请人');
|
||||||
|
$table->decimal('amount', 12, 2)->comment('提现数量');
|
||||||
|
$table->decimal('balance', 12, 2)->comment('剩余');
|
||||||
|
$table->unsignedInteger('status')->default(0)->comment('状态');
|
||||||
|
$table->json('payee')->nullable()->comment('收款账户');
|
||||||
|
$table->json('payer')->nullable()->comment('打款账户');
|
||||||
|
$table->string('reason')->nullable()->comment('原因');
|
||||||
|
$table->string('remarks')->nullable()->comment('备注');
|
||||||
|
$table->timestamp('finish_at')->nullable()->comment('完成时间');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('user_withdraw');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => [
|
||||||
|
'withdraw' => '提现管理',
|
||||||
|
'Withdraw' => '提现管理',
|
||||||
|
],
|
||||||
|
'fields' => [
|
||||||
|
'balance' => '余额',
|
||||||
|
'amount' => '提现数量',
|
||||||
|
'payee' => '收款账户',
|
||||||
|
'payer' => '打款账户',
|
||||||
|
'remarks' => '备注',
|
||||||
|
'status' => '状态',
|
||||||
|
'user_id' => '申请人',
|
||||||
|
'user' => [
|
||||||
|
'phone' => '申请人'
|
||||||
|
],
|
||||||
|
'created_at' => '申请时间',
|
||||||
|
'finish_at' => '完成时间',
|
||||||
|
'reason' => '失败原因',
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
@ -12,4 +12,6 @@ Route::group([
|
||||||
|
|
||||||
Route::resource('users', UserController::class)->names('dcat.admin.users');
|
Route::resource('users', UserController::class)->names('dcat.admin.users');
|
||||||
Route::resource('user-balance', UserBalanceController::class)->names('dcat.admin.user_balance');
|
Route::resource('user-balance', UserBalanceController::class)->names('dcat.admin.user_balance');
|
||||||
|
|
||||||
|
Route::resource('withdraw', WithdrawController::class)->names('dcat.admin.withdraw')->only(['index', 'show']);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -26,5 +26,8 @@ Route::group([
|
||||||
|
|
||||||
Route::get('address/default', [AddressController::class, 'default']);
|
Route::get('address/default', [AddressController::class, 'default']);
|
||||||
Route::apiResource('address', AddressController::class);
|
Route::apiResource('address', AddressController::class);
|
||||||
|
|
||||||
|
Route::post('withdraw/{id}/cancel', [WithdrawController::class, 'cancel']);
|
||||||
|
Route::apiResource('withdraw', WithdrawController::class)->only(['index', 'show', 'store']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Action\Withdraw;
|
||||||
|
|
||||||
|
use Dcat\Admin\Grid\BatchAction;
|
||||||
|
use Dcat\Admin\Widgets\Modal;
|
||||||
|
use Peidikeji\User\Form\Withdraw\CheckForm;
|
||||||
|
|
||||||
|
class BatchCheck extends BatchAction
|
||||||
|
{
|
||||||
|
protected $title = '审核';
|
||||||
|
|
||||||
|
protected function html()
|
||||||
|
{
|
||||||
|
$form = CheckForm::make()->payload(['remarks' => '']);
|
||||||
|
return Modal::make()
|
||||||
|
->lg()
|
||||||
|
->title($this->title)
|
||||||
|
->body($form)
|
||||||
|
->onLoad($this->getModalScript())
|
||||||
|
->button($this->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getModalScript()
|
||||||
|
{
|
||||||
|
// 弹窗显示后往隐藏的id表单中写入批量选中的行ID
|
||||||
|
return <<<JS
|
||||||
|
// 获取选中的ID数组
|
||||||
|
var key = {$this->getSelectedKeysScript()}
|
||||||
|
|
||||||
|
$('#batch-check-id').val(key);
|
||||||
|
JS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Action\Withdraw;
|
||||||
|
|
||||||
|
use Dcat\Admin\Grid\RowAction;
|
||||||
|
use Dcat\Admin\Widgets\Modal;
|
||||||
|
use Peidikeji\User\Form\Withdraw\CheckForm;
|
||||||
|
|
||||||
|
class RowCheck extends RowAction
|
||||||
|
{
|
||||||
|
protected $title = '审核';
|
||||||
|
|
||||||
|
protected function html()
|
||||||
|
{
|
||||||
|
$model = $this->row();
|
||||||
|
$form = CheckForm::make()->payload(['remarks' => $model->remarks ?: '', 'id' => $model->id]);
|
||||||
|
return Modal::make()
|
||||||
|
->lg()
|
||||||
|
->title($this->title)
|
||||||
|
->body($form)
|
||||||
|
->button($this->title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Enums;
|
||||||
|
|
||||||
|
enum WithdrawStatus: int
|
||||||
|
{
|
||||||
|
case None = 0;
|
||||||
|
case Processing = 1;
|
||||||
|
case Success = 2;
|
||||||
|
case Fail = 3;
|
||||||
|
case Cancel = 4;
|
||||||
|
|
||||||
|
public static function options()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
self::None->value => '待处理',
|
||||||
|
self::Processing->value => '处理中',
|
||||||
|
self::Success->value => '成功',
|
||||||
|
self::Fail->value => '失败',
|
||||||
|
self::Cancel->value => '已取消',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color()
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
static::None => 'warning',
|
||||||
|
static::Processing => 'secondary',
|
||||||
|
static::Success => 'success',
|
||||||
|
static::Fail => 'danger',
|
||||||
|
static::Cancel => 'secondary',
|
||||||
|
default => 'dark',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function text()
|
||||||
|
{
|
||||||
|
return data_get(self::options(), $this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function label()
|
||||||
|
{
|
||||||
|
$color = $this->color();
|
||||||
|
|
||||||
|
$name = $this->text();
|
||||||
|
|
||||||
|
return "<span class='label bg-${color}'>{$name}</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dot()
|
||||||
|
{
|
||||||
|
$color = $this->color();
|
||||||
|
|
||||||
|
$name = $this->text();
|
||||||
|
|
||||||
|
return "<i class='fa fa-circle text-$color'> {$name}</span>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Exceptions;
|
||||||
|
|
||||||
|
use Dcat\Admin\Traits\JsonResponse;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class WitdrawException extends Exception
|
||||||
|
{
|
||||||
|
use JsonResponse;
|
||||||
|
|
||||||
|
protected $code = 400;
|
||||||
|
|
||||||
|
public function report()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render($request)
|
||||||
|
{
|
||||||
|
return $this->error($this->message, $this->code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Filters;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use EloquentFilter\ModelFilter;
|
||||||
|
|
||||||
|
class WithdrawFilter extends ModelFilter
|
||||||
|
{
|
||||||
|
public function user($v)
|
||||||
|
{
|
||||||
|
$this->where('user_id', $v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function status($v)
|
||||||
|
{
|
||||||
|
$this->whereIn('status', is_array($v) ? $v : explode(',', $v));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createdStart($v)
|
||||||
|
{
|
||||||
|
$time = Carbon::createFromTimestamp(strtotime($v));
|
||||||
|
$this->where('created_at', '>=', $time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createdEnd($v)
|
||||||
|
{
|
||||||
|
$time = Carbon::createFromTimestamp(strtotime($v));
|
||||||
|
$this->where('created_at', '<=', $time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Form\Withdraw;
|
||||||
|
|
||||||
|
use Dcat\Admin\Contracts\LazyRenderable;
|
||||||
|
use Dcat\Admin\Traits\LazyWidget;
|
||||||
|
use Dcat\Admin\Widgets\Form;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Peidikeji\User\Enums\WithdrawStatus;
|
||||||
|
use Peidikeji\User\Models\UserWithdraw;
|
||||||
|
use Peidikeji\User\WithdrawService;
|
||||||
|
|
||||||
|
class CheckForm extends Form implements LazyRenderable
|
||||||
|
{
|
||||||
|
use LazyWidget;
|
||||||
|
|
||||||
|
protected $buttons = ['reset' => false, 'submit' => true];
|
||||||
|
|
||||||
|
public function handle(array $input)
|
||||||
|
{
|
||||||
|
$ids = $input['id'] ? explode(',', $input['id']) : null;
|
||||||
|
if (!$ids) {
|
||||||
|
$ids = [$this->payload['id']];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
foreach(UserWithdraw::where('status', WithdrawStatus::None)->get() as $info) {
|
||||||
|
if ($input['status'] == WithdrawStatus::Success->value) {
|
||||||
|
WithdrawService::make()->success($info);
|
||||||
|
} else {
|
||||||
|
WithdrawService::make()->fail($info, ['reason' => $input['reason']]);
|
||||||
|
}
|
||||||
|
$info->update(['remarks' => $input['remarks']]);
|
||||||
|
}
|
||||||
|
DB::commit();
|
||||||
|
return $this->response()->success('操作成功')->refresh();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
return $this->response()->error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function form()
|
||||||
|
{
|
||||||
|
$options = WithdrawStatus::options();
|
||||||
|
$this->radio('status')->options(Arr::only($options, [WithdrawStatus::Success->value, WithdrawStatus::Fail->value]))->required();
|
||||||
|
$this->text('reason');
|
||||||
|
$this->text('remarks');
|
||||||
|
$this->hidden('id')->attribute('id', 'batch-check-id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function default()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'remarks' => $this->payload['remarks'],
|
||||||
|
'status' => WithdrawStatus::Success->value
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Http\Admin;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Dcat\Admin\Admin;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use Dcat\Admin\Grid\Displayers\Actions;
|
||||||
|
use Dcat\Admin\Grid\Filter;
|
||||||
|
use Dcat\Admin\Grid\Tools\BatchActions;
|
||||||
|
use Dcat\Admin\Show;
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use Peidikeji\User\Action\Withdraw\BatchCheck;
|
||||||
|
use Peidikeji\User\Action\Withdraw\RowCheck;
|
||||||
|
use Peidikeji\User\Enums\WithdrawStatus;
|
||||||
|
use Peidikeji\User\Models\UserWithdraw;
|
||||||
|
|
||||||
|
class WithdrawController extends AdminController
|
||||||
|
{
|
||||||
|
protected $translation = 'dcat-admin-user::withdraw';
|
||||||
|
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
return Grid::make(UserWithdraw::with(['user']), function (Grid $grid) {
|
||||||
|
$grid->model()->sort();
|
||||||
|
|
||||||
|
$grid->rowSelector()->disable(function ($row) {
|
||||||
|
return $row->status !== WithdrawStatus::None;
|
||||||
|
});
|
||||||
|
$grid->column('user.phone');
|
||||||
|
$grid->column('amount');
|
||||||
|
$grid->column('status')->display(fn() => $this->status->label());
|
||||||
|
$grid->column('created_at');
|
||||||
|
|
||||||
|
$grid->filter(function (Filter $filter) {
|
||||||
|
$filter->panel();
|
||||||
|
$filter->equal('user_id')->select()->ajax('api/users?_paginate=1')->width(3);
|
||||||
|
$filter->equal('status')->select(WithdrawStatus::options())->width(3);
|
||||||
|
$filter->whereBetween('created_at', function ($q) {
|
||||||
|
$start = data_get($this->input, 'start');
|
||||||
|
$start = $start ? Carbon::createFromFormat('Y-m-d', $start) : null;
|
||||||
|
$end = data_get($this->input, 'end');
|
||||||
|
$end = $end ? Carbon::createFromFormat('Y-m-d', $end) : null;
|
||||||
|
if ($start) {
|
||||||
|
if ($end) {
|
||||||
|
$q->whereBetween('created_at', [$start, $end]);
|
||||||
|
}
|
||||||
|
$q->where('created_at', '>=', $start);
|
||||||
|
} else if ($end) {
|
||||||
|
$q->where('created_at', '<=', $end);
|
||||||
|
}
|
||||||
|
})->date()->width(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
$grid->showRowSelector();
|
||||||
|
$grid->disableCreateButton();
|
||||||
|
$grid->disableDeleteButton();
|
||||||
|
$grid->disableEditButton();
|
||||||
|
$grid->disableBatchDelete();
|
||||||
|
|
||||||
|
$user = Admin::user();
|
||||||
|
$grid->actions(function (Actions $actions) use ($user) {
|
||||||
|
$row = $actions->row;
|
||||||
|
if ($user->can('dcat.admin.withdraw.check') && $row->status === WithdrawStatus::None) {
|
||||||
|
$actions->append(new RowCheck());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$grid->batchActions(function (BatchActions $actions) use ($user) {
|
||||||
|
if ($user->can('dcat.admin.withdraw.check')) {
|
||||||
|
$actions->add(new BatchCheck());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function detail($id)
|
||||||
|
{
|
||||||
|
return Show::make($id, UserWithdraw::with(['user']), function (Show $show) {
|
||||||
|
|
||||||
|
$show->field('user_id')->as(fn() => $this->user?->phone);
|
||||||
|
$show->field('amount');
|
||||||
|
$show->field('balance');
|
||||||
|
$show->field('status')->as(fn() => $this->status->label())->unescape();
|
||||||
|
$show->field('created_at');
|
||||||
|
$show->field('finish_at');
|
||||||
|
$show->field('reason');
|
||||||
|
$show->field('remarks');
|
||||||
|
|
||||||
|
$show->disableEditButton();
|
||||||
|
$show->disableDeleteButton();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Http\Api;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Peidikeji\User\Http\Resources\WithdrawResource;
|
||||||
|
use Peidikeji\User\WithdrawService;
|
||||||
|
|
||||||
|
class WithdrawController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$user = auth('api')->user();
|
||||||
|
|
||||||
|
$query = $user->withdraws()->filter($request->all());
|
||||||
|
|
||||||
|
$list = $query->paginate($request->input('per_page'));
|
||||||
|
|
||||||
|
return $this->json(WithdrawResource::collection($list));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
$user = auth('api')->user();
|
||||||
|
$info = $user->withdraws()->findOrFail($id);
|
||||||
|
|
||||||
|
return $this->json(WithdrawResource::make($info));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$user = auth('api')->user();
|
||||||
|
$request->validate([
|
||||||
|
'amount' => 'numeric|gt:0|lte:' . floatval($user->balance)
|
||||||
|
], [
|
||||||
|
'amount.gt' => '提现数量必须大于 0',
|
||||||
|
'amount.lte' => '余额不足'
|
||||||
|
]);
|
||||||
|
$amount = $request->input('amount');
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
$info = WithdrawService::make()->apply($user, $amount);
|
||||||
|
DB::commit();
|
||||||
|
return $this->success('申请成功', WithdrawResource::make($info));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
return $this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancel($id)
|
||||||
|
{
|
||||||
|
$user = auth('api')->user();
|
||||||
|
$info = $user->withdraws()->findOrFail($id);
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
WithdrawService::make()->cancel($info);
|
||||||
|
DB::commit();
|
||||||
|
return $this->success('取消成功');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
return $this->error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Http\Resources;
|
||||||
|
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class WithdrawResource extends JsonResource
|
||||||
|
{
|
||||||
|
public function toArray($request)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'balance' => floatval($this->balance),
|
||||||
|
'amount' => floatval($this->amount),
|
||||||
|
'payee' => $this->payee,
|
||||||
|
'payer' => $this->payer,
|
||||||
|
'remarks' => $this->remarks,
|
||||||
|
'status' => $this->status,
|
||||||
|
'status_text' => $this->status->text(),
|
||||||
|
'user_id' => $this->id,
|
||||||
|
'finish_at' => $this->finish_at?->timestamp,
|
||||||
|
'reason' => $this->reason,
|
||||||
|
'created_at' => $this->created_at?->timestamp,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -75,6 +75,11 @@ class User extends Authenticatable
|
||||||
return $this->hasMany(UserCoupon::class, 'user_id');
|
return $this->hasMany(UserCoupon::class, 'user_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withdraws()
|
||||||
|
{
|
||||||
|
return $this->hasMany(UserWithdraw::class, 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function scopeSort($q)
|
public function scopeSort($q)
|
||||||
{
|
{
|
||||||
return $q->latest('id');
|
return $q->latest('id');
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ use Peidikeji\Region\Models\Region;
|
||||||
|
|
||||||
class UserAddress extends Model
|
class UserAddress extends Model
|
||||||
{
|
{
|
||||||
|
protected $table = 'user_address';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'user_id', 'contact_name', 'phone', 'address', 'province_id', 'city_id', 'area_id', 'is_default',
|
'user_id', 'contact_name', 'phone', 'address', 'province_id', 'city_id', 'area_id', 'is_default',
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User\Models;
|
||||||
|
|
||||||
|
use Dcat\Admin\Traits\HasDateTimeFormatter;
|
||||||
|
use EloquentFilter\Filterable;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Peidikeji\User\Enums\WithdrawStatus;
|
||||||
|
use Peidikeji\User\Filters\WithdrawFilter;
|
||||||
|
|
||||||
|
class UserWithdraw extends Model
|
||||||
|
{
|
||||||
|
use HasDateTimeFormatter;
|
||||||
|
use Filterable;
|
||||||
|
|
||||||
|
protected $table = 'user_withdraw';
|
||||||
|
|
||||||
|
protected $fillable = ['balance', 'amount', 'payee', 'payer', 'remarks', 'status', 'user_id', 'finish_at', 'reason'];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'status' => WithdrawStatus::class,
|
||||||
|
'payee' => 'json',
|
||||||
|
'payer' => 'json',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $dates = ['finish_at'];
|
||||||
|
|
||||||
|
protected $attributes = [
|
||||||
|
'status' => WithdrawStatus::None
|
||||||
|
];
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'user_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function modelFilter()
|
||||||
|
{
|
||||||
|
return WithdrawFilter::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeSort($q)
|
||||||
|
{
|
||||||
|
return $q->latest('created_at');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ class UserServiceProvider extends ServiceProvider
|
||||||
['id' => 1, 'parent_id' => 0, 'title' => '用户模块', 'icon' => 'feather icon-user', 'uri' => ''],
|
['id' => 1, 'parent_id' => 0, 'title' => '用户模块', 'icon' => 'feather icon-user', 'uri' => ''],
|
||||||
['id' => 2, 'parent_id' => 1, 'title' => '用户管理', 'icon' => '', 'uri' => '/users'],
|
['id' => 2, 'parent_id' => 1, 'title' => '用户管理', 'icon' => '', 'uri' => '/users'],
|
||||||
['id' => 3, 'parent_id' => 1, 'title' => '余额流水', 'icon' => '', 'uri' => '/user-balance'],
|
['id' => 3, 'parent_id' => 1, 'title' => '余额流水', 'icon' => '', 'uri' => '/user-balance'],
|
||||||
|
['id' => 4, 'parent_id' => 1, 'title' => '提现管理', 'icon' => '', 'uri' => '/withdraw'],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Peidikeji\User;
|
||||||
|
|
||||||
|
use Peidikeji\User\Enums\WithdrawStatus;
|
||||||
|
use Peidikeji\User\Exceptions\WitdrawException;
|
||||||
|
use Peidikeji\User\Models\UserWithdraw;
|
||||||
|
|
||||||
|
class WithdrawService
|
||||||
|
{
|
||||||
|
public static function make(): static
|
||||||
|
{
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply($user, $amount, $data = [])
|
||||||
|
{
|
||||||
|
// 扣除余额
|
||||||
|
$user->decrement('balance', $amount);
|
||||||
|
$balance = $user->balance;
|
||||||
|
|
||||||
|
// 添加申请记录
|
||||||
|
$info = $user->withdraws()->create([
|
||||||
|
'balance' => $balance,
|
||||||
|
'amount' => $amount,
|
||||||
|
'payee' => data_get($data, 'payee')
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 添加流水记录
|
||||||
|
$user->balanceLogs()->create([
|
||||||
|
'user_name' => $user->phone,
|
||||||
|
'amount' => 0-$amount,
|
||||||
|
'balance' => $balance,
|
||||||
|
'cate' => '提现',
|
||||||
|
'description' => '申请提现, 扣除余额',
|
||||||
|
'source_id' => $info->id,
|
||||||
|
'source_type' => (new UserWithdraw())->getMorphClass()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancel(UserWithdraw $info)
|
||||||
|
{
|
||||||
|
if ($info->status !== WithdrawStatus::None) {
|
||||||
|
throw new WitdrawException('申请已处理, 无法取消');
|
||||||
|
}
|
||||||
|
|
||||||
|
$info->update(['status' => WithdrawStatus::Cancel]);
|
||||||
|
|
||||||
|
$this->refund($info);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function refund(UserWithdraw $info)
|
||||||
|
{
|
||||||
|
$user = $info->user;
|
||||||
|
$amount = $info->amount;
|
||||||
|
// 退回余额
|
||||||
|
$user->increment('balance', $amount);
|
||||||
|
$balance = $user->balance;
|
||||||
|
|
||||||
|
$cate = match($info->status) {
|
||||||
|
WithdrawStatus::Cancel => '提现',
|
||||||
|
WithdrawStatus::Fail => '提现',
|
||||||
|
default => '提现'
|
||||||
|
};
|
||||||
|
$description = match($info->status) {
|
||||||
|
WithdrawStatus::Cancel => '取消提现申请, 退回余额',
|
||||||
|
WithdrawStatus::Fail => $info->reason,
|
||||||
|
default => '退回余额'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加流水记录
|
||||||
|
$user->balanceLogs()->create([
|
||||||
|
'user_name' => $user->phone,
|
||||||
|
'amount' => $amount,
|
||||||
|
'balance' => $balance,
|
||||||
|
'cate' => $cate,
|
||||||
|
'description' => $description,
|
||||||
|
'source_id' => $info->id,
|
||||||
|
'source_type' => $info->getMorphClass()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function success(UserWithdraw $info, $time = null)
|
||||||
|
{
|
||||||
|
if ($info->status !== WithdrawStatus::None) {
|
||||||
|
throw new WitdrawException('申请已处理');
|
||||||
|
}
|
||||||
|
$info->update([
|
||||||
|
'status' => WithdrawStatus::Success,
|
||||||
|
'finish_at' => $time ?: now(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 申请失败
|
||||||
|
*
|
||||||
|
* @param UserWithdraw $info
|
||||||
|
* @param array $options 其他参数(reason)
|
||||||
|
*/
|
||||||
|
public function fail(UserWithdraw $info, $options = [])
|
||||||
|
{
|
||||||
|
if ($info->status !== WithdrawStatus::None) {
|
||||||
|
throw new WitdrawException('申请已处理');
|
||||||
|
}
|
||||||
|
$info->update([
|
||||||
|
'status' => WithdrawStatus::Fail,
|
||||||
|
'reason' => data_get($options, 'reason'),
|
||||||
|
'finish_at' => data_get($options, 'finish_at', now())
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->refund($info);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue