Orders.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. <?php
  2. namespace app\common\model;
  3. use app\admin\controller\City;
  4. use app\admin\model\Address;
  5. use app\admin\model\AdminMoneyLog;
  6. use app\common\service\GyeUdun;
  7. use app\common\service\UserSvc;
  8. use Carbon\Carbon;
  9. use think\Db;
  10. use think\db\Query;
  11. use think\Model;
  12. use Yansongda\Supports\Arr;
  13. /**
  14. * @property OrderInfo[] info
  15. * @property OrderAddress address
  16. * @property OrderLogistics logistics
  17. * @property User user
  18. * @property bool is_wait_pay
  19. * @property bool is_wait_send
  20. * @property bool is_payed
  21. * @property bool is_closed
  22. * @property Payment payment
  23. * @property int user_id
  24. * @property int id
  25. * @property int status
  26. * @property int admin_id
  27. * @property int coupon_id
  28. * @property int send_time
  29. * @property string order_no
  30. * @property float amount_cmn
  31. * @property float amount_pay
  32. * @property float amount_coupon
  33. * @method static static payed()
  34. * @method static static hasRefund()
  35. * @method static static statusPay()
  36. * @method Query hasGoods($goods_id)
  37. */
  38. class Orders extends Model
  39. {
  40. protected $type=[
  41. 'tax'=>'json',
  42. ];
  43. #未支付过期时间
  44. const EXP_PAY=1800;
  45. #线下付款超时
  46. const EXP_PAY_OFFLINE=3*86400;
  47. #代付超时
  48. const EXP_PAY_DF=3*86400;
  49. #待收货过期时间
  50. const EXP_REC=30*86400;
  51. #已完成可售后时间
  52. const EXP_OVER=7*86400;
  53. const PT_QYWY=1;
  54. const PT_WX=2;
  55. const PT_ZFB=3;
  56. const PT_YL=4;
  57. const PT_DF=5;
  58. const PT_OFF=6;
  59. const PT_GYEU=7;
  60. const PT_GYEUZJ=8;
  61. public static $pay_types=[
  62. self::PT_QYWY=>'企业网银',
  63. self::PT_WX=>'微信',
  64. self::PT_ZFB=>'支付宝',
  65. self::PT_YL=>'银联',
  66. self::PT_DF=>'代付',
  67. self::PT_OFF=>'线下支付',
  68. self::PT_GYEU=>'工银U盾保留支付',
  69. self::PT_GYEUZJ=>'工银U盾直接支付',
  70. ];
  71. const S_WAIT_PAY=0;
  72. const S_WAIT_SEND=5;
  73. const S_WAIT_REC=10;
  74. const S_OVER=20;
  75. const S_CANCEL=30;
  76. const S_REFUND=40;
  77. public static $status=[
  78. self::S_WAIT_PAY=>'待支付',
  79. self::S_WAIT_SEND=>'待发货',
  80. self::S_WAIT_REC=>'待收货',
  81. self::S_OVER=>'已完成',
  82. self::S_CANCEL=>'已取消',
  83. //self::S_REFUND=>'退款退货',
  84. ];
  85. public static function getStatus()
  86. {
  87. return self::$status;
  88. }
  89. protected $autoWriteTimestamp=true;
  90. public function info(){
  91. return $this->hasMany(OrderInfo::class,'order_id');
  92. }
  93. public function user(){
  94. return $this->belongsTo(User::class);
  95. }
  96. public function payment(){
  97. return $this->belongsTo(Payment::class);
  98. }
  99. public function address(){
  100. return $this->hasOne(OrderAddress::class,'order_id');
  101. }
  102. public function logistics(){
  103. return $this->hasOne(OrderLogistics::class,'order_id');
  104. }
  105. public function voucher(){
  106. return $this->belongsTo(OrderVoucher::class,'order_voucher_id');
  107. }
  108. /*public function getGoodsAttr(){
  109. $info=$this->info()->with(['goodsBak'])->find();
  110. $goods=$info['goodsBak'];
  111. return [
  112. 'goods'=>$goods['goods'],
  113. 'sku'=>$goods['sku'],
  114. ];
  115. }*/
  116. public function getIsWaitPayAttr($_,$model){
  117. return $model['status']==self::S_WAIT_PAY;
  118. }
  119. public function getIsWaitSendAttr($_,$model){
  120. return $model['status']==self::S_WAIT_SEND;
  121. }
  122. public function getIsEvaledAttr($_,$model){
  123. $goodsIds=array_unique(OrderInfo::where('order_id',$model['id'])->column('goods_id'));
  124. $has=GoodsEval::where('order_id',$model['id'])->whereIn('goods_id',$goodsIds)->count();
  125. return $has==count($goodsIds);
  126. }
  127. public function getPayTypeTextAttr($_,$model){
  128. if(empty($model['pay_type'])){
  129. return null;
  130. }
  131. return self::getPayTypes()[$model['pay_type']];
  132. }
  133. /**
  134. * @return string[]
  135. */
  136. public static function getPayTypes(): array
  137. {
  138. return self::$pay_types;
  139. }
  140. public static function continue($status){
  141. $now=time();
  142. return self::where('status',$status)->limit(20)->where('continue_expire_time','<',$now);
  143. }
  144. #未支付过期
  145. public function makeCancel(){
  146. $this['status']=self::S_CANCEL;
  147. $this['cancel_time']=time();
  148. foreach ($this->info as $orderInfo){
  149. $stock=$orderInfo['is_kill']?'num_stock_kill':'num_stock';
  150. try {
  151. Goods::where('id',$orderInfo['goods_id'])->setDec('num_sell',$orderInfo['num']);
  152. }catch (\Exception $e){
  153. user_log('orders/cancel',"订单{$this['id']}减已售失败");
  154. }
  155. try {
  156. GoodsSku::where('id',$orderInfo['goods_sku_id'])->setDec('num_sell',$orderInfo['num']);
  157. }catch (\Exception $e){
  158. user_log('orders/cancel',"订单{$this['id']}减sku已售失败");
  159. }
  160. try {
  161. GoodsSku::where('id',$orderInfo['goods_sku_id'])->setInc($stock,$orderInfo['num']);
  162. }catch (\Exception $e){
  163. user_log('orders/cancel',"订单{$this['id']}增库存【{$stock}】失败");
  164. }
  165. }
  166. #返还优惠券
  167. $hasCoupon=$this->info()->where('coupon_id','>',0)->value('coupon_id');
  168. if($hasCoupon){
  169. UserCoupon::makeUse($hasCoupon,0);
  170. }
  171. UserCoupon::makeUse($this->coupon_id,0);
  172. $this->save();
  173. }
  174. #待收货过期
  175. public function makeRec(){
  176. $this->makeOverUdun();
  177. $this->makeOver();
  178. }
  179. #支付
  180. public function makePayInfo($pay_type){
  181. $user=$this->user;
  182. $this['pay_type']=$pay_type;
  183. if($pay_type==self::PT_OFF){
  184. $this['continue_expire_time']=Carbon::now()->startOfDay()->addSeconds(self::EXP_PAY_OFFLINE+86400-1)->timestamp;
  185. $this->save();
  186. return [
  187. 'account_name'=>config('site.account_name'),
  188. 'bank_no'=>config('site.account_bank_no'),
  189. 'bank_name'=>config('site.accout_bank_name'),
  190. ];
  191. }elseif($pay_type==self::PT_DF){
  192. $this['continue_expire_time']=Carbon::now()->startOfDay()->addSeconds(self::EXP_PAY_DF+86400-1)->timestamp;
  193. $this->save();
  194. return [
  195. 'expire'=>$this['continue_expire_time'],
  196. ];
  197. }else{
  198. $this['continue_expire_time']=Carbon::now()->addSeconds(self::EXP_PAY)->timestamp;
  199. $this->save();
  200. }
  201. return Payment::pay(
  202. $user,
  203. $pay_type,
  204. $this['amount_pay'],
  205. $this['id'],
  206. "订单【{$this['order_no']}】付款",
  207. $this->getTable()
  208. );
  209. }
  210. #支付后
  211. public static function makePayed(Payment $payment){
  212. $order=Orders::find($payment['payment_id']);
  213. if(!$order){
  214. return false;
  215. }
  216. if(!$order->isNotPay()){
  217. return false;
  218. }
  219. $order['payment_id']=$payment['id'];
  220. #代付
  221. if($order['user_id']!=$payment->user_id){
  222. }
  223. $order->save();
  224. $order->makePay($payment['pay_type']);
  225. return true;
  226. }
  227. public function makePay($payType=self::PT_OFF){
  228. $order=$this;
  229. $order['status']=self::S_WAIT_SEND;
  230. $order['pay_time']=time();
  231. $order['pay_type']=$payType;
  232. $order->save();
  233. }
  234. #发货
  235. public function makeSend($logistics,$data){
  236. $newData=Arr::only($data,['com_id','trans_no','remark','from_area','from_address','from_username','from_mobile']);
  237. if(!$logistics) {
  238. $this->logistics()->save($newData);
  239. $this['status']=self::S_WAIT_REC;
  240. $this['send_time']=time();
  241. $this->save();
  242. }else{
  243. $logistics->save($newData);
  244. }
  245. }
  246. public function makeSendSelf(Address $address,$remark){
  247. $com=LogisticsCompany::getSelf();
  248. $orderAddress = OrderAddress::where(['order_id'=>$this->id])->find()->toArray();
  249. // dump($orderAddress);
  250. $newData=[
  251. 'com_id'=>$com->id,
  252. 'remark'=>$remark,
  253. 'from_area'=>$address->area,
  254. 'from_address'=>$address->address,
  255. 'from_username'=>$address['name'],
  256. 'from_mobile'=>$address['mobile'],
  257. //'md_link'=>$label,
  258. //'trans_no'=>$data['kuaidinum'],
  259. ];
  260. $newData = array_filter($newData);
  261. $cityId = explode(',',$orderAddress['area']);
  262. $toCity = Area::where(['id'=>$cityId[1]])->value('name');
  263. $fromCitys = Address::where(['id'=>$address->id])->value('area');
  264. $fromCity = explode("/",$fromCitys);
  265. $expType = '';
  266. if($toCity == $fromCity[1]){
  267. $expType = '同城次日';
  268. }
  269. $logistics=$this->logistics()->save($newData);
  270. $names=[];
  271. foreach ($this->info as $info){
  272. $names[]=sprintf('%s:%s',$info->goods_name,$info->sku_name);
  273. }
  274. list($res,$data)=logistics()
  275. ->setLogistics($com)
  276. ->setUserName($logistics->from_username)
  277. ->setPhone($logistics->from_mobile)
  278. ->setToArea($logistics->fullArea())
  279. ->setFromUsername($this->address['name'])
  280. ->setFromMobile($this->address['mobile'])
  281. ->setFromArea($this->address['address'])
  282. ->setCargo(implode('&',$names))
  283. ->setCount($this->info()->sum('num'))
  284. ->setOrderNo($this->order_no)
  285. ->setExpType($expType)
  286. ->labelOrder();
  287. if(!$res){
  288. throw_user($data);
  289. }
  290. $pubname='/uploads/ordermd/'.$this['id'];
  291. $dir=ROOT_PATH.'public'.$pubname;
  292. $filename=session_create_id().'.png';
  293. $saveName=$dir.'/'.$filename;
  294. if(!is_dir($dir)){
  295. @mkdir($dir,0755,true);
  296. }
  297. file_put_contents($saveName,file_get_contents($data['label']));
  298. $label=request()->domain().$pubname.'/'.$filename;
  299. $logistics->trans_no=$data['kuaidinum'];
  300. $logistics->md_link=$label;
  301. $logistics->save();
  302. $this['status']=self::S_WAIT_REC;
  303. $this['send_time']=time();
  304. $this->save();
  305. }
  306. #确认收货
  307. public function makeOver(){
  308. $this['status']=self::S_OVER;
  309. $this->save();
  310. }
  311. // 保留支付需要去银行确认
  312. public function makeOverUdun(){
  313. if ($this['pay_type'] == self::PT_GYEU) {
  314. //网银手动确认收货
  315. $payment = Payment::where('pay_type', self::PT_GYEU)->where('payment_id', $this['id'])->order('id', 'desc')->findOrFail();
  316. if (!$payment) {
  317. user_log('log/icbc', "未查询到支付记录" . json_encode($this));
  318. }
  319. GyeUdun::MybankPayCpayCppreservationpayV2Test($this['id'],$payment->order_no,moneyFormat($payment->amount,'f'));
  320. }
  321. }
  322. #发放提成
  323. public function makeSendCommission(){
  324. if($this->admin_id){
  325. AdminMoneyLog::money($this->admin_id,$this->amount_cmn,AdminMoneyLog::T_COMMISSION,$this['user_id'],$this['id']);
  326. }
  327. }
  328. /*
  329. * 是否未支付
  330. */
  331. public function isNotPay(){
  332. return $this['status']===self::S_WAIT_PAY;
  333. }
  334. /**
  335. * 是否已支付
  336. */
  337. public function getIsPayedAttr($_,$model){
  338. return in_array($model['status'],
  339. [
  340. self::S_WAIT_SEND,
  341. self::S_WAIT_REC,
  342. self::S_OVER,
  343. ]
  344. );
  345. }
  346. /**
  347. * 订单是否已关闭
  348. */
  349. public function getIsClosedAttr($_,$model){
  350. return in_array($model['status'],
  351. [
  352. self::S_CANCEL,
  353. ]
  354. );
  355. }
  356. /**
  357. * 是否允许退款
  358. */
  359. public function allowRefund(){
  360. if(in_array($this['status'],[
  361. self::S_WAIT_PAY,
  362. self::S_CANCEL,
  363. ])){
  364. throw_user('订单状态不允许售后');
  365. }
  366. }
  367. /**
  368. * 网银直接支付不允许退款
  369. */
  370. public function allowPayTypeRefund(){
  371. if(in_array($this['pay_type'],[
  372. self::PT_GYEUZJ,
  373. ])){
  374. throw_user('网银直接支付不允许退款');
  375. }
  376. }
  377. /**
  378. * 是否允许取消
  379. */
  380. public function allowCancel(){
  381. return in_array($this['status'],[
  382. self::S_WAIT_PAY,
  383. ]);
  384. }
  385. /**
  386. * 是否允许确认收货
  387. */
  388. public function allowOver(){
  389. return in_array($this['status'],[
  390. self::S_WAIT_REC,
  391. ]);
  392. }
  393. public function scopePayed(Query $query){
  394. $query->whereNotIn('status',[self::S_CANCEL,self::S_WAIT_PAY]);
  395. }
  396. public function scopeStatusPay(Query $query){
  397. $query->where('status',self::S_WAIT_SEND);
  398. }
  399. public function scopeHasGoods(Query $query,$goods_id){
  400. $query->whereExists(
  401. OrderInfo::whereRaw("orders.id=order_info.order_id and order_info.goods_id={$goods_id}")->buildSql()
  402. );
  403. }
  404. public function scopeHasRefund(Query $query){
  405. $query->whereExists(
  406. OrderInfo::where('refund_id','>',0)->where('order_info.order_id',Db::raw('orders.id'))->buildSql()
  407. );
  408. }
  409. protected static function init()
  410. {
  411. self::beforeInsert(function (self $orders){
  412. #优惠总金额
  413. //$orders['amount_discount']=bcAddAll($orders['amount_coupon']??0,$orders['amount_coupon_kill']);
  414. #过期时间
  415. $orders['continue_expire_time']=time()+self::EXP_PAY;
  416. #去除无发票的
  417. if(empty($orders['tax']) || !in_array($orders['tax']['paper_type']??0,[1,2])){
  418. $orders['tax']=null;
  419. }
  420. #属于哪个销售员
  421. $orders['admin_id']=UserSvc::getSellerId($orders->user_id);
  422. #发货时间
  423. /*if(empty($orders['customer_send_time'])){
  424. $orders['customer_send_time']=Carbon::now()->addDays(config('site.send_delay_day')?:0);
  425. }*/
  426. });
  427. self::afterInsert(function (self $orders){
  428. #添加发票
  429. UserTax::fromOrder($orders);
  430. #合同链接
  431. $contract_link=request()->root(true)."/contract/view/show/{$orders['id']}";
  432. $orders->where('id',$orders['id'])->update([
  433. 'contract_link'=>$contract_link,
  434. ]);
  435. });
  436. self::beforeUpdate(function (self $order){
  437. $data=$order->getChangedData();
  438. if(!empty($data['status'])){
  439. #待发货
  440. if($data['status']==self::S_WAIT_SEND){
  441. $order['continue_expire_time']=null;
  442. }
  443. #待收货过期时间
  444. elseif ($data['status']==self::S_WAIT_REC){
  445. $order['continue_expire_time']=time()+self::EXP_REC;
  446. }
  447. #已完成
  448. elseif($data['status']==self::S_OVER){
  449. $order['rec_time']=time();
  450. $order['continue_expire_time']=null;
  451. }
  452. $order['status_pre']=$order->origin['status'];
  453. }
  454. if(isset($order['amount_profit']) && $order['amount_profit']<0){
  455. $order['amount_profit']=0;
  456. }
  457. if(isset($order['amount_profit_per']) && $order['amount_profit_per']<0){
  458. $order['amount_profit_per']=0;
  459. }
  460. if(isset($order['amount_cmn']) && $order['amount_cmn']<0){
  461. $order['amount_cmn']=0;
  462. }
  463. });
  464. self::afterUpdate(function (self $orders){
  465. if(!empty($orders->status) && $orders->status==self::S_WAIT_SEND){
  466. Transaction::addTransaction($orders);
  467. }
  468. #如果已完成发放提成
  469. if(!empty($orders->status) && $orders->status==self::S_OVER){
  470. $orders->makeSendCommission();
  471. }
  472. });
  473. }
  474. }