Order.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. <?php
  2. namespace app\admin\controller\shopro\order;
  3. use think\Db;
  4. use think\exception\HttpResponseException;
  5. use app\admin\controller\shopro\Common;
  6. use app\admin\model\shopro\order\Order as OrderModel;
  7. use app\admin\model\shopro\order\Address as OrderAddressModel;
  8. use app\admin\model\shopro\order\OrderItem;
  9. use app\admin\model\shopro\Admin;
  10. use app\admin\model\shopro\user\User;
  11. use app\admin\model\shopro\order\Action as OrderActionModel;
  12. use app\admin\model\shopro\order\Express as OrderExpressModel;
  13. use addons\shopro\service\pay\PayOper;
  14. use addons\shopro\service\order\OrderOper;
  15. use addons\shopro\service\order\OrderRefund;
  16. use app\admin\model\shopro\Pay as PayModel;
  17. use addons\shopro\service\order\OrderDispatch as OrderDispatchService;
  18. use addons\shopro\library\express\Express as ExpressLib;
  19. use addons\shopro\library\Operator;
  20. use addons\shopro\facade\Wechat;
  21. class Order extends Common
  22. {
  23. protected $noNeedRight = ['getType', 'dispatchList'];
  24. public function _initialize()
  25. {
  26. parent::_initialize();
  27. $this->model = new OrderModel;
  28. }
  29. /**
  30. * 订单列表
  31. *
  32. * @return \think\Response
  33. */
  34. public function index()
  35. {
  36. if (!$this->request->isAjax()) {
  37. if (sheep_config('shop.platform.WechatMiniProgram.status')) { // 如果开启了小程序平台
  38. // 设置小程序发货信息管理,消息通知跳转地址,有缓存, 不会每次都设置
  39. $uploadshoppingInfo = new \addons\shopro\library\easywechatPlus\WechatMiniProgramShop(Wechat::miniProgram());
  40. $uploadshoppingInfo->checkAndSetMessageJumpPath();
  41. }
  42. $exportConfig = (new \addons\shopro\library\Export())->getConfig();
  43. $this->assignconfig("save_type", $exportConfig['save_type'] ?? 'download');
  44. return $this->view->fetch();
  45. }
  46. $orders = $this->model->withTrashed()->sheepFilter()->with(['user', 'items', 'address', 'activity_orders'])
  47. ->paginate(request()->param('list_rows', 10))->each(function ($order) {
  48. $order->pay_types = $order->pay_types;
  49. $order->pay_types_text = $order->pay_types_text;
  50. $order->items = collection($order->items);
  51. $order->items->each(function ($item) use ($order) {
  52. // 处理每个商品的 activity_order
  53. $item->activity_orders = new \think\Collection;
  54. foreach ($order->activity_orders as $activityOrder) {
  55. if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) {
  56. $item->activity_orders->push($activityOrder);
  57. }
  58. }
  59. return $item;
  60. });
  61. })->toArray();
  62. foreach ($orders['data'] as &$order) {
  63. $order = $this->model->setOrderItemStatusByOrder($order);
  64. }
  65. $result = [
  66. 'orders' => $orders,
  67. ];
  68. // 查询各个状态下的订单数量
  69. $searchStatus = $this->model->searchStatusList();
  70. // 所有的数量
  71. $result['all'] = $this->model->withTrashed()->sheepFilter(true, function ($filters) {
  72. unset($filters['status']);
  73. return $filters;
  74. })->count();
  75. foreach ($searchStatus as $status => $text) {
  76. $result[$status] = $this->model->withTrashed()->sheepFilter(true, function ($filters) use ($status) {
  77. $filters['status'] = $status;
  78. return $filters;
  79. })->count();
  80. }
  81. $this->success('获取成功', null, $result);
  82. }
  83. // 获取数据类型
  84. public function getType()
  85. {
  86. $type = $this->model->typeList();
  87. $payType = (new PayModel)->payTypeList();
  88. $platform = $this->model->platformList();
  89. $classify = (new \app\admin\model\shopro\activity\Activity)->classifies();
  90. $activityType = $classify['activity'];
  91. $promoType = $classify['promo'];
  92. $applyRefundStatus = $this->model->applyRefundStatusList();
  93. $status = $this->model->searchStatusList();
  94. $result = [
  95. 'type' => $type,
  96. 'pay_type' => $payType,
  97. 'platform' => $platform,
  98. 'activity_type' => $activityType,
  99. 'promo_types' => $promoType,
  100. 'apply_refund_status' => $applyRefundStatus,
  101. 'status' => $status
  102. ];
  103. $data = [];
  104. foreach ($result as $key => $list) {
  105. $data[$key][] = ['name' => '全部', 'type' => 'all'];
  106. foreach ($list as $k => $v) {
  107. $data[$key][] = [
  108. 'name' => $v,
  109. 'type' => $k
  110. ];
  111. }
  112. }
  113. $this->success('获取成功', null, $data);
  114. }
  115. /**
  116. * 订单详情
  117. *
  118. * @param $id
  119. * @return \think\Response
  120. */
  121. public function detail($id)
  122. {
  123. if (!$this->request->isAjax()) {
  124. return $this->view->fetch();
  125. }
  126. // 更新包裹信息(5分钟缓存)
  127. (new ExpressLib)->updateOrderExpress($id);
  128. $order = $this->model->withTrashed()->with(['user', 'items', 'address', 'activity_orders', 'pays', 'invoice'])->where('id', $id)->find();
  129. if (!$order) {
  130. $this->error(__('No Results were found'));
  131. }
  132. $order->express = $order->express;
  133. if ($order->invoice) {
  134. $order->invoice->order_status = $order->invoice->order_status;
  135. $order->invoice->order_status_text = $order->invoice->order_status_text;
  136. $order->invoice->order_fee = $order->invoice->order_fee;
  137. }
  138. foreach ($order->activity_orders as $activityOrder) {
  139. // 处理每个活动中参与的商品
  140. $activityOrder->items = new \think\Collection();
  141. foreach ($order->items as $item) {
  142. if ($activityOrder->goods_ids && in_array($item->goods_id, $activityOrder->goods_ids)) {
  143. $activityOrder->items->push($item);
  144. }
  145. }
  146. }
  147. foreach ($order->items as $item) {
  148. // 处理 order_item 建议退款金额
  149. $item->suggest_refund_fee = $item->suggest_refund_fee;
  150. }
  151. // 处理未支付订单 item status_code
  152. $order = $order->setOrderItemStatusByOrder($order);
  153. $this->success('获取成功', null, $order);
  154. }
  155. /**
  156. * 批量发货渲染模板
  157. *
  158. * @return void
  159. */
  160. public function batchDispatch()
  161. {
  162. return $this->view->fetch();
  163. }
  164. /**
  165. * 批量发货渲染模板
  166. *
  167. * @return void
  168. */
  169. public function dispatchList()
  170. {
  171. return $this->view->fetch();
  172. }
  173. /**
  174. * 手动发货
  175. *
  176. * @return void
  177. */
  178. public function customDispatch()
  179. {
  180. if (!$this->request->isAjax()) {
  181. return $this->view->fetch();
  182. }
  183. $params = $this->request->only(['order_id', 'order_item_ids', 'custom_type', 'custom_content']);
  184. $this->svalidate($params, '.custom_dispatch');
  185. Db::transaction(function () use ($params) {
  186. $service = new OrderDispatchService($params);
  187. $service->customDispatch($params);
  188. });
  189. $this->success('发货成功');
  190. }
  191. /**
  192. * 发货
  193. *
  194. * @description
  195. * 支持分包裹发货
  196. * 支持手动发货
  197. * 支持上传发货单发货
  198. * 支持推送api运单发货 默认使用配置项
  199. * 支持修改发货信息
  200. * 支持取消发货
  201. *
  202. * @remark 此处接口设计如此复杂是因为考虑到权限的问题,订单发货权限可以完成所有发货行为
  203. *
  204. * @param array $action 发货行为(默认:confirm=确认发货, cancel=取消发货, change=修改运单, multiple=解析批量发货单)
  205. * @param int $order_id 订单id
  206. * @param array $order_item_ids 订单商品id
  207. * @param string $method 发货方式(input=手动发货, api=推送运单, upload=上传发货单)
  208. * @param array $sender 发货人信息
  209. * @param array $express 物流信息
  210. *
  211. * @return \think\Response
  212. */
  213. public function dispatch()
  214. {
  215. if (!$this->request->isAjax()) {
  216. return $this->view->fetch();
  217. }
  218. $params = $this->request->only(['action', 'order_id', 'order_ids', 'order_item_ids', 'method', 'sender', 'express', 'order_express_id']);
  219. $action = $params['action'] ?? 'confirm';
  220. if (!in_array($action, ['confirm', 'cancel', 'change', 'multiple'])) {
  221. $this->error('发货参数错误');
  222. }
  223. $service = new OrderDispatchService($params);
  224. switch ($action) {
  225. case 'confirm':
  226. $express = $service->confirm($params);
  227. $this->success('发货成功', null, $express);
  228. break;
  229. case 'cancel':
  230. $result = $service->cancel($params);
  231. if ($result) {
  232. $this->success('取消发货成功');
  233. }
  234. break;
  235. case 'change':
  236. $express = $service->change($params);
  237. $this->success('修改成功', null, $express);
  238. break;
  239. case 'multiple':
  240. $params['file'] = $this->request->file('file');
  241. $result = $service->multiple($params);
  242. $this->success('待发货列表', null, $result);
  243. break;
  244. }
  245. $this->error('操作失败');
  246. }
  247. /**
  248. * 获取物流快递信息
  249. */
  250. public function updateExpress($order_express_id = 0)
  251. {
  252. $type = $this->request->param('type');
  253. // 获取包裹
  254. $orderExpress = OrderExpressModel::where('id', $order_express_id)->find();
  255. if (!$orderExpress) {
  256. $this->error('包裹不存在');
  257. }
  258. $expressLib = new ExpressLib();
  259. try {
  260. if ($type == 'subscribe') {
  261. // 重新订阅
  262. $expressLib->subscribe([
  263. 'express_code' => $orderExpress['express_code'],
  264. 'express_no' => $orderExpress['express_no']
  265. ]);
  266. } else {
  267. // 手动查询
  268. $result = $expressLib->search([
  269. 'order_id' => $orderExpress['order_id'],
  270. 'express_code' => $orderExpress['express_code'],
  271. 'express_no' => $orderExpress['express_no']
  272. ], $orderExpress);
  273. }
  274. } catch (HttpResponseException $e) {
  275. $data = $e->getResponse()->getData();
  276. $message = $data ? ($data['msg'] ?? '') : $e->getMessage();
  277. $this->error($message);
  278. } catch (\Exception $e) {
  279. $this->error(($type == 'subscribe' ? '订阅失败:' : '刷新失败:') . $e->getMessage());
  280. }
  281. $this->success(($type == 'subscribe' ? '订阅成功' : '刷新成功'));
  282. }
  283. /**
  284. * 线下付款,确认收货
  285. */
  286. public function offlineConfirm($id)
  287. {
  288. $admin = $this->auth->getUserInfo();
  289. $order = OrderModel::offline()->where('id', $id)->find();
  290. if (!$order) {
  291. $this->error(__('No Results were found'));
  292. }
  293. $order = Db::transaction(function () use ($order, $admin) {
  294. // 加锁读订单
  295. $order = $order->lock(true)->find($order->id);
  296. $user = User::get($order->user_id);
  297. $payOper = new PayOper($user);
  298. $order = $payOper->offline($order, $order->remain_pay_fee, 'order'); // 订单会变为已支付 paid
  299. // 触发订单支付完成事件
  300. $data = ['order' => $order, 'user' => $user];
  301. \think\Hook::listen('order_offline_paid_after', $data);
  302. $orderOper = new OrderOper();
  303. // 确认收货
  304. $order = $orderOper->confirm($order, [], $admin, 'admin');
  305. return $order;
  306. });
  307. $this->success('收货成功', $order);
  308. }
  309. /**
  310. * 线下付款,拒收
  311. */
  312. public function offlineRefuse($id)
  313. {
  314. $admin = $this->auth->getUserInfo();
  315. $order = OrderModel::offline()->where('id', $id)->find();
  316. if (!$order) {
  317. $this->error(__('No Results were found'));
  318. }
  319. $order = Db::transaction(function () use ($order, $admin) {
  320. // 加锁读订单
  321. $order = $order->lock(true)->find($order->id);
  322. // 拒收
  323. $orderOper = new OrderOper();
  324. $order = $orderOper->refuse($order, $admin, 'admin');
  325. // 交易关闭
  326. $order = $orderOper->close($order, $admin, 'admin', '用户拒绝收货,管理员关闭订单', ['closed_type' => 'refuse']);
  327. return $order;
  328. });
  329. $this->success('收货成功', $order);
  330. }
  331. /**
  332. * 订单改价,当剩余应支付金额为 0 时,订单将自动支付
  333. *
  334. * @param int $id
  335. * @return void
  336. */
  337. public function changeFee($id)
  338. {
  339. if (!$this->request->isAjax()) {
  340. return $this->view->fetch();
  341. }
  342. $admin = $this->auth->getUserInfo();
  343. $params = $this->request->param();
  344. $this->svalidate($params, '.change_fee');
  345. $order = $this->model->unpaid()->where('id', $id)->find();
  346. if (!$order) {
  347. $this->error('订单不可改价');
  348. }
  349. Db::transaction(function () use ($order, $admin, $params) {
  350. $pay_fee = $params['pay_fee'];
  351. $change_msg = $params['change_msg'];
  352. $payOper = new PayOper($order->user_id);
  353. $payed_fee = $payOper->getPayedFee($order, 'order');
  354. if ($pay_fee < $payed_fee) {
  355. $this->error('改价金额不能低于订单已支付金额');
  356. }
  357. // 记录原始值
  358. $last_pay_fee = $order->pay_fee; // 上次pay_fee,非原始 pay_fee
  359. $order->pay_fee = $pay_fee;
  360. $order->save();
  361. OrderActionModel::add($order, null, $admin, 'admin', "应支付金额由 ¥" . $last_pay_fee . " 改为 ¥" . $pay_fee . ",改价原因:" . $change_msg);
  362. // 检查订单支付状态, 改价可以直接将订单变为已支付
  363. $order = $payOper->checkAndPaid($order, 'order');
  364. });
  365. $this->success('改价成功');
  366. }
  367. /**
  368. * 修改收货人信息
  369. *
  370. * @param Request $request
  371. * @param integer $id
  372. * @return void
  373. */
  374. public function editConsignee($id)
  375. {
  376. $admin = $this->auth->getUserInfo();
  377. $params = $this->request->param();
  378. $this->svalidate($params, '.edit_consignee');
  379. $order = $this->model->withTrashed()->where('id', $id)->find();
  380. if (!$order) {
  381. $this->error(__('No Results were found'));
  382. }
  383. Db::transaction(function () use ($admin, $order, $params) {
  384. $orderAddress = OrderAddressModel::where('order_id', $order->id)->find();
  385. if (!$orderAddress) {
  386. $this->error(__('No Results were found'));
  387. }
  388. $orderAddress->consignee = $params['consignee'];
  389. $orderAddress->mobile = $params['mobile'];
  390. $orderAddress->province_name = $params['province_name'];
  391. $orderAddress->city_name = $params['city_name'];
  392. $orderAddress->district_name = $params['district_name'];
  393. $orderAddress->address = $params['address'];
  394. $orderAddress->province_id = $params['province_id'];
  395. $orderAddress->city_id = $params['city_id'];
  396. $orderAddress->district_id = $params['district_id'];
  397. $orderAddress->save();
  398. OrderActionModel::add($order, null, $admin, 'admin', "修改订单收货人信息");
  399. });
  400. $this->success('收货人信息修改成功');
  401. }
  402. /**
  403. * 编辑商家备注
  404. *
  405. * @param Request $request
  406. * @param integer $id
  407. * @return void
  408. */
  409. public function editMemo($id)
  410. {
  411. if (!$this->request->isAjax()) {
  412. return $this->view->fetch();
  413. }
  414. $admin = $this->auth->getUserInfo();
  415. $params = $this->request->param();
  416. $this->svalidate($params, '.edit_memo');
  417. $order = $this->model->withTrashed()->where('id', $id)->find();
  418. if (!$order) {
  419. $this->error(__('No Results were found'));
  420. }
  421. Db::transaction(function () use ($admin, $order, $params) {
  422. $order->memo = $params['memo'];
  423. $order->save();
  424. OrderActionModel::add($order, null, $admin, 'admin', "修改卖家备注:" . $params['memo']);
  425. });
  426. $this->success('卖家备注修改成功');
  427. }
  428. /**
  429. * 拒绝用户全额退款申请
  430. *
  431. * @param Request $request
  432. * @param integer $id
  433. * @return void
  434. */
  435. public function applyRefundRefuse($id)
  436. {
  437. $admin = $this->auth->getUserInfo();
  438. $params = $this->request->param();
  439. // $this->svalidate($params, '.apply_refund_refuse');
  440. $order = $this->model->withTrashed()->paid()->applyRefundIng()->where('id', $id)->find();
  441. if (!$order) {
  442. $this->error('订单未找到或不可拒绝申请');
  443. }
  444. Db::transaction(function () use ($admin, $order, $params) {
  445. $order->apply_refund_status = OrderModel::APPLY_REFUND_STATUS_REFUSE;
  446. $order->save();
  447. OrderActionModel::add($order, null, $admin, 'admin', "拒绝用户申请全额退款");
  448. });
  449. $this->success('拒绝申请成功');
  450. }
  451. /**
  452. * 全额退款 (必须没有进行过任何退款才能使用)
  453. *
  454. * @param Request $request
  455. * @param integer $id
  456. * @return void
  457. */
  458. public function fullRefund($id)
  459. {
  460. if (!$this->request->isAjax()) {
  461. return $this->view->fetch();
  462. }
  463. $admin = $this->auth->getUserInfo();
  464. $admin = Admin::where('id', $admin['id'])->find();
  465. $params = $this->request->param();
  466. $refund_type = $params['refund_type'] ?? 'back';
  467. Db::transaction(function () use ($admin, $id, $refund_type) {
  468. $order = $this->model->paid()->where('id', $id)->lock(true)->find();
  469. if (!$order) {
  470. $this->error('订单不存在或不可退款');
  471. }
  472. $orderRefund = new OrderRefund($order);
  473. $orderRefund->fullRefund($admin, [
  474. 'refund_type' => $refund_type,
  475. 'remark' => '平台主动全额退款'
  476. ]);
  477. });
  478. $this->success('全额退款成功');
  479. }
  480. /**
  481. * 订单单商品退款
  482. *
  483. * @param Request $request
  484. * @param integer $id
  485. * @param integer $item_id
  486. * @return void
  487. */
  488. public function refund($id, $item_id)
  489. {
  490. if (!$this->request->isAjax()) {
  491. return $this->view->fetch();
  492. }
  493. $admin = $this->auth->getUserInfo();
  494. $params = $this->request->param();
  495. $this->svalidate($params, '.refund');
  496. $refund_money = round(floatval($params['refund_money']), 2);
  497. $refund_type = $params['refund_type'] ?? 'back';
  498. if ($refund_money <= 0) {
  499. $this->error('请输入正确的退款金额');
  500. }
  501. $order = $this->model->paid()->where('id', $id)->find();
  502. if (!$order) {
  503. $this->error('订单不存在或不可退款');
  504. }
  505. $item = OrderItem::where('order_id', $order->id)->where('id', $item_id)->find();
  506. if (!$item) {
  507. $this->error(__('No Results were found'));
  508. }
  509. if (in_array($item['refund_status'], [
  510. OrderItem::REFUND_STATUS_AGREE,
  511. OrderItem::REFUND_STATUS_COMPLETED,
  512. ])) {
  513. $this->error('订单商品已退款,不能重复退款');
  514. }
  515. $payOper = new PayOper($order->user_id);
  516. // 获取订单最大可退款金额(不含积分抵扣金额)
  517. $remain_max_refund_money = $payOper->getRemainRefundMoney($order->id);
  518. // 如果退款金额大于订单支付总金额
  519. if (bccomp((string)$refund_money, $remain_max_refund_money, 2) === 1) {
  520. $this->error('退款总金额不能大于实际支付金额');
  521. }
  522. Db::transaction(function () use ($admin, $order, $item, $refund_money, $refund_type) {
  523. // 重新锁定读查询 orderItem
  524. $item = OrderItem::where('order_id', $order->id)->lock(true)->where('id', $item->id)->find();
  525. if (!$item) {
  526. $this->error('订单不存在或不可退款');
  527. }
  528. $orderRefund = new OrderRefund($order);
  529. $orderRefund->refund($item, $refund_money, $admin, [
  530. 'refund_type' => $refund_type,
  531. 'remark' => '平台主动退款'
  532. ]);
  533. });
  534. $this->success('退款成功');
  535. }
  536. /**
  537. * 获取订单操作记录
  538. *
  539. * @param integer $id
  540. * @return void
  541. */
  542. public function action($id)
  543. {
  544. if (!$this->request->isAjax()) {
  545. return $this->view->fetch();
  546. }
  547. $actions = OrderActionModel::where('order_id', $id)->order('id', 'desc')->select();
  548. $morphs = [
  549. 'user' => \app\admin\model\shopro\user\User::class,
  550. 'admin' => \app\admin\model\Admin::class,
  551. 'system' => \app\admin\model\Admin::class,
  552. ];
  553. $actions = morph_to($actions, $morphs, ['oper_type', 'oper_id']);
  554. foreach ($actions as &$action) {
  555. $action['oper'] = Operator::info($action['oper_type'], $action['oper'] ?? null);
  556. }
  557. $this->success('获取成功', null, $actions);
  558. }
  559. public function export()
  560. {
  561. $cellTitles = [
  562. // 订单表字段
  563. 'order_id' => 'Id',
  564. 'order_sn' => '订单号',
  565. 'type_text' => '订单类型',
  566. 'user_nickname' => '下单用户',
  567. 'user_mobile' => '手机号',
  568. 'status_text' => '订单状态',
  569. 'pay_text' => '支付状态',
  570. 'pay_types_text' => '支付类型',
  571. 'remark' => '用户备注',
  572. 'memo' => '卖家备注',
  573. 'order_amount' => '订单总金额',
  574. 'score_amount' => '积分支付数量',
  575. 'dispatch_amount' => '运费',
  576. 'pay_fee' => '应付总金额',
  577. 'real_pay_fee' => '实付总金额',
  578. 'remain_pay_fee' => '剩余支付金额',
  579. 'total_discount_fee' => '总优惠金额',
  580. 'coupon_discount_fee' => '优惠券金额',
  581. 'promo_discount_fee' => '营销优惠金额',
  582. 'paid_time' => '支付完成时间',
  583. 'platform_text' => '交易平台',
  584. 'consignee_info' => '收货信息',
  585. 'createtime' => '下单时间',
  586. // 订单商品表字段
  587. 'activity_type_text' => '活动',
  588. 'promo_types' => '促销',
  589. 'goods_title' => '商品名称',
  590. 'goods_sku_text' => '商品规格',
  591. 'goods_num' => '购买数量',
  592. 'goods_original_price' => '商品原价',
  593. 'goods_price' => '商品价格',
  594. 'goods_weight' => '商品重量',
  595. 'discount_fee' => '优惠金额',
  596. 'goods_pay_fee' => '商品支付金额',
  597. 'dispatch_type_text' => '发货方式',
  598. 'dispatch_status_text' => '发货状态',
  599. 'aftersale_refund' => '售后/退款',
  600. 'comment_status_text' => '评价状态',
  601. 'refund_fee' => '退款金额',
  602. 'refund_msg' => '退款原因',
  603. 'express_name' => '快递公司',
  604. 'express_no' => '快递单号',
  605. ];
  606. // 数据总条数
  607. $total = $this->model->withTrashed()->sheepFilter()->count();
  608. if ($total <= 0) {
  609. $this->error('导出数据为空');
  610. }
  611. $export = new \addons\shopro\library\Export();
  612. $params = [
  613. 'file_name' => '订单列表',
  614. 'cell_titles' => $cellTitles,
  615. 'total' => $total,
  616. 'is_sub_cell' => true,
  617. 'sub_start_cell' => 'activity_type_text',
  618. 'sub_field' => 'items'
  619. ];
  620. $total_order_amount = 0;
  621. $total_pay_fee = 0;
  622. $total_real_pay_fee = 0;
  623. $total_discount_fee = 0;
  624. $total_score_amount = 0;
  625. $result = $export->export($params, function ($pages) use (&$total_order_amount, &$total_pay_fee, &$total_real_pay_fee, &$total_discount_fee, &$total_score_amount, $total) {
  626. $datas = $this->model->withTrashed()->sheepFilter()->with(['user', 'items' => function ($query) {
  627. $query->with(['express']);
  628. }, 'address'])
  629. ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows'])
  630. ->select();
  631. $datas = collection($datas);
  632. $datas = $datas->each(function ($order) {
  633. $order->pay_types_text = $order->pay_types_text;
  634. })->toArray();
  635. $newDatas = [];
  636. foreach ($datas as &$order) {
  637. $order = $this->model->setOrderItemStatusByOrder($order);
  638. // 收货人信息
  639. $consignee_info = '';
  640. if ($order['address']) {
  641. $address = $order['address'];
  642. $consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address'];
  643. }
  644. $data = [
  645. 'order_id' => $order['id'],
  646. 'order_sn' => $order['order_sn'],
  647. 'type_text' => $order['type_text'],
  648. 'user_nickname' => $order['user'] ? $order['user']['nickname'] : '-',
  649. 'user_mobile' => $order['user'] ? $order['user']['mobile'] . ' ' : '-',
  650. 'status_text' => $order['status_text'],
  651. 'pay_text' => in_array($order['status'], [OrderModel::STATUS_PAID, OrderModel::STATUS_COMPLETED]) ? '已支付' : '未支付',
  652. 'pay_types_text' => is_array($order['pay_types_text']) ? join(',', $order['pay_types_text']) : ($order['pay_types_text'] ?: ''),
  653. 'remark' => $order['remark'],
  654. 'memo' => $order['memo'],
  655. 'order_amount' => $order['order_amount'],
  656. 'score_amount' => $order['score_amount'],
  657. 'dispatch_amount' => $order['dispatch_amount'],
  658. 'pay_fee' => $order['pay_fee'],
  659. 'real_pay_fee' => bcsub($order['pay_fee'], $order['remain_pay_fee'], 2),
  660. 'remain_pay_fee' => $order['remain_pay_fee'],
  661. 'total_discount_fee' => $order['total_discount_fee'],
  662. 'coupon_discount_fee' => $order['coupon_discount_fee'],
  663. 'promo_discount_fee' => $order['promo_discount_fee'],
  664. 'paid_time' => $order['paid_time'],
  665. 'platform_text' => $order['platform_text'],
  666. 'consignee_info' => $consignee_info,
  667. 'createtime' => $order['createtime'],
  668. ];
  669. $items = [];
  670. foreach ($order['items'] as $item) {
  671. $items[] = [
  672. 'activity_type_text' => $item['activity_type_text'],
  673. 'promo_types_text' => is_array($item['promo_types_text']) ? join(',', $item['promo_types_text']) : ($item['promo_types_text'] ?: '-'),
  674. 'goods_title' => $item['goods_title'],
  675. 'goods_sku_text' => $item['goods_sku_text'],
  676. 'goods_num' => $item['goods_num'],
  677. 'goods_original_price' => $item['goods_original_price'],
  678. 'goods_price' => $item['goods_price'],
  679. 'goods_weight' => $item['goods_weight'],
  680. 'discount_fee' => $item['discount_fee'],
  681. 'goods_pay_fee' => $item['pay_fee'],
  682. 'dispatch_type_text' => $item['dispatch_type_text'],
  683. 'dispatch_status_text' => $item['dispatch_status_text'],
  684. 'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'],
  685. 'comment_status_text' => $item['comment_status_text'],
  686. 'refund_fee' => $item['refund_fee'],
  687. 'refund_msg' => $item['refund_msg'],
  688. 'express_name' => $item['express'] ? $item['express']['express_name'] : '-',
  689. 'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '-',
  690. ];
  691. }
  692. $data['items'] = $items;
  693. $newDatas[] = $data;
  694. }
  695. $total_order_amount += array_sum(array_column($newDatas, 'order_amount'));
  696. $total_score_amount += array_sum(array_column($newDatas, 'score_amount'));
  697. $total_pay_fee += array_sum(array_column($newDatas, 'pay_fee'));
  698. $total_real_pay_fee += array_sum(array_column($newDatas, 'real_pay_fee'));
  699. $total_discount_fee += array_sum(array_column($newDatas, 'discount_fee'));
  700. if ($pages['is_last_page']) {
  701. $newDatas[] = ['order_id' => "订单总数:" . $total . ";订单总金额:¥" . $total_order_amount . ";优惠总金额:¥" . $total_discount_fee . ";应付总金额:¥" . $total_pay_fee . ";实付总金额:¥" . $total_real_pay_fee . ";支付总积分:" . $total_score_amount];
  702. }
  703. return $newDatas;
  704. });
  705. $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result);
  706. }
  707. public function exportDelivery()
  708. {
  709. $cellTitles = [
  710. // 订单表字段
  711. 'order_id' => 'Id',
  712. 'order_sn' => '订单号',
  713. 'type_text' => '订单类型',
  714. 'consignee_info' => '收货信息',
  715. 'remark' => '用户备注',
  716. 'memo' => '商家备注',
  717. 'createtime' => '下单时间',
  718. // 订单商品表字段
  719. 'order_item_id' => '子订单Id',
  720. 'goods_title' => '商品名称',
  721. 'goods_sku_text' => '商品规格',
  722. 'goods_num' => '购买数量',
  723. // 'dispatch_fee' => '发货费用',
  724. 'dispatch_type_text' => '发货方式',
  725. 'dispatch_status_text' => '发货状态',
  726. 'aftersale_refund' => '售后/退款',
  727. 'express_no' => '快递单号',
  728. ];
  729. // 数据总条数
  730. $total = $this->model->sheepFilter()->count(); // nosend 加了 noApplyRefund
  731. if ($total <= 0) {
  732. $this->error('导出数据为空');
  733. }
  734. $export = new \addons\shopro\library\Export();
  735. $params = [
  736. 'file_name' => '订单发货单列表',
  737. 'cell_titles' => $cellTitles,
  738. 'total' => $total,
  739. 'is_sub_cell' => true,
  740. 'sub_start_cell' => 'order_item_id',
  741. 'sub_field' => 'items'
  742. ];
  743. $result = $export->export($params, function ($pages) use (&$total) {
  744. // 未申请全额退款的
  745. $datas = $this->model->sheepFilter()->with(['user', 'items' => function ($query) { // nosend 加了 noApplyRefund
  746. $query->with(['express']);
  747. }, 'address'])
  748. ->limit((($pages['page'] - 1) * $pages['list_rows']), $pages['list_rows'])
  749. ->select();
  750. $datas = collection($datas)->toArray();
  751. $newDatas = [];
  752. foreach ($datas as &$order) {
  753. $order = $this->model->setOrderItemStatusByOrder($order);
  754. if (in_array($order['status_code'], ['groupon_ing', 'groupon_invalid'])) {
  755. // 拼团正在进行中,不发货
  756. $total--; // total 减少 1
  757. continue;
  758. }
  759. // 收货人信息
  760. $consignee_info = '';
  761. if ($order['address']) {
  762. $address = $order['address'];
  763. $consignee_info = ($address['consignee'] ? ($address['consignee'] . ':' . $address['mobile'] . '-') : '') . ($address['province_name'] . '-' . $address['city_name'] . '-' . $address['district_name']) . ' ' . $address['address'];
  764. }
  765. $data = [
  766. 'order_id' => $order['id'],
  767. 'order_sn' => $order['order_sn'],
  768. 'type_text' => $order['type_text'],
  769. 'consignee_info' => $consignee_info,
  770. 'remark' => $order['remark'],
  771. 'memo' => $order['memo'],
  772. 'createtime' => $order['createtime']
  773. ];
  774. $items = [];
  775. foreach ($order['items'] as $k => $item) {
  776. // 未发货,并且未退款,并且未在申请售后中,并且是快递物流的
  777. if (
  778. $item['dispatch_status'] == OrderItem::DISPATCH_STATUS_NOSEND
  779. && !in_array($item['refund_status'], [OrderItem::REFUND_STATUS_AGREE, OrderItem::REFUND_STATUS_COMPLETED])
  780. && $item['aftersale_status'] != OrderItem::AFTERSALE_STATUS_ING
  781. && $item['dispatch_type'] == 'express'
  782. ) {
  783. $items[] = [
  784. 'order_item_id' => $item['id'],
  785. 'goods_title' => strpos($item['goods_title'], '=') === 0 ? ' ' . $item['goods_title'] : $item['goods_title'],
  786. 'goods_sku_text' => $item['goods_sku_text'],
  787. 'goods_num' => $item['goods_num'],
  788. // 'dispatch_fee' => $item['dispatch_fee'],
  789. 'dispatch_type_text' => $item['dispatch_type_text'],
  790. 'dispatch_status_text' => $item['dispatch_status_text'],
  791. 'aftersale_refund' => $item['aftersale_status_text'] . '/' . $item['refund_status_text'],
  792. 'express_no' => $item['express'] ? $item['express']['express_no'] . ' ' : '',
  793. ];
  794. }
  795. }
  796. $data['items'] = $items;
  797. $newDatas[] = $data;
  798. };
  799. if ($pages['is_last_page']) {
  800. $newDatas[] = ['order_id' => "订单总数(仅快递物流的待发货订单):" . $total . ";备注:订单中同一包裹请填写相同运单号"];
  801. }
  802. return $newDatas;
  803. });
  804. $this->success('导出成功' . (isset($result['file_path']) && $result['file_path'] ? ',请在服务器: “' . $result['file_path'] . '” 查看' : ''), null, $result);
  805. }
  806. }