Order.php 13 KB

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