Order.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * Author: 芸众商城 www.yunzshop.com
  5. * Date: 2017/2/28
  6. * Time: 上午11:32
  7. */
  8. namespace app\common\models;
  9. use app\backend\modules\order\observers\OrderObserver;
  10. use app\common\events\order\AfterOrderCreatedImmediatelyEvent;
  11. use app\common\events\order\AfterOrderPaidEvent;
  12. use app\common\events\order\AfterOrderPaidImmediatelyEvent;
  13. use app\common\events\order\AfterOrderReceivedEvent;
  14. use app\common\events\order\AfterOrderReceivedImmediatelyEvent;
  15. use app\common\events\order\AfterOrderRefundSuccessEvent;
  16. use app\common\events\order\AfterOrderSentImmediatelyEvent;
  17. use app\common\events\order\BeforeOrderCreateEvent;
  18. use app\common\exceptions\AppException;
  19. use app\common\facades\SiteSetting as SiteSettingFacades;
  20. use app\common\models\member\MemberCancel;
  21. use app\common\models\order\Express;
  22. use app\common\models\order\ManualRefundLog;
  23. use app\common\models\order\OrderChangePriceLog;
  24. use app\common\models\order\OrderCoinExchange;
  25. use app\common\models\order\OrderCoupon;
  26. use app\common\models\order\OrderDeduction;
  27. use app\common\models\order\OrderDiscount;
  28. use app\common\models\order\OrderFee;
  29. use app\common\models\order\OrderFreightDeduction;
  30. use app\common\models\order\OrderInvoice;
  31. use app\common\models\order\OrderServiceFee;
  32. use app\common\models\order\OrderSetting;
  33. use app\common\models\order\OrderTaxFee;
  34. use app\common\models\order\Plugin;
  35. use app\common\models\order\Remark;
  36. use app\common\models\refund\RefundApply;
  37. use app\common\models\refund\RefundGoodsLog;
  38. use app\common\models\refund\RefundProcessLog;
  39. use app\common\modules\order\OrderOperationsCollector;
  40. use app\common\modules\payType\events\AfterOrderPayTypeChangedEvent;
  41. use app\common\modules\refund\services\RefundService;
  42. use app\common\modules\shop\ShopConfig;
  43. use app\common\services\PayFactory;
  44. use app\common\traits\HasProcessTrait;
  45. use app\frontend\modules\order\OrderCollection;
  46. use app\frontend\modules\order\services\OrderService;
  47. use app\frontend\modules\order\services\status\StatusFactory;
  48. use app\frontend\modules\orderPay\models\PreOrderPay;
  49. use app\host\HostManager;
  50. use app\Jobs\OrderCreatedEventQueueJob;
  51. use app\Jobs\OrderPaidEventQueueJob;
  52. use app\Jobs\OrderReceivedEventQueueJob;
  53. use app\Jobs\OrderSentEventQueueJob;
  54. use Carbon\Carbon;
  55. use Illuminate\Database\Eloquent\Builder;
  56. use Illuminate\Database\Eloquent\Collection;
  57. use Illuminate\Foundation\Bus\DispatchesJobs;
  58. use Illuminate\Support\Facades\DB;
  59. use Yunshop\JdSupply\models\JdSupplyOrderGoods;
  60. use Yunshop\PackageDeliver\model\PackageDeliverOrder;
  61. use Yunshop\StoreCashier\common\models\CashierOrder;
  62. use Yunshop\StoreCashier\common\models\StoreOrder;
  63. use Yunshop\Supplier\common\models\InsuranceOrder;
  64. use app\common\services\SystemMsgService;
  65. use Yunshop\Supplier\supplier\models\SupplierOrder;
  66. /**
  67. * Class Order
  68. * @package app\common\models
  69. * @property int uniacid
  70. * @property int id
  71. * @property int uid
  72. * @property string order_sn
  73. * @property int price
  74. * @property string statusName
  75. * @property string statusCode
  76. * @property int status
  77. * @property int pay_type_name
  78. * @property int pay_type_id
  79. * @property int order_pay_id
  80. * @property int is_pending
  81. * @property int is_virtual
  82. * @property int dispatch_type_id
  83. * @property int refund_id
  84. * @property int no_refund
  85. * @property float deduction_price
  86. * @property float order_goods_price
  87. * @property float discount_price
  88. * @property float dispatch_price
  89. * @property float change_price
  90. * @property float cost_amount
  91. * @property float change_dispatch_price
  92. * @property float fee_amount
  93. * @property float service_fee_amount
  94. * @property int plugin_id
  95. * @property int is_plugin
  96. * @property Collection orderGoods
  97. * @property Collection hasManyOrderGoods
  98. * @property Collection allStatus
  99. * @property Collection orderCoinExchanges
  100. * @property Member belongsToMember
  101. * @property OrderDiscount discount
  102. * @property Collection orderPays
  103. * @property OrderPay hasOneOrderPay
  104. * @property PayType hasOnePayType
  105. * @property RefundApply hasOneRefundApply
  106. * @property Carbon finish_time
  107. * @property OrderCreatedJob orderCreatedJob
  108. * @property OrderPaidJob orderPaidJob
  109. * @property OrderReceivedJob orderReceivedJob
  110. * @property OrderSentJob orderSentJob
  111. * @property Address address
  112. * @property Address orderAddress
  113. * @property Express express
  114. * @method static self isPlugin()
  115. * @method static self orders(array $searchParam)
  116. * @method static self cancelled()
  117. */
  118. class Order extends BaseModel
  119. {
  120. use HasProcessTrait, DispatchesJobs;
  121. public $table = 'yz_order';
  122. public $setting = null;
  123. private $StatusService;
  124. protected $guarded = ['id'];
  125. protected $appends = ['status_name', 'pay_type_name'];
  126. protected $with = ['process', 'hasOnePayType'];
  127. protected $search_fields = ['yz_order.id', 'yz_order.order_sn'];
  128. protected $attributes = [
  129. 'plugin_id' => 0,
  130. 'is_virtual' => 0,
  131. ];
  132. static protected $needLog = true;
  133. //protected $attributes = ['discount_price'=>0];
  134. const CLOSE = -1;
  135. const WAIT_PAY = 0;
  136. const WAIT_SEND = 1;
  137. const WAIT_RECEIVE = 2;
  138. const COMPLETE = 3;
  139. const REFUND = 11;
  140. /**
  141. * 时间类型字段
  142. * @return array
  143. */
  144. public function getDates()
  145. {
  146. return ['create_time', 'refund_time', 'operate_time', 'send_time', 'return_time', 'end_time', 'pay_time', 'send_time', 'cancel_time', 'create_time', 'cancel_pay_time', 'cancel_send_time', 'finish_time'] + parent::getDates();
  147. }
  148. /**
  149. * 获取用户消费次数
  150. *
  151. * @param $uid
  152. * @return mixed
  153. */
  154. public static function getCostTotalNum($uid)
  155. {
  156. return self::where('status', '>=', 1)
  157. ->Where('status', '<=', 3)
  158. ->where('uid', $uid)
  159. ->count('id');
  160. }
  161. /**
  162. * 隐藏插件订单
  163. * 订单流程和标准订单不一样的插件订单,不显示在前端我的订单里
  164. * @param $query
  165. * @return mixed
  166. */
  167. public function scopeHidePluginIds($query, $plugin_ids = [], $other_plugin_ids = [])
  168. {
  169. if (empty($plugin_ids)) {
  170. //酒店订单、租赁订单、网约车订单、服务站补货订单、拼团订单、拼购订单、抢团订单、聚合CPS订单、门店余额充值订单,益生线下订单,圈仓订单(购买,提货),新拼团订单,蛋糕叔叔,周边游订单,任务包复活订单
  171. $plugin_ids = [33, 40, 41, 43, 54, 59, 69, 46, 70, 106, 96, 77, 78, 115, 74, 99, 39, 127,128,62,147,151,133,144,154,155,156,157,158,159];
  172. }
  173. if ($other_plugin_ids) {
  174. $plugin_ids = array_values(array_diff($plugin_ids, $other_plugin_ids));
  175. }
  176. return $query->whereNotIn('plugin_id', $plugin_ids)->where('plugin_id', '<', '900');
  177. }
  178. /**
  179. * 获取用户消费总额
  180. *
  181. * @param $uid
  182. * @return mixed
  183. */
  184. public static function getCostTotalPrice($uid)
  185. {
  186. return self::where('status', '>=', 1)
  187. ->where('status', '<=', 3)
  188. ->where('uid', $uid)
  189. ->sum('price');
  190. }
  191. //获取发票信息
  192. public static function getInvoice($order)
  193. {
  194. //return self ::select('invoice_type','rise_type','call','company_number','invoice')
  195. return self::select('invoice_type', 'rise_type', 'collect_name', 'company_number', 'invoice')
  196. ->where('id', $order)
  197. ->first();
  198. }
  199. public function scopePayFail($query)
  200. {
  201. return $query->where('refund_id', '0');
  202. }
  203. /**
  204. * 订单状态:待付款
  205. * @param $query
  206. * @return mixed
  207. */
  208. public function scopeWaitPay($query)
  209. {
  210. //AND o.status = 0 and o.paytype<>3
  211. return $query->where([$this->getTable() . '.status' => self::WAIT_PAY]);
  212. }
  213. public function scopeNormal($query)
  214. {
  215. return $query->where('refund_id', 0)->where('is_pending', 0);
  216. }
  217. /**
  218. * 订单状态:待发货
  219. * @param $query
  220. * @return mixed
  221. */
  222. public function scopeWaitSend($query)
  223. {
  224. //AND ( o.status = 1 or (o.status=0 and o.paytype=3) )
  225. return $query->where([$this->getTable() . '.status' => self::WAIT_SEND]);
  226. }
  227. /**
  228. * 订单状态:待收货
  229. * @param $query
  230. * @return mixed
  231. */
  232. public function scopeWaitReceive($query)
  233. {
  234. return $query->where([$this->getTable() . '.status' => self::WAIT_RECEIVE]);
  235. }
  236. /**
  237. * 订单状态:完成
  238. * @param $query
  239. * @return mixed
  240. */
  241. public function scopeCompleted($query)
  242. {
  243. return $query->where([$this->getTable() . '.status' => self::COMPLETE]);
  244. }
  245. /**
  246. * 订单状态:退款中
  247. * @param $query
  248. * @return mixed
  249. */
  250. public function scopeRefund($query)
  251. {
  252. return $query->where('refund_id', '>', '0')->whereHas('hasOneRefundApply', function ($query) {
  253. return $query->refunding();
  254. });
  255. }
  256. /**
  257. * 订单状态:已退款
  258. * @param $query
  259. * @return mixed
  260. */
  261. public function scopeRefunded($query)
  262. {
  263. return $query->where('refund_id', '>', '0')->whereHas('hasOneRefundApply', function ($query) {
  264. return $query->refunded()->where('refund_type', '<', 2);
  265. });
  266. }
  267. /**
  268. * 订单状态:取消
  269. * @param $query
  270. * @return mixed
  271. */
  272. public function scopeCancelled($query)
  273. {
  274. return $query->where([$this->getTable() .'.status' => self::CLOSE]);
  275. }
  276. /**
  277. * 关联模型 1对多:订单商品
  278. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  279. * @throws \app\common\exceptions\ShopException
  280. */
  281. public function hasManyOrderGoods()
  282. {
  283. return $this->hasMany(self::getNearestModel('OrderGoods'), 'order_id', 'id');
  284. }
  285. /**
  286. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  287. * @throws \app\common\exceptions\ShopException
  288. */
  289. public function orderGoods()
  290. {
  291. return $this->hasMany(self::getNearestModel('OrderGoods'), 'order_id', 'id');
  292. }
  293. /**
  294. * 关联模型 1对多:订单优惠信息
  295. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  296. */
  297. public function discounts()
  298. {
  299. return $this->hasMany(app('OrderManager')->make('OrderDiscount'), 'order_id', 'id');
  300. }
  301. /**
  302. * 关联模型 1对多:订单抵扣信息
  303. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  304. */
  305. public function deductions()
  306. {
  307. return $this->hasMany(app('OrderManager')->make('OrderDeduction'), 'order_id', 'id');
  308. }
  309. /**
  310. * 关联模型 1对多:订单信息
  311. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  312. */
  313. public function coupons()
  314. {
  315. return $this->hasMany(app('OrderManager')->make('OrderCoupon'), 'order_id', 'id');
  316. }
  317. public function orderCoupons()
  318. {
  319. return $this->hasMany(app('OrderManager')->make('OrderCoupon'), 'order_id', 'id');
  320. }
  321. /**
  322. * 关联模型 1对多:改价记录
  323. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  324. */
  325. public function orderChangePriceLogs()
  326. {
  327. return $this->hasMany(OrderChangePriceLog::class, 'order_id', 'id');
  328. }
  329. public function hasOneStoreOrder()
  330. {
  331. return $this->hasOne(StoreOrder::class, 'order_id', 'id');
  332. }
  333. public function hasOneCashierOrder()
  334. {
  335. return $this->hasOne(CashierOrder::class, 'order_id', 'id');
  336. }
  337. public function hasManyInsOrder()
  338. {
  339. return $this->hasMany(InsuranceOrder::class, 'order_id', 'id');
  340. }
  341. public function hasManyJdOrderGoods()
  342. {
  343. return $this->hasMany(JdSupplyOrderGoods::Class, 'order_id', 'id');
  344. }
  345. /**
  346. * 关联模型 1对1:购买者
  347. * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
  348. */
  349. public function belongsToMember()
  350. {
  351. return $this->belongsTo(Member::class, 'uid', 'uid');
  352. }
  353. /**
  354. * 关联模型 1对1:进行中的退款申请记录
  355. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  356. */
  357. public function hasOneRefundApply()
  358. {
  359. return $this->hasOne(RefundApply::class, 'id', 'refund_id')->orderBy('created_at', 'desc');
  360. }
  361. /**
  362. * 关联模型 1对n:退款列表
  363. * @return \Illuminate\Database\Eloquent\Relations\hasMany
  364. */
  365. public function hasManyRefundApply()
  366. {
  367. return $this->hasMany(RefundApply::class, 'order_id', 'id')->orderBy('created_at', 'desc');
  368. }
  369. // 关联模型 1对n:退款操作记录列表
  370. public function refundProcessLog()
  371. {
  372. return $this->hasMany(RefundProcessLog::class, 'order_id', 'id');
  373. }
  374. /**
  375. * 关联模型 1对多:订单运费抵扣信息
  376. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  377. */
  378. public function freightDeductions()
  379. {
  380. return $this->hasMany(OrderFreightDeduction::class, 'order_id', 'id');
  381. }
  382. /**
  383. * 关联模型 1对1:订单配送方式
  384. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  385. */
  386. public function hasOneDispatchType()
  387. {
  388. return $this->hasOne(DispatchType::class, 'id', 'dispatch_type_id');
  389. }
  390. /**
  391. * 关联模型 1对1:订单备注
  392. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  393. */
  394. public function hasOneOrderRemark()
  395. {
  396. return $this->hasOne(Remark::class, 'order_id', 'id');
  397. }
  398. /**
  399. * 关联模型 1对1:订单退款并关闭
  400. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  401. */
  402. public function manualRefundLog()
  403. {
  404. return $this->hasOne(ManualRefundLog::class, 'order_id', 'id');
  405. }
  406. /**
  407. * 关联模型 1对1:支付方式
  408. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  409. */
  410. public function hasOnePayType()
  411. {
  412. return $this->hasOne(PayType::class, 'id', 'pay_type_id');
  413. }
  414. /**
  415. * 代付记录
  416. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  417. */
  418. public function hasOneBehalfPay()
  419. {
  420. return $this->hasOne(OrderBehalfPayRecord::class, 'order_pay_id', 'order_pay_id');
  421. }
  422. /**
  423. * 关联模型 1对1:订单支付信息
  424. * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
  425. */
  426. public function hasOneOrderPay()
  427. {
  428. return $this->belongsTo(OrderPay::class, 'order_pay_id', 'id');
  429. }
  430. /**
  431. * 关联模型 1对1:订单快递
  432. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  433. */
  434. public function express()
  435. {
  436. return $this->hasOne(Express::class, 'order_id', 'id');
  437. }
  438. /**
  439. * 关联模型 1对多:订单快递
  440. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  441. */
  442. public function expressmany()
  443. {
  444. return $this->hasMany(Express::class, 'order_id', 'id');
  445. }
  446. /**
  447. * 对应每个订单状态的状态类,过于啰嗦,考虑删除
  448. * @return \app\frontend\modules\order\services\status\Complete|\app\frontend\modules\order\services\status\WaitPay|\app\frontend\modules\order\services\status\WaitReceive|\app\frontend\modules\order\services\status\WaitSend
  449. */
  450. public function getStatusService()
  451. {
  452. if (!isset($this->StatusService)) {
  453. $this->StatusService = (new StatusFactory($this))->create();
  454. }
  455. return $this->StatusService;
  456. }
  457. /**
  458. * 关联模型 1对1:收货地址
  459. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  460. */
  461. public function address()
  462. {
  463. return $this->hasOne(OrderAddress::class, 'order_id', 'id');
  464. }
  465. public function orderAddress()
  466. {
  467. return $this->hasOne(OrderAddress::class, 'order_id', 'id');
  468. }
  469. /**
  470. * 关联模型 1对1:订单发票信息
  471. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  472. */
  473. public function orderInvoice()
  474. {
  475. return $this->hasOne(OrderInvoice::class, 'order_id', 'id');
  476. }
  477. /**
  478. * 关联模型 1对1:订单支付信息
  479. * @return \Illuminate\Database\Eloquent\Relations\HasOne
  480. */
  481. public function hasOnePay()
  482. {
  483. return $this->hasOne(OrderPay::class, 'order_id', 'id');
  484. }
  485. public function getStatusCodeAttribute()
  486. {
  487. return app('OrderManager')->setting('status')[$this->status];
  488. }
  489. /**
  490. * @return array
  491. */
  492. public function getOperationsSetting()
  493. {
  494. return [];
  495. }
  496. public function isClose()
  497. {
  498. return $this->status == self::CLOSE;
  499. }
  500. /**
  501. * 订单状态汉字
  502. * @return string
  503. */
  504. public function getStatusNameAttribute()
  505. {
  506. if (!$this->isClose() && $this->currentProcess()) {
  507. return $this->currentProcess()->status_name;
  508. }
  509. $statusName = $this->getStatusService()->getStatusName();
  510. if ($this->isPending()) {
  511. $statusName .= ' : 锁定';
  512. }
  513. return $statusName;
  514. }
  515. /**
  516. * 支付类型汉字
  517. * @return string
  518. */
  519. public function getPayTypeNameAttribute()
  520. {
  521. if ($this->pay_type_id != PayType::CASH_PAY && $this->status == self::WAIT_PAY) {
  522. return '未支付';
  523. }
  524. $append = '';
  525. if ($this->hasOneBehalfPay) {
  526. $append = "(代付:{$this->hasOneBehalfPay->behalf_id})";
  527. }
  528. if ($this->pay_type_id == 3) {
  529. $set = \Setting::get('shop.shop');
  530. return ($set['credit'] ?: '余额') . $append;
  531. }
  532. return $this->hasOnePayType->name . $append;
  533. }
  534. /**
  535. * 订单可点的按钮
  536. * @return array
  537. */
  538. public function getButtonModelsAttribute()
  539. {
  540. $result = $this->memberButtons();
  541. return $result;
  542. }
  543. private function memberButtons()
  544. {
  545. return app('OrderManager')->make(OrderOperationsCollector::class)->getOperations($this);
  546. }
  547. /**
  548. * 按状态分组获取订单数量
  549. * @param $query
  550. * @param array $status
  551. * @return array
  552. */
  553. public function scopeGetOrderCountGroupByStatus($query, $status = [])
  554. {
  555. $newQuery = $query->newQuery();
  556. //dump($newQuery->dump());
  557. //$status = [Order::WAIT_PAY, Order::WAIT_SEND, Order::WAIT_RECEIVE, Order::COMPLETE, Order::REFUND];
  558. $status_counts = $query->select('status', DB::raw('count(*) as total'))
  559. ->whereIn('status', $status)->where('plugin_id', '<', 900)
  560. ->HidePluginIds()
  561. ->groupBy('status')->get()->makeHidden(['status_name', 'pay_type_name', 'has_one_pay_type', 'button_models'])
  562. ->toArray();
  563. $refund_status = [];
  564. $icon = [
  565. Order::REFUND => 'icon-fontclass-shouhouliebiao',
  566. Order::WAIT_PAY => 'icon-fontclass-daifukuan',
  567. Order::WAIT_SEND => 'icon-fontclass-daifahuo',
  568. Order::WAIT_RECEIVE => 'icon-fontclass-daishouhuo1',
  569. Order::COMPLETE => 'icon-fontclass-daishouhuo1',
  570. ];
  571. if (in_array(Order::REFUND, $status)) {
  572. $refund_count = \app\frontend\models\Order::select(DB::raw('count(*) as total'))
  573. ->where('status', '>', self::WAIT_PAY)->where('plugin_id', '<', 900)
  574. ->hidePluginIds()->refund()->count();
  575. // $refund_count = $query->refund()->count();
  576. $refund_status[] = [
  577. 'status' => Order::REFUND,
  578. 'status_name' => '售后列表',
  579. 'class' => $icon[Order::REFUND],
  580. 'total' => $refund_count
  581. ];
  582. }
  583. $status_counts = array_column($status_counts, null, 'status');
  584. foreach ($status as $state) {
  585. if (!in_array($state, array_column($refund_status, 'status'))) {
  586. $refund_status[] = [
  587. 'status' => $state,
  588. 'status_name' => $this->getAllStatusAttribute()->where('id', $state)->first()['name'] ?: '',
  589. 'class' => $icon[$state],
  590. 'total' => $status_counts[$state]['total'] ?: 0
  591. ];
  592. }
  593. }
  594. return $refund_status;
  595. }
  596. /**
  597. * 区分订单属于插件或商城,考虑使用新添加的scopePluginId方法替代
  598. * @param $query
  599. * @return mixed
  600. */
  601. public function scopeIsPlugin($query)
  602. {
  603. return $query->where('is_plugin', 0);
  604. }
  605. /**
  606. * 用来区分订单属于哪个.当插件需要查询自己的订单时,复写此方法
  607. * @param $query
  608. * @param int $pluginId
  609. * @return mixed
  610. */
  611. public function scopePluginId($query, $pluginId = 0)
  612. {
  613. return $query->where('plugin_id', $pluginId);
  614. }
  615. /**
  616. * @return \Illuminate\Database\Eloquent\Relations\HasMany
  617. */
  618. public function orderPlugin()
  619. {
  620. return $this->hasMany(Plugin::class);
  621. }
  622. /**
  623. * 用来区分订单属于哪个.当插件需要查询自己的订单时,复写此方法
  624. * @param $query
  625. * @param int $pluginId
  626. * @return mixed
  627. */
  628. public function scopeHasPluginId($query, $pluginId = 0)
  629. {
  630. if (!$pluginId) {
  631. return $query;
  632. }
  633. return $query->whereHas('orderPlugin', function ($query) use ($pluginId) {
  634. $query->where('plugin_id', $pluginId);
  635. });
  636. }
  637. /**
  638. * 通过会员ID获取订单信息
  639. * @param $member_id
  640. * @param $status
  641. * @return mixed
  642. */
  643. public static function getOrderInfoByMemberId($member_id, $status)
  644. {
  645. return self::where('uid', $member_id)->isComment($status);
  646. }
  647. /**
  648. * 关系链 指定商品
  649. *
  650. * @param $uid
  651. * @return \Illuminate\Database\Eloquent\Collection|static[]
  652. */
  653. public static function getOrderListByUid($uid)
  654. {
  655. return self::select(['*'])
  656. ->where('uid', $uid)
  657. ->where('status', '>=', 1)
  658. ->where('status', '<=', 3)
  659. ->with(['hasManyOrderGoods' => function ($query) {
  660. return $query->select(['*']);
  661. }])
  662. ->get();
  663. }
  664. public function isVirtual()
  665. {
  666. return $this->is_virtual == 1;
  667. }
  668. public function orderDeduction()
  669. {
  670. return $this->hasMany(OrderDeduction::class, 'order_id', 'id');
  671. }
  672. public function orderDeductions()
  673. {
  674. return $this->hasMany(OrderDeduction::class, 'order_id', 'id');
  675. }
  676. public function orderCoupon()
  677. {
  678. return $this->hasMany(OrderCoupon::class, 'order_id', 'id');
  679. }
  680. public function orderDiscounts()
  681. {
  682. return $this->hasMany(OrderDiscount::class, 'order_id', 'id');
  683. }
  684. //订单手续费
  685. public function orderFees()
  686. {
  687. return $this->hasMany(OrderFee::class, 'order_id', 'id');
  688. }
  689. //订单税率优惠
  690. public function orderTaxFees()
  691. {
  692. return $this->hasMany(OrderTaxFee::class, 'order_id', 'id');
  693. }
  694. //订单服务费
  695. public function orderServiceFees()
  696. {
  697. return $this->hasMany(OrderServiceFee::class, 'order_id', 'id');
  698. }
  699. public function orderDiscount()
  700. {
  701. return $this->hasMany(OrderDiscount::class, 'order_id', 'id');
  702. }
  703. public function receive()
  704. {
  705. return \app\frontend\modules\order\services\OrderService::orderReceive(['order_id' => $this->id]);
  706. }
  707. public function orderPays()
  708. {
  709. return $this->belongsToMany(OrderPay::class, (new OrderPayOrder())->getTable(), 'order_id', 'order_pay_id');
  710. }
  711. public function memberCancel()
  712. {
  713. return $this->hasOne(MemberCancel::class, 'member_id', 'uid');
  714. }
  715. /**
  716. * 订单直接关闭
  717. * @return mixed
  718. */
  719. public function close()
  720. {
  721. return \app\backend\modules\order\services\OrderService::close($this);
  722. }
  723. /**
  724. * 取消订单退款状态
  725. * @return mixed
  726. */
  727. public function cancelRefund()
  728. {
  729. return \app\backend\modules\order\services\OrderService::cancelRefund($this);
  730. }
  731. /**
  732. * 初始化方法
  733. */
  734. public static function boot()
  735. {
  736. parent::boot();
  737. static::observe(new OrderObserver());
  738. // 添加了公众号id的全局条件.
  739. static::addGlobalScope(function (Builder $builder) {
  740. $builder->uniacid();
  741. $builder->hasPluginId();
  742. });
  743. }
  744. public function needSend()
  745. {
  746. return isset($this->hasOneDispatchType) && $this->hasOneDispatchType->needSend();
  747. }
  748. public function orderSettings()
  749. {
  750. return $this->hasMany(OrderSetting::class, 'order_id', 'id');
  751. }
  752. public function setPayTypeIdAttribute($value)
  753. {
  754. $this->attributes['pay_type_id'] = $value;
  755. if ($this->pay_type_id != $this->getOriginal('pay_type_id')) {
  756. event(new AfterOrderPayTypeChangedEvent($this));
  757. }
  758. }
  759. /**
  760. * @param $value
  761. * @throws AppException
  762. */
  763. public function setStatusAttribute($value)
  764. {
  765. if ($this->isPending()) {
  766. throw new AppException("订单已锁定,无法继续操作");
  767. }
  768. $this->attributes['status'] = $value;
  769. }
  770. public function isPending()
  771. {
  772. return $this->is_pending;
  773. }
  774. public function getSetting($key)
  775. {
  776. // 全局设置
  777. $result = \app\common\facades\Setting::get($key);
  778. if (isset($this->orderSettings) && $this->orderSettings->isNotEmpty()) {
  779. // 订单设置
  780. $keys = collect(explode('.', $key));
  781. $orderSettingKey = $keys->shift();
  782. if ($orderSettingKey == 'plugin') {
  783. // 获取第一个不为plugin的key
  784. $orderSettingKey = $keys->shift();
  785. }
  786. $orderSettingValueKeys = $keys;
  787. if ($orderSettingValueKeys->isNotEmpty()) {
  788. $orderSettingValue = array_get($this->orderSettings->where('key', $orderSettingKey)->first()->value, $orderSettingValueKeys->implode('.'));
  789. } else {
  790. $orderSettingValue = $this->orderSettings->where('key', $orderSettingKey)->first()->value;
  791. }
  792. if (isset($orderSettingValue)) {
  793. if (is_array($result)) {
  794. // 数组合并
  795. $result = array_merge($result, $orderSettingValue);
  796. } else {
  797. // 其他覆盖
  798. $result = $orderSettingValue;
  799. }
  800. }
  801. }
  802. return $result;
  803. }
  804. //关联商城订单表
  805. public function hasOneMemberShopInfo()
  806. {
  807. return $this->hasOne(MemberShopInfo::class, 'member_id', 'uid');
  808. }
  809. /**
  810. * 已退款
  811. * @return bool
  812. */
  813. public function isRefunded()
  814. {
  815. // 存在处理中的退款申请
  816. if (empty($this->refund_id) || !isset($this->hasOneRefundApply)) {
  817. return false;
  818. }
  819. if ($this->hasOneRefundApply->isRefunded()) {
  820. return true;
  821. }
  822. return false;
  823. }
  824. /**
  825. * 退款中
  826. * @return bool
  827. */
  828. public function isRefunding()
  829. {
  830. // 存在处理中的退款申请
  831. if (empty($this->refund_id) || !isset($this->hasOneRefundApply)) {
  832. return false;
  833. }
  834. if ($this->hasOneRefundApply->isRefunding()) {
  835. return true;
  836. }
  837. return false;
  838. }
  839. /**
  840. * 可以退款
  841. * @return bool
  842. */
  843. public function canRefund()
  844. {
  845. $shop_set = \Setting::get('shop.trade');
  846. //关闭后不许退款
  847. if (!RefundService::allowRefund()) {
  848. return false;
  849. }
  850. //收货后禁止退款
  851. if ($this->status>=2) {
  852. if (in_array($this->plugin_id, [31,32])) {
  853. if (app('plugins')->isEnabled('store-cashier')) {
  854. $store_order = StoreOrder::where('order_id', $this->id)->first();
  855. if ($store_order) {
  856. $store_id = $store_order->store_id;
  857. } else {
  858. $cashier_order = CashierOrder::where('order_id', $this->id)->first();
  859. $store_id = \Yunshop\StoreCashier\common\models\Store::uniacid()->where('cashier_id', $cashier_order->cashier_id)->value('id');
  860. }
  861. $store_trade_set = \Setting::get("store_cashier_{$store_id}.trade");
  862. if ($store_trade_set['send_refund_status']) {
  863. //0或空禁止退款
  864. $store_send_refund_time = $store_trade_set['send_refund_time'];
  865. if (!$store_send_refund_time) {
  866. return false;
  867. }
  868. if ($this->send_time->addMinutes($store_send_refund_time)->timestamp < time()) {
  869. return false;
  870. }
  871. } else {
  872. if ($shop_set['send_refund_status']) {
  873. //0或空禁止退款
  874. $send_refund_time = $shop_set['send_refund_time'];
  875. if (!$send_refund_time) {
  876. return false;
  877. }
  878. if ($this->send_time->addMinutes($send_refund_time)->timestamp < time()) {
  879. return false;
  880. }
  881. }
  882. }
  883. } else {
  884. if ($shop_set['send_refund_status']) {
  885. //0或空禁止退款
  886. $send_refund_time = $shop_set['send_refund_time'];
  887. if (!$send_refund_time) {
  888. return false;
  889. }
  890. if ($this->send_time->addMinutes($send_refund_time)->timestamp < time()) {
  891. return false;
  892. }
  893. }
  894. }
  895. } elseif ($this->plugin_id == 92) {
  896. if (app('plugins')->isEnabled('supplier')) {
  897. $supplier_order = SupplierOrder::uniacid()->where('order_id', $this->id)->first();
  898. $supplier_trade_set = \Setting::get("plugin.supplier.trade_{$supplier_order->supplier_id}");
  899. if ($supplier_trade_set['send_refund_status']) {
  900. //0或空禁止退款
  901. $store_send_refund_time = $supplier_trade_set['send_refund_time'];
  902. if (!$store_send_refund_time) {
  903. return false;
  904. }
  905. if ($this->send_time->addMinutes($store_send_refund_time)->timestamp < time()) {
  906. return false;
  907. }
  908. } else {
  909. if ($shop_set['send_refund_status']) {
  910. //0或空禁止退款
  911. $send_refund_time = $shop_set['send_refund_time'];
  912. if (!$send_refund_time) {
  913. return false;
  914. }
  915. if ($this->send_time->addMinutes($send_refund_time)->timestamp < time()) {
  916. return false;
  917. }
  918. }
  919. }
  920. } else {
  921. if ($shop_set['send_refund_status']) {
  922. //0或空禁止退款
  923. $send_refund_time = $shop_set['send_refund_time'];
  924. if (!$send_refund_time) {
  925. return false;
  926. }
  927. if ($this->send_time->addMinutes($send_refund_time)->timestamp < time()) {
  928. return false;
  929. }
  930. }
  931. }
  932. } else {
  933. if ($shop_set['send_refund_status']) {
  934. //0或空禁止退款
  935. $send_refund_time = $shop_set['send_refund_time'];
  936. if (!$send_refund_time) {
  937. return false;
  938. }
  939. if ($this->send_time->addMinutes($send_refund_time)->timestamp < time()) {
  940. return false;
  941. }
  942. }
  943. }
  944. }
  945. if ($this->status == self::COMPLETE) {
  946. // 完成后n天不许退款
  947. if ($this->finish_time && $this->finish_time->diffInDays() >= $shop_set['refund_days']) {
  948. return false;
  949. }
  950. // 完成后不许退款
  951. if ($shop_set['refund_days'] === '0') {
  952. return false;
  953. }
  954. }
  955. // 存在处理中的退款申请
  956. if (!empty($this->refund_id) || isset($this->hasOneRefundApply)) {
  957. return false;
  958. }
  959. if (app('plugins')->isEnabled('blind-box')) {
  960. $has = \Yunshop\BlindBox\models\BlindBoxSubOrderModel::where('sub_order_id',$this->id)->count();
  961. if ($has) {//盲盒子订单不给退
  962. return false;
  963. }
  964. }
  965. return true;
  966. }
  967. /**
  968. * 可以部分退款,用于后端列表部分退款按钮显示
  969. * @return bool
  970. */
  971. public function canPartRefund()
  972. {
  973. if (!$this->canRefund()) {
  974. return false;
  975. }
  976. // 如果商品为1则不部分退款
  977. if ($this->goods_total <= 1) {
  978. return false;
  979. }
  980. return true;
  981. }
  982. public function getAllStatusAttribute()
  983. {
  984. return collect([
  985. [
  986. 'id' => self::CLOSE,
  987. 'name' => '已关闭',
  988. ], [
  989. 'id' => self::WAIT_PAY,
  990. 'name' => '待支付',
  991. ], [
  992. 'id' => self::WAIT_SEND,
  993. 'name' => '待发货',
  994. ], [
  995. 'id' => self::WAIT_RECEIVE,
  996. 'name' => '待收货',
  997. ], [
  998. 'id' => self::COMPLETE,
  999. 'name' => '已完成',
  1000. ], [
  1001. 'id' => self::REFUND,
  1002. 'name' => '已退款',
  1003. ],
  1004. ]);
  1005. }
  1006. /**
  1007. * 后台支付
  1008. * @throws AppException
  1009. */
  1010. public function backendPay()
  1011. {
  1012. // 生成支付记录 记录订单号,支付金额,用户,支付号
  1013. $orderPay = new PreOrderPay(['pay_type_id' => PayType::BACKEND]);
  1014. // 添加关联订单
  1015. $orders = new OrderCollection([$this]);
  1016. $orderPay->setOrders($orders);
  1017. $orderPay->store();
  1018. // 获取支付信息
  1019. $orderPay->getPayResult(PayFactory::PAY_BACKEND);
  1020. // 保存支付状态
  1021. $orderPay->pay();
  1022. }
  1023. /**
  1024. * 系统退款
  1025. * @throws AppException
  1026. */
  1027. public function refund()
  1028. {
  1029. $result = $this->hasOneOrderPay->fastRefund($this);
  1030. if (!$result['status']) {
  1031. throw new AppException($result['msg']);
  1032. }
  1033. OrderService::orderForceClose(['order_id' => $this->id]);
  1034. return $result;
  1035. }
  1036. /**
  1037. * 系统退款(不管什么类型都退回余额)
  1038. * @throws AppException
  1039. */
  1040. public function refund2()
  1041. {
  1042. $result = $this->hasOneOrderPay->fastRefund2($this);
  1043. OrderService::orderForceClose(['order_id' => $this->id]);
  1044. return $result;
  1045. }
  1046. public function fireCreatedEvent()
  1047. {
  1048. event(new AfterOrderCreatedImmediatelyEvent($this));
  1049. OrderCreatedJob::create([
  1050. 'order_id' => $this->id,
  1051. ]);
  1052. $this->dispatch(new OrderCreatedEventQueueJob($this->id));
  1053. }
  1054. public function firePaidEvent()
  1055. {
  1056. event(new AfterOrderPaidImmediatelyEvent($this));
  1057. //异步
  1058. OrderPaidJob::create([
  1059. 'order_id' => $this->id,
  1060. ]);
  1061. $this->dispatch(new OrderPaidEventQueueJob($this->id));
  1062. }
  1063. public function fireSentEvent()
  1064. {
  1065. event(new AfterOrderSentImmediatelyEvent($this));
  1066. //异步
  1067. OrderSentJob::create([
  1068. 'order_id' => $this->id,
  1069. ]);
  1070. $this->dispatch(new OrderSentEventQueueJob($this->id));
  1071. }
  1072. public function fireReceivedEvent()
  1073. {
  1074. event(new AfterOrderReceivedImmediatelyEvent($this));
  1075. // 去掉同步设置(已没用,相关设置也注释掉了,之前为了解决成为分销商和分销升级异步问题)
  1076. // if (\Setting::get('shop.order.receive_process')) {
  1077. // //同步
  1078. // event(new AfterOrderReceivedEvent($this));
  1079. //
  1080. // } else {
  1081. //异步
  1082. OrderReceivedJob::create([
  1083. 'order_id' => $this->id,
  1084. ]);
  1085. $this->dispatch(new OrderReceivedEventQueueJob($this->id));
  1086. // }
  1087. }
  1088. //取消发货,删除队列记录
  1089. public function delOrderSent()
  1090. {
  1091. OrderSentJob::where('order_id', $this->id)->delete();
  1092. }
  1093. public function orderCreatedJob()
  1094. {
  1095. return $this->hasOne(OrderCreatedJob::class, 'order_id');
  1096. }
  1097. public function orderSentJob()
  1098. {
  1099. return $this->hasOne(OrderSentJob::class, 'order_id');
  1100. }
  1101. public function orderPaidJob()
  1102. {
  1103. return $this->hasOne(OrderPaidJob::class, 'order_id');
  1104. }
  1105. public function orderReceivedJob()
  1106. {
  1107. return $this->hasOne(OrderReceivedJob::class, 'order_id');
  1108. }
  1109. public function stockEnough()
  1110. {
  1111. $this->orderGoods->each(function (OrderGoods $orderGoods) {
  1112. // 付款后扣库存
  1113. if ($orderGoods->goods->reduce_stock_method == 1) {
  1114. $orderGoods->stockEnough();
  1115. }
  1116. });
  1117. }
  1118. public function orderRequest()
  1119. {
  1120. return $this->hasOne(OrderRequest::class, 'order_id');
  1121. }
  1122. public function orderCoinExchanges()
  1123. {
  1124. return $this->hasMany(OrderCoinExchange::class, 'order_id');
  1125. }
  1126. public function refundGoodsLog()
  1127. {
  1128. return $this->hasMany(refundGoodsLog::class, 'order_id');
  1129. }
  1130. static function queueCount()
  1131. {
  1132. $hostCount = count((new HostManager())->hosts() ?: []) ? : 1;
  1133. if ($count = SiteSettingFacades::get('queue.order')) {
  1134. return $count;
  1135. }
  1136. foreach (ShopConfig::current()->getItem('queue') as $item) {
  1137. if ($item['key'] == 'order') {
  1138. break;
  1139. }
  1140. }
  1141. $diy_count = SiteSettingFacades::get('queue.order', $item['total']);
  1142. if (!$diy_count) {
  1143. return $item['total'] * $hostCount;
  1144. }
  1145. return $diy_count * $hostCount;
  1146. }
  1147. /**
  1148. * 不发送消息通知的订单
  1149. * @return bool
  1150. */
  1151. public function notSendMessage()
  1152. {
  1153. //酒店有自己的消息通知
  1154. if ($this->plugin_id == 33) {
  1155. return true;
  1156. }
  1157. //聚合CPS的订单(不包括卡券订单)是每天凌晨定时任务请求第三方数据创建的,不发送消息通知
  1158. if ($this->plugin_id == 70) {
  1159. return true;
  1160. }
  1161. //芸cps的订单为同步第三方订单,不发送消息通知
  1162. if ($this->plugin_id == 74) {
  1163. return true;
  1164. }
  1165. //珍惠拼
  1166. if ($this->plugin_id == 115) {
  1167. if (!is_null(\app\common\modules\shop\ShopConfig::current()->get('zhp_group_lottery_sent_msg_status'))) {
  1168. $class = array_get(\app\common\modules\shop\ShopConfig::current()->get('zhp_group_lottery_sent_msg_status'), 'class');
  1169. $function = array_get(\app\common\modules\shop\ShopConfig::current()->get('zhp_group_lottery_sent_msg_status'), 'function');
  1170. $ret = $class::$function($this->id, $this->uid);//true
  1171. if ($ret) {
  1172. return $ret;
  1173. }
  1174. }
  1175. }
  1176. }
  1177. /**
  1178. * 是否盲盒订单(todo 已经有别的插件覆盖了原来的物流按钮配置,无法重写)
  1179. * @return bool
  1180. */
  1181. public function isBlindBox()
  1182. {
  1183. return $this->plugin_id == 107;
  1184. }
  1185. }