You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

692 lines
25 KiB

  1. <?php
  2. namespace app\admin\controller\unishop;
  3. use addons\nzf\PayService;
  4. use addons\unishop\extend\Hashids;
  5. use addons\unishop\extend\Redis;
  6. use app\admin\model\unishop\Area;
  7. use app\admin\model\unishop\OrderProduct;
  8. use app\admin\model\unishop\OrderRefund;
  9. use app\common\controller\Backend;
  10. use think\Db;
  11. use think\Exception;
  12. use think\exception\PDOException;
  13. use think\exception\ValidateException;
  14. use think\Hook;
  15. /**
  16. * 订单管理
  17. *
  18. * @icon fa fa-circle-o
  19. */
  20. class Order extends Backend
  21. {
  22. /**
  23. * 是否是关联查询
  24. */
  25. protected $relationSearch = true;
  26. protected $noNeedLogin=["export","finish","doRefund"];
  27. /**
  28. * Order模型对象
  29. * @var \app\admin\model\unishop\Order
  30. */
  31. protected $model = null;
  32. public function _initialize()
  33. {
  34. parent::_initialize();
  35. $this->model = new \app\admin\model\unishop\Order;
  36. $this->view->assign("payTypeList", $this->model->getPayTypeList());
  37. $this->view->assign("statusList", $this->model->getStatusList());
  38. $this->view->assign("refundStatusList", $this->model->getRefundStatusList());
  39. }
  40. /**
  41. * 查看
  42. */
  43. public function index()
  44. {
  45. //设置过滤方法
  46. $this->request->filter(['strip_tags']);
  47. if ($this->request->isAjax()) {
  48. //如果发送的来源是Selectpage,则转发到Selectpage
  49. if ($this->request->request('keyField')) {
  50. return $this->selectpage();
  51. }
  52. list($where, $sort, $order, $offset, $limit) = $this->buildparams();
  53. $total = $this->model
  54. ->alias('order')
  55. ->join('user', 'user.id = order.user_id')
  56. ->where($where)
  57. ->count();
  58. $list = $this->model
  59. ->alias('order')
  60. ->join('user', 'user.id = order.user_id')
  61. ->where($where)
  62. ->order($sort, $order)
  63. ->limit($offset, $limit)
  64. ->field('order.*,user.username')
  65. ->select();
  66. $list = collection($list)->toArray();
  67. foreach ($list as &$item) {
  68. $item['id'] = (string)$item['id']; // 整形数字太大js会失准
  69. $item['user'] = [];
  70. $item['user']['username'] = $item['username'] ? $item['username'] : __('Tourist');
  71. $item['have_paid_status'] = $item['have_paid'];
  72. $item['have_delivered_status'] = $item['have_delivered'];
  73. $item['have_received_status'] = $item['have_received'];
  74. $item['have_commented_status'] = $item['have_commented'];
  75. }
  76. $result = array("total" => $total, "rows" => $list);
  77. return json($result);
  78. }
  79. return $this->view->fetch();
  80. }
  81. /**
  82. * 生成查询所需要的条件,排序方式
  83. * @param mixed $searchfields 快速查询的字段
  84. * @param boolean $relationSearch 是否关联查询
  85. * @return array
  86. */
  87. protected function buildparams($searchfields = null, $relationSearch = null)
  88. {
  89. $searchfields = is_null($searchfields) ? $this->searchFields : $searchfields;
  90. $relationSearch = is_null($relationSearch) ? $this->relationSearch : $relationSearch;
  91. $search = $this->request->get("search", '');
  92. $filter = $this->request->get("filter", '');
  93. $op = $this->request->get("op", '', 'trim');
  94. $sort = $this->request->get("sort", "id");
  95. $order = $this->request->get("order", "DESC");
  96. $offset = $this->request->get("offset", 0);
  97. $limit = $this->request->get("limit", 0);
  98. $filter = (array)json_decode($filter, true);
  99. $op = (array)json_decode($op, true);
  100. $filter = $filter ? $filter : [];
  101. $where = [];
  102. $tableName = '';
  103. if ($relationSearch) {
  104. if (!empty($this->model)) {
  105. $name = \think\Loader::parseName(basename(str_replace('\\', '/', get_class($this->model))));
  106. $tableName = '' . $name . '.';
  107. }
  108. $sortArr = explode(',', $sort);
  109. foreach ($sortArr as $index => & $item) {
  110. $item = stripos($item, ".") === false ? $tableName . trim($item) : $item;
  111. }
  112. unset($item);
  113. $sort = implode(',', $sortArr);
  114. }
  115. $adminIds = $this->getDataLimitAdminIds();
  116. if (is_array($adminIds)) {
  117. $where[] = [$tableName . $this->dataLimitField, 'in', $adminIds];
  118. }
  119. if ($search) {
  120. $searcharr = is_array($searchfields) ? $searchfields : explode(',', $searchfields);
  121. foreach ($searcharr as $k => &$v) {
  122. $v = stripos($v, ".") === false ? $tableName . $v : $v;
  123. }
  124. unset($v);
  125. $where[] = [implode("|", $searcharr), "LIKE", "%{$search}%"];
  126. }
  127. foreach ($filter as $k => $v) {
  128. // 搜索订单状态
  129. if (in_array($k, ['have_paid_status', 'have_delivered_status', 'have_received_status', 'have_commented_status'])) {
  130. switch ($k) {
  131. case 'have_paid_status':
  132. $k = 'have_paid';
  133. break;
  134. case 'have_delivered_status':
  135. $k = 'have_delivered';
  136. break;
  137. case 'have_received_status':
  138. $k = 'have_received';
  139. break;
  140. case 'have_commented_status':
  141. $k = 'have_commented';
  142. break;
  143. }
  144. $v == 0 ? ($op[$k] = '=') : ($op[$k] = '>');
  145. $v = 0;
  146. }
  147. $sym = isset($op[$k]) ? $op[$k] : '=';
  148. if (stripos($k, ".") === false) {
  149. $k = $tableName . $k;
  150. }
  151. $v = !is_array($v) ? trim($v) : $v;
  152. $sym = strtoupper(isset($op[$k]) ? $op[$k] : $sym);
  153. switch ($sym) {
  154. case '=':
  155. case '<>':
  156. $where[] = [$k, $sym, (string)$v];
  157. break;
  158. case 'LIKE':
  159. case 'NOT LIKE':
  160. case 'LIKE %...%':
  161. case 'NOT LIKE %...%':
  162. $where[] = [$k, trim(str_replace('%...%', '', $sym)), "%{$v}%"];
  163. break;
  164. case '>':
  165. case '>=':
  166. case '<':
  167. case '<=':
  168. $where[] = [$k, $sym, intval($v)];
  169. break;
  170. case 'FINDIN':
  171. case 'FINDINSET':
  172. case 'FIND_IN_SET':
  173. $where[] = "FIND_IN_SET('{$v}', " . ($relationSearch ? $k : '`' . str_replace('.', '`.`', $k) . '`') . ")";
  174. break;
  175. case 'IN':
  176. case 'IN(...)':
  177. case 'NOT IN':
  178. case 'NOT IN(...)':
  179. $where[] = [$k, str_replace('(...)', '', $sym), is_array($v) ? $v : explode(',', $v)];
  180. break;
  181. case 'BETWEEN':
  182. case 'NOT BETWEEN':
  183. $arr = array_slice(explode(',', $v), 0, 2);
  184. if (stripos($v, ',') === false || !array_filter($arr)) {
  185. continue 2;
  186. }
  187. //当出现一边为空时改变操作符
  188. if ($arr[0] === '') {
  189. $sym = $sym == 'BETWEEN' ? '<=' : '>';
  190. $arr = $arr[1];
  191. } elseif ($arr[1] === '') {
  192. $sym = $sym == 'BETWEEN' ? '>=' : '<';
  193. $arr = $arr[0];
  194. }
  195. $where[] = [$k, $sym, $arr];
  196. break;
  197. case 'RANGE':
  198. case 'NOT RANGE':
  199. $v = str_replace(' - ', ',', $v);
  200. $arr = array_slice(explode(',', $v), 0, 2);
  201. if (stripos($v, ',') === false || !array_filter($arr)) {
  202. continue 2;
  203. }
  204. //当出现一边为空时改变操作符
  205. if ($arr[0] === '') {
  206. $sym = $sym == 'RANGE' ? '<=' : '>';
  207. $arr = $arr[1];
  208. } elseif ($arr[1] === '') {
  209. $sym = $sym == 'RANGE' ? '>=' : '<';
  210. $arr = $arr[0];
  211. }
  212. $where[] = [$k, str_replace('RANGE', 'BETWEEN', $sym) . ' time', $arr];
  213. break;
  214. case 'LIKE':
  215. case 'LIKE %...%':
  216. $where[] = [$k, 'LIKE', "%{$v}%"];
  217. break;
  218. case 'NULL':
  219. case 'IS NULL':
  220. case 'NOT NULL':
  221. case 'IS NOT NULL':
  222. $where[] = [$k, strtolower(str_replace('IS ', '', $sym))];
  223. break;
  224. default:
  225. break;
  226. }
  227. }
  228. $where = function ($query) use ($where) {
  229. foreach ($where as $k => $v) {
  230. if (is_array($v)) {
  231. call_user_func_array([$query, 'where'], $v);
  232. } else {
  233. $query->where($v);
  234. }
  235. }
  236. };
  237. return [$where, $sort, $order, $offset, $limit];
  238. }
  239. /**
  240. * 编辑
  241. */
  242. public function edit($ids = null)
  243. {
  244. $row = $this->model->get($ids);
  245. if (!$row) {
  246. $this->error(__('No Results were found'));
  247. }
  248. $adminIds = $this->getDataLimitAdminIds();
  249. if (is_array($adminIds)) {
  250. if (!in_array($row[$this->dataLimitField], $adminIds)) {
  251. $this->error(__('You have no permission'));
  252. }
  253. }
  254. if ($this->request->isPost()) {
  255. $params = $this->request->post("row/a");
  256. if ($params) {
  257. $params = $this->preExcludeFields($params);
  258. $result = false;
  259. Db::startTrans();
  260. try {
  261. //是否采用模型验证
  262. if ($this->modelValidate) {
  263. $name = str_replace("\\model\\", "\\validate\\", get_class($this->model));
  264. $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate;
  265. $row->validateFailException(true)->validate($validate);
  266. }
  267. $updatetime = $this->request->post('updatetime');
  268. // 乐观锁
  269. $result = $this->model->allowField(true)->save($params, ['id' => $ids, 'updatetime' => $updatetime]);
  270. if (!$result) {
  271. throw new Exception(__('Data had been update before saved, close windows and do it again'));
  272. }
  273. Db::commit();
  274. } catch (ValidateException $e) {
  275. Db::rollback();
  276. $this->error($e->getMessage());
  277. } catch (PDOException $e) {
  278. Db::rollback();
  279. $this->error($e->getMessage());
  280. } catch (Exception $e) {
  281. Db::rollback();
  282. $this->error($e->getMessage());
  283. }
  284. if ($result !== false) {
  285. $this->success();
  286. } else {
  287. $this->error(__('No rows were updated'));
  288. }
  289. }
  290. $this->error(__('Parameter %s can not be empty', ''));
  291. }
  292. $this->view->assign("row", $row);
  293. return $this->view->fetch();
  294. }
  295. /**
  296. * 物流管理
  297. */
  298. public function delivery($ids = null)
  299. {
  300. $row = $this->model->get($ids, ['extend']);
  301. if (!$row) {
  302. $this->error(__('No Results were found'));
  303. }
  304. $adminIds = $this->getDataLimitAdminIds();
  305. if (is_array($adminIds)) {
  306. if (!in_array($row[$this->dataLimitField], $adminIds)) {
  307. $this->error(__('You have no permission'));
  308. }
  309. }
  310. if ($this->request->isPost()) {
  311. $result = false;
  312. Db::startTrans();
  313. try {
  314. $express_number = $this->request->post('express_number');
  315. $have_delivered = $express_number ? time() : 0;
  316. $res1 = $row->allowField(true)->save(['have_delivered' => $have_delivered]);
  317. $res2 = $row->extend->allowField(true)->save(['express_number' => $express_number]);
  318. if ($res1 && $res2) {
  319. $result = true;
  320. } else {
  321. throw new Exception(__('No rows were updated'));
  322. }
  323. Db::commit();
  324. } catch (ValidateException $e) {
  325. Db::rollback();
  326. $this->error($e->getMessage());
  327. } catch (PDOException $e) {
  328. Db::rollback();
  329. $this->error($e->getMessage());
  330. } catch (Exception $e) {
  331. Db::rollback();
  332. $this->error($e->getMessage());
  333. }
  334. if ($result !== false) {
  335. $this->success();
  336. } else {
  337. $this->error(__('No rows were updated'));
  338. }
  339. $this->error(__('Parameter %s can not be empty', ''));
  340. }
  341. $address = json_decode($row->extend->address_json,true);
  342. if ($address) {
  343. $area = (new Area)->whereIn('id',[$address['province_id'],$address['city_id'],$address['area_id']])->column('name', 'id');
  344. $row['addressText'] = $area[$address['province_id']].$area[$address['city_id']].$area[$address['area_id']].' '.$address['address'];
  345. $row['address'] = $address;
  346. }
  347. $this->view->assign("row", $row);
  348. return $this->view->fetch();
  349. }
  350. /**
  351. * 商品管理
  352. */
  353. public function product($ids = null)
  354. {
  355. if ($this->request->isPost()) {
  356. $this->success();
  357. }
  358. $row = $this->model->get($ids, ['product','evaluate']);
  359. $this->view->assign('product', $row->product);
  360. $evaluate = [];
  361. foreach ($row->evaluate as $key => $item) {
  362. $evaluate[$item['product_id']] = $item;
  363. }
  364. $this->view->assign('order', $row);
  365. $this->view->assign('evaluate', $evaluate);
  366. return $this->view->fetch();
  367. }
  368. /**
  369. * 退货管理
  370. */
  371. public function refund($ids = null)
  372. {
  373. $row = $this->model->get($ids, ['refund']);
  374. if ($row['status'] != \app\admin\model\unishop\Order::STATUS_REFUND) {
  375. $this->error(__('This order is not returned'));
  376. }
  377. if ($this->request->isPost()) {
  378. $params = $this->request->post("row/a");
  379. if ($params) {
  380. $params = $this->preExcludeFields($params);
  381. $result = false;
  382. Db::startTrans();
  383. try {
  384. // 退款
  385. if($params['refund_action'] == 1) {
  386. $params['had_refund'] = time();
  387. Hook::add('order_refund', 'addons\\unishop\\behavior\\Order');
  388. }
  389. $updatetime = $this->request->post('updatetime');
  390. // 乐观锁
  391. $result = $this->model->allowField(true)->save($params, ['id' => $ids, 'updatetime' => $updatetime]);
  392. if (!$result) {
  393. throw new Exception(__('Data had been update before saved, close windows and do it again'));
  394. }
  395. Db::commit();
  396. } catch (ValidateException $e) {
  397. Db::rollback();
  398. $this->error($e->getMessage());
  399. } catch (PDOException $e) {
  400. Db::rollback();
  401. $this->error($e->getMessage());
  402. } catch (Exception $e) {
  403. Db::rollback();
  404. $this->error($e->getMessage());
  405. }
  406. if ($result !== false) {
  407. Hook::listen('order_refund', $row);
  408. $this->success();
  409. } else {
  410. $this->error(__('No rows were updated'));
  411. }
  412. }
  413. $this->error(__('Parameter %s can not be empty', ''));
  414. }
  415. $products = $row->product;
  416. $refundProducts = $row->refundProduct;
  417. foreach ($products as &$product) {
  418. $product['choose'] = 0;
  419. foreach ($refundProducts as $refundProduct) {
  420. if ($product['id'] == $refundProduct['order_product_id']) {
  421. $product['choose'] = 1;
  422. }
  423. }
  424. }
  425. if ($row->refund) {
  426. $refund = $row->refund->append(['receiving_status_text', 'service_type_text'])->toArray();
  427. } else {
  428. $refund = [
  429. 'service_type' => 0,
  430. 'express_number' => -1,
  431. 'receiving_status_text' => -1,
  432. 'receiving_status' => -1,
  433. 'service_type_text' => -1,
  434. 'amount' => -1,
  435. 'reason_type' => -1,
  436. 'refund_explain' => -1,
  437. ];
  438. }
  439. $this->view->assign('row', $row);
  440. $this->view->assign('product', $products);
  441. $this->view->assign('refund', $refund);
  442. return $this->view->fetch();
  443. }
  444. /**
  445. * 回收站
  446. */
  447. public function recyclebin()
  448. {
  449. //设置过滤方法
  450. $this->request->filter(['strip_tags']);
  451. if ($this->request->isAjax()) {
  452. list($where, $sort, $order, $offset, $limit) = $this->buildparams();
  453. $total = $this->model
  454. ->onlyTrashed()
  455. ->alias('order')
  456. ->join('user', 'user.id = order.user_id')
  457. ->where($where)
  458. ->count();
  459. $list = $this->model
  460. ->onlyTrashed()
  461. ->alias('order')
  462. ->join('user', 'user.id = order.user_id')
  463. ->where($where)
  464. ->field('order.*,user.username')
  465. ->order($sort, $order)
  466. ->limit($offset, $limit)
  467. ->select();
  468. $list = collection($list)->toArray();
  469. foreach ($list as &$item) {
  470. $item['id'] = (string)$item['id'];
  471. $item['user'] = [];
  472. $item['user']['username'] = $item['username'] ? $item['username'] : __('Tourist');
  473. $item['have_paid_status'] = $item['have_paid'];
  474. $item['have_delivered_status'] = $item['have_delivered'];
  475. $item['have_received_status'] = $item['have_received'];
  476. $item['have_commented_status'] = $item['have_commented'];
  477. }
  478. $result = array("total" => $total, "rows" => $list);
  479. return json($result);
  480. }
  481. return $this->view->fetch();
  482. }
  483. public function export(){
  484. $order_model=New \app\admin\model\unishop\Order();
  485. $list = $order_model
  486. ->alias('o')
  487. ->join('user', 'user.id = o.user_id')
  488. ->join('unishop_order_product', 'unishop_order_product.order_id = o.id')
  489. ->where([
  490. 'o.have_received'=>0,
  491. 'o.status'=>1
  492. ])
  493. ->field('
  494. user.username,
  495. user.nickname,
  496. user.floor,
  497. user.email,
  498. GROUP_CONCAT(unishop_order_product.title) as title,
  499. o.total_price,
  500. SUM(unishop_order_product.number) as number,
  501. FROM_UNIXTIME(o.createtime,\'%Y-%m-%d %H:%i:%S\'),
  502. o.remark')
  503. ->group("o.id")
  504. ->select();
  505. $list = collection($list)->toArray();
  506. $title = ['工号','姓名','楼层','邮箱','商品','金额','购买数量','下单时间','备注'];
  507. // Create new Spreadsheet object
  508. $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
  509. $sheet = $spreadsheet->getActiveSheet();
  510. $sheet->setTitle("订单信息");
  511. // 方法一,使用 setCellValueByColumnAndRow
  512. //表头
  513. //设置单元格内容
  514. foreach ($title as $key => $value) {
  515. // 单元格内容写入
  516. $sheet->setCellValueByColumnAndRow($key + 1, 1, $value);
  517. }
  518. $row = 2; // 从第二行开始
  519. foreach ($list as $item) {
  520. $column = 1;
  521. foreach ($item as $value) {
  522. // 单元格内容写入
  523. $sheet->setCellValueByColumnAndRow($column, $row, $value);
  524. $column++;
  525. }
  526. $row++;
  527. }
  528. $sheet_two = $spreadsheet->createSheet(2)->setTitle('商品信息');
  529. $title1=["商品名称","件数","单价","总价"];
  530. $product=new \app\admin\model\unishop\OrderProduct();
  531. $product_list = $product->alias("p")
  532. ->join("unishop_order","p.order_id = unishop_order.id")
  533. ->where([
  534. 'unishop_order.have_received'=>0,
  535. 'unishop_order.status'=>1
  536. ])
  537. ->field('
  538. p.title,
  539. SUM(p.number) num,
  540. p.price,
  541. SUM(p.number)*p.price as total,
  542. p.product_id')
  543. ->group("p.id")
  544. ->select();
  545. $product_list = collection($product_list)->toArray();
  546. $product_arr=[];
  547. foreach ($product_list as $tmp_product){
  548. if (isset($product_arr[$tmp_product['product_id']])){
  549. $product_arr[$tmp_product['product_id']]["num"]+=$tmp_product['num'];
  550. $product_arr[$tmp_product['product_id']]["total"]+=$tmp_product['total'];
  551. }else{
  552. $product_arr[$tmp_product['product_id']]=$tmp_product;
  553. }
  554. }
  555. foreach ($title1 as $key => $value) {
  556. // 单元格内容写入
  557. $sheet_two->setCellValueByColumnAndRow($key + 1, 1, $value);
  558. }
  559. $row=2;
  560. foreach ($product_arr as $item) {
  561. unset($item['product_id']);
  562. $column = 1;
  563. foreach ($item as $value) {
  564. // 单元格内容写入
  565. $sheet_two->setCellValueByColumnAndRow($column, $row, $value);
  566. $column++;
  567. }
  568. $row++;
  569. }
  570. $file_name="导出订单.xlsx";
  571. // Redirect output to a client’s web browser (Xlsx)
  572. header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  573. header('Content-Disposition: attachment;filename='.$file_name);
  574. header('Cache-Control: max-age=0');
  575. // If you're serving to IE 9, then the following may be needed
  576. header('Cache-Control: max-age=1');
  577. // If you're serving to IE over SSL, then the following may be needed
  578. header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
  579. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
  580. header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
  581. header('Pragma: public'); // HTTP/1.0
  582. $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
  583. $writer->save('php://output');
  584. exit;
  585. }
  586. public function finish(){
  587. $this->model->save(
  588. ['have_received'=>time(),
  589. "have_delivered"=>time()],
  590. ['have_received'=>0,
  591. 'status'=>1]
  592. );
  593. $this->success("提交成功", null);
  594. }
  595. public function doRefund(){
  596. $order_id = $this->request->get('id');
  597. $order = $this->model->where([
  598. 'id' => $order_id,
  599. 'status'=>1,//订单状态正常
  600. // 'have_paid'=>[">",0],//已支付
  601. // 'had_refund'=>0
  602. ])->find();
  603. if (!$order){
  604. $this->error("订单已取消,请勿重复操作");
  605. }
  606. if ($order->have_paid>0){
  607. //已支付
  608. self::refundOrder($order);
  609. }else{
  610. $order->status = \addons\unishop\model\Order::STATUS_CANCEL;
  611. $order->save();
  612. }
  613. //回退库存
  614. $this->refundProduct($order->id);
  615. $this->success("提交成功", null);
  616. }
  617. static function refundOrder($order){
  618. $order->status = \addons\unishop\model\Order::STATUS_REFUND;
  619. $order->refund_status = \addons\unishop\model\Order::REFUND_STATUS_AGREE;
  620. $result = $order->save();
  621. if ($result !== false){
  622. //order_id:订单ID name:订单名称 total_fee:总金额-元 refund_fee退款金额
  623. $param = [
  624. "order_id"=>$order['out_trade_no'],
  625. "total_fee"=>$order['total_price'],
  626. "refund_fee"=>$order['total_price'],
  627. "memo"=>'订单退款',
  628. ];
  629. PayService::cancel($param,$order["pay_type"]);
  630. return;
  631. }
  632. }
  633. public function refundProduct($order_id){
  634. $model=new OrderProduct();
  635. $order =$model->where([
  636. 'order_id' => $order_id,
  637. ])->select();
  638. if (!$order){
  639. return;
  640. }
  641. $list = collection($order)->toArray();
  642. $product = new \app\admin\model\unishop\Product();
  643. foreach ($list as $val){
  644. $product->where(["id"=>$val['product_id']])->setInc("stock",$val["number"]);
  645. }
  646. }
  647. }