add origin
parent
bc58992d3a
commit
68161a1e55
|
|
@ -32,6 +32,7 @@ return new class extends Migration
|
|||
|
||||
$table->unsignedInteger('pay_status')->default(0)->comment('支付状态');
|
||||
$table->unsignedInteger('refund_status')->default(0)->comment('退款状态');
|
||||
$table->decimal('refund_money', 12, 2)->default(0)->comment('已退款金额');
|
||||
$table->unsignedInteger('ship_status')->default(0)->comment('配送状态');
|
||||
|
||||
$table->string('ship_way')->nullable()->comment('配送方式');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<?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('order_refunds', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('order_id')->comment('订单ID, orders.id');
|
||||
$table->string('sn')->comment('退款流水号');
|
||||
|
||||
$table->unsignedInteger('refund_status')->default(0)->comment('退款状态');
|
||||
$table->string('refund_way')->nullable()->comment('退款方式');
|
||||
$table->decimal('refund_money', 12, 2)->nullable()->comment('退款金额');
|
||||
$table->dateTime('finish_at')->nullable()->comment('完成时间');
|
||||
$table->string('refund_reason')->nullable()->comment('退款原因');
|
||||
$table->json('refund_data')->nullable()->comment('退款备注');
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->comment('订单退款');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('order_refunds');
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
];
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'labels' => [
|
||||
'Refund' => '退款申请',
|
||||
'order-refunds' => '退款申请',
|
||||
],
|
||||
'fields' => [
|
||||
'order_id' => '订单',
|
||||
'order' => [
|
||||
'sn' => '订单号'
|
||||
],
|
||||
'sn' => '退款单号',
|
||||
'refund_status' => '退款状态',
|
||||
'refund_way' => '退款方式',
|
||||
'refund_money' => '退款金额',
|
||||
'finish_at' => '完成时间',
|
||||
'refund_reason' => '原因',
|
||||
'refund_data' => '其他',
|
||||
'created_at' => '申请时间',
|
||||
]
|
||||
];
|
||||
|
|
@ -9,4 +9,5 @@ Route::group([
|
|||
'middleware' => config('admin.route.middleware'),
|
||||
], function () {
|
||||
Route::resource('orders', OrderController::class)->only(['index', 'show', 'destroy'])->names('dcat.admin.order');
|
||||
Route::resource('order-refunds', RefundController::class)->only(['index', 'show', 'edit', 'update'])->names('dcat.admin.order_refunds');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Order\Action;
|
||||
|
||||
use Dcat\Admin\Show\AbstractTool;
|
||||
use Dcat\Admin\Widgets\Modal;
|
||||
use Peidikeji\Order\Form\RefundForm;
|
||||
|
||||
class ShowRefund extends AbstractTool
|
||||
{
|
||||
protected $style = 'btn btn-sm btn-danger';
|
||||
|
||||
protected $title = '申请退款';
|
||||
|
||||
public function html()
|
||||
{
|
||||
$model = $this->parent->model();
|
||||
|
||||
return Modal::make()
|
||||
->lg()
|
||||
->title($this->title)
|
||||
->body(RefundForm::make()->payload(['id' => $model->id, 'pay_money' => $model->pay_money, 'refund_money' => $model->refund_money]))
|
||||
->button('<button type="button" class="'.$this->style.'">'.$this->title.'</button>');
|
||||
}
|
||||
|
||||
protected function authorize($user): bool
|
||||
{
|
||||
return $user->can('dcat.admin.orders.refund');
|
||||
}
|
||||
|
||||
public function allowed()
|
||||
{
|
||||
$model = $this->parent->model();
|
||||
|
||||
return $model->canRefund();
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,10 @@ enum OrderStatus: int
|
|||
case PayFail = 7;
|
||||
case Cancel = 8;
|
||||
|
||||
case Refunding = 9;
|
||||
case Refund = 10;
|
||||
case RefundFull = 11;
|
||||
|
||||
public static function options()
|
||||
{
|
||||
return [
|
||||
|
|
@ -31,6 +35,10 @@ enum OrderStatus: int
|
|||
|
||||
self::PayFail->value => '支付失败',
|
||||
self::Cancel->value => '已取消',
|
||||
|
||||
self::Refunding->value => '退款中',
|
||||
self::Refund->value => '已退款(部分)',
|
||||
self::RefundFull->value => '已退款(全额)',
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ enum RefundStatus: int
|
|||
{
|
||||
return [
|
||||
self::None->value => '未申请',
|
||||
self::Apply->value => '已申请',
|
||||
self::Apply->value => '待审核',
|
||||
self::Processing->value => '处理中',
|
||||
self::Success->value => '退款成功',
|
||||
self::Fail->value => '退款失败',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Order\Enums;
|
||||
|
||||
enum RefundWay: int
|
||||
{
|
||||
case Offline = 0;
|
||||
case Origin = 1;
|
||||
|
||||
public static function options()
|
||||
{
|
||||
return [
|
||||
self::Offline->value => '线下',
|
||||
self::Origin->value => '原路退回',
|
||||
];
|
||||
}
|
||||
|
||||
public function text()
|
||||
{
|
||||
return data_get(self::options(), $this->value);
|
||||
}
|
||||
|
||||
public function color()
|
||||
{
|
||||
return match ($this) {
|
||||
static::Offline => 'warning',
|
||||
static::Origin => 'success',
|
||||
};
|
||||
}
|
||||
|
||||
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,66 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Order\Form;
|
||||
|
||||
use Dcat\Admin\Admin;
|
||||
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\Order\Enums\RefundWay;
|
||||
use Peidikeji\Order\Exceptions\OrderException;
|
||||
use Peidikeji\Order\Models\Order;
|
||||
use Peidikeji\Order\RefundService;
|
||||
|
||||
class RefundForm extends Form implements LazyRenderable
|
||||
{
|
||||
use LazyWidget;
|
||||
|
||||
protected $buttons = ['reset' => false, 'submit' => true];
|
||||
|
||||
public function handle(array $input)
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$order = Order::findOrFail($this->payload['id']);
|
||||
$money = $input['refund_money'];
|
||||
$attributes = Arr::only($input, ['refund_reason', 'refund_way', 'refund_money', 'refund_sn']);
|
||||
$info = RefundService::make()->apply($order, $attributes);
|
||||
|
||||
$admin = Admin::user();
|
||||
$attributes['refund_id'] = $info->id;
|
||||
$order->options()->create([
|
||||
'user_type' => $admin->getMorphClass(),
|
||||
'user_id' => $admin->id,
|
||||
'description' => '管理员: '.$admin->name.' 申请退款: ' . $money,
|
||||
'attribute' => $attributes,
|
||||
]);
|
||||
DB::commit();
|
||||
return $this->response()->success('申请成功, 等待审核')->refresh();
|
||||
} catch (OrderException $e) {
|
||||
DB::rollBack();
|
||||
return $this->response()->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
// 支付金额
|
||||
$payMoney = $this->payload['pay_money'];
|
||||
// 已退款金额
|
||||
$refundedMoney = $this->payload['refund_money'];
|
||||
$this->text('refund_reason', __('dcat-admin-order::refund.fields.refund_reason'))->required();
|
||||
$this->select('refund_way', __('dcat-admin-order::refund.fields.refund_way'))->options(RefundWay::options())->required();
|
||||
$this->currency('refund_money', __('dcat-admin-order::refund.fields.refund_money'))->symbol('¥')->help('最大值: ' . floatval($payMoney - $refundedMoney));
|
||||
$this->text('refund_sn', __('dcat-admin-order::refund.fields.sn'));
|
||||
}
|
||||
|
||||
public function default()
|
||||
{
|
||||
return [
|
||||
'refund_way' => RefundWay::Origin,
|
||||
'refund_money' => floatval($this->payload['pay_money'] - $this->payload['refund_money'])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -15,14 +15,6 @@ use Dcat\Admin\Show;
|
|||
use Dcat\Admin\Show\Tools;
|
||||
use Dcat\Admin\Widgets\Box;
|
||||
use Illuminate\Support\Arr;
|
||||
use Peidikeji\Order\Action\ShowCancel;
|
||||
use Peidikeji\Order\Action\ShowDelete;
|
||||
use Peidikeji\Order\Action\ShowPay;
|
||||
use Peidikeji\Order\Action\ShowPayQuery;
|
||||
use Peidikeji\Order\Action\ShowPrice;
|
||||
use Peidikeji\Order\Action\ShowReceive;
|
||||
use Peidikeji\Order\Action\ShowRemarks;
|
||||
use Peidikeji\Order\Action\ShowShip;
|
||||
use Peidikeji\Order\Enums\OrderStatus;
|
||||
use Peidikeji\Order\Enums\PayStatus;
|
||||
use Peidikeji\Order\Models\Order;
|
||||
|
|
@ -44,9 +36,9 @@ class OrderController extends AdminController
|
|||
|
||||
$grid->filter(function (Filter $filter) {
|
||||
$filter->panel();
|
||||
$filter->like('sn')->width(4);
|
||||
$filter->equal('user_id')->select()->ajax('api/user?_paginate=1')->model(User::class, 'id', 'phone')->width(4);
|
||||
$filter->where('order_status', fn($q) => $q->filter(['status' => $this->input]))->select(OrderStatus::options())->width(4);
|
||||
$filter->like('sn')->width(3);
|
||||
$filter->equal('user_id')->select()->ajax('api/user?_paginate=1')->model(User::class, 'id', 'phone')->width(3);
|
||||
$filter->where('order_status', fn($q) => $q->filter(['status' => $this->input]))->select(OrderStatus::options())->width(3);
|
||||
$filter->whereBetween('created_at', function ($q) {
|
||||
$start = data_get($this->input, 'start');
|
||||
$start = $start ? Carbon::createFromFormat('Y-m-d', $start) : null;
|
||||
|
|
@ -60,7 +52,7 @@ class OrderController extends AdminController
|
|||
} else if ($end) {
|
||||
$q->where('created_at', '<=', $end);
|
||||
}
|
||||
})->date()->width(4);
|
||||
})->date()->width(6);
|
||||
});
|
||||
|
||||
$grid->column('sn')->copyable();
|
||||
|
|
@ -128,14 +120,15 @@ class OrderController extends AdminController
|
|||
$show->width(6)->field('remarks');
|
||||
});
|
||||
$show->tools(function (Tools $tools) {
|
||||
$tools->append(new ShowCancel());
|
||||
$tools->append(new ShowPay());
|
||||
$tools->append(new ShowPayQuery());
|
||||
$tools->append(new ShowShip());
|
||||
$tools->append(new ShowReceive());
|
||||
$tools->append(new ShowDelete());
|
||||
$tools->append(new ShowPrice());
|
||||
$tools->append(new ShowRemarks());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowCancel());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowPay());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowPayQuery());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowShip());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowReceive());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowDelete());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowPrice());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowRefund());
|
||||
$tools->append(new \Peidikeji\Order\Action\ShowRemarks());
|
||||
});
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Order\Http\Admin;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Dcat\Admin\Admin;
|
||||
use Dcat\Admin\Grid;
|
||||
use Dcat\Admin\Grid\Filter;
|
||||
use Dcat\Admin\Http\Controllers\AdminController;
|
||||
use Dcat\Admin\Show;
|
||||
use Dcat\Admin\Form;
|
||||
use Dcat\Admin\Grid\Displayers\Actions;
|
||||
use Peidikeji\Order\Enums\RefundStatus;
|
||||
use Peidikeji\Order\Enums\RefundWay;
|
||||
use Peidikeji\Order\Models\OrderRefund;
|
||||
|
||||
class RefundController extends AdminController
|
||||
{
|
||||
protected $translation = 'dcat-admin-order::refund';
|
||||
|
||||
protected function grid()
|
||||
{
|
||||
return Grid::make(OrderRefund::with(['order']), function (Grid $grid) {
|
||||
$grid->model()->sort();
|
||||
|
||||
$grid->column('order.sn');
|
||||
$grid->column('sn');
|
||||
$grid->column('refund_reason');
|
||||
$grid->column('refund_money');
|
||||
$grid->column('refund_status')->display(fn() => $this->refund_status->label());
|
||||
$grid->column('created_at');
|
||||
|
||||
$grid->filter(function (Filter $filter) {
|
||||
$filter->panel();
|
||||
$filter->like('order.sn')->width(3);
|
||||
$filter->like('sn')->width(3);
|
||||
$filter->equal('refund_status')->select(RefundStatus::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->disableRowSelector();
|
||||
$grid->disableCreateButton();
|
||||
$grid->disableDeleteButton();
|
||||
|
||||
$user = Admin::user();
|
||||
$grid->actions(function (Actions $actions) use ($user) {
|
||||
$row = $actions->row;
|
||||
$actions->edit($row->refund_status === RefundStatus::Apply && $user->can('dcat.admin.order_refunds.edit'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected function detail($id)
|
||||
{
|
||||
return Show::make($id, OrderRefund::with(['order']), function (Show $show) {
|
||||
$show->field('order.sn');
|
||||
$show->field('sn');
|
||||
$show->field('refund_reason');
|
||||
$show->field('refund_money');
|
||||
$show->field('refund_way')->as(fn() => $this->refund_way->label())->unescape();
|
||||
$show->field('refund_status')->as(fn() => $this->refund_status->label())->unescape();
|
||||
$show->field('finish_at');
|
||||
$show->field('created_at');
|
||||
$show->field('refund_data')->json();
|
||||
|
||||
$show->disableDeleteButton();
|
||||
$show->disableEditButton();
|
||||
});
|
||||
}
|
||||
|
||||
protected function form()
|
||||
{
|
||||
return Form::make(OrderRefund::with(['order']), function (Form $form) {
|
||||
$form->display('order.sn');
|
||||
$form->text('sn')->required();
|
||||
$form->select('refund_way')->options(RefundWay::options())->required();
|
||||
$form->currency('refund_money')->symbol('¥')->required();
|
||||
$form->text('refund_reason')->required();
|
||||
|
||||
$form->disableEditingCheck();
|
||||
$form->disableCreatingCheck();
|
||||
$form->disableViewCheck();
|
||||
|
||||
$form->disableViewButton();
|
||||
$form->disableDeleteButton();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -42,8 +42,6 @@ class OrderController extends Controller
|
|||
$request->validate([
|
||||
'goods' => [Rule::requiredIf(!$request->filled('cart_id')), 'array'],
|
||||
'cart_id' => ['array'],
|
||||
'address' => [Rule::requiredIf(!$request->filled('address_id')), 'array'],
|
||||
'address_id' => ['numeric']
|
||||
]);
|
||||
$user = auth('api')->user();
|
||||
|
||||
|
|
@ -107,8 +105,7 @@ class OrderController extends Controller
|
|||
|
||||
$request->validate([
|
||||
'goods' => [Rule::requiredIf(!$request->filled('cart_id')), 'array'],
|
||||
'cart_id' => ['array'],
|
||||
'address' => [Rule::requiredIf(!$request->filled('address_id')), 'array'],
|
||||
'cart_id' => ['array']
|
||||
]);
|
||||
$store = OrderStore::init()->user($user)->remarks($request->input('remarks'));
|
||||
|
||||
|
|
@ -118,8 +115,8 @@ class OrderController extends Controller
|
|||
}
|
||||
$store->goods($goods);
|
||||
|
||||
$address = $this->getAddress();
|
||||
$store->ship($request->input('ship_way', ShipWay::None->value), $address);
|
||||
$shipWay = $request->input('ship_way', ShipWay::None->value);
|
||||
$store->ship($shipWay, $shipWay === ShipWay::Express ? $this->getAddress() : null);
|
||||
|
||||
$store->score($request->input('score', 0));
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class Order extends Model
|
|||
'ship_address', 'ship_at', 'ship_money', 'ship_status', 'ship_way', 'receive_at',
|
||||
'score_discount_amount', 'score_discount_money', 'score_discount_ratio',
|
||||
'pay_at', 'pay_money', 'pay_no', 'pay_status', 'pay_way', 'discount_price',
|
||||
'auto_close_at', 'extra', 'is_closed', 'refund_status', 'remarks', 'review_at', 'user_remarks'
|
||||
'refund_status', 'refund_money',
|
||||
'auto_close_at', 'extra', 'is_closed', 'remarks', 'review_at', 'user_remarks'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
|
|
@ -67,6 +68,11 @@ class Order extends Model
|
|||
return $this->hasMany(OrderOption::class, 'order_id');
|
||||
}
|
||||
|
||||
public function refunds()
|
||||
{
|
||||
return $this->hasMany(OrderRefund::class, 'order_id');
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
if ($this->is_closed) {
|
||||
|
|
@ -82,6 +88,12 @@ class Order extends Model
|
|||
return OrderStatus::PayFail;
|
||||
}
|
||||
if ($this->pay_status === PayStatus::Success) {
|
||||
if ($this->refund_status === RefundStatus::Apply || $this->refund_status === RefundStatus::Processing) {
|
||||
return OrderStatus::Refunding;
|
||||
}
|
||||
if ($this->refund_status === RefundStatus::Success) {
|
||||
return $this->refund_money >= $this->pay_money ? OrderStatus::RefundFull : OrderStatus::Refund;
|
||||
}
|
||||
if ($this->ship_status === ShipStatus::Processing) {
|
||||
return OrderStatus::Send;
|
||||
}
|
||||
|
|
@ -203,6 +215,35 @@ class Order extends Model
|
|||
return true;
|
||||
}
|
||||
|
||||
public function canRefund($throw = false)
|
||||
{
|
||||
if ($this->pay_status === PayStatus::None || $this->pay_status === PayStatus::Processing) {
|
||||
if ($throw) {
|
||||
throw new OrderException('订单未支付, 无法申请退款');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ($this->pay_status === PayStatus::Refund) {
|
||||
if ($throw) {
|
||||
throw new OrderException('订单已经退款');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ($this->refund_status === RefundStatus::Apply || $this->refund_status === RefundStatus::Processing) {
|
||||
if ($throw) {
|
||||
throw new OrderException('退款申请处理中');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ($this->refund_money >= $this->pay_money) {
|
||||
if ($throw) {
|
||||
throw new OrderException('订单已经全额退款, 不能再申请退款');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('created_at', 'desc');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Order\Models;
|
||||
|
||||
use Dcat\Admin\Traits\HasDateTimeFormatter;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Peidikeji\Order\Enums\RefundStatus;
|
||||
use Peidikeji\Order\Enums\RefundWay;
|
||||
|
||||
class OrderRefund extends Model
|
||||
{
|
||||
use HasDateTimeFormatter;
|
||||
|
||||
protected $fillable = ['order_id', 'sn', 'refund_status', 'refund_way', 'refund_money', 'finish_at', 'refund_reason', 'refund_data'];
|
||||
|
||||
protected $casts = [
|
||||
'refund_data' => 'json',
|
||||
'refund_status' => RefundStatus::class,
|
||||
'refund_way' => RefundWay::class,
|
||||
];
|
||||
|
||||
protected $dates = ['finish_at'];
|
||||
|
||||
protected $attributes = [
|
||||
'refund_status' => RefundStatus::None,
|
||||
];
|
||||
|
||||
public function order()
|
||||
{
|
||||
return $this->belongsTo(Order::class, 'order_id');
|
||||
}
|
||||
|
||||
public function scopeSort($q)
|
||||
{
|
||||
return $q->orderBy('created_at', 'desc');
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,8 @@ class OrderServiceProvider extends ServiceProvider
|
|||
// ]],
|
||||
$menu->add([
|
||||
['id' => 1, 'parent_id' => 0, 'title' => '订单模块', 'icon' => 'feather icon-book', 'uri' => '', 'permission' => ['orders']],
|
||||
['id' => 2, 'parent_id' => 1, 'title' => '订单管理', 'icon' => '', 'uri' => '/orders', 'permission' => 'orders.index']
|
||||
['id' => 2, 'parent_id' => 1, 'title' => '订单管理', 'icon' => '', 'uri' => '/orders', 'permission' => 'orders.index'],
|
||||
['id' => 3, 'parent_id' => 1, 'title' => '退款申请', 'icon' => '', 'uri' => '/order-refunds', 'permission' => 'order_refunds.index'],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Peidikeji\Order;
|
||||
|
||||
use Peidikeji\Order\Enums\RefundStatus;
|
||||
use Peidikeji\Order\Enums\RefundWay;
|
||||
use Peidikeji\Order\Exceptions\OrderException;
|
||||
use Peidikeji\Order\Models\Order;
|
||||
use Peidikeji\Order\Models\OrderRefund;
|
||||
|
||||
class RefundService
|
||||
{
|
||||
public static function make()
|
||||
{
|
||||
return new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单申请退款
|
||||
*
|
||||
* @param Order $order
|
||||
* @param array $params [refund_reason, refund_way, refund_money, refund_sn]
|
||||
* @return OrderRefund
|
||||
* @throws OrderException
|
||||
*/
|
||||
public function apply(Order $order, $params = [])
|
||||
{
|
||||
$order->canRefund(true);
|
||||
$money = data_get($params, 'refund_money', $order->pay_money - $order->refund_money);
|
||||
if ($money + $order->refund_money > $order->pay_money) {
|
||||
throw new OrderException('退款金额超过支付金额');
|
||||
}
|
||||
if ($order->refunds()->whereIn('refund_status', [RefundStatus::Apply, RefundStatus::Processing])->exists()) {
|
||||
throw new OrderException('退款申请处理中, 请勿重复申请');
|
||||
}
|
||||
$orderService = OrderService::make();
|
||||
$sn = $orderService->generateSn();
|
||||
$way = data_get($params, 'way', RefundWay::Origin->value);
|
||||
|
||||
$info = $order->refunds()->create([
|
||||
'sn' => $sn,
|
||||
'refund_status' => RefundStatus::Apply,
|
||||
'refund_way' => $way,
|
||||
'refund_money' => $money,
|
||||
'refund_reason' => data_get($params, 'refund_reason'),
|
||||
]);
|
||||
|
||||
$order->update(['refund_status' => RefundStatus::Apply]);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理退款申请
|
||||
*
|
||||
* @param Order $order
|
||||
* @param bool $status 同意/不同意
|
||||
* @param string $reason 不同意的原因
|
||||
* @throws OrderException
|
||||
*/
|
||||
public function handle(OrderRefund $info, bool $status, $reason = '')
|
||||
{
|
||||
if ($info->refund_status !== RefundStatus::Apply && $info->refund_status !== RefundStatus::Fail) {
|
||||
throw new OrderException('退款已经处理');
|
||||
}
|
||||
|
||||
$info->refund_status = RefundStatus::Processing;
|
||||
if ($status) {
|
||||
if ($info->refund_way === RefundWay::Origin) {
|
||||
$orderService = OrderService::make();
|
||||
$result = $orderService->refundPay($info->order, $info->refund_money, $info->refund_reason, $info->sn);
|
||||
if ($result) {
|
||||
$this->success($info, $result);
|
||||
} else {
|
||||
$this->fail($info, $result['message']);
|
||||
}
|
||||
}
|
||||
else if ($info->refund_way === RefundWay::Offline) {
|
||||
$this->success($info);
|
||||
}
|
||||
} else {
|
||||
$this->fail($info, $reason);
|
||||
}
|
||||
}
|
||||
|
||||
public function success(OrderRefund $info, array $params = null)
|
||||
{
|
||||
if ($info->refund_status !== RefundStatus::Processing) {
|
||||
throw new OrderException('退款已经处理');
|
||||
}
|
||||
|
||||
$info->update([
|
||||
'refund_status' => RefundStatus::Success,
|
||||
'finish_at' => data_get($params, 'finish_at', now()),
|
||||
'refund_data' => $params
|
||||
]);
|
||||
|
||||
$order = $info->order;
|
||||
if ($order) {
|
||||
$order->increment('refund_money', $info->refund_money, ['refund_status' => RefundStatus::Success]);
|
||||
}
|
||||
}
|
||||
|
||||
public function fail(OrderRefund $info, $reason = '')
|
||||
{
|
||||
if ($info->refund_status !== RefundStatus::Processing) {
|
||||
throw new OrderException('退款已经处理');
|
||||
}
|
||||
|
||||
$info->update([
|
||||
'refund_status' => RefundStatus::Fail,
|
||||
'finish_at' => now(),
|
||||
'refund_data' => ['fail_reason' => $reason],
|
||||
]);
|
||||
|
||||
$order = $info->order;
|
||||
if ($order) {
|
||||
$order->update(['refund_status' => RefundStatus::Fail]);
|
||||
}
|
||||
}
|
||||
|
||||
public function cancel(OrderRefund $info)
|
||||
{
|
||||
$info->update([
|
||||
'refund_status' => RefundStatus::Cancel,
|
||||
'finish_at' => now(),
|
||||
]);
|
||||
$order = $info->order;
|
||||
if ($order) {
|
||||
$order->update(['refund_status' => RefundStatus::Cancel]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue