Order.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2025 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\store\model;
  13. use app\common\model\Order as OrderModel;
  14. use app\common\service\Order as OrderService;
  15. use app\common\service\order\Refund as RefundService;
  16. use app\common\service\order\Printer as PrinterService;
  17. use app\common\service\order\PaySuccess as OrderPaySuccessService;
  18. use app\common\enum\order\{
  19. DataType as DataTypeEnum,
  20. PayStatus as PayStatusEnum,
  21. OrderStatus as OrderStatusEnum,
  22. ReceiptStatus as ReceiptStatusEnum,
  23. DeliveryStatus as DeliveryStatusEnum
  24. };
  25. use app\common\enum\payment\Method as PaymentMethod;
  26. use app\common\library\helper;
  27. use cores\exception\BaseException;
  28. /**
  29. * 订单管理
  30. * Class Order
  31. * @package app\store\model
  32. */
  33. class Order extends OrderModel
  34. {
  35. /**
  36. * 订单详情页数据
  37. * @param int $orderId
  38. * @return Order|array|null
  39. */
  40. public function getDetail(int $orderId)
  41. {
  42. return static::detail($orderId, [
  43. 'user', 'address', 'express',
  44. 'goods.image',
  45. 'delivery' => ['goods', 'express'],
  46. 'trade',
  47. ]);
  48. }
  49. /**
  50. * 订单列表
  51. * @param array $param
  52. * @return mixed
  53. */
  54. public function getList(array $param = [])
  55. {
  56. // 检索查询条件
  57. $filter = $this->getQueryFilter($param);
  58. // 设置订单类型条件
  59. $dataTypeFilter = $this->getFilterDataType($param['dataType']);
  60. // 获取数据列表
  61. return $this->with(['goods.image', 'user.avatar', 'address', 'trade'])
  62. ->alias('order')
  63. ->field('order.*')
  64. ->leftJoin('user', 'user.user_id = order.user_id')
  65. ->leftJoin('order_address address', 'address.order_id = order.order_id')
  66. ->leftJoin('payment_trade trade', 'trade.trade_id = order.trade_id')
  67. ->where($dataTypeFilter)
  68. ->where($filter)
  69. ->where('order.is_delete', '=', 0)
  70. ->order(['order.create_time' => 'desc'])
  71. ->paginate(10);
  72. }
  73. /**
  74. * 订单列表(全部)
  75. * @param array $param
  76. * @return iterable|\think\model\Collection|\think\Paginator
  77. */
  78. public function getListAll(array $param = [])
  79. {
  80. // 检索查询条件
  81. $queryFilter = $this->getQueryFilter($param);
  82. // 设置订单类型条件
  83. $dataTypeFilter = $this->getFilterDataType($param['dataType']);
  84. // 获取数据列表
  85. return $this->with(['goods.image', 'address', 'user.avatar', 'express', 'trade'])
  86. ->alias('order')
  87. ->field('order.*')
  88. ->join('user', 'user.user_id = order.user_id')
  89. ->where($dataTypeFilter)
  90. ->where($queryFilter)
  91. ->where('order.is_delete', '=', 0)
  92. ->order(['order.create_time' => 'desc'])
  93. ->select();
  94. }
  95. /**
  96. * 设置检索查询条件
  97. * @param array $param
  98. * @return array
  99. */
  100. private function getQueryFilter(array $param): array
  101. {
  102. // 默认参数
  103. $params = $this->setQueryDefaultValue($param, [
  104. 'searchType' => '', // 关键词类型 (10订单号 20会员昵称 30会员ID 40收货人姓名 50收货人电话 60第三方支付订单号)
  105. 'searchValue' => '', // 关键词内容
  106. 'orderSource' => -1, // 订单来源
  107. 'payMethod' => '', // 支付方式
  108. 'deliveryType' => -1, // 配送方式
  109. 'betweenTime' => [], // 起止时间
  110. 'userId' => 0, // 会员ID
  111. ]);
  112. // 检索查询条件
  113. $filter = [];
  114. // 关键词
  115. if (!empty($params['searchValue'])) {
  116. $searchWhere = [
  117. 10 => ['order.order_no', 'like', "%{$params['searchValue']}%"],
  118. 20 => ['user.nick_name', 'like', "%{$params['searchValue']}%"],
  119. 30 => ['order.user_id', '=', (int)$params['searchValue']],
  120. 40 => ['address.name', 'like', "%{$params['searchValue']}%"],
  121. 50 => ['address.phone', 'like', "%{$params['searchValue']}%"],
  122. 60 => ['trade.out_trade_no', 'like', "%{$params['searchValue']}%"],
  123. ];
  124. \array_key_exists($params['searchType'], $searchWhere) && $filter[] = $searchWhere[$params['searchType']];
  125. }
  126. // 起止时间
  127. if (!empty($params['betweenTime'])) {
  128. $times = between_time($params['betweenTime']);
  129. $filter[] = ['order.create_time', '>=', $times['start_time']];
  130. $filter[] = ['order.create_time', '<', $times['end_time'] + 86400];
  131. }
  132. // 订单来源
  133. $params['orderSource'] > -1 && $filter[] = ['order.order_source', '=', (int)$params['orderSource']];
  134. // 支付方式
  135. !empty($params['payMethod']) && $filter[] = ['order.pay_method', '=', $params['payMethod']];
  136. // 配送方式
  137. $params['deliveryType'] > -1 && $filter[] = ['order.delivery_type', '=', (int)$params['deliveryType']];
  138. // 会员ID
  139. $params['userId'] > 0 && $filter[] = ['order.user_id', '=', (int)$params['userId']];
  140. return $filter;
  141. }
  142. /**
  143. * 设置订单类型条件
  144. * @param string $dataType
  145. * @return array
  146. */
  147. private function getFilterDataType(string $dataType): array
  148. {
  149. // 数据类型
  150. $filter = [];
  151. switch ($dataType) {
  152. case DataTypeEnum::ALL:
  153. break;
  154. case DataTypeEnum::PAY:
  155. $filter[] = ['pay_status', '=', PayStatusEnum::PENDING];
  156. $filter[] = ['order_status', '=', OrderStatusEnum::NORMAL];
  157. break;
  158. case DataTypeEnum::DELIVERY:
  159. $filter = [
  160. ['pay_status', '=', PayStatusEnum::SUCCESS],
  161. ['delivery_status', '<>', DeliveryStatusEnum::DELIVERED],
  162. ['order_status', 'in', [OrderStatusEnum::NORMAL, OrderStatusEnum::APPLY_CANCEL]]
  163. ];
  164. break;
  165. case DataTypeEnum::RECEIPT:
  166. $filter = [
  167. ['pay_status', '=', PayStatusEnum::SUCCESS],
  168. ['delivery_status', '=', DeliveryStatusEnum::DELIVERED],
  169. ['receipt_status', '=', ReceiptStatusEnum::NOT_RECEIVED]
  170. ];
  171. break;
  172. case DataTypeEnum::COMPLETE:
  173. $filter[] = ['order_status', '=', OrderStatusEnum::COMPLETED];
  174. break;
  175. case DataTypeEnum::APPLY_CANCEL:
  176. $filter[] = ['order_status', '=', OrderStatusEnum::APPLY_CANCEL];
  177. break;
  178. case DataTypeEnum::CANCEL:
  179. $filter[] = ['order_status', '=', OrderStatusEnum::CANCELLED];
  180. break;
  181. }
  182. return $filter;
  183. }
  184. /**
  185. * 修改订单价格
  186. * @param array $data
  187. * @return bool
  188. */
  189. public function updatePrice(array $data): bool
  190. {
  191. if ($this['pay_status'] != PayStatusEnum::PENDING) {
  192. $this->error = '该订单不合法';
  193. return false;
  194. }
  195. // 实际付款金额
  196. $payPrice = helper::bcadd($data['order_price'], $data['express_price']);
  197. if ($payPrice <= 0) {
  198. $this->error = '订单实付款价格不能为0.00元';
  199. return false;
  200. }
  201. // 改价的金额差价
  202. $updatePrice = helper::bcsub($data['order_price'], $this['order_price']);
  203. // 更新订单记录
  204. return $this->save([
  205. 'order_price' => $data['order_price'],
  206. 'pay_price' => $payPrice,
  207. 'update_price' => $updatePrice,
  208. 'express_price' => $data['express_price']
  209. ]) !== false;
  210. }
  211. /**
  212. * 修改商家备注
  213. * @param array $data
  214. * @return bool
  215. */
  216. public function updateRemark(array $data): bool
  217. {
  218. return $this->save(['merchant_remark' => $data['content'] ?? '']);
  219. }
  220. /**
  221. * 修改收货地址
  222. * @param array $data
  223. * @return bool
  224. */
  225. public function updateAddress(array $data): bool
  226. {
  227. return $this->save(['merchant_remark' => $data['content'] ?? '']);
  228. }
  229. /**
  230. * 小票打印
  231. * @param array $data
  232. * @return bool
  233. * @throws BaseException
  234. * @throws \think\db\exception\DataNotFoundException
  235. * @throws \think\db\exception\DbException
  236. * @throws \think\db\exception\ModelNotFoundException
  237. */
  238. public function printer(array $data): bool
  239. {
  240. // 实例化打印机驱动
  241. $Printer = new PrinterService;
  242. // 手动打印小票
  243. $status = $Printer->printEvent($this, $data['printerId']);
  244. if ($status === false) {
  245. $this->error = $Printer->getError();
  246. }
  247. return $status;
  248. }
  249. /**
  250. * 审核:用户取消订单
  251. * @param array $data
  252. * @return bool|mixed
  253. */
  254. public function confirmCancel(array $data)
  255. {
  256. // 判断订单是否有效
  257. if (
  258. $this['pay_status'] != PayStatusEnum::SUCCESS
  259. || $this['order_status'] != OrderStatusEnum::APPLY_CANCEL
  260. ) {
  261. $this->error = '该订单不合法';
  262. return false;
  263. }
  264. // 订单取消事件
  265. return $this->transaction(function () use ($data) {
  266. if ($data['status']) {
  267. // 执行退款操作
  268. if (!(new RefundService)->handle($this)) {
  269. throwError('执行订单退款失败');
  270. }
  271. // 订单取消事件
  272. OrderService::cancelEvent($this);
  273. }
  274. // 更新订单状态
  275. return $this->save([
  276. 'order_status' => $data['status'] ? OrderStatusEnum::CANCELLED : OrderStatusEnum::NORMAL
  277. ]);
  278. });
  279. }
  280. /**
  281. * 将订单记录设置为已删除
  282. * @return bool
  283. */
  284. public function setDelete(): bool
  285. {
  286. return $this->save(['is_delete' => 1]);
  287. }
  288. /**
  289. * 获取已付款订单总数 (可指定某天)
  290. * @param null $startDate
  291. * @param null $endDate
  292. * @return int
  293. */
  294. public function getPayOrderTotal($startDate = null, $endDate = null): int
  295. {
  296. $filter = [
  297. ['pay_status', '=', PayStatusEnum::SUCCESS],
  298. ['order_status', '<>', OrderStatusEnum::CANCELLED]
  299. ];
  300. if (!is_null($startDate) && !is_null($endDate)) {
  301. $filter[] = ['pay_time', '>=', strtotime($startDate)];
  302. $filter[] = ['pay_time', '<', strtotime($endDate) + 86400];
  303. }
  304. return $this->getOrderTotal($filter);
  305. }
  306. /**
  307. * 获取未发货订单数量
  308. * @return int
  309. */
  310. public function getNotDeliveredOrderTotal(): int
  311. {
  312. $filter = [
  313. ['pay_status', '=', PayStatusEnum::SUCCESS],
  314. ['delivery_status', '<>', DeliveryStatusEnum::DELIVERED],
  315. ['order_status', 'in', [OrderStatusEnum::NORMAL, OrderStatusEnum::APPLY_CANCEL]]
  316. ];
  317. return $this->getOrderTotal($filter);
  318. }
  319. /**
  320. * 获取未付款订单数量
  321. * @return int
  322. */
  323. public function getNotPayOrderTotal(): int
  324. {
  325. $filter = [
  326. ['pay_status', '=', PayStatusEnum::PENDING],
  327. ['order_status', '=', OrderStatusEnum::NORMAL]
  328. ];
  329. return $this->getOrderTotal($filter);
  330. }
  331. /**
  332. * 获取订单总数
  333. * @param array $filter
  334. * @return int
  335. */
  336. private function getOrderTotal(array $filter = []): int
  337. {
  338. // 获取订单总数量
  339. return $this->where($filter)
  340. ->where('is_delete', '=', 0)
  341. ->count();
  342. }
  343. /**
  344. * 获取某天的总销售额
  345. * @param null $startDate
  346. * @param null $endDate
  347. * @return float
  348. */
  349. public function getOrderTotalPrice($startDate = null, $endDate = null): float
  350. {
  351. // 查询对象
  352. $query = $this->getNewQuery();
  353. // 设置查询条件
  354. if (!is_null($startDate) && !is_null($endDate)) {
  355. $query->where('pay_time', '>=', strtotime($startDate))
  356. ->where('pay_time', '<', strtotime($endDate) + 86400);
  357. }
  358. // 总销售额
  359. return $query->where('pay_status', '=', PayStatusEnum::SUCCESS)
  360. ->where('order_status', '<>', OrderStatusEnum::CANCELLED)
  361. ->where('is_delete', '=', 0)
  362. ->sum('pay_price');
  363. }
  364. /**
  365. * 获取某天的下单用户数
  366. * @param string $day
  367. * @return float|int
  368. */
  369. public function getPayOrderUserTotal(string $day)
  370. {
  371. $startTime = strtotime($day);
  372. return $this->field('user_id')
  373. ->where('pay_time', '>=', $startTime)
  374. ->where('pay_time', '<', $startTime + 86400)
  375. ->where('pay_status', '=', PayStatusEnum::SUCCESS)
  376. ->where('is_delete', '=', '0')
  377. ->group('user_id')
  378. ->count();
  379. }
  380. /**
  381. * 根据订单号获取ID集
  382. * @param array $orderNoArr
  383. * @return array
  384. * @throws \think\db\exception\DataNotFoundException
  385. * @throws \think\db\exception\DbException
  386. * @throws \think\db\exception\ModelNotFoundException
  387. */
  388. public static function getOrderIds(array $orderNoArr): array
  389. {
  390. $list = (new static)->where('order_no', 'in', $orderNoArr)->select();
  391. $data = [];
  392. foreach ($list as $item) {
  393. $data[$item['order_no']] = $item['order_id'];
  394. }
  395. return $data;
  396. }
  397. }