线下订单优化
parent
1b30f44c6d
commit
827b3f4037
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\Admin\Actions\Grid;
|
||||
|
||||
use App\Admin\Forms\OrderClose as OrderCloseForm;
|
||||
use App\Exceptions\BizException;
|
||||
use App\Models\OfflineOrder;
|
||||
use App\Services\OfflineOrderService;
|
||||
use Dcat\Admin\Grid\RowAction;
|
||||
use Dcat\Admin\Widgets\Modal;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
class OfflineOrderRevoke extends RowAction
|
||||
{
|
||||
public function title()
|
||||
{
|
||||
return '<i class="feather icon-x grid-action-icon"></i> 取消订单';
|
||||
}
|
||||
|
||||
protected function authorize($user): bool
|
||||
{
|
||||
return $user->can('dcat.admin.offline_orders.revoke');
|
||||
}
|
||||
|
||||
public function handle(Request $request)
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$order = OfflineOrder::lockForUpdate()->findOrFail($this->getKey());
|
||||
|
||||
if (! $order->isPending()) {
|
||||
throw new BizException('订单状态已更新');
|
||||
}
|
||||
|
||||
(new OfflineOrderService())->revoke($order);
|
||||
|
||||
DB::commit();
|
||||
} catch (Throwable $th) {
|
||||
DB::rollBack();
|
||||
report($th);
|
||||
return $this->response()->error('操作失败,'.$th->getMessage())->refresh();
|
||||
}
|
||||
|
||||
return $this->response()->success('操作成功')->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|array|void
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
return ['是否取消选中订单?'];
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Admin\Controllers;
|
||||
|
||||
use App\Admin\Actions\Grid\OfflineOrderRevoke;
|
||||
use App\Admin\Repositories\OfflineOrder as OfflineOrderRepository;
|
||||
use App\Admin\Widgets\InfoBox;
|
||||
use App\Enums\OfflineOrderStatus;
|
||||
|
|
@ -59,7 +60,7 @@ class OfflineOrderController extends AdminController
|
|||
$grid->column('discount_reduction_amount')->display(fn($v) => bcdiv($v, 100, 2))->prepend('¥');
|
||||
$grid->column('points_deduction_amount')->display(fn($v) => bcdiv($v, 100, 2))->prepend('¥');
|
||||
$grid->column('payment_amount')->display(fn($v) => bcdiv($v, 100, 2))->prepend('¥');
|
||||
$grid->column('status')->display(fn($v) => $v->label());
|
||||
$grid->column('status')->display(fn($v) => $v->dot());
|
||||
$grid->column('payment_method')->display(fn($v) => $v?->dot());
|
||||
$grid->column('payment_time');
|
||||
$grid->column('created_at');
|
||||
|
|
@ -97,6 +98,10 @@ class OfflineOrderController extends AdminController
|
|||
if ($user->can('dcat.admin.offline_orders.show')) {
|
||||
$actions->disableView(false);
|
||||
}
|
||||
|
||||
if ($user->can('dcat.admin.offline_orders.revoke') && $actions->row->isPending()) {
|
||||
$actions->append(new OfflineOrderRevoke());
|
||||
}
|
||||
});
|
||||
|
||||
$grid->header(function ($collection) use ($grid) {
|
||||
|
|
@ -153,7 +158,7 @@ class OfflineOrderController extends AdminController
|
|||
->prepend('¥');
|
||||
$show->field('status')
|
||||
->escape(false)
|
||||
->as(fn () => $this->status->label());
|
||||
->as(fn () => $this->status->dot());
|
||||
$show->field('payment_method')
|
||||
->escape(false)
|
||||
->as(fn () => $this->payment_method?->dot());
|
||||
|
|
@ -161,6 +166,7 @@ class OfflineOrderController extends AdminController
|
|||
$show->field('payment_sn');
|
||||
$show->field('out_trade_no');
|
||||
$show->field('created_at');
|
||||
$show->field('revoked_at');
|
||||
});
|
||||
$show->panel()->tools(function (Show\Tools $tools) use ($show) {
|
||||
$tools->disableEdit();
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class App extends Form
|
|||
$this->text('search_hot_keys', '搜索热词(英文半角逗号隔开)')->value($appSettings['search_hot_keys'] ?? '');
|
||||
$this->text('invite_uri', '分享邀请地址(链接)')->value($appSettings['invite_uri'] ?? '');
|
||||
$this->divider();
|
||||
$this->number('offline_order_payment_expires_at', '线下订单支付过期时间(秒)')->value($appSettings['offline_order_payment_expires_at'] ?? '');
|
||||
$this->number('order_payment_expires_at', '订单支付过期时间(秒)')->value($appSettings['order_payment_expires_at'] ?? '');
|
||||
$this->text('order_auto_complete_days', '订单自动完成时间(天)')->value($appSettings['order_auto_complete_days'] ?? '')->rules('required|numeric|min:0');
|
||||
$this->text('sale_after_expire_days', '售后过期时间(天)')->value($appSettings['sale_after_expire_days'] ?? '')->rules('required|numeric|min:0');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\OfflineOrderStatus;
|
||||
use App\Models\OfflineOrder;
|
||||
use App\Services\OfflineOrderService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
class OfflineOrderCloseExpiredCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'offline-order:close-expired';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = '关闭过期的线下订单';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(OfflineOrderService $offlineOrderService)
|
||||
{
|
||||
while (true) {
|
||||
// 待付款的线下订单的有效期
|
||||
$expirs = (int) app_settings('app.offline_order_payment_expires_at', 0);
|
||||
|
||||
if ($expirs > 0) {
|
||||
$ids = OfflineOrder::where('status', OfflineOrderStatus::Pending)
|
||||
->where('created_at', '<=', now()->subSeconds($expirs))
|
||||
->oldest('id')
|
||||
->limit(500)
|
||||
->pluck('id');
|
||||
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$order = OfflineOrder::lockForUpdate()->findOrFail($id);
|
||||
|
||||
$offlineOrderService->revoke($order);
|
||||
|
||||
DB::commit();
|
||||
} catch (Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids->isEmpty()) {
|
||||
sleep(60);
|
||||
} else {
|
||||
sleep(10);
|
||||
}
|
||||
} else {
|
||||
sleep(60);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,10 @@
|
|||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Services\OrderService;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Throwable;
|
||||
|
||||
class OrderCloseExpiredCommand extends Command
|
||||
{
|
||||
|
|
@ -26,25 +29,31 @@ class OrderCloseExpiredCommand extends Command
|
|||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
public function handle(OrderService $orderService)
|
||||
{
|
||||
while (true) {
|
||||
$page = 0;
|
||||
$ids = Order::expired()->oldest('id')->limit(500)->pluck('id');
|
||||
|
||||
Order::select('id')->expired()->chunkById(1000, function ($orders) use (&$page) {
|
||||
Order::whereIn('id', $orders->pluck('id')->all())->where('status', Order::STATUS_PENDING)->update([
|
||||
'status' => Order::STATUS_CANCELLED,
|
||||
]);
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$page++;
|
||||
});
|
||||
$order = Order::lockForUpdate()->findOrFail($id);
|
||||
|
||||
if ($page === 0) {
|
||||
$orderService->cancel($order);
|
||||
|
||||
DB::commit();
|
||||
} catch (Throwable $e) {
|
||||
DB::rollBack();
|
||||
|
||||
report($e);
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids->isEmpty()) {
|
||||
sleep(60);
|
||||
} elseif ($page === 1) {
|
||||
sleep(30);
|
||||
} else {
|
||||
sleep(15);
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,13 @@ enum OfflineOrderStatus: int {
|
|||
case Paid = 1;
|
||||
case Revoked = 10;
|
||||
|
||||
public function label()
|
||||
public function dot()
|
||||
{
|
||||
$background = Admin::color()->get($this->color());
|
||||
$color = $this->color();
|
||||
|
||||
return "<span class='label' style='background: $background;'>{$this->text()}</span>";
|
||||
$background = Admin::color()->get($color, $color);
|
||||
|
||||
return '<i class="fa fa-circle" style="font-size: 13px;color: '.$background.'"></i> '.$this->text() ?? 'Unknown';
|
||||
}
|
||||
|
||||
public function text()
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ enum PayWay: string {
|
|||
static::Wallet => '#ff8acc',
|
||||
static::WxpayApp, static::WxpayH5, static::WxpayJsApi, static::WxpayMiniProgram, static::WxpayTransfer, static::WxPayShare => '#21b978',
|
||||
static::AlipayApp => '#3085d6',
|
||||
static::None => '#b3b9bf',
|
||||
default => '#ea5455',
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,26 @@ class OfflineOrderService
|
|||
];
|
||||
}
|
||||
|
||||
public function revoke(OfflineOrder $order)
|
||||
{
|
||||
if ($order->isPaid()) {
|
||||
throw new BizException('订单已付款');
|
||||
}
|
||||
|
||||
if ($order->isRevoked()) {
|
||||
throw new BizException('订单已取消');
|
||||
}
|
||||
|
||||
if ($order->points_deduction_amount > 0) {
|
||||
(new PointService())->change($order->user, $order->points_deduction_amount, PointLogAction::Refund, "线下订单{$order->sn}退还积分", $order);
|
||||
}
|
||||
|
||||
$order->update([
|
||||
'status' => OfflineOrderStatus::Revoked,
|
||||
'revoked_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
protected function insertOrderItems(OfflineOrder $order, array $items)
|
||||
{
|
||||
$remainingPointDiscountAmount = $order->points_deduction_amount;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ return [
|
|||
'payment_time' => '支付时间',
|
||||
'out_trade_no' => '外部交易单号',
|
||||
'status' => '状态',
|
||||
'revoked_at' => '取消时间',
|
||||
],
|
||||
'options' => [
|
||||
],
|
||||
|
|
|
|||
Loading…
Reference in New Issue