6
0
Fork 0

添加资金相关接口

release
vine_liutk 2022-01-18 18:01:41 +08:00 committed by 李静
parent 0b845f4efc
commit 2e6d09e8e0
13 changed files with 439 additions and 21 deletions

View File

@ -479,6 +479,7 @@ class OrderProcessCommand extends Command
return $dealer;
}
}
return null;
}
/**

View File

@ -40,29 +40,43 @@ class QuotaV1SendCommand extends Command
UserInfo::with('user')->where('quota_v1', '>', 0)->chunkById(100, function ($userInfos) use ($totalQuotaV1, $job) {
$walletService = new WalletService();
//依次分红
$nowTime = now();
$logs = [];
foreach ($userInfos as $userInfo) {
// if ($userInfo->bonusable) {//只针对享受分红的人发放
$quotaV1amount = round(bcmul(bcdiv($job->amount, $totalQuotaV1, 5), $userInfo->quota_v1, 3));
if ($quotaV1amount >0) {
$log = new QuotaV1SendLog();
$log->user_id = $userInfo->user_id;
$log->job_id = $job->id;
$log->amount = $quotaV1amount;
$log->save();
// $log = new QuotaV1SendLog();
// $log->user_id = $userInfo->user_id;
// $log->job_id = $job->id;
// $log->amount = $quotaV1amount;
// $log->save();
// try {
// DB::beginTransaction();
// $log->update(['status'=>1]);
// $walletService->changeBalance($userInfo->user, $log->amount, WalletLog::ACTION_QUOTA_V1, '老配额分红', $log);
// DB::commit();
// } catch (Throwable $th) {
// DB::rollBack();
// report($th);
// }
// DB::beginTransaction();
// $log->update(['status'=>1]);
// $walletService->changeBalance($userInfo->user, $log->amount, WalletLog::ACTION_QUOTA_V1, '老配额分红', $log);
// DB::commit();
// } catch (Throwable $th) {
// DB::rollBack();
// report($th);
// }
$_log = [
'user_id'=>$userInfo->user_id,
'job_id' =>$job->id,
'amount' =>$quotaV1amount,
'created_at'=>$nowTime,
'updated_at'=>$nowTime,
];
$logs[] = $_log;
}
// }
}
if (count($logs) > 0) {
QuotaV1SendLog::insert($logs);
}
});
}
$job->update([
'status' => 2,
]);

View File

@ -0,0 +1,107 @@
<?php
namespace App\Endpoint\Api\Http\Controllers\Dealer;
use App\Endpoint\Api\Http\Controllers\Controller;
use App\Endpoint\Api\Http\Resources\Dealer\DealerEarningResource;
use App\Endpoint\Api\Http\Resources\Dealer\DealerEarningSimpleResource;
use App\Enums\DealerEarningStatus;
use App\Exceptions\BizException;
use App\Helpers\Paginator;
use App\Models\DealerEarning;
use Illuminate\Http\Request;
class EarningController extends Controller
{
/**
* 列表
*
* @param Request $request
* @return void
*/
public function index(Request $request)
{
$cate = $request->input('cate', '');//获取订单类别
$user = $request->user();
switch ($cate) {
case 'pending'://
$query = $user->dealerEarnings()->onlyPending()->whereNotNull('settle_at');
break;
case 'paid':
$query = $user->dealerPayEarnings()->onlyPaid()->whereNotNull('settle_at');
break;
default://全部
$query = DealerEarning::where(function ($q) use ($user) {
return $q->where('user_id', $user->id)->orWhere('payer_id', $user->id);
});
break;
}
$earnings = $query->latest('id')->simplePaginate(Paginator::resolvePerPage('per_page', 20, 50));
return DealerEarningSimpleResource::collection($earnings);
}
/**
* 详情
*
* @param [type] $id
* @param Request $request
* @return void
*/
public function show($id, Request $request)
{
$earning = $request->user()->dealerEarnings()->findOrFail($id);
return DealerEarningResource::make($earning);
}
/**
* 确认打款
*
* @param [type] $id
* @param Request $request
* @return void
*/
public function payEarning($id, Request $request)
{
$earning = DealerEarning::findOrFail($id);
if (!$earning->isPayer($request->user()->id)) {
throw new BizException('无法操作该订单');
}
$input = $request->validate([
'pay_image' => ['bail', 'required', 'string'],
]);
if ($earning->isPending()) {
$earning->update([
'status' => DealerEarningStatus::Paid,
'pay_info' => $earning->getPayInfo(),
'pay_image'=> $input['pay_image'],
'pay_at' => now(),
]);
}
return response()->noContent();
}
/**
* 确认收款
*
* @param [type] $id
* @param Request $request
*/
public function confirmEarning($id, Request $request)
{
$earning = DealerEarning::findOrFail($id);
if (!$earning->isUser($request->user()->id)) {
throw new BizException('无法操作该订单');
}
if ($earning->isPaid()) {
$earning->update([
'status' => DealerEarningStatus::Completed,
]);
}
return response()->noContent();
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Endpoint\Api\Http\Resources\Dealer;
use Illuminate\Http\Resources\Json\JsonResource;
class DealerEarningResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'type' => $this->type_name,
'created_at' => $this->created_at->toDateTimeString(),
'total_amount' => $this->total_amount,
'fee_rate' => $this->fee_rate,
'fee' => $this->fee,
'total_earnings'=> $this->total_earnings,
'status' => $this->status,
'status_name' => $this->status_name,
'remark' => $this->remark,
'pay_info' => $this->getPayInfo(),
'pay_image' => $this->pay_image,
'pay_at' => $this->pay_at?->toDateTimeString(),
'is_payer' => $this->payer_id ? ($this->payer_id == $request->user()->id) : false,
];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Endpoint\Api\Http\Resources\Dealer;
use Illuminate\Http\Resources\Json\JsonResource;
class DealerEarningSimpleResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
return [
'id' => $this->id,
'type' => $this->type_name,
'created_at' => $this->created_at->toDateTimeString(),
'total_earnings'=> $this->total_earnings,
'status' => $this->status,
'status_name' => $this->status_name,
// 'settle_at'
];
}
}

View File

@ -250,5 +250,11 @@ Route::group([
Route::post('orders/{order}/shippinged', [Dealer\OrderController::class, 'shippingedOrder']);
//取消订单
Route::post('orders/{order}/cancel', [Dealer\OrderController::class, 'cancelOrder']);
//资金列表
Route::get('earnings', [Dealer\EarningController::class, 'index']);
Route::get('earnings/{earning}', [Dealer\EarningController::class, 'show']);
Route::post('earnings/{earning}/pay', [Dealer\EarningController::class, 'payEarning']);
Route::post('earnings/{earning}/confirm', [Dealer\EarningController::class, 'confirmEarning']);
});
});

View File

@ -3,6 +3,19 @@
namespace App\Enums;
enum DealerEarningStatus: int {
case Pending = 0;
case Completed = 5;
case Pending = 0;//待打款
case Paid = 1;//已打款
case Completed = 5;//已完成
/**
* @return string
*/
public function text()
{
return match ($this) {
static::Pending => '待打款',
static::Paid => '待收款',
static::Completed => '已完成',
};
}
}

View File

@ -2,12 +2,18 @@
namespace App\Models;
use App\Casts\JsonArray;
use App\Enums\DealerEarningStatus;
use App\Enums\DealerLvl;
use Dcat\Admin\Traits\HasDateTimeFormatter;
use EloquentFilter\Filterable;
use Illuminate\Database\Eloquent\Model;
class DealerEarning extends Model
{
use HasDateTimeFormatter;
use Filterable;
protected $attributes = [
'status' => DealerEarningStatus::Pending,
];
@ -16,6 +22,9 @@ class DealerEarning extends Model
'lvl' => DealerLvl::class,
'is_manager'=> 'bool',
'status' => DealerEarningStatus::class,
'pay_at' => 'datetime',
'settle_at'=> 'datetime',
'pay_info' => JsonArray::class,
];
protected $fillable = [
@ -30,9 +39,127 @@ class DealerEarning extends Model
'fee_rate',
'payer_id',
'pay_info',
'pay_image',
'pay_at',
'settle_at',
'status',
'remark',
];
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function payer()
{
return $this->belongsTo(User::class, 'payer_id');
}
/**
* 待打款
*
* @param [type] $query
*/
public function scopeOnlyPending($query)
{
return $query->where('status', DealerEarningStatus::Pending);
}
/**
* 待收款
*
* @param [type] $query
*/
public function scopeOnlyPaid($query)
{
return $query->where('status', DealerEarningStatus::Paid);
}
/**
* 待打款状态
*
* @return boolean
*/
public function isPending()
{
return $this->status == DealerEarningStatus::Pending;
}
/**
* 待收款
*
* @return boolean
*/
public function isPaid()
{
return $this->status == DealerEarningStatus::Paid;
}
/**
* 是否自己的
*
* @param [type] $userId
* @return boolean
*/
public function isUser($userId)
{
return $this->user_id == $userId;
}
/**
* 是否是支付人
*
* @param [type] $userId
* @return boolean
*/
public function isPayer($userId)
{
return $this->payer_id ? ($this->payer_id == $userId) : false;
}
public function getStatusNameAttribute()
{
if ($this->settle_at) {
return $this->status::text();
} else {
return '待结算';
}
}
public function getTypeNameAttribute()
{
$name = '未知';
switch ($this->attributes['earningable_type']) {
case (new DealerManagerSubsidy())->getMorphClass():
$name = '管理者津贴';
break;
case (new DealerManageSubsidy())->getMorphClass():
$name = '管理津贴';
break;
case (new DealerChannelSubsidyLog())->getMorphClass():
$name = '渠道补贴';
break;
default:
$name = '进货补贴';
break;
}
return $name;
}
/**
* 获取用户的打款信息
*
* @return void
*/
public function getPayInfo()
{
if ($this->isPending()) {//待打款订单显示发货人收款信息
$payInfo = $this->user->dealer->pay_info;
} else {
$payInfo = $this->pay_info;
}
return $payInfo ?: null;
}
}

