'boolean', 'from_addr'=>'json', 'to_addr'=>'json', 'refund_images'=>'array', 'created_at'=>'datetime', 'pay_time'=>'datetime', ]; const FREIGHT_FAST='fast';#快车 const FREIGHT_AIR='air';#空运 const FREIGHT_SPECIAL='special';#专车 public static $freights=[ self::FREIGHT_FAST=>'快车', self::FREIGHT_AIR=>'空运', self::FREIGHT_SPECIAL=>'专车', ]; const STATUS_WAIT_PAY=0; const STATUS_WAIT_GET=1; const STATUS_GETTING=10; const STATUS_SENDING=20; const STATUS_GIVING=30; const STATUS_GIVED=40; const STATUS_COMPLETE=50; const STATUS_WAIT_SET=55; const STATUS_SET=60; const STATUS_SET_REJECT=65; const STATUS_REFUND=70; const STATUS_CANCEL=80; public static $statusList=[ self::STATUS_WAIT_PAY=>'待支付', self::STATUS_WAIT_GET=>'待接单', self::STATUS_GETTING=>'已接单,取宠中', self::STATUS_SENDING=>'已取宠,运输中', self::STATUS_GIVING=>'送宠中', self::STATUS_GIVED=>'送宠完成,待用户确认', self::STATUS_COMPLETE=>'已完成,待提交结算信息', self::STATUS_WAIT_SET=>'已提交结算信息,待后台结算', self::STATUS_SET=>'已结算', self::STATUS_SET_REJECT=>'已拒绝结算', self::STATUS_REFUND=>'退款处理中', self::STATUS_CANCEL=>'已取消', ]; const REFUND_STATUS_DEFAULT=0; const REFUND_STATUS_PASS=1; const REFUND_STATUS_REJECT=2; public static $refundStatus=[ self::REFUND_STATUS_DEFAULT=>'审核中', self::REFUND_STATUS_PASS=>'审核通过', self::REFUND_STATUS_REJECT=>'驳回', ]; protected $append=[ 'protect_valid', ]; public function user(){ return $this->belongsTo(User::class); } public function log(){ return $this->hasMany(UserOrderLog::class,'order_id')/*->order('id','desc')*/; } public static function payed($params){ $id=$params['id']; $order=self::find($id); $order['pay_time']=time(); $order['status']=self::STATUS_WAIT_GET; $order->save(); } public function setImagesAttr($v){ if(!$v){ $v=[]; } return json_encode($v); } public function getImagesAttr($v){ return array_filter(json_decode($v)); } /** * 取消 *user.type 1未支付2驳回3退款 *user.reason */ const CT_NOT_PAY=1; const CT_REJECT=2; const CT_REFUND=3; public function cancel($type=self::CT_NOT_PAY,$force=false){ if($this['status']!==self::STATUS_WAIT_PAY && !$force){ throw_user('非待付款订单无法取消'); } $this['status']=self::STATUS_CANCEL; $this['cancel_at']=time(); $this['cancel_type']=$type; $this->save(); if($this['coupon_id']){ UserCoupon::setUse($this['coupon_id'],0); } } public function pay(){ if($this['status']!=self::STATUS_WAIT_PAY){ throw_user('状态有误'); } if($this['expired_at']cancel(); throw_user('该订单已超时'); } if($this['pay_type']==1){ $params=''; $this->user->money(bcsub(0,$this['real_amount'],2),$this['user_id'],MoneyLog::TYPE_ORDER_PAY,"订单[{$this['no']}]付款"); self::payed(['id'=>$this['id']]); }else { $params = Payment::pay($this->user, $this['real_amount'], self::class, 'payed', ['id' => $this['id']]); } return $params; } public function complete(){ $this->refunding(); if($this['status']!=self::STATUS_GIVED){ throw_user('非已送达状态无法确认完成'); } $this['status']=self::STATUS_COMPLETE; $this['completed_at']=time(); if(!$this->save()){ throw_user('保存状态失败'); } } public function refund($reason,$images){ if(!in_array($this['status'],[self::STATUS_WAIT_GET,self::STATUS_GETTING,self::STATUS_SENDING])){ throw_user('当前无法申请退款'); } $this['refund_reason']=$reason; $this['refund_apply_at']=time(); $this['refund_amount']=$this->refund_money; $this['refund_images']=$images; $this['refund_status']=self::REFUND_STATUS_DEFAULT; $this['status_bak']=$this['status']; $this['status']=self::STATUS_REFUND; if(!$this->save()){ throw_user('保存失败'); } } public function refundCancel(){ if(!in_array($this['refund_status'],[self::REFUND_STATUS_DEFAULT])){ throw_user('当前无法取消申请退款'); } $this['refund_status']=-1; $this['status']=$this['status_bak']; if(!$this->save()){ throw_user('保存失败'); } } public function refunding(bool $throw=true){ $is=in_array($this['refund_status'],[self::REFUND_STATUS_DEFAULT,self::REFUND_STATUS_PASS]); if($throw && $is){ throw_user('当前正在申请退款或已退款,您无法操作'); } return $is; } /** *处理退款申请 * @status 1同意2拒绝 */ public function dealRefund($data){ if($data['status']==1){ $this['refund_status']=self::REFUND_STATUS_PASS; $this['refund_at']=time(); PayUser::setType($this['pay_type']); PayUser::getClass()->payment($this['refund_amount'],$this['user'],MoneyLog::TYPE_REFUND,"订单退款",['order_id'=>$this['id']]); $this->cancel(self::CT_REFUND,true); }elseif($data['status']==2){ $this['refund_status']=self::REFUND_STATUS_REJECT; $this['status']=$this['status_bak']; } $this->save(); } /** *处理结算申请 * @status 1同意2拒绝 */ public function dealSettle($data){ if($this['status']!=self::STATUS_WAIT_SET){ throw_user(sprintf('非待结算订单无法结算[%s]',self::$statusList[$this['status']])); } if($data['status']==1){ $this['status']=self::STATUS_SET; foreach ($this->currentSenderOrder as $senderOrder){ if(is_null($senderOrder['fee_total'])){ throw_user('有配送员尚未申请结算,请稍后再试'); } PayUser::getClass()->payment($senderOrder['fee_total'],$senderOrder['user'],MoneyLog::TYPE_SETTLE,"订单[{$this['no']}]结算",['order_id'=>$this['id']]); } #加一个盈利记录 User::money($this->profit,0,MoneyLog::TYPE_PROFIT,"订单[{$this['no']}]盈利",['order_id'=>$this['id']],false); }elseif($data['status']==2){ $this['status']=self::STATUS_SET_REJECT; } $this['settled_at']=time(); if(!$this->save()){ throw_user('保存失败'); } } public function getCalRefundAttr($v,$data){ if($this['freight']==self::FREIGHT_AIR){ $fee=bcdiv(config('site.order_refund_air'),100,4); }else{ $fee=bcdiv(config('site.order_refund_land'),100,4); } $amount=bcmul($this['real_amount'],$fee); return $amount; } public function getRefundMoneyAttr($v,$data){ return bcsub($this['real_amount'],$this->cal_refund); } public function getSendDetailAttr(){ $log=$this->log()->select(); $res['addr']=$this['to_addr']['address']; $res['log']=$log; return $res; } public function remove(){ if(!in_array($this['status'],[self::STATUS_CANCEL,self::STATUS_SET,self::STATUS_SET_REJECT])){ throw_user('非已取消或完成订单无法删除'); } if($this['user_deleted_at']){ throw_user('该订单已删除'); } $this['user_deleted_at']=time(); $this->save(); } public function submit($data){ $this['status']=self::STATUS_WAIT_PAY; $res=$this->allowField(true)->save($data); if(!$res){ throw_user('保存失败'); } if($this['coupon_id']){ UserCoupon::setUse($this['coupon_id']); } } /** 待接单 */ public static function wait(User $user){ $q=self::alias('user_order'); $city=$user->area()->column('name'); if(empty($city)){ $q->where('user_order.id',0); return $q; } $q ->join('sender_order b','user_order.id=b.user_order_id and b.now=1','LEFT') ->whereIn('user_order.from_city|user_order.to_city',$city) ->whereBetween('user_order.status',[self::STATUS_WAIT_GET,self::STATUS_GIVING]) ->whereNotExists("select user_id from sender_order where user_order_id=user_order.id and user_id={$user['id']}") ->group('user_order.id') ->having('COUNT(b.id)<2') ->order('user_order.id','desc'); return $q; } public function getProtectValidAttr($v,$d){ if(!isset($this['status'])){ return false; } return $this['status']>=self::STATUS_WAIT_GET && $this['status']<=self::STATUS_GIVED; } public function senderOrder(){ return $this->hasMany(SenderOrder::class); } /** 当前的配送员订单 */ public function currentSenderOrder($user_id=null){ $q=$this->senderOrder()->where('now',1); if($user_id){ $q->where('user_id',$user_id); } return $q; } /** 是否能接单 */ public function checkGet(User $user){ if(count($this->currentSenderOrder)>1){ throw_user('订单已被接单,您暂时无法接单(1)'); } $type=$user->getSendType($this); /*if($user->isSendGet($type) && $this['status'] != self::STATUS_WAIT_GET) { throw_user("订单已被接单,您暂时无法接单(2)"); }*/ return $type; } /** 接单 */ public function accept(User $user){ $type=$this->checkGet($user); if($this['status']==self::STATUS_WAIT_GET) { $this['status'] = self::STATUS_GETTING; } #取送都是自己,取送订单创建 if($user->isSendAll($type)){ $time=time(); $get=$this->senderOrder()->save([ 'user_id'=>$user['id'], 'status' =>$this['status'], 'type' =>1, 'now' =>1, 'get_at' =>$time, ]); if(!$get){ throw_user('保存取宠订单失败'); } $sender=$this->senderOrder()->save([ 'user_id'=>$user['id'], 'status' =>$this['status'], 'type' =>2, 'now' =>1, 'get_at' =>$time, ]); if(!$sender){ throw_user('保存送宠订单失败'); } }else{ $sender_order=$this->senderOrder()->save([ 'user_id'=>$user['id'], 'status' =>$this['status'], 'type' =>$type, 'now' =>1, 'get_at' =>time(), ]); if(!$sender_order){ throw_user('保存配送订单失败'); } } if(!$this['get_at']){ $this['get_at']=time(); } $this->save(); } /** 更新状态 */ public function updateStatus($status,User $user){ if(!$status){ return; } $this->refunding(); $senderS=[ self::STATUS_GETTING=>self::STATUS_SENDING, self::STATUS_SENDING=>self::STATUS_GIVING, ]; $recS=[ self::STATUS_GIVING=>self::STATUS_GIVED, ]; $arr=$senderS+$recS; if($this['status']!=$status){ throw_user('状态'.self::$statusList[$this['status']].'有误'); } if(!$this->is_same_user) { $type = $user->getSendType($this); if ($user->isSendGet($type) && !isset($senderS[$status])) { throw_user('您是取宠人,无法更新状态' . self::$statusList[$status]); } elseif ($user->isSendSend($type) && !isset($recS[$status])) { throw_user('您是送宠人,无法更新状态' . self::$statusList[$status]); } } $this['status']=$arr[$status]; if(!$this->save()){ throw_user('更新失败'); } return $this['status']; } /** 进行中的订单 */ public function scopeRunning(Query $query,$from=''){ $status=[]; if($from=='user'){ $status=[ self::STATUS_WAIT_GET, ]; } $query ->show() ->whereIn('status',array_merge($status,[ self::STATUS_GETTING, self::STATUS_SENDING, self::STATUS_GIVING, self::STATUS_GIVED, self::STATUS_REFUND, ])); } /** 计算收益 */ public static function calcProfit($date=null){ $profit=Db::query($sql=UserOrder::withTrashed()->profit($date)->buildSql()); return bcadd(array_sum(array_column($profit,'profit')),0); } /** 结算金额 */ public function getSettleAmountAttr($v,$data){ $sender_order=$this->currentSenderOrder; $amount=0; foreach ($sender_order as $order){ $amount=bcadd($amount,$order['fee_total']); } return $amount; } /** 配送员 */ public function getSendersAttr($v,$data){ $sender_order=$this->currentSenderOrder; foreach ($sender_order as $item){ $item->append(['user']); } return array_column($sender_order->toArray(),null); } /** 结算总的金额 */ public function getSettleInfoAttr($v,$data){ $arr=$this->senders; $total=[ 'fee_total'=>0 ]; foreach ($arr as $item){ for ($i=1;$i<=6;$i++) { $total["fee_$i"] = bcadd($total["fee_$i"]??0,$item["fee_$i"]); } $total['fee_total']=bcadd($total['fee_total']??0,$item['fee_total']); } return $total; } /** 盈利总的金额 */ public function getProfitAttr($v,$data){ return bcsub($this['real_amount'],$this->settle_info['fee_total']); } /** 是否相同接单人 */ public function getIsSameUserAttr(){ return $this->currentSenderOrder->count()==2 && count(array_unique($this->currentSenderOrder->column('user_id')))==1; } /** init */ protected static function init() { self::beforeInsert(function (self $order){ $order['no']=order_no(); $order['expired_at']=strtotime("+10minutes"); }); self::afterInsert(function (self $order){ /* if(!$order->user->address()->exists($order['from_addr'])->find()) { $addr=$order['from_addr']; $addr['user_id']=$order->user_id; (new UserAddress())->allowField(true)->save($addr); }*/ }); self::afterUpdate(function (self $order){ #更新配送订单状态 $data=$order->getChangedData(); if(isset($data['status'])){ $order->senderOrder()->update(['status'=>$data['status']]); } }); } /** scope */ /** * 待结算 * @param Query $query */ public function scopeSettle(Query $query){ $query->where('status',self::STATUS_WAIT_SET); } /** * 待处理退款 * @param Query $query */ public function scopeWaitRefund(Query $query){ $query->where('status',self::STATUS_REFUND); } /** * 售后 * @param static $query */ public function scopeService(Query $query){ $query->show()->where('refund_status','>',-1); } /** * 总营业额 * @param Query $query * @param null|array $date */ public function scopeTurnover(Query $query,$date='today'){ $query ->whereBetween(($table=$this->getTable()).'.status', [self::STATUS_WAIT_GET, self::STATUS_SET]); if($date=='today') { $query->whereTime($table.'.created_at', $date); }elseif(is_array($date)){ list($type,$time)=$date; if($type=='day'){ $query->whereBetween($table.'.created_at',[strtotime($time),strtotime("+1day",strtotime($time))]); }elseif ($type=='month'){ if (substr_count($time,'-')==1){ $time="$time-01 00:00:00"; } $query->whereBetween($table.'.created_at',[strtotime($time),strtotime("+1month",strtotime($time))]); }elseif ($type=='year'){ $query->whereBetween($table.'.created_at',[strtotime("$time-01-01"),strtotime(($time+1).'-01-01')]); } } } /** * 盈利统计 * @param null $date * @param bool $detail * @return string * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public function scopeProfit(Query $query,$date=null){ $profit=$query ->join('sender_order b','user_order.id=b.user_order_id and b.now=1') ->where('user_order.status',self::STATUS_SET) ->show() ->group('user_order.id') ->fieldRaw("(user_order.real_amount-SUM(b.fee_total)) AS profit,SUM(b.fee_total) as fee_total"); if(is_null($date)){ $profit->whereTime('user_order.created_at','today'); }else{ list($type,$time)=$date; if($type=='day'){ $query->whereTime('user_order.created_at',$time); }elseif ($type=='month'){ if (substr_count($time,'-')==1){ $time="$time-01 00:00:00"; } $query->whereBetween('user_order.created_at',[strtotime($time),strtotime("+1month",strtotime($time))]); }elseif ($type=='year'){ $query->whereBetween('user_order.created_at',[strtotime("$time-01-01"),strtotime(($time+1).'-01-01')]); } } } /** 已付款 */ public function scopeWaitGet(Query $query){ $query ->show() ->where('status',self::STATUS_WAIT_GET)->show(); } /** 已完成 */ public function scopeSuccess(Query $query){ $query ->show() ->whereBetween('status',[ self::STATUS_COMPLETE, self::STATUS_WAIT_SET, ]); } /** 已结算 * @param Query|static $query */ public function scopeSettled(Query $query){ $query ->whereIn('status',[self::STATUS_SET,self::STATUS_SET_REJECT]); } /** * 已取消 * @param Query|self $query */ public function scopeCanceled(Query $query){ $query->where('status',self::STATUS_CANCEL); } /** 已超时 */ public function scopeExpired(Query $query,$limit=10){ $query->where('expired_at','<',time()) ->where('status',self::STATUS_WAIT_PAY) ->order('id','desc') ->limit($limit); } /** * 已完成 * @param Query|self $query */ public function scopeOver(Query $query){ $query ->show() ->whereBetween('status',[self::STATUS_COMPLETE,self::STATUS_SET_REJECT]); } /** 展示的 */ public function scopeShow(Query $query){ $query->whereNull("{$this->getTable()}.user_deleted_at"); } /** 后台使用已完成 */ public function scopeCompleted(Query $query){ $query->where('status',UserOrder::STATUS_COMPLETE) ->show() ->order('id','desc'); } /** 后台使用已结算 */ public function scopeCaled(Query $query){ $query->where('status',UserOrder::STATUS_SET) ->show() ->order('id','desc'); } /** trashed */ public static function withTrashed(){ return new self; } }