Order.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2021 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\api\model;
  13. use app\api\model\{Goods as GoodsModel, OrderRefund as OrderRefundModel, Setting as SettingModel};
  14. use app\api\service\{User as UserService, Goods as GoodsService, Payment as PaymentService};
  15. use app\api\service\order\{PaySuccess as OrderPaySuccesService, source\Factory as OrderSourceFactory};
  16. use app\common\model\Order as OrderModel;
  17. use app\common\service\{Order as OrderService, order\Complete as OrderCompleteService};
  18. use app\common\enum\{
  19. Setting as SettingEnum,
  20. OrderType as OrderTypeEnum,
  21. order\PayType as OrderPayTypeEnum,
  22. order\PayStatus as PayStatusEnum,
  23. order\OrderStatus as OrderStatusEnum,
  24. order\DeliveryType as DeliveryTypeEnum,
  25. order\ReceiptStatus as ReceiptStatusEnum,
  26. order\DeliveryStatus as DeliveryStatusEnum
  27. };
  28. use app\common\library\helper;
  29. use cores\exception\BaseException;
  30. /**
  31. * 订单模型
  32. * Class Order
  33. * @package app\api\model
  34. */
  35. class Order extends OrderModel
  36. {
  37. /**
  38. * 隐藏字段
  39. * @var array
  40. */
  41. protected $hidden = [
  42. 'store_id',
  43. 'update_time'
  44. ];
  45. // 信息提示
  46. private $message = '';
  47. /**
  48. * 待支付订单详情
  49. * @param string $orderNo 订单号
  50. * @return null|static
  51. */
  52. public static function getPayDetail(string $orderNo): ?Order
  53. {
  54. return self::detail(['order_no' => $orderNo, 'pay_status' => PayStatusEnum::PENDING, 'is_delete' => 0], ['goods', 'user']);
  55. }
  56. /**
  57. * 订单支付事件
  58. * @param int $payType
  59. * @return bool
  60. */
  61. public function onPay(int $payType = OrderPayTypeEnum::WECHAT): bool
  62. {
  63. // 判断订单状态
  64. $orderSource = OrderSourceFactory::getFactory($this['order_source']);
  65. if (!$orderSource->checkOrderStatusOnPay($this)) {
  66. $this->error = $orderSource->getError();
  67. return false;
  68. }
  69. // 余额支付
  70. if ($payType == OrderPayTypeEnum::BALANCE) {
  71. return $this->onPaymentByBalance($this['order_no']);
  72. }
  73. return true;
  74. }
  75. /**
  76. * 构建支付请求的参数
  77. * @param self $order 订单信息
  78. * @param int $payType 订单支付方式
  79. * @return array
  80. * @throws BaseException
  81. * @throws \think\db\exception\DataNotFoundException
  82. * @throws \think\db\exception\DbException
  83. * @throws \think\db\exception\ModelNotFoundException
  84. */
  85. public function onOrderPayment(self $order, int $payType): array
  86. {
  87. if ($payType == OrderPayTypeEnum::WECHAT) {
  88. return $this->onPaymentByWechat($order);
  89. }
  90. return [];
  91. }
  92. /**
  93. * 构建微信支付请求
  94. * @param self $order 订单详情
  95. * @return array
  96. * @throws BaseException
  97. * @throws \think\db\exception\DataNotFoundException
  98. * @throws \think\db\exception\DbException
  99. * @throws \think\db\exception\ModelNotFoundException
  100. */
  101. protected function onPaymentByWechat(self $order): array
  102. {
  103. return PaymentService::wechat(
  104. $order['order_id'],
  105. $order['order_no'],
  106. $order['pay_price'],
  107. OrderTypeEnum::ORDER
  108. );
  109. }
  110. /**
  111. * 立即购买:获取订单商品列表
  112. * @param int $goodsId 商品ID
  113. * @param string $goodsSkuId 商品SKU
  114. * @param int $goodsNum 购买数量
  115. * @return mixed
  116. * @throws BaseException
  117. */
  118. public function getOrderGoodsListByNow(int $goodsId, string $goodsSkuId, int $goodsNum)
  119. {
  120. // 获取商品列表
  121. $model = new GoodsModel;
  122. $goodsList = $model->getListByIdsFromApi([$goodsId]);
  123. if ($goodsList->isEmpty()) {
  124. throwError('未找到商品信息');
  125. }
  126. // 隐藏冗余的属性
  127. $goodsList->hidden(array_merge($model->hidden, ['content', 'goods_images', 'images']));
  128. foreach ($goodsList as &$item) {
  129. // 商品sku信息
  130. $item['skuInfo'] = GoodsService::getSkuInfo($item['goods_id'], $goodsSkuId);
  131. // 商品单价
  132. $item['goods_price'] = $item['skuInfo']['goods_price'];
  133. // 商品购买数量
  134. $item['total_num'] = $goodsNum;
  135. // 商品SKU索引
  136. $item['goods_sku_id'] = $item['skuInfo']['goods_sku_id'];
  137. // 商品购买总金额
  138. $item['total_price'] = helper::bcmul($item['goods_price'], $goodsNum);
  139. }
  140. return $goodsList;
  141. }
  142. /**
  143. * 余额支付标记订单已支付
  144. * @param string $orderNo 订单号
  145. * @return bool
  146. */
  147. public function onPaymentByBalance(string $orderNo): bool
  148. {
  149. // 获取订单详情
  150. $service = new OrderPaySuccesService($orderNo);
  151. // 发起余额支付
  152. $status = $service->onPaySuccess(OrderPayTypeEnum::BALANCE);
  153. if (!$status) {
  154. $this->error = $service->getError();
  155. }
  156. return $status;
  157. }
  158. /**
  159. * 获取用户订单列表
  160. * @param string $type 订单类型 (all全部 payment待付款 received待发货 deliver待收货 comment待评价)
  161. * @return \think\Paginator
  162. * @throws \think\db\exception\DbException
  163. * @throws BaseException
  164. */
  165. public function getList(string $type = 'all'): \think\Paginator
  166. {
  167. // 筛选条件
  168. $filter = [];
  169. // 订单数据类型
  170. switch ($type) {
  171. case 'all':
  172. break;
  173. case 'payment':
  174. $filter['pay_status'] = PayStatusEnum::PENDING;
  175. $filter['order_status'] = OrderStatusEnum::NORMAL;
  176. break;
  177. case 'delivery':
  178. $filter['pay_status'] = PayStatusEnum::SUCCESS;
  179. $filter['delivery_status'] = DeliveryStatusEnum::NOT_DELIVERED;
  180. $filter['order_status'] = OrderStatusEnum::NORMAL;
  181. break;
  182. case 'received':
  183. $filter['pay_status'] = PayStatusEnum::SUCCESS;
  184. $filter['delivery_status'] = DeliveryStatusEnum::DELIVERED;
  185. $filter['receipt_status'] = ReceiptStatusEnum::NOT_RECEIVED;
  186. $filter['order_status'] = OrderStatusEnum::NORMAL;
  187. break;
  188. case 'comment':
  189. $filter['is_comment'] = 0;
  190. $filter['order_status'] = OrderStatusEnum::COMPLETED;
  191. break;
  192. }
  193. // 当前用户ID
  194. $userId = UserService::getCurrentLoginUserId();
  195. // 查询列表数据
  196. return $this->with(['goods.image'])
  197. ->where($filter)
  198. ->where('user_id', '=', $userId)
  199. ->where('is_delete', '=', 0)
  200. ->order(['create_time' => 'desc'])
  201. ->paginate(15);
  202. }
  203. /**
  204. * 取消订单
  205. * @return bool|mixed
  206. */
  207. public function cancel()
  208. {
  209. if ($this['delivery_status'] == DeliveryStatusEnum::DELIVERED) {
  210. $this->error = '已发货订单不可取消';
  211. return false;
  212. }
  213. // 订单是否已支付
  214. $isPay = $this['pay_status'] == PayStatusEnum::SUCCESS;
  215. // 提示信息
  216. $this->message = $isPay ? '订单已申请取消,需等待后台审核' : '订单已取消成功';
  217. // 订单取消事件
  218. return $this->transaction(function () use ($isPay) {
  219. // 订单取消事件
  220. $isPay == false && OrderService::cancelEvent($this);
  221. // 更新订单状态: 已付款的订单设置为"待取消", 等待后台审核
  222. return $this->save(['order_status' => $isPay ? OrderStatusEnum::APPLY_CANCEL : OrderStatusEnum::CANCELLED]);
  223. });
  224. }
  225. /**
  226. * 确认收货
  227. * @return bool|mixed
  228. */
  229. public function receipt()
  230. {
  231. // 验证订单是否合法
  232. // 条件1: 订单必须已发货
  233. // 条件2: 订单必须未收货
  234. if ($this['delivery_status'] != 20 || $this['receipt_status'] != 10) {
  235. $this->error = '该订单不合法';
  236. return false;
  237. }
  238. return $this->transaction(function () {
  239. // 更新订单状态
  240. $status = $this->save([
  241. 'receipt_status' => 20,
  242. 'receipt_time' => time(),
  243. 'order_status' => 30
  244. ]);
  245. // 执行订单完成后的操作
  246. $OrderCompleteService = new OrderCompleteService();
  247. $OrderCompleteService->complete([$this], static::$storeId);
  248. return $status;
  249. });
  250. }
  251. /**
  252. * 获取当前用户订单数量
  253. * @param string $type 订单类型 (all全部 payment待付款 received待发货 deliver待收货 comment待评价)
  254. * @return int
  255. * @throws BaseException
  256. */
  257. public function getCount(string $type = 'all'): int
  258. {
  259. // 筛选条件
  260. $filter = [];
  261. // 订单数据类型
  262. switch ($type) {
  263. case 'all':
  264. break;
  265. case 'payment':
  266. $filter['pay_status'] = PayStatusEnum::PENDING;
  267. break;
  268. case 'received':
  269. $filter['pay_status'] = PayStatusEnum::SUCCESS;
  270. $filter['delivery_status'] = DeliveryStatusEnum::DELIVERED;
  271. $filter['receipt_status'] = ReceiptStatusEnum::NOT_RECEIVED;
  272. break;
  273. case 'delivery':
  274. $filter['pay_status'] = PayStatusEnum::SUCCESS;
  275. $filter['delivery_status'] = DeliveryStatusEnum::NOT_DELIVERED;
  276. $filter['order_status'] = OrderStatusEnum::NORMAL;
  277. break;
  278. case 'comment':
  279. $filter['is_comment'] = 0;
  280. $filter['order_status'] = OrderStatusEnum::COMPLETED;
  281. break;
  282. }
  283. // 当前用户ID
  284. $userId = UserService::getCurrentLoginUserId();
  285. // 查询数据
  286. return $this->where('user_id', '=', $userId)
  287. ->where('order_status', '<>', 20)
  288. ->where($filter)
  289. ->where('is_delete', '=', 0)
  290. ->count();
  291. }
  292. /**
  293. * 获取用户订单详情(含关联数据)
  294. * @param int $orderId 订单ID
  295. * @return Order|array|null
  296. * @throws BaseException
  297. * @throws \think\db\exception\DataNotFoundException
  298. * @throws \think\db\exception\DbException
  299. * @throws \think\db\exception\ModelNotFoundException
  300. */
  301. public static function getUserOrderDetail(int $orderId)
  302. {
  303. // 关联查询
  304. $with = [
  305. 'goods' => ['image', 'goods', 'refund'],
  306. 'address', 'express'
  307. ];
  308. // 查询订单记录
  309. $order = static::getDetail($orderId, $with);
  310. // 该订单是否允许申请售后
  311. $order['isAllowRefund'] = static::isAllowRefund($order);
  312. return $order;
  313. }
  314. /**
  315. * 获取用户订单详情(仅订单记录)
  316. * @param int $orderId
  317. * @param array $with
  318. * @return Order|array|null
  319. * @throws BaseException
  320. */
  321. public static function getDetail(int $orderId, array $with = [])
  322. {
  323. // 查询订单记录
  324. $order = static::detail([
  325. 'order_id' => $orderId,
  326. 'user_id' => UserService::getCurrentLoginUserId(),
  327. ], $with);
  328. empty($order) && throwError('订单不存在');
  329. return $order;
  330. }
  331. /**
  332. * 获取当前用户待处理的订单数量
  333. * @return array
  334. * @throws BaseException
  335. */
  336. public function getTodoCounts(): array
  337. {
  338. return [
  339. 'payment' => $this->getCount('payment'), // 待付款的订单
  340. 'delivery' => $this->getCount('delivery'), // 待发货的订单
  341. 'received' => $this->getCount('received'), // 待收货的订单
  342. 'refund' => OrderRefundModel::getCountByUnderway(), // 进行中的售后单
  343. ];
  344. }
  345. // 返回提示信息
  346. public function getMessage(): string
  347. {
  348. return $this->message;
  349. }
  350. /**
  351. * 当前订单是否允许申请售后
  352. * @param Order $order
  353. * @return bool
  354. * @throws \think\db\exception\DataNotFoundException
  355. * @throws \think\db\exception\DbException
  356. * @throws \think\db\exception\ModelNotFoundException
  357. */
  358. private static function isAllowRefund(self $order): bool
  359. {
  360. // 必须是已发货的订单
  361. if ($order['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
  362. return false;
  363. }
  364. // 允许申请售后期限(天)
  365. $refundDays = SettingModel::getItem(SettingEnum::TRADE)['order']['refund_days'];
  366. // 不允许售后
  367. if ($refundDays == 0) {
  368. return false;
  369. }
  370. // 当前时间超出允许申请售后期限
  371. if (
  372. $order['receipt_status'] == ReceiptStatusEnum::RECEIVED
  373. && time() > ($order->getData('receipt_time') + ((int)$refundDays * 86400))
  374. ) {
  375. return false;
  376. }
  377. return true;
  378. }
  379. }