View File

@ -15,11 +15,16 @@ class QuotaV1SendLog extends Model
public const STATUS_SUCCESS = 1;
protected $attributes = [
'status'=>0,
'status'=>self::STATUS_FAILED,
];
protected $fillable = [
'user_id',
'job_id',
'amount',
'status',
'created_at',
'updated_at',
];
public static $statusText = [
@ -41,7 +46,7 @@ class QuotaV1SendLog extends Model
{
$query->where('status', static::STATUS_FAILED);
if ($hours = app_settings('quota_v1_receive', 0)) {
if ($hours = app_settings('distribution.quota_v1_receive', 0)) {
return $query->where('created_at', '>', now()->subHours($hours));
} else {
return $query;

View File

@ -199,13 +199,28 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac
}
/**
* todo-确认这个用户是否是经销商
* 确认这个用户是否是经销商
*
* @return boolean
*/
public function isDealer(): bool
{
return true;
return $this->dealer ? true : false;
}
/**
* 用户收益记录
*
* @return void
*/
public function dealerEarnings()
{
return $this->hasMany(DealerEarning::class, 'user_id');
}
public function dealerPayEarnings()
{
return $this->hasMany(DealerEarning::class, 'payer_id');
}
/**

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddPayImageToDealerEarningsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('dealer_earnings', function (Blueprint $table) {
//
$table->string('pay_image')->nullable()->comment('打款凭证');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('dealer_earnings', function (Blueprint $table) {
//
$table->dropColumn('pay_image');
});
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddBondsToDealersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('dealers', function (Blueprint $table) {
//
$table->unsignedDecimal('bonds')->default(0.00)->comment('已缴纳保证金');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('dealers', function (Blueprint $table) {
//
$table->dropColumn('bonds');
});
}
}

View File

@ -2,8 +2,8 @@
return [
'labels' => [
'Dealer' => 'Dealer',
'dealer' => 'Dealer',
'Dealer' => '经销商管理',
'dealers' => '经销商管理',
],
'fields' => [
'user_id' => '用户ID',
@ -26,6 +26,7 @@ return [
'is_sale' => '是否可销售',
'is_manager' => '是否是管理者',
'pay_info' => '用户保留的收款信息',
'bonds' => '保证金',
],
'options' => [
],