loadlang('shopro/order/order'); $this->model = new \app\admin\model\shopro\order\Aftersale; $this->orderModel = new \app\admin\model\shopro\order\Order; $this->view->assign("dispatchStatusList", $this->model->getDispatchStatusList()); $this->view->assign("aftersaleStatusList", $this->model->getAftersaleStatusList()); $this->view->assign("refundStatusList", $this->model->getRefundStatusList()); } /** * 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法 * 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑 * 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改 */ /** * 查看 */ public function index() { //当前是否为关联查询 $this->relationSearch = false; //设置过滤方法 $this->request->filter(['strip_tags', 'trim']); if ($this->request->isAjax()) { //如果发送的来源是Selectpage,则转发到Selectpage if ($this->request->request('keyField')) { return $this->selectpage(); } // list($where, $sort, $order, $offset, $limit) = $this->buildparams(); $sort = $this->request->get("sort", !empty($this->model) && $this->model->getPk() ? $this->model->getPk() : 'id'); $order = $this->request->get("order", "DESC"); $offset = $this->request->get("offset", 0); $limit = $this->request->get("limit", 0); $search = $this->request->get("search", ''); // 关键字 $status = $this->request->get("status", 'all'); $total = $this->buildSearchOrder()// 返回的是 orderModel ->order($sort, $order) ->count(); $list = $this->buildSearchOrder()// 返回的是 orderModel ->with(['aftersale' => function ($query) { $query->removeOption('soft_delete')->alias('a')->field('a.*, u.nickname as user_nickname, u.mobile as user_mobile')->join('user u', 'u.id = a.user_id', 'LEFT'); }]) ->order($sort, $order) ->limit($offset, $limit) ->select(); // foreach ($list as $row) { // $row->visible(['id','aftersale_sn','user_id','type','order_id','order_item_id','goods_id','goods_sku_price_id','goods_sku_text','goods_title','goods_image','goods_original_price','discount_fee','goods_price','goods_num','dispatch_status','dispatch_fee','aftersale_status','refund_status','refund_fee','createtime','updatetime','deletetime', 'user', 'activity_type']); // } $list = collection($list)->toArray(); $result = array("total" => $total, "rows" => $list); return $this->success('获取成功', null, $result); } return $this->view->fetch(); } /** * 详情 */ public function detail($id) { if ($this->request->isAjax()) { $row = $this->model->withTrashed()->with(['user', 'order' => function ($query) { $query->removeOption('soft_delete'); }, 'logs'])->where('id', $id)->find(); if (!$row) { $this->error(__('No Results were found')); } $result = [ 'detail' => $row, 'logs' => $row->logs, 'order' => $row->order, ]; return $this->success('获取成功', null, $result); } $this->assignconfig('id', $id); return $this->view->fetch(); } public function newAftersale($id) { if ($this->request->isAjax()) { $aftersale = $this->model->withTrashed()->where('id', $id)->find(); if (!$aftersale) { $this->error('售后单不存在或不可完成'); } $order = Order::withTrashed()->where('id', $aftersale->order_id)->find(); $orderItem = OrderItem::where('id', $aftersale->order_item_id)->find(); if (!$order || !$orderItem) { $this->error('订单或订单商品不存在'); } Db::transaction(function () use ($aftersale, $order, $orderItem) { $orderItem->aftersale_status = 0; $orderItem->refund_status = 1; $orderItem->save(); $aftersale->aftersale_status = 1; // 售后重启 $aftersale->save(); }); return $this->success('操作成功'); } } /** * 完成售后 */ public function finish($id) { if ($this->request->isAjax()) { $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); if (!$aftersale) { $this->error('售后单不存在或不可完成'); } $order = Order::withTrashed()->where('id', $aftersale->order_id)->find(); $orderItem = OrderItem::where('id', $aftersale->order_item_id)->find(); if (!$order || !$orderItem) { $this->error('订单或订单商品不存在'); } Db::transaction(function () use ($aftersale, $order, $orderItem) { // 售后单完成前 $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; \think\Hook::listen('aftersale_finish_before', $data); $aftersale->aftersale_status = \app\admin\model\shopro\order\Aftersale::AFTERSALE_STATUS_OK; // 售后完成 $aftersale->save(); // 增加售后单变动记录、 OrderAftersaleLog::operAdd($order, $aftersale, $this->auth->getUserInfo(), 'admin', [ 'reason' => '卖家完成售后', 'content' => '售后订单已完成', 'images' => [] ]); $orderItem->aftersale_status = OrderItem::AFTERSALE_STATUS_OK; $orderItem->save(); \addons\shopro\model\OrderAction::operAdd($order, $orderItem, $this->auth->getUserInfo(), 'admin', '管理员完成售后'); // 售后单完成之后 $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; \think\Hook::listen('aftersale_finish_after', $data); }); return $this->success('操作成功'); } } /** * 拒绝售后 */ public function refuse($id = 0) { if ($this->request->isAjax()) { $refuse_msg = $this->request->post('refuse_msg', ''); if (!$refuse_msg) { $this->error('请输入拒绝原因'); } $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); if (!$aftersale) { $this->error('售后单不存在或不可拒绝'); } $order = Order::withTrashed()->where('id', $aftersale->order_id)->find(); $orderItem = OrderItem::where('id', $aftersale->order_item_id)->find(); if (!$order || !$orderItem) { $this->error('订单或订单商品不存在'); } Db::transaction(function () use ($aftersale, $order, $orderItem, $refuse_msg) { // 售后单拒绝前 $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; \think\Hook::listen('aftersale_refuse_before', $data); $aftersale->aftersale_status = \app\admin\model\shopro\order\Aftersale::AFTERSALE_STATUS_REFUSE; // 售后拒绝 $aftersale->save(); // 增加售后单变动记录 OrderAftersaleLog::operAdd($order, $aftersale, $this->auth->getUserInfo(), 'admin', [ 'reason' => '卖家拒绝售后', 'content' => $refuse_msg, 'images' => [] ]); $orderItem->aftersale_status = OrderItem::AFTERSALE_STATUS_REFUSE; // 拒绝售后 $orderItem->save(); \addons\shopro\model\OrderAction::operAdd($order, $orderItem, $this->auth->getUserInfo(), 'admin', '管理员拒绝订单售后:' . $refuse_msg); // 售后单拒绝后 $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; \think\Hook::listen('aftersale_refuse_after', $data); }); return $this->success('操作成功'); } } /** * 同意退款 */ public function refund($id = 0) { if ($this->request->isAjax()) { $refund_money = round($this->request->post('refund_money', 0), 2); if ($refund_money <= 0) { $this->error('退款金额必须大于 0'); } $aftersale = $this->model->withTrashed()->canOper()->where('id', $id)->find(); if (!$aftersale) { $this->error('售后单不存在或不可退款'); } $order = Order::withTrashed()->with('item')->where('id', $aftersale->order_id)->find(); if (!$order) { $this->error('订单不存在'); } $lockKey = 'lock:refund:aftersale:' . $id; if (\think\Cache::get($lockKey)) { $this->error('退款正在处理中,请勿重复操作'); } \think\Cache::set($lockKey, 1, 10); try { Db::transaction(function () use ($id, $refund_money, $order) { // 行级排他锁锁定售后单与订单商品,防止并发重复退款 $aftersale = $this->model->withTrashed()->where('id', $id)->lock(true)->find(); if (!$aftersale || $aftersale['refund_status'] == \app\admin\model\shopro\order\Aftersale::REFUND_STATUS_FINISH) { throw new \Exception('售后单已退款,不能重复退款'); } $orderItem = OrderItem::where('id', $aftersale['order_item_id'])->lock(true)->find(); if (!$orderItem) { throw new \Exception('订单商品不存在'); } if (in_array($orderItem['refund_status'], [ \app\admin\model\shopro\order\OrderItem::REFUND_STATUS_OK, \app\admin\model\shopro\order\OrderItem::REFUND_STATUS_FINISH, ])) { // 订单商品此前已完成退款,自动对齐售后单状态,防重复扣款与卡死 $aftersale->aftersale_status = \app\admin\model\shopro\order\Aftersale::AFTERSALE_STATUS_OK; $aftersale->refund_status = \app\admin\model\shopro\order\Aftersale::REFUND_STATUS_FINISH; $aftersale->refund_fee = $orderItem->refund_fee ?: $refund_money; $aftersale->save(); OrderAftersaleLog::operAdd($order, $aftersale, $this->auth->getUserInfo(), 'admin', [ 'reason' => '卖家同意退款(自动对齐)', 'content' => '订单商品此前已完成退款,系统自动对齐售后单为已完成', 'images' => [] ]); return; } // 实时剩余可退金额硬校验(防多退) $refund_surplus_money = bcsub($orderItem->pay_price, $orderItem->refund_fee, 2); if (bccomp($refund_money, $refund_surplus_money, 2) === 1) { throw new \Exception('退款金额不能大于' . $refund_surplus_money . '元'); } $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; \think\Hook::listen('aftersale_finish_before', $data); $aftersale->aftersale_status = \app\admin\model\shopro\order\Aftersale::AFTERSALE_STATUS_OK; // 售后同意 $aftersale->refund_status = \app\admin\model\shopro\order\Aftersale::REFUND_STATUS_FINISH; // 售后同意退款 $aftersale->refund_fee = bcadd($refund_money, $aftersale->refund_fee, 2); // 已退款金额 $aftersale->save(); // 增加售后单变动记录 OrderAftersaleLog::operAdd($order, $aftersale, $this->auth->getUserInfo(), 'admin', [ 'reason' => '卖家同意退款', 'content' => '售后订单已退款', 'images' => [] ]); $orderItem->aftersale_status = OrderItem::AFTERSALE_STATUS_OK; $orderItem->save(); \addons\shopro\model\OrderAction::operAdd($order, $orderItem, $this->auth->getUserInfo(), 'admin', '管理员同意售后退款'); // 退款 \app\admin\model\shopro\order\Order::startRefund($order, $orderItem, $refund_money, $this->auth->getUserInfo(), '管理员同意售后退款'); $data = ['aftersale' => $aftersale, 'order' => $order, 'item' => $orderItem]; \think\Hook::listen('aftersale_finish_after', $data); }); } catch (\Throwable $e) { \think\Cache::rm($lockKey); $this->error($e->getMessage()); } \think\Cache::rm($lockKey); return $this->success('操作成功'); } } /** * 留言 */ public function addLog($id = 0) { if ($this->request->isAjax()) { $reason = $this->request->post('reason', '卖家留言'); $content = $this->request->post('content', ''); $images = $this->request->post('images', []); if (!$content) { $this->error('留言内容不能为空'); } $aftersale = $this->model->withTrashed()->where('id', $id)->find(); if (!$aftersale) { $this->error('售后单不存在'); } $order = Order::withTrashed()->with('item')->where('id', $aftersale->order_id)->find(); if (!$order) { $this->error('订单不存在'); } Db::transaction(function () use ($order, $aftersale, $reason, $content, $images) { if ($aftersale['aftersale_status'] == 0) { $aftersale->aftersale_status = \app\admin\model\shopro\order\Aftersale::AFTERSALE_STATUS_AFTERING; // 售后处理中 $aftersale->save(); } // 增加售后单变动记录 OrderAftersaleLog::operAdd($order, $aftersale, $this->auth->getUserInfo(), 'admin', [ 'reason' => $reason, 'content' => $content, 'images' => $images ]); }); return $this->success('操作成功'); } } private function buildSearchOrder() { $search = $this->request->get("search", ''); // 关键字 $status = $this->request->get("status", 'all'); $orders = $this->orderModel->withTrashed(); $orders = $orders->whereExists(function ($query) use ($search, $status) { extract($this->getModelTable()); $aftersales = $query->table($aftersaleName)->where($aftersaleName . '.order_id=' . $orderName . '.id'); $aftersales = $this->aftersaleSearch($aftersales, $search, $status); return $aftersales; }); return $orders; } private function aftersaleSearch($aftersales, $search, $status) { extract($this->getModelTable()); if ($search) { // 模糊搜索字段 $searcharr = ['goods_title', 'goods_sku_text', 'aftersale_sn']; foreach ($searcharr as $k => &$v) { $v = stripos($v, ".") === false ? $aftersaleName . '.' . $v : $v; } unset($v); $aftersales = $aftersales->where(function ($query) use ($searcharr, $search, $aftersaleName) { $query->where(implode("|", $searcharr), "LIKE", "%{$search}%") ->whereOr(function ($query) use ($search, $aftersaleName) { // 用户 $query->whereExists(function ($query) use ($search, $aftersaleName) { $userTableName = (new \app\admin\model\User())->getQuery()->getTable(); $query->table($userTableName)->where($userTableName . '.id=' . $aftersaleName . '.user_id') ->where(function ($query) use ($search) { $query->where('nickname', 'like', "%{$search}%") ->whereOr('mobile', 'like', "%{$search}%"); }); }); }); }); } // 售后单状态 if ($status != 'all') { if (in_array($status, ['cancel', 'refuse', 'nooper', 'ing', 'finish'])) { $aftersales = $aftersales->where(OrderAftersale::getScopeWhere($status)); } } return $aftersales; } private function getModelTable() { $orderName = $this->orderModel->getQuery()->getTable(); $aftersaleName = $this->model->getQuery()->getTable(); return compact("orderName", "aftersaleName"); } }