Order.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. <?php
  2. /**
  3. *
  4. * @copyright ©2020 点小铺
  5. * @author hanj
  6. * @link: https://dyuit.com
  7. * Created by VSCode
  8. */
  9. namespace app\store\model\sharing;
  10. use app\common\model\sharing\Order as OrderModel;
  11. use app\store\model\User as UserModel;
  12. use app\store\model\UserCoupon as UserCouponModel;
  13. use app\store\service\order\Export as Exportservice;
  14. use app\common\library\helper;
  15. use app\common\enum\OrderType as OrderTypeEnum;
  16. use app\common\enum\DeliveryType as DeliveryTypeEnum;
  17. use app\common\service\Message as MessageService;
  18. use app\common\service\order\Refund as RefundService;
  19. /**
  20. * 拼团订单模型
  21. * Class Order
  22. * @package app\store\model\sharing
  23. */
  24. class Order extends OrderModel
  25. {
  26. /**
  27. * 订单列表
  28. * @param string $dataType
  29. * @param array $query
  30. * @return \think\Paginator
  31. * @throws \think\exception\DbException
  32. */
  33. public function getList($dataType, $query = [])
  34. {
  35. // 检索查询条件
  36. !empty($query) && $this->setWhere($query);
  37. // 获取数据列表
  38. return $this->with(['active', 'goods.image', 'address', 'user'])
  39. ->alias('order')
  40. ->field('order.*, active.status as active_status')
  41. ->join('user', 'user.user_id = order.user_id', 'LEFT')
  42. ->join('sharing_active active', 'order.active_id = active.active_id', 'LEFT')
  43. ->where($this->transferDataType($dataType))
  44. ->where('order.is_delete', '=', 0)
  45. ->order(['order.create_time' => 'desc'])
  46. ->paginate(10, false, [
  47. 'query' => \request()->request()
  48. ]);
  49. }
  50. /**
  51. * 订单列表(全部)
  52. * @param $dataType
  53. * @param array $query
  54. * @return false|\PDOStatement|string|\think\Collection
  55. * @throws \think\db\exception\DataNotFoundException
  56. * @throws \think\db\exception\ModelNotFoundException
  57. * @throws \think\exception\DbException
  58. */
  59. public function getListAll($dataType, $query = [])
  60. {
  61. // 检索查询条件
  62. !empty($query) && $this->setWhere($query);
  63. // 获取数据列表
  64. return $this->with(['goods.image', 'address', 'user', 'extract', 'extract_shop'])
  65. ->alias('order')
  66. ->field('order.*, active.status as active_status')
  67. ->join('sharing_active active', 'order.active_id = active.active_id', 'LEFT')
  68. ->where($this->transferDataType($dataType))
  69. ->where('order.is_delete', '=', 0)
  70. ->order(['create_time' => 'desc'])
  71. ->select();
  72. }
  73. /**
  74. * 订单导出
  75. * @param $dataType
  76. * @param $query
  77. * @throws \think\db\exception\DataNotFoundException
  78. * @throws \think\db\exception\ModelNotFoundException
  79. * @throws \think\exception\DbException
  80. */
  81. public function exportList($dataType, $query)
  82. {
  83. // 获取订单列表
  84. $list = $this->getListAll($dataType, $query);
  85. // 导出csv文件
  86. return (new Exportservice)->orderList($list);
  87. }
  88. /**
  89. * 批量发货模板
  90. */
  91. public function deliveryTpl()
  92. {
  93. return (new Exportservice)->deliveryTpl();
  94. }
  95. /**
  96. * 设置检索查询条件
  97. * @param $query
  98. */
  99. private function setWhere($query)
  100. {
  101. if (isset($query['search']) && !empty($query['search'])) {
  102. $this->where('order_no|user.nickName', 'like', '%' . trim($query['search']) . '%');
  103. }
  104. if (isset($query['start_time']) && !empty($query['start_time'])) {
  105. $this->where('order.create_time', '>=', strtotime($query['start_time']));
  106. }
  107. if (isset($query['end_time']) && !empty($query['end_time'])) {
  108. $this->where('order.create_time', '<', strtotime($query['end_time']) + 86400);
  109. }
  110. if (isset($query['active_id']) && $query['active_id'] > 0) {
  111. $this->where('order.active_id', '=', (int)$query['active_id']);
  112. }
  113. if (isset($query['delivery_type']) && !empty($query['delivery_type'])) {
  114. $query['delivery_type'] > -1 && $this->where('delivery_type', '=', $query['delivery_type']);
  115. }
  116. if (isset($query['extract_shop_id']) && !empty($query['extract_shop_id'])) {
  117. $query['extract_shop_id'] > -1 && $this->where('extract_shop_id', '=', $query['extract_shop_id']);
  118. }
  119. }
  120. /**
  121. * 转义数据类型条件
  122. * @param $dataType
  123. * @return array
  124. */
  125. private function transferDataType($dataType)
  126. {
  127. // 数据类型
  128. $filter = [];
  129. switch ($dataType) {
  130. case 'all':
  131. // 全部
  132. $filter = [];
  133. break;
  134. case 'pay':
  135. // 待支付
  136. $filter = ['pay_status' => 10, 'order_status' => 10];
  137. break;
  138. case 'sharing';
  139. // 拼团中
  140. $filter['active.status'] = 10;
  141. break;
  142. case 'sharing_succeed';
  143. // 拼团成功
  144. $filter['active.status'] = 20;
  145. break;
  146. case 'sharing_fail';
  147. // 拼团失败
  148. $filter['active.status'] = 30;
  149. break;
  150. case 'delivery':
  151. // 待发货
  152. $this->where('IF ( (`order`.`order_type` = 20), (`active`.`status` = 20), TRUE)');
  153. $filter = [
  154. 'pay_status' => 20,
  155. 'delivery_status' => 10,
  156. 'order_status' => ['in', [10, 21]]
  157. ];
  158. break;
  159. case 'receipt':
  160. // 待收货
  161. $filter = [
  162. 'pay_status' => 20,
  163. 'delivery_status' => 20,
  164. 'receipt_status' => 10
  165. ];
  166. break;
  167. case 'complete':
  168. // 已完成
  169. $filter = ['order_status' => 30];
  170. break;
  171. case 'cancel':
  172. // 已取消
  173. $filter = ['order_status' => 20];
  174. break;
  175. }
  176. return $filter;
  177. }
  178. /**
  179. * 确认发货(单独订单)
  180. * @param $data
  181. * @return array|bool|false
  182. * @throws \app\common\exception\BaseException
  183. * @throws \think\Exception
  184. * @throws \think\exception\DbException
  185. * @throws \Exception
  186. */
  187. public function delivery($data)
  188. {
  189. // 转义为订单列表
  190. $orderList = [$this];
  191. // 验证订单是否满足发货条件
  192. if (!$this->verifyDelivery($orderList)) {
  193. return false;
  194. }
  195. // 整理更新的数据
  196. $updateList = [[
  197. 'order_id' => $this['order_id'],
  198. 'express_id' => $data['express_id'],
  199. 'express_no' => $data['express_no']
  200. ]];
  201. // 更新订单发货状态
  202. if ($status = $this->updateToDelivery($updateList)) {
  203. // 获取已发货的订单
  204. $completed = self::detail($this['order_id'], ['user', 'address', 'goods', 'express']);
  205. // 发送消息通知
  206. $this->sendDeliveryMessage([$completed]);
  207. }
  208. return $status;
  209. }
  210. /**
  211. * 批量发货
  212. * @param $data
  213. * @return bool
  214. * @throws \think\db\exception\DataNotFoundException
  215. * @throws \think\db\exception\ModelNotFoundException
  216. * @throws \think\exception\DbException
  217. * @throws \Exception
  218. */
  219. public function batchDelivery($data)
  220. {
  221. // 获取csv文件中的数据
  222. if (!$csvData = $this->getCsvData()) {
  223. return false;
  224. }
  225. // 整理订单id集
  226. $orderNos = helper::getArrayColumn($csvData, 0);
  227. // 获取订单列表数据
  228. $orderList = helper::arrayColumn2Key($this->getListByOrderNos($orderNos), 'order_no');
  229. // 验证订单是否存在
  230. $tempArr = array_values(array_diff($orderNos, array_keys($orderList)));
  231. if (!empty($tempArr)) {
  232. $this->error = "订单号[{$tempArr[0]}] 不存在!";
  233. return false;
  234. }
  235. // 整理物流单号
  236. $updateList = [];
  237. foreach ($csvData as $item) {
  238. $updateList[] = [
  239. 'order_id' => $orderList[$item[0]]['order_id'],
  240. 'express_id' => $data['express_id'],
  241. 'express_no' => $item[1],
  242. ];
  243. }
  244. // 验证订单是否满足发货条件
  245. if (!$this->verifyDelivery($orderList)) {
  246. return false;
  247. }
  248. // 更新订单发货状态(批量)
  249. if ($status = $this->updateToDelivery($updateList)) {
  250. // 获取已发货的订单
  251. $completed = $this->getListByOrderNos($orderNos, ['user', 'address', 'goods', 'express']);
  252. // 发送消息通知
  253. $this->sendDeliveryMessage($completed);
  254. }
  255. return $status;
  256. }
  257. /**
  258. * 确认发货后发送消息通知
  259. * @param array|\think\Collection $orderList
  260. * @return bool
  261. * @throws \app\common\exception\BaseException
  262. * @throws \think\Exception
  263. * @throws \think\exception\DbException
  264. */
  265. private function sendDeliveryMessage($orderList)
  266. {
  267. // 实例化消息通知服务类
  268. $Service = new MessageService;
  269. foreach ($orderList as $item) {
  270. // 发送消息通知
  271. $Service->delivery($item, OrderTypeEnum::SHARING);
  272. }
  273. return true;
  274. }
  275. /**
  276. * 更新订单发货状态(批量)
  277. * @param $orderList
  278. * @return array|false
  279. * @throws \Exception
  280. */
  281. private function updateToDelivery($orderList)
  282. {
  283. $data = [];
  284. foreach ($orderList as $item) {
  285. $data[] = [
  286. 'order_id' => $item['order_id'],
  287. 'express_no' => $item['express_no'],
  288. 'express_id' => $item['express_id'],
  289. 'delivery_status' => 20,
  290. 'delivery_time' => time(),
  291. ];
  292. }
  293. return $this->isUpdate()->saveAll($data);
  294. }
  295. /**
  296. * 验证订单是否满足发货条件
  297. * @param $orderList
  298. * @return bool
  299. */
  300. private function verifyDelivery($orderList)
  301. {
  302. foreach ($orderList as $order) {
  303. if (
  304. $order['pay_status']['value'] != 20
  305. || $order['delivery_type']['value'] != DeliveryTypeEnum::EXPRESS
  306. || $order['delivery_status']['value'] != 10
  307. || (
  308. // 拼团订单验证拼单状态
  309. $order['order_type']['value'] == 20
  310. && $order['active']['status']['value'] != 20
  311. )
  312. ) {
  313. $this->error = "订单号[{$order['order_no']}] 不满足发货条件!";
  314. return false;
  315. }
  316. }
  317. return true;
  318. }
  319. /**
  320. * 获取csv文件中的数据
  321. * @return array|bool
  322. */
  323. private function getCsvData()
  324. {
  325. // 获取表单上传文件 例如上传了001.jpg
  326. $file = \request()->file('iFile');
  327. if (empty($file)) {
  328. $this->error = '请上传发货模板';
  329. return false;
  330. }
  331. // 设置区域信息
  332. setlocale(LC_ALL, 'zh_CN');
  333. // 打开上传的文件
  334. $csvFile = fopen($file->getInfo()['tmp_name'], 'r');
  335. // 忽略第一行(csv标题)
  336. fgetcsv($csvFile);
  337. // 遍历并记录订单信息
  338. $orderList = [];
  339. while ($item = fgetcsv($csvFile)) {
  340. if (!isset($item[0]) || empty($item[0]) || !isset($item[1]) || empty($item[1])) {
  341. $this->error = '模板文件数据不合法';
  342. return false;
  343. }
  344. $orderList[] = $item;
  345. }
  346. if (empty($orderList)) {
  347. $this->error = '模板文件中没有订单数据';
  348. return false;
  349. }
  350. return $orderList;
  351. }
  352. /**
  353. * 修改订单价格
  354. * @param $data
  355. * @return bool
  356. */
  357. public function updatePrice($data)
  358. {
  359. if ($this['pay_status']['value'] != 10) {
  360. $this->error = '该订单不合法';
  361. return false;
  362. }
  363. // 实际付款金额
  364. $payPrice = bcadd($data['update_price'], $data['update_express_price'], 2);
  365. if ($payPrice <= 0) {
  366. $this->error = '订单实付款价格不能为0.00元';
  367. return false;
  368. }
  369. return $this->save([
  370. 'order_no' => $this->orderNo(), // 修改订单号, 否则微信支付提示重复
  371. 'order_price' => $data['update_price'],
  372. 'pay_price' => $payPrice,
  373. 'update_price' => helper::bcsub($data['update_price'], helper::bcsub($this['total_price'], $this['coupon_money'])),
  374. 'express_price' => $data['update_express_price']
  375. ]) !== false;
  376. }
  377. /**
  378. * 审核:用户取消订单
  379. * @param $data
  380. * @return bool
  381. */
  382. public function confirmCancel($data)
  383. {
  384. // 判断订单是否有效
  385. if ($this['pay_status']['value'] != 20) {
  386. $this->error = '该订单不合法';
  387. return false;
  388. }
  389. // 订单取消事件
  390. return $this->transaction(function () use ($data) {
  391. if ($data['is_cancel'] == true) {
  392. // 执行退款操作
  393. (new RefundService)->execute($this);
  394. // 回退商品库存
  395. (new OrderGoods)->backGoodsStock($this['goods'], true);
  396. // 回退用户优惠券
  397. $this['coupon_id'] > 0 && UserCouponModel::setIsUse($this['coupon_id'], false);
  398. // 回退用户积分
  399. $User = UserModel::detail($this['user_id']);
  400. $describe = "订单取消:{$this['order_no']}";
  401. $this['points_num'] > 0 && $User->setIncPoints($this['points_num'], $describe);
  402. }
  403. // 更新订单状态
  404. return $this->save(['order_status' => $data['is_cancel'] ? 20 : 10]);
  405. });
  406. }
  407. /**
  408. * 拼团失败手动退款
  409. * @return bool|false|int
  410. * @throws \app\common\exception\BaseException
  411. * @throws \think\Exception
  412. * @throws \think\exception\DbException
  413. */
  414. public function refund()
  415. {
  416. if (
  417. $this['order_type']['value'] != 20
  418. || $this['pay_status']['value'] != 20
  419. || $this['active']['status']['value'] != 30
  420. || $this['is_refund'] == 1
  421. ) {
  422. $this->error = '该订单不合法';
  423. return false;
  424. }
  425. // 执行退款操作
  426. (new RefundService)->execute($this);
  427. // 更新订单状态
  428. return $this->save(['order_status' => 20, 'is_refund' => 1]);
  429. }
  430. /**
  431. * 获取已付款订单总数 (可指定某天)
  432. * @param null $day
  433. * @return int|string
  434. * @throws \think\Exception
  435. */
  436. public function getPayOrderTotal($day = null)
  437. {
  438. $filter = ['pay_status' => 20];
  439. if (!is_null($day)) {
  440. $startTime = strtotime($day);
  441. $filter['pay_time'] = [
  442. ['>=', $startTime],
  443. ['<', $startTime + 86400],
  444. ];
  445. }
  446. return $this->getOrderTotal($filter);
  447. }
  448. /**
  449. * 获取订单总数量
  450. * @param array $filter
  451. * @return int|string
  452. * @throws \think\Exception
  453. */
  454. public function getOrderTotal($filter = [])
  455. {
  456. return $this->where($filter)
  457. ->where('is_delete', '=', 0)
  458. ->count();
  459. }
  460. /**
  461. * 获取某天的总销售额
  462. * @param $day
  463. * @return float|int
  464. */
  465. public function getOrderTotalPrice($day)
  466. {
  467. $startTime = strtotime($day);
  468. return $this->where('pay_time', '>=', $startTime)
  469. ->where('pay_time', '<', $startTime + 86400)
  470. ->where('pay_status', '=', 20)
  471. ->where('is_delete', '=', 0)
  472. ->sum('pay_price');
  473. }
  474. /**
  475. * 获取某天的下单用户数
  476. * @param $day
  477. * @return float|int
  478. */
  479. public function getPayOrderUserTotal($day)
  480. {
  481. $startTime = strtotime($day);
  482. $userIds = $this->distinct(true)
  483. ->where('pay_time', '>=', $startTime)
  484. ->where('pay_time', '<', $startTime + 86400)
  485. ->where('pay_status', '=', 20)
  486. ->where('is_delete', '=', 0)
  487. ->column('user_id');
  488. return count($userIds);
  489. }
  490. }