diff --git a/app/Endpoint/Api/Filters/BalanceLogFilter.php b/app/Endpoint/Api/Filters/BalanceLogFilter.php new file mode 100644 index 00000000..1cab171d --- /dev/null +++ b/app/Endpoint/Api/Filters/BalanceLogFilter.php @@ -0,0 +1,17 @@ +onlyTransfer(); + break; + } + } +} diff --git a/app/Endpoint/Api/Filters/WalletLogFilter.php b/app/Endpoint/Api/Filters/WalletLogFilter.php new file mode 100644 index 00000000..08637125 --- /dev/null +++ b/app/Endpoint/Api/Filters/WalletLogFilter.php @@ -0,0 +1,17 @@ +onlyWithdrawBalance(); + break; + } + } +} diff --git a/app/Endpoint/Api/Http/Controllers/Account/UserController.php b/app/Endpoint/Api/Http/Controllers/Account/UserController.php index a6074211..6a487930 100644 --- a/app/Endpoint/Api/Http/Controllers/Account/UserController.php +++ b/app/Endpoint/Api/Http/Controllers/Account/UserController.php @@ -3,6 +3,7 @@ namespace App\Endpoint\Api\Http\Controllers\Account; use App\Endpoint\Api\Http\Controllers\Controller; +use App\Endpoint\Api\Http\Resources\UserBalanceResource; use App\Endpoint\Api\Http\Resources\UserInfoResource; use App\Endpoint\Api\Http\Resources\UserWalletResource; use App\Models\UserInfo; @@ -26,6 +27,7 @@ class UserController extends Controller 'user_info' => UserInfoResource::make($user->userInfo), 'is_vip' => $user->isVip(), 'wallet' => UserWalletResource::make($user->wallet), + 'balance' => UserBalanceResource::make($user->balance), ]); } diff --git a/app/Endpoint/Api/Http/Controllers/Account/WalletController.php b/app/Endpoint/Api/Http/Controllers/Account/WalletController.php index a5b91dc8..5da74a4e 100644 --- a/app/Endpoint/Api/Http/Controllers/Account/WalletController.php +++ b/app/Endpoint/Api/Http/Controllers/Account/WalletController.php @@ -3,19 +3,26 @@ namespace App\Endpoint\Api\Http\Controllers\Account; use App\Endpoint\Api\Http\Controllers\Controller; +use App\Endpoint\Api\Http\Resources\BalanceLogResource; use App\Endpoint\Api\Http\Resources\DistributionPreIncomeResource; use App\Endpoint\Api\Http\Resources\WalletLogResource; +use App\Exceptions\BizException; use App\Exceptions\InvalidPaySerialNumberException; use App\Exceptions\PayPasswordIncorrectException; +use App\Exceptions\WalletNotEnoughException; use App\Helpers\Paginator as PaginatorHelper; use App\Models\BalanceLog; use App\Models\Order; use App\Models\PayLog; +use App\Models\User; use App\Models\WalletLog; +use App\Models\WalletToBankLog; +use App\Rules\PhoneNumber; use App\Services\BalanceService; use App\Services\PayService; use App\Services\WalletService; use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; use Throwable; @@ -32,7 +39,7 @@ class WalletController extends Controller { return response()->json([ 'distribution_pre' => $request->user()->distributionPreIncomes()->pending()->sum('total_revenue'), - 'wallet_balance'=> $request->user()->wallet?->balance ?? 0, + 'wallet_balance'=> $request->user()->wallet?->balance_format ?? 0, ]); } @@ -64,7 +71,7 @@ class WalletController extends Controller { $perPage = PaginatorHelper::resolvePerPage('per_page', 20, 50); - $walletLogs = $request->user()->walletLogs() + $walletLogs = $request->user()->walletLogs()->filter($request->all()) ->latest('id') ->simplePaginate($perPage); @@ -78,6 +85,13 @@ class WalletController extends Controller */ public function balanceLogs(Request $request) { + $perPage = PaginatorHelper::resolvePerPage('per_page', 20, 50); + + $walletLogs = $request->user()->balanceLogs()->filter($request->all()) + ->latest('id') + ->simplePaginate($perPage); + + return BalanceLogResource::collection($walletLogs); } /** @@ -141,4 +155,127 @@ class WalletController extends Controller return response()->noContent(); } + + /** + * 提现到银行 + * + * @return void + */ + public function walletToBank(Request $request, WalletService $walletService) + { + $input = $request->validate([ + 'amount' => ['bail', 'required', 'int', 'min:1'], + 'wallet_password' => ['bail', 'required', 'filled', 'string', 'size:6'], + ]); + + $user = $request->user(); + //校验安全密码 + if (! $user->wallet?->verifyPassword($input['wallet_password'])) { + throw new PayPasswordIncorrectException(); + } + + if (is_null($user->bank)) { + throw new BizException('请先绑定设置银行卡'); + } + try { + DB::beginTransaction(); + + //生成提现记录 + $log = WalletToBankLog::create([ + 'user_id' =>$user->id, + 'bank_name' => $user->bank->bank_name, + 'bank_number' => $user->bank->bank_number, + 'bank_description' => $user->bank->bank_description, + 'username' => $user->bank->real_name, + 'amount'=> Arr::get($input, 'amount', 0), + ]); + + //减去用户可提金额 + $walletService->changeBalance($user, -Arr::get($input, 'amount', 0), WalletLog::ACTION_WITHDRAW_BANK, '提现-银行卡', $log); + DB::commit(); + } catch (WalletNotEnoughException $th) { + DB::rollBack(); + throw new BizException('可提金额不足'); + } catch (Throwable $th) { + DB::rollBack(); + report($th); + throw new BizException('系统繁忙,请稍后再试'); + } + return response()->noContent(); + } + + /** + * 提现到余额 + * + * @param Request $request + * @return void + */ + public function walletToBalance(Request $request, WalletService $walletService, BalanceService $balanceService) + { + $input = $request->validate([ + 'amount' => ['bail', 'required', 'int', 'min:1'], + 'wallet_password' => ['bail', 'required', 'filled', 'string', 'size:6'], + ]); + + $user = $request->user(); + //校验安全密码 + if (! $user->wallet?->verifyPassword($input['wallet_password'])) { + throw new PayPasswordIncorrectException(); + } + + try { + DB::beginTransaction(); + + //余额添加 + $log = $balanceService->changeBalance($user, Arr::get($input, 'amount', 0), BalanceLog::ACTION_WALLET_IN, '可提-转入'); + //减去用户可提金额 + $walletService->changeBalance($user, -Arr::get($input, 'amount', 0), WalletLog::ACTION_WITHDRAW_BALACNE, '提现-余额', $log); + + DB::commit(); + } catch (WalletNotEnoughException $th) { + DB::rollBack(); + throw new BizException('可提金额不足'); + } catch (Throwable $th) { + DB::rollBack(); + report($th); + throw new BizException('系统繁忙,请稍后再试'); + } + return response()->noContent(); + } + + /** + * 余额转账 + * + * @return void + */ + public function balanceTransfer(Request $request, BalanceService $balanceService) + { + $input = $request->validate([ + 'phone' => ['bail', 'required', new PhoneNumber()], + 'amount' => ['bail', 'required', 'int', 'min:1'], + 'wallet_password' => ['bail', 'required', 'filled', 'string', 'size:6'], + ]); + + //判断转账对象是否存在 + $toUser = User::where('phone', '=', $input['phone'])->first(); + if (is_null($toUser)) { + throw new BizException('转账对象不存在'); + } + $user = $request->user(); + try { + DB::beginTransaction(); + //转出对象 + $log = $balanceService->changeBalance($user, -Arr::get($input, 'amount', 0), BalanceLog::ACTION_TRANSFER_OUT, '转出-'.$toUser->phone); + + //转入对象 + $balanceService->changeBalance($toUser, Arr::get($input, 'amount', 0), BalanceLog::ACTION_TRANSFER_IN, $toUser->phone.'-转入', $log); + + DB::commit(); + } catch (Throwable $th) { + DB::rollBack(); + report($th); + } + + return response()->noContent(); + } } diff --git a/app/Endpoint/Api/Http/Controllers/Account/WalletPasswordController.php b/app/Endpoint/Api/Http/Controllers/Account/WalletPasswordController.php index 31443222..7ab67753 100644 --- a/app/Endpoint/Api/Http/Controllers/Account/WalletPasswordController.php +++ b/app/Endpoint/Api/Http/Controllers/Account/WalletPasswordController.php @@ -17,8 +17,8 @@ class WalletPasswordController extends Controller public function reset(Request $request) { $input = $request->validate([ - 'old_password' => ['bail', 'filled', 'string', 'min:6', 'max:6'], - 'new_password' => ['bail', 'required', 'string', 'min:6', 'max:6'], + 'old_password' => ['bail', 'filled', 'string', 'size:6'], + 'new_password' => ['bail', 'required', 'string', 'size:6'], ]); $wallet = $request->user()->wallet; diff --git a/app/Endpoint/Api/Http/Resources/BalanceLogResource.php b/app/Endpoint/Api/Http/Resources/BalanceLogResource.php new file mode 100644 index 00000000..984b7f20 --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/BalanceLogResource.php @@ -0,0 +1,23 @@ + $this->remarks, + 'created_at' => $this->created_at->format('y-m-d H:i'), + 'change_balance' => $this->change_balance_format, + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/UserBalanceResource.php b/app/Endpoint/Api/Http/Resources/UserBalanceResource.php new file mode 100644 index 00000000..8d40ff93 --- /dev/null +++ b/app/Endpoint/Api/Http/Resources/UserBalanceResource.php @@ -0,0 +1,32 @@ +resource)) {//没有钱包数据时默认数据 + return [ + 'balance'=> '0.00', + // 'total_expenses'=> $this->total_expenses, + // 'total_revenue' => $this->total_revenue, + // 'withdrawable' => $this->withdrawable, + ]; + } + return [ + 'balance'=>$this->balance_format, + // 'total_expenses'=> $this->total_expenses, + // 'total_revenue' => $this->total_revenue, + // 'withdrawable' => $this->withdrawable, + ]; + } +} diff --git a/app/Endpoint/Api/Http/Resources/UserWalletResource.php b/app/Endpoint/Api/Http/Resources/UserWalletResource.php index be667d9c..97964c0e 100644 --- a/app/Endpoint/Api/Http/Resources/UserWalletResource.php +++ b/app/Endpoint/Api/Http/Resources/UserWalletResource.php @@ -16,7 +16,7 @@ class UserWalletResource extends JsonResource { if (is_null($this->resource)) {//没有钱包数据时默认数据 return [ - // 'balance'=>$this->balance, + 'balance'=> '0.00', // 'total_expenses'=> $this->total_expenses, // 'total_revenue' => $this->total_revenue, // 'withdrawable' => $this->withdrawable, @@ -24,7 +24,7 @@ class UserWalletResource extends JsonResource ]; } return [ - // 'balance'=>$this->balance, + 'balance'=>$this->balance_format, // 'total_expenses'=> $this->total_expenses, // 'total_revenue' => $this->total_revenue, // 'withdrawable' => $this->withdrawable, diff --git a/app/Endpoint/Api/Http/Resources/WalletLogResource.php b/app/Endpoint/Api/Http/Resources/WalletLogResource.php index 222afbb4..104170d0 100644 --- a/app/Endpoint/Api/Http/Resources/WalletLogResource.php +++ b/app/Endpoint/Api/Http/Resources/WalletLogResource.php @@ -17,7 +17,7 @@ class WalletLogResource extends JsonResource return [ 'remarks' => $this->remarks, 'created_at' => $this->created_at->format('y-m-d H:i'), - 'change_balance' => $this->change_balance, + 'change_balance' => $this->change_balance_format, ]; } } diff --git a/app/Endpoint/Api/routes.php b/app/Endpoint/Api/routes.php index 40e7c0b0..198be79c 100644 --- a/app/Endpoint/Api/routes.php +++ b/app/Endpoint/Api/routes.php @@ -91,7 +91,11 @@ Route::group([ Route::get('wallet', [WalletController::class, 'index']); Route::get('wallet/distribution-logs', [WalletController::class, 'distributionLogs']); Route::get('wallet/wallet-logs', [WalletController::class, 'walletLogs']); + Route::get('wallet/balance-logs', [WalletController::class, 'balanceLogs']); Route::post('wallet/pay', [WalletController::class, 'pay']); + Route::post('wallet/wallet-to-bank', [WalletController::class, 'walletToBank']); + Route::post('wallet/wallet-to-balance', [WalletController::class, 'walletToBalance']); + Route::post('wallet/balance-transfer', [WalletController::class, 'balanceTransfer']); //银行卡 Route::get('user-bank', [UserBankController::class, 'show']); diff --git a/app/Models/Balance.php b/app/Models/Balance.php index 0945ad92..888de031 100644 --- a/app/Models/Balance.php +++ b/app/Models/Balance.php @@ -33,4 +33,9 @@ class Balance extends Model protected $casts = [ 'transferable' => 'bool', ]; + + public function getBalanceFormatAttribute() + { + return trim_trailing_zeros(bcdiv($this->attributes['balance'], 100, 2)); + } } diff --git a/app/Models/BalanceLog.php b/app/Models/BalanceLog.php index 1bb91af1..a3f527d3 100644 --- a/app/Models/BalanceLog.php +++ b/app/Models/BalanceLog.php @@ -2,13 +2,19 @@ namespace App\Models; +use EloquentFilter\Filterable; use Illuminate\Database\Eloquent\Model; class BalanceLog extends Model { + use Filterable; + public const ACTION_ORDER_PAID = 1; public const ACTION_ORDER_CANCELLED = 2; public const ACTION_ORDER_AFTER_SALE = 3; + public const ACTION_WALLET_IN = 4; + public const ACTION_TRANSFER_OUT = 5; + public const ACTION_TRANSFER_IN = 6; /** * @var array @@ -23,6 +29,16 @@ class BalanceLog extends Model 'remarks', ]; + /** + * 转账记录 + * + * @return void + */ + public function scopeOnlyTransfer($query) + { + return $query->whereIn('action', [self::ACTION_TRANSFER_OUT, self::ACTION_TRANSFER_IN]); + } + /** * 获取变动金额 * diff --git a/app/Models/Wallet.php b/app/Models/Wallet.php index f022367e..16d00bcd 100644 --- a/app/Models/Wallet.php +++ b/app/Models/Wallet.php @@ -45,6 +45,11 @@ class Wallet extends Model 'withdrawable' => 'bool', ]; + public function getBalanceFormatAttribute() + { + return trim_trailing_zeros(bcdiv($this->attributes['balance'], 100, 2)); + } + /** * 设置此用户的安全密码 * diff --git a/app/Models/WalletLog.php b/app/Models/WalletLog.php index 154502b2..4375659b 100644 --- a/app/Models/WalletLog.php +++ b/app/Models/WalletLog.php @@ -2,13 +2,18 @@ namespace App\Models; +use EloquentFilter\Filterable; use Illuminate\Database\Eloquent\Model; class WalletLog extends Model { + use Filterable; + public const ACTION_ORDER_PAID = 1; public const ACTION_ORDER_CANCELLED = 2; public const ACTION_ORDER_AFTER_SALE = 3; + public const ACTION_WITHDRAW_BANK = 4; + public const ACTION_WITHDRAW_BALACNE = 5; /** * @var array @@ -23,6 +28,16 @@ class WalletLog extends Model 'remarks', ]; + /** + * 提现到余额的明细 + * + * @param [type] $query + */ + public function scopeOnlyWithdrawBalance($query) + { + return $query->where('action', '=', self::ACTION_WITHDRAW_BALACNE); + } + /** * 获取变动金额 * diff --git a/app/Models/WalletToBankLog.php b/app/Models/WalletToBankLog.php new file mode 100644 index 00000000..d92fb753 --- /dev/null +++ b/app/Models/WalletToBankLog.php @@ -0,0 +1,25 @@ + \App\Models\Order::class, 'order_refund_log' => \App\Models\OrderRefundLog::class, 'after_sale' => \App\Models\AfterSale::class, + 'wallet_to_bank_log' => \App\Models\WalletToBankLog::class, + 'balance_log' => \App\Models\BalanceLog::class, ]); JsonResource::withoutWrapping(); diff --git a/app/Services/BalanceService.php b/app/Services/BalanceService.php index 88ce7051..7b29e18c 100644 --- a/app/Services/BalanceService.php +++ b/app/Services/BalanceService.php @@ -60,7 +60,7 @@ class BalanceService ]); } - $user->balanceLogs()->create([ + return $user->balanceLogs()->create([ 'loggable_id' => $loggable?->id, 'loggable_type' => $loggable?->getMorphClass(), 'before_balance' => $beforeBalance, diff --git a/database/migrations/2021_12_28_132115_create_wallet_to_bank_logs_table.php b/database/migrations/2021_12_28_132115_create_wallet_to_bank_logs_table.php new file mode 100644 index 00000000..68f2737b --- /dev/null +++ b/database/migrations/2021_12_28_132115_create_wallet_to_bank_logs_table.php @@ -0,0 +1,39 @@ +id(); + $table->unsignedBigInteger('user_id')->comment('用户ID'); + $table->string('bank_name')->comment('收款银行'); + $table->string('bank_number')->comment('收款银行卡号'); + $table->string('bank_description')->comment('收款银行开户行'); + $table->string('username')->comment('持卡人'); + $table->unsignedBigInteger('amount')->comment('提现金额:分'); + $table->unsignedTinyInteger('status')->default(0)->comment('状态:0未处理,1成功,2失败'); + $table->string('remarks')->nullable()->comment('备注'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('wallet_to_bank_logs'); + } +}