OrderStatus.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <?php
  2. namespace app\admin\model\shopro\order\traits;
  3. use app\admin\model\shopro\order\Order;
  4. use app\admin\model\shopro\order\OrderItem;
  5. use app\admin\model\shopro\activity\Groupon;
  6. use app\admin\model\shopro\Config;
  7. trait OrderStatus
  8. {
  9. protected function getStatus($data, $type)
  10. {
  11. $btns = []; // 前端订单操作按钮
  12. $backendBtns = [];
  13. $status_text = '';
  14. $status_desc = '';
  15. $ext = $this->ext;
  16. switch ($this->status_code) {
  17. case 'cancel':
  18. $status_text = '已取消';
  19. $status_desc = '买家已取消';
  20. $btns[] = 'delete'; // 删除订单
  21. break;
  22. case 'closed':
  23. $status_text = '交易关闭';
  24. if (isset($ext['closed_type']) && $ext['closed_type'] == 'refuse') {
  25. $status_desc = '买家拒绝收货';
  26. } else {
  27. $status_desc = '买家未在规定时间内付款';
  28. }
  29. $btns[] = 'delete'; // 删除订单
  30. break;
  31. case 'unpaid':
  32. $status_text = '待付款';
  33. $status_desc = '等待买家付款';
  34. $btns[] = 'cancel'; // 取消订单
  35. $btns[] = 'pay'; // 支付
  36. $backendBtns[] = 'change_fee'; // 订单改价
  37. break;
  38. // 已支付的
  39. case 'apply_refund':
  40. $status_text = '申请退款中';
  41. $status_desc = '等待卖家处理退款';
  42. $backendBtns[] = 'apply_refund_oper'; // 处理申请退款按钮
  43. $backendBtns[] = 'refund'; // 只能退款,或者在列表上拒绝申请退款
  44. break;
  45. case 'commented':
  46. $status_text = '已评价';
  47. $status_desc = '订单已评价';
  48. $dispatchType = $this->getItemDispatchTypes();
  49. if (in_array('express', $dispatchType)) {
  50. //$btns[] = 'express'; // 查看物流, 放到详情里去了
  51. }
  52. $backendBtns[] = 'refund';
  53. break;
  54. case 'nocomment':
  55. $status_text = '待评价';
  56. $status_desc = '等待买家评价';
  57. $dispatchType = $this->getItemDispatchTypes();
  58. if (in_array('express', $dispatchType)) {
  59. //$btns[] = 'express'; // 查看物流, 放到详情里去了
  60. }
  61. $btns[] = 'comment';
  62. //应该申请售后
  63. // $btns[] = 'aftersale';
  64. $backendBtns[] = 'refund';
  65. break;
  66. case 'noget':
  67. $status_text = '待收货';
  68. $status_desc = '等待买家收货';
  69. $dispatchType = $this->getItemDispatchTypes();
  70. if (in_array('express', $dispatchType)) {
  71. //$btns[] = 'express'; // 查看物流, 放到详情里去了
  72. }
  73. if ($this->isOffline($data)) {
  74. $status_desc = '卖家已发货,等待包裹运达';
  75. // 用户可以拒收,后台确认收货
  76. $btns[] = 'refuse'; // 用户拒收
  77. $backendBtns[] = 'confirm';
  78. }else {
  79. // 计算自动确认收货时间
  80. $send_time = $ext['send_time'] ?? 0;
  81. $auto_confirm = Config::getConfigField('shop.order.auto_confirm');
  82. $auto_confirm_unix = $auto_confirm * 86400;
  83. if ($send_time && $auto_confirm_unix) {
  84. $auto_confirm_time = $send_time + $auto_confirm_unix;
  85. $status_desc .= ',还剩' . diff_in_time($auto_confirm_time, null, true, true) . '自动确认';
  86. }
  87. $btns[] = 'confirm'; // 确认收货
  88. //应该申请售后
  89. // $btns[] = 'aftersale';
  90. $backendBtns[] = 'refund';
  91. }
  92. break;
  93. case 'nosend':
  94. $status_text = '待发货';
  95. $status_desc = '等待卖家发货';
  96. $statusCodes = $this->getItemStatusCode();
  97. if (in_array('noget', $statusCodes)) { // 只要存在待收货的item
  98. $btns[] = 'confirm'; // 确认收货 (部分发货时也可以收货)
  99. }
  100. if ($this->isOffline($data)) {
  101. // 发货之前用户可以取消
  102. $btns[] = 'cancel'; // 用户取消订单
  103. } else {
  104. $backendBtns[] = 'refund';
  105. }
  106. $backendBtns[] = 'send';
  107. if (!isset($ext['need_address']) || $ext['need_address']) { // 自动发货这些不需要收货地址的,没有 edit_consignee
  108. $backendBtns[] = 'edit_consignee'; //修改收货地址
  109. }
  110. //应该申请全额退款,有了
  111. break;
  112. case 'refund_completed':
  113. $status_text = '退款完成';
  114. $status_desc = '订单退款完成';
  115. break;
  116. case 'refund_agree':
  117. $status_text = '退款完成';
  118. $status_desc = '订单退款完成';
  119. break;
  120. case 'groupon_ing':
  121. $status_text = '等待成团';
  122. $status_desc = '等待拼团成功';
  123. if ($this->isOffline($data)) {
  124. // 货到付款未付款,不能退款,等待拼团时还未发货,用户可取消订单
  125. $btns[] = 'cancel'; // 用户取消订单
  126. } else {
  127. $backendBtns[] = 'refund'; // 全部退款 直接不申请退款
  128. }
  129. //应该团购,有了
  130. //应该申请全额退款,有了
  131. break;
  132. case 'groupon_invalid':
  133. $status_text = '拼团失败';
  134. $status_desc = '拼团失败';
  135. break;
  136. // 已支付的结束
  137. case 'completed':
  138. $status_text = '交易完成';
  139. $status_desc = '交易已完成';
  140. // $btns[] = 'delete'; // 删除订单
  141. break;
  142. }
  143. // 有活动
  144. if (in_array($data['activity_type'], ['groupon', 'groupon_ladder', 'groupon_lucky'])) {
  145. // 是拼团订单
  146. if (isset($ext['groupon_id']) && $ext['groupon_id']) {
  147. $btns[] = 'groupon'; // 拼团详情
  148. }
  149. }
  150. if (in_array($this->status_code, ['nosend', 'groupon_ing']) && !$this->isOffline($data)) { // 线下付款订单,不可申请全额退款
  151. if (in_array($data['apply_refund_status'], [Order::APPLY_REFUND_STATUS_NOAPPLY, Order::APPLY_REFUND_STATUS_REFUSE])) {
  152. // 获取所有的 item 状态
  153. $statusCodes = $this->getItemStatusCode();
  154. if (count($statusCodes) == 1 && current($statusCodes) == 'nosend') {
  155. // items 只有 未发货,就显示 申请退款按钮
  156. if ($data['apply_refund_status'] == Order::APPLY_REFUND_STATUS_REFUSE) {
  157. $btns[] = 're_apply_refund'; // 重新申请全额退款
  158. } else {
  159. $btns[] = 'apply_refund'; // 申请全额退款
  160. }
  161. }
  162. }
  163. }
  164. //申请售后
  165. if (in_array($this->status_code, ['noget', 'nocomment']) && !$this->isOffline($data)) { // 线下付款订单,不可申请全额退款
  166. // 获取所有的 item 状态
  167. $statusCodes = $this->getItemStatusCode_aftersale('item');
  168. if(!in_array('refund_agree',$statusCodes) && !in_array('refund_completed',$statusCodes)){
  169. $aftersale_times = count($statusCodes);//默认能申请N次
  170. foreach($statusCodes as $key => $statusCode){
  171. if(strpos($statusCode,'aftersale_ing') !== false || strpos($statusCode,'aftersale_completed') !== false){
  172. $aftersale_times--; //减1次
  173. }
  174. }
  175. if($aftersale_times != 0){
  176. $btns[] = 'aftersale'; // 申请售后
  177. }
  178. }
  179. }
  180. if ($data['invoice_status'] == 1) {
  181. $btns[] = 'invoice'; // 查看发票
  182. }
  183. $return = null;
  184. switch ($type) {
  185. case 'status_text':
  186. $return = $status_text;
  187. break;
  188. case 'btns':
  189. $return = $btns;
  190. break;
  191. case 'status_desc':
  192. $return = $status_desc;
  193. break;
  194. case 'backend_btns':
  195. if (in_array('refund', $backendBtns)) {
  196. // 判断是否有退款,如果存在退款就移除 refund 按钮
  197. $refundCount = OrderItem::where('order_id', $data['id'])->where('refund_status', '<>', OrderItem::REFUND_STATUS_NOREFUND)->count();
  198. if ($refundCount && ($key = array_search('refund', $backendBtns))) {
  199. unset($backendBtns[$key]);
  200. }
  201. $backendBtns = array_values($backendBtns);
  202. }
  203. $return = $backendBtns;
  204. break;
  205. }
  206. return $return;
  207. }
  208. /**
  209. * 获取支付成功之后的子状态
  210. */
  211. public function getPayedStatusCode($data)
  212. {
  213. $status_code = '';
  214. // 获取所有的 item 状态
  215. $statusCodes = $this->getItemStatusCode();
  216. if (in_array('nosend', $statusCodes)) {
  217. // 存在待发货就是待发货
  218. $status_code = 'nosend';
  219. } else if (in_array('noget', $statusCodes)) {
  220. // 存在待收货,就是待收货
  221. $status_code = 'noget';
  222. } else if (in_array('nocomment', $statusCodes)) {
  223. // 存在待评价,就是待评价
  224. $status_code = 'nocomment';
  225. } else if (in_array('commented', $statusCodes)) {
  226. // 存在已评价,就是已评价
  227. $status_code = 'commented';
  228. } else if (in_array('refund_completed', $statusCodes)) {
  229. // 所有商品退款完成,或者退款中(不可能存在待发货或者收货的商品,上面已判断过)
  230. $status_code = 'refund_completed';
  231. } else if (in_array('refund_agree', $statusCodes)) {
  232. // 所有商品都同意退款了 (不可能存在待发货或者收货的商品,上面已判断过)
  233. $status_code = 'refund_agree';
  234. } // 售后都不在总状态显示
  235. if ($data['apply_refund_status'] == Order::APPLY_REFUND_STATUS_APPLY && !in_array($status_code, ['refund_completed', 'refund_agree'])) {
  236. return $status_code = 'apply_refund'; // 申请退款中,并且还没退款
  237. }
  238. $ext = $this->ext;
  239. // 是拼团订单
  240. if (
  241. in_array($data['activity_type'], ['groupon', 'groupon_ladder', 'groupon_lucky']) &&
  242. isset($ext['groupon_id']) && $ext['groupon_id']
  243. ) {
  244. $groupon = Groupon::where('id', $ext['groupon_id'])->find();
  245. if ($groupon) {
  246. if ($groupon['status'] == 'ing') {
  247. // 尚未成团
  248. $status_code = $statusCodes[0] ?? ''; // 拼团订单只能有一个商品
  249. $status_code = in_array($status_code, ['refund_agree', 'refund_completed']) ? $status_code : 'groupon_ing'; // 如果订单已退款,则是退款状态,不显示拼团中
  250. } else if ($groupon['status'] == 'invalid') {
  251. $status_code = 'groupon_invalid';
  252. }
  253. }
  254. }
  255. return $status_code;
  256. }
  257. /**
  258. * 获取订单items状态
  259. *
  260. * @param string $type
  261. * @return array
  262. */
  263. private function getItemStatusCode($type = 'order')
  264. {
  265. // 循环判断 item 状态
  266. $itemStatusCode = [];
  267. foreach ($this->items as $key => $item) {
  268. // 获取 item status
  269. $itemStatusCode[] = (new OrderItem)->getBaseStatusCode($item, $type);
  270. }
  271. // 取出不重复不为空的 status_code
  272. $statusCodes = array_values(array_unique(array_filter($itemStatusCode)));
  273. return $statusCodes;
  274. }
  275. //和上面一样,除了 array_unique,这里不需要去重
  276. private function getItemStatusCode_aftersale($type = 'order')
  277. {
  278. // 循环判断 item 状态
  279. $itemStatusCode = [];
  280. foreach ($this->items as $key => $item) {
  281. // 获取 item status
  282. $itemStatusCode[] = (new OrderItem)->getBaseStatusCode($item, $type);
  283. }
  284. // 取出不重复不为空的 status_code
  285. $statusCodes = array_values(array_filter($itemStatusCode));
  286. return $statusCodes;
  287. }
  288. private function getItemDispatchTypes()
  289. {
  290. $dispatchType = [];
  291. foreach ($this->items as $key => $item) {
  292. // 获取 item status
  293. $dispatchType[] = $item['dispatch_type'];
  294. }
  295. $dispatchType = array_unique(array_filter($dispatchType)); // 过滤重复,过滤空值
  296. return $dispatchType;
  297. }
  298. public function getStatusTextAttr($value, $data)
  299. {
  300. return $this->getStatus($data, 'status_text');
  301. }
  302. public function getStatusDescAttr($value, $data)
  303. {
  304. return $this->getStatus($data, 'status_desc');
  305. }
  306. public function getBtnsAttr($value, $data)
  307. {
  308. $btn_name = strpos(request()->url(), 'addons/shopro') !== false ? 'btns' : 'backend_btns';
  309. return $this->getStatus($data, $btn_name);
  310. }
  311. // 获取订单状态
  312. //订单状态:closed=交易关闭,cancel=已取消,unpaid=未支付,paid=已支付,completed=已完成,pending=待定
  313. public function getStatusCodeAttr($value, $data)
  314. {
  315. $status_code = '';
  316. switch ($data['status']) {
  317. case Order::STATUS_CLOSED:
  318. $status_code = 'closed'; // 订单交易关闭
  319. break;
  320. case Order::STATUS_CANCEL:
  321. $status_code = 'cancel'; // 订单已取消
  322. break;
  323. case Order::STATUS_UNPAID:
  324. $status_code = 'unpaid'; // 订单等待支付
  325. break;
  326. case Order::STATUS_PENDING:
  327. $status_code = $this->getPayedStatusCode($data); // 订单线下付款
  328. break;
  329. case Order::STATUS_PAID:
  330. // 根据 item 获取支付后的状态信息
  331. $status_code = $this->getPayedStatusCode($data);
  332. break;
  333. case Order::STATUS_COMPLETED:
  334. $status_code = 'completed';
  335. break;
  336. }
  337. return $status_code;
  338. }
  339. /**
  340. * 处理未支付 item status_code
  341. * 查询列表 item status_code 不关联订单表,使用这个方法进行处理
  342. */
  343. public function setOrderItemStatusByOrder($order)
  344. {
  345. $order = $order instanceof \think\Model ? $order->toArray() : $order;
  346. foreach ($order['items'] as $key => &$item) {
  347. if ((!in_array($order['status'], [Order::STATUS_PAID, Order::STATUS_COMPLETED]) && !$this->isOffline($order)) // 没有支付,并且也不是货到付款
  348. || $order['apply_refund_status'] == Order::APPLY_REFUND_STATUS_APPLY) {
  349. // 未支付,status_code = 订单的 status_code
  350. $item['status_code'] = $order['status_code'];
  351. $item['status_text'] = '';
  352. $item['status_desc'] = '';
  353. $item['btns'] = [];
  354. } else {
  355. if (strpos(request()->url(), 'addons/shopro') !== false) {
  356. // 前端
  357. if (strpos($item['status_code'], 'nosend') !== false) {
  358. if (!$this->isOffline($order) && !array_intersect(['re_apply_refund', 'apply_refund'], $order['btns'])) {
  359. // 不能申请全额退款了(有部分发货,或者退款)的待发货的 item 要显示申请售后的按钮
  360. $aftersale_id = (isset($item['ext']['aftersale_id']) && !empty($item['ext']['aftersale_id'])) ? $item['ext']['aftersale_id'] : 0;
  361. if (strpos($item['status_code'], 'aftersale_ing') === false && strpos($item['status_code'], 'aftersale_completed') === false) {
  362. // 不是售后中,也不是售后完成
  363. if (strpos($item['status_code'], 'aftersale_refuse') !== false && $aftersale_id) {
  364. // 如果申请过退款,被拒绝了,则为重新申请售后
  365. $item['btns'][] = 're_aftersale';
  366. } else {
  367. // 取消售后是 re_aftersale ,未申请过是 aftersale
  368. $item['btns'][] = $aftersale_id ? 're_aftersale' : 'aftersale'; // 售后,售后取消会有 aftersale_id
  369. }
  370. }
  371. }
  372. } else if (strpos($item['status_code'], 'noget') !== false) {
  373. // 如果是货到付款的 待收货
  374. if ($this->isOffline($order)) {
  375. foreach($item['btns'] as $btnk => $btn) {
  376. if (in_array($btn, ['re_aftersale', 'aftersale'])) {
  377. unset($item['btns'][$btnk]);
  378. }
  379. }
  380. }
  381. }
  382. } else {
  383. // 后端,如果是货到付款, 并且还没收货,不显示退款按钮
  384. if ($this->isOffline($order) && (strpos($item['status_code'], 'nosend') !== false || strpos($item['status_code'], 'noget') !== false)) {
  385. $refund_key = array_search('refund', $item['btns']);
  386. if ($refund_key !== false) {
  387. unset($item['btns'][$refund_key]);
  388. }
  389. }
  390. }
  391. }
  392. }
  393. return $order;
  394. }
  395. }