__('Pay_type yue'), 'wechat' => __('Pay_type wechat')]; } public function getStatusList() { return ['-3' => __('Status -3'), '0' => __('Status 0'), '2' => __('Status 2'), '3' => __('Status 3'), '4' => __('Status 4'), '5' => __('Status 5'), '6' => __('Status 6'),'7' => __('Status 7'), '9' => __('Status 9')]; } public function getSuspendStatusList() { return ['0' => __('未挂起'), '1' => __('售后拒绝'), '2' => __('售后超时')]; } public function getSuspendStatusTextAttr($value, $data) { $value = $value ? $value : (isset($data['suspend_status']) ? $data['suspend_status'] : ''); $list = $this->getSuspendStatusList(); return isset($list[$value]) ? $list[$value] : ''; } public function getBeforeStatusList() { return ['-3' => __('Before_status -3'), '0' => __('Before_status 0'), '2' => __('Before_status 2'), '3' => __('Before_status 3'), '4' => __('Before_status 4'), '6' => __('Before_status 6'), '9' => __('Before_status 9')]; } public function getServerStatusList() { return ['0' => __('Server_status 0'), '3' => __('Server_status 3'), '6' => __('Server_status 6')]; } public function getAuthStatusList() { return ['0' => __('Auth_status 0'), '1' => __('Auth_status 1'), '2' => __('Auth_status 2')]; } public function getPayTypeTextAttr($value, $data) { $value = $value ? $value : (isset($data['pay_type']) ? $data['pay_type'] : ''); $list = $this->getPayTypeList(); return isset($list[$value]) ? $list[$value] : ''; } public function getStatusTextAttr($value, $data) { $value = $value ? $value : (isset($data['status']) ? $data['status'] : ''); $list = $this->getStatusList(); return isset($list[$value]) ? $list[$value] : ''; } public function getBeforeStatusTextAttr($value, $data) { $value = $value ? $value : (isset($data['before_status']) ? $data['before_status'] : ''); $list = $this->getBeforeStatusList(); return isset($list[$value]) ? $list[$value] : ''; } public function getServerStatusTextAttr($value, $data) { $value = $value ? $value : (isset($data['server_status']) ? $data['server_status'] : ''); $list = $this->getServerStatusList(); return isset($list[$value]) ? $list[$value] : ''; } public function getCanceltimeTextAttr($value, $data) { $value = $value ? $value : (isset($data['canceltime']) ? $data['canceltime'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getPaytimeTextAttr($value, $data) { $value = $value ? $value : (isset($data['paytime']) ? $data['paytime'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getAuthTimeTextAttr($value, $data) { $value = $value ? $value : (isset($data['auth_time']) ? $data['auth_time'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getReservationTimeTextAttr($value, $data) { $value = $value ? $value : (isset($data['reservation_time']) ? $data['reservation_time'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getFinishtimeTextAttr($value, $data) { $value = $value ? $value : (isset($data['finishtime']) ? $data['finishtime'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getRefundtimeTextAttr($value, $data) { $value = $value ? $value : (isset($data['refundtime']) ? $data['refundtime'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } public function getAuthStatusTextAttr($value, $data) { $value = $value ? $value : (isset($data['auth_status']) ? $data['auth_status'] : ''); $list = $this->getAuthStatusList(); return isset($list[$value]) ? $list[$value] : ''; } public function getRefundsendtimeTextAttr($value, $data) { $value = $value ? $value : (isset($data['refundsendtime']) ? $data['refundsendtime'] : ''); return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; } protected function setCanceltimeAttr($value) { return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); } protected function setPaytimeAttr($value) { return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); } protected function setAuthTimeAttr($value) { return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); } protected function setReservationTimeAttr($value) { return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); } protected function setFinishtimeAttr($value) { return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); } protected function setRefundtimeAttr($value) { return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); } protected function setRefundsendtimeAttr($value) { return $value === '' ? null : ($value && !is_numeric($value) ? strtotime($value) : $value); } public function user() { return $this->belongsTo('app\common\model\User', 'user_id', 'id', [], 'LEFT')->setEagerlyType(0); } public function activity() { return $this->belongsTo(Activity::class, 'activity_id', 'id', [], 'LEFT')->setEagerlyType(0); } public function detail() { return $this->belongsTo(OrderDetail::class, 'activity_order_detail_id', 'id', [], 'LEFT')->setEagerlyType(0); } public function ordercode() { return $this->hasMany(OrderCode::class,'activity_order_id'); } /**得到基础条件 * @param $status * @param null $model * @param string $alisa */ public static function getBaseWhere($whereData = [], $model = null, $alisa = '',$with = false) { if (!$model) { $model = new static; if ($alisa&&!$with) $model = $model->alias($alisa); } if ($alisa) $alisa = $alisa . '.'; $tableFields = (new static)->getTableFields(); foreach ($tableFields as $fields) { if(in_array($fields, ['status',"auth_status","server_status",'activity_id','user_id','activity_order_detail_id']))continue; // if (isset($whereData[$fields]) && $whereData[$fields]) $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]); if (isset($whereData[$fields]) && $whereData[$fields]){ if(is_array($whereData[$fields])){ $model = $model->where("{$alisa}{$fields}", $whereData[$fields][0], $whereData[$fields][1]); }else{ $model = $model->where("{$alisa}{$fields}", '=', $whereData[$fields]); } } } if (isset($whereData['status'])) $model = $model->where("{$alisa}status", 'in', $whereData['status']); if (isset($whereData['not_status'])) $model = $model->where("{$alisa}status", 'not in', $whereData['not_status']); if (isset($whereData['server_status']) && $whereData['server_status']!=="") $model = $model->where("{$alisa}server_status", 'in', $whereData['server_status']); if (isset($whereData['not_server_status'])&& $whereData['not_server_status']!=="") $model = $model->where("{$alisa}server_status", 'not in', $whereData['not_server_status']); if (isset($whereData['auth_status']) && $whereData['auth_status']!=="") $model = $model->where("{$alisa}auth_status", 'in', $whereData['auth_status']); if (isset($whereData['not_auth_status'])&& $whereData['not_auth_status']!=="") $model = $model->where("{$alisa}auth_status", 'not in', $whereData['not_auth_status']); if (isset($whereData['keywords'])&&$whereData['keywords']){ $model = $model->where("{$alisa}order_no|{$alisa}pay_no|user.nickname|user.realname|user.mobile|detail.title|detail.address|detail.address_detail", 'LIKE', "%{$whereData['keywords']}%" ); } if (isset($whereData['time'])&&$whereData['time']){ $model = $model->time(["{$alisa}createtime",$whereData['time']]); } if (isset($whereData['user_id']) && $whereData['user_id']) $model = $model->where("{$alisa}user_id", '=', $whereData['user_id']); if (isset($whereData['activity_ids']) && $whereData['activity_ids']) $model = $model->where("{$alisa}activity_id", 'in', $whereData['activity_ids']); if (isset($whereData['activity_id']) && $whereData['activity_id']) $model = $model->where("{$alisa}activity_id", 'in', $whereData['activity_id']); if (isset($whereData['activity_order_detail_id']) && $whereData['activity_order_detail_id']) $model = $model->where("{$alisa}activity_order_detail_id", 'in', $whereData['activity_order_detail_id']); // if (isset($whereData['has_evaluate'])&&$whereData['has_evaluate']){ // //1查已评价 2查未评价 // if($whereData['has_evaluate'] == 1){ // //1查已评价 // $model = $model->where("{$alisa}classes_evaluate_id", '<>', 0); // }else{ // //2查未评价 // $model = $model->whereExists(function ($query) use ($alisa) { // $order_table_name = (new \app\common\model\school\classes\hourorder\Order())->getQuery()->getTable(); // $query->table($order_table_name)->where($order_table_name . '.classes_order_id=' . $alisa . 'id')->where('status', '=', '3'); // })->where("{$alisa}classes_evaluate_id", '=', 0); // // } // } return $model; } public static function allList($user_id,$page, $limit,$keywords,$status,$activity_id=[],$has_evaluate=0,$server_status="",$params=[]){ $with_field = [ 'user'=>['nickname','mobile','avatar','realname'], 'base'=>['*'], 'detail'=>['*'], 'ordercode'=>['*'], ]; $CANCEL = '-3'; $NOPAY = '0'; $PAYED = '2'; $RESERV = '3'; $REFUND = '6'; $FINISH = '9'; $IN_SERVICE = '4'; $IN_REFUND = '5'; $alisa = (new self)->getWithAlisaName(); $sort = "field({$alisa}.status,'{$NOPAY}','{$PAYED}','{$RESERV}','{$FINISH}','{$REFUND}','{$IN_SERVICE}','{$CANCEL}' ,'{$IN_REFUND}') asc,{$alisa}.id desc"; $serch_where = ['status'=>$status,'user_id'=>$user_id,'keywords'=>$keywords,"activity_id"=>$activity_id,"has_evaluate"=>$has_evaluate,"server_status"=>$server_status]; // if($type)$serch_where['type'] = $type; return (new self)->getBaseList(array_merge($serch_where,$params), $page, $limit,$sort,$with_field); } public static function baseCount($where = []){ $CANCEL = '-3'; $NOPAY = '0'; $PAYED = '2'; $RESERV = '3'; $REFUND = '6'; $FINISH = '9'; $IN_SERVICE = '4'; $IN_REFUND = '5'; $cancel_number = self::getBaseWhere(array_merge(['status'=>$CANCEL],$where))->count(); $nopay_number = self::getBaseWhere(array_merge(['status'=>$NOPAY],$where))->count(); $payed_number = self::getBaseWhere(array_merge(['status'=>$PAYED],$where))->count(); $reserv_number = self::getBaseWhere(array_merge(['status'=>$RESERV],$where))->count(); $retund_number = self::getBaseWhere(array_merge(['status'=>$REFUND],$where))->count(); $in_retund_number = self::getBaseWhere(array_merge(['status'=>$IN_REFUND],$where))->count(); $finish_number = self::getBaseWhere(array_merge(['status'=>$FINISH],$where))->count(); $in_service_number = self::getBaseWhere(array_merge(['status'=>$IN_SERVICE],$where))->count(); return compact('cancel_number','nopay_number','payed_number','in_service_number','retund_number','in_retund_number','finish_number','reserv_number'); } /**订单数量统计 * @param int $user_id * @return array */ public static function orderCount($user_id = 0,$activity_id=[],$params=[]){ return self::baseCount(array_merge(['user_id'=>$user_id,"activity_id"=>$activity_id],$params)); } public static function workList($user_id,$page, $limit,$keywords,$status,$activity_id=[],$activity_ids=[],$has_evaluate=0,$server_status="",$params=[]){ if(!$activity_ids) $activity_ids = [-5]; $with_field = [ 'user'=>['nickname','mobile','avatar','realname'], 'base'=>['*'], 'detail'=>['*'], 'ordercode'=>['*'], ]; $CANCEL = '-3'; $NOPAY = '0'; $PAYED = '2'; $RESERV = '3'; $REFUND = '6'; $FINISH = '9'; $IN_SERVICE = '4'; $IN_REFUND = '5'; $alisa = (new self)->getWithAlisaName(); $sort = "field({$alisa}.status,'{$NOPAY}','{$PAYED}','{$RESERV}','{$FINISH}','{$REFUND}','{$IN_SERVICE}','{$CANCEL}','{$IN_REFUND}') asc,{$alisa}.id desc"; $serch_where = ['status'=>$status,'keywords'=>$keywords,"activity_id"=>$activity_id,"activity_ids"=>$activity_ids,"server_status"=>$server_status]; // if($type)$serch_where['type'] = $type; return (new self)->getBaseList(array_merge($serch_where,$params), $page, $limit,$sort,$with_field); } /**订单数量统计 * @param int $user_id * @return array */ public static function workCount($activity_id=[],$activity_ids=[],$params=[]){ if(!$activity_ids) $activity_ids = [-5]; return self::baseCount(array_merge(["activity_id"=>$activity_id,"activity_ids"=>$activity_ids],$params)); } /**得到订单详情 * @param $order_no */ public static function getDetail($order_no,$activity_id = []){ $model = self::where('order_no|id|pay_no',$order_no); if($activity_id)$model = $model->where("activity_id","in",$activity_id); $data = $model->find(); if(!$data) return $data; //加载订单详情 $data->detail; //订单用户 // $data->user; $data->user->visible(['id','nickname','mobile','avatar','realname']); //超时检测 self::timeoutCheck($data["id"]); //规格信息 $data->ordercode; return $data; } /**展示订单信息 * @param $order_no * @param $price_info * @return array */ public static function showInfo($order_no, $price_info = []){ $data = []; $data['order_no'] =$order_no; $data['order_info'] = self::getDetail($order_no); return array_merge($data,$price_info); } /**得到订单 * @param $order_no * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getOrder($order_no){ $order = self::where('order_no|id|pay_no',$order_no)->find(); if(!$order)throw new \Exception("订单不存在"); return $order; } /** * 设置订单缓存 * @param $uid * @param $data * @return bool */ public static function setOrderCache($uid, $data) { //缓存名 = uid + order_no $cacheNmae = 'activity_order_cache' . $uid . $data['order_no']; // 缓存在3600秒之后过期 return Cache::set($cacheNmae, $data, config("site.unpaid_activity_expire_time")); } /** * 得到订单缓存 * @param $uid * @param $order_no * @return mixed */ public static function getOrderCache($uid, $order_no) { //缓存名 = uid + order_no $cacheNmae = 'activity_order_cache' . $uid . $order_no; // 缓存在3600秒之后过期 return Cache::get($cacheNmae); } /** * 删除订单缓存 * @param $uid * @param $order_no * @return mixed */ public static function deleteOrderCache($uid, $order_no) { //缓存名 = uid + order_no $cacheNmae = 'activity_order_cache' . $uid . $order_no; // 缓存在3600秒之后过期 return Cache::rm($cacheNmae); } /**订单校验 * @param $user_id 用户id * @param $classes_lib_id 课程id * @param $order_no 订单号 * @param $param 表单扩展参数 * @return bool */ public function orderVaild($user_id,$activity_id,$num, $order_no, $param,$check=false){ if(!$user_id||!$activity_id )throw new \Exception("缺少必要参数"); //更新活动状态 (new Activity)->updateStatus($activity_id); //校验订单参数 //课程活动是否存在并上架 $activity = Activity::where('id',$activity_id)->find(); if(!$activity ) throw new \Exception("该活动不存在!"); //默认校验订单是否已创建 if($check){ if($activity['status']!='2' || $activity['auth_status']!='1') throw new \Exception("该活动不在可报名时间段!"); //此为创建订单时判断 //判断订单是否已创建 $order_info = self::where(['order_no'=>$order_no])->find(); if($order_info) throw new \Exception("订单已生成,如需重新下单请退出页面重新进入!"); //下单必须传规格id $num = (int)$num; if(!$num) throw new \Exception("请选择报名人数!"); $activity_max_people = config("site.activity_max_people"); if($activity_max_people){ if($num > $activity_max_people || $num < 1) throw new \Exception("单次报名人数不能超过{$activity_max_people}人!"); } //判断数量是否超出库存 $sale = Activity::activitySale($activity_id,$num);//活动销量 if($sale > $activity['stock']){ //判断超了几人 $sub = $sale - $activity['stock']; throw new \Exception("活动人数已超{$sub}人!无法下单!"); } } //用户存不存在 $user_info = User::where('id',$user_id)->find(); if(!$user_info) throw new \Exception("用户不存在!"); return true; } public static function getCost($user_id,$activity_id,$num=1,$param=[],$other_params=[],$check = false){ //用户 $user_info = User::get($user_id); $user_data = [ "nickname"=>$user_info["nickname"], "realname"=>$user_info["realname"], "avatar"=>$user_info["avatar"], "mobile"=>$user_info["mobile"], "money" =>$user_info["money"], "score" =>$user_info["score"], ]; //课程 $activity_info = Activity::get($activity_id); $activity_info = $activity_info->toArray(); $activity_info["activity_id"] = $activity_id; $activity_info["activity_order_id"] = 0; $activity_info["refund_scale_json"] = ""; $activity_info["refund_status"]= "1"; $refund = Refund::get($activity_info["refund_id"]); if($refund){ $activity_info["refund_scale_json"]= $refund["refund_scale_json"]; $activity_info["refund_status"]= $refund["status"]; } // $activity_info['use_num'] = 0;//已使用课时 // $activity_info['sub_num'] = $activity_info['classes_num'];//剩余课时 // //单价 = 课程价格/总课时 // $activity_info["unit_price"] = bcdiv($activity_info['price'],$activity_info['classes_num'],2); // $activity_info["used_price"] = 0; $totalprice = bcmul($num,$activity_info["price"],2); //组装订单下单数据 $order_data = []; $order_data["user_id"] = $user_id; $order_data["activity_id"] = $activity_id; $order_data["activity_order_detail_id"] = 0; $order_data["num"] = $num; $order_data["fee_scale"] = $activity_info["fee_scale"]; $order_data["beforeprice"] = $totalprice; $order_data["totalprice"] = $totalprice; $order_data["payprice"] = 0; $order_data["status"] = '0'; $order_data["settle_log_time"] = $activity_info["settlement_time"]; $order_data["last_time"] = $activity_info["end_time"] + config("site.activity_end_sales"); //根据手续费比例$activity_info["fee_scale"] 计算手续费 $order_data["fee_price"] = bcmul($order_data["totalprice"],$order_data["fee_scale"],2); //手续费保底不能低于site.activity_settle_min_fee $min_fee = config("site.activity_settle_min_fee"); $order_data["fee_price"] = $order_data["fee_price"] < $min_fee ? $min_fee : $order_data["fee_price"]; return compact('order_data','activity_info','user_data',"num"); } /** 订单确认(订单计算) * @param $user_id 下单用户 * @param $order_no 订单号(缓存标识) * @param $activity_id 課程活动id * @param $num 人数 * @param $param 額外参数(扩展用) * @param bool $is_compute 是否重新计算订单 * @return array */ public function confirm($user_id, $activity_id,$num,$order_no,$param=[], $is_compute = false) { if ($order_no && !$is_compute) { //得到缓存 $data = self::getOrderCache($user_id, $order_no); if (!$data) throw new \Exception('请您完善参加信息!'); $price_info = $data['price_info']; } else { //订单信息计算 // if(!$param) throw new \Exception('缺少必要信息'); $this->orderVaild($user_id,$activity_id,$num,$order_no, $param); //订单支付信息 $price_info = $this->getCost($user_id,$activity_id,$num,$param); //生成订单号 if (!$order_no) $order_no = get_order_sn(); //生成缓存 $data = compact('user_id', 'activity_id','num','param', 'order_no', 'price_info'); self::setOrderCache($user_id, $data); } \think\Hook::listen('activity_order_create_before', $data); //下单数据展示 return $this->showInfo($order_no, $price_info); } /**得到待支付订单 * @param $order_no * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getNopayOrder($order_no){ $order = self::where('order_no|id|pay_no',$order_no)->where("status","in",['0',"-3"])->find(); if(!$order)throw new \Exception("待支付订单不存在"); return $order; } /**调用订单支付成功事件 * @param $order_no * @param $pay_type * @param $price * @param $price_check * @param bool $trans * @return bool * @throws \Exception */ public function paySuccess($order_no,$notify=[],$price=0,$price_check=false,$trans=false){ $order = self::getNopayOrder($order_no); //金额校验 :第三方支付时回调入口判断 if($price_check){ if(bccomp($price,$order['totalprice'])==-1)throw new \Exception("支付金额与订单需要支付金额对应不上,回调失败!"); } //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //事务逻辑 //不拆分订单,直接执行 self::paySetData($order,$notify); //释放结算订单 // (new SettleLog)->generatorLog($order['id']); // //如果需要快捷预约 // $classes_lib_spec_id = $order['classes_lib_spec_id']; // if($classes_lib_spec_id){ // //记录代下单人信息 // $param = [ // "type" =>'2', // "help_user_id" =>$order["user_id"] , // "help_type" => 'admin', // ]; // // //确认订单 // $res = (new \app\common\model\school\classes\hourorder\Order)->confirm($order["user_id"],$order['id'],null, $classes_lib_spec_id,$param, true); // $remark = "订单支付完成同时快捷预约一个课时"; // //创建订单 // $result = (new \app\common\model\school\classes\hourorder\Order)->cacheCreateOrder($res['order_no'], $order["user_id"],$remark); // // } if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); } return $res; } public static function updatePay($order,$notify=[]){ if(is_string($order)||is_numeric($order))$order = self::getNopayOrder($order); $order->status ='2'; $order->paytime = time(); $order->pay_no = $notify['transaction_id'] ?? null; $order->pay_json = $notify['payment_json'] ?? '{}'; $order->pay_type = $notify['pay_type'] ?? 'yue'; //如果订单创建时间大于预约时间,则等于预约时间 // if($order['createtime']>$order['starttime'])$order['createtime'] = $order['starttime']; switch ($order->pay_type) { case "offline": //线下付款,线上不需要支付 break; default: $order->payprice = $notify['pay_fee'] ?? $order->totalprice; } $order->platform = $notify['platform'] ?? 'miniapp'; $order->sub_refundprice = $order->payprice; //剩余未退 = 支付金额 $order->save(); return $order; } public static function paySetData($order,$notify=[]){ //订单支付更新 $order = self::updatePay($order,$notify); //生成订单一维码和二维码 $order = self::buildCode($order); //记录订单日志 OrderLog::log($order['id'],"活动订单支付成功,核销码生成,等待核销",'user',$order['user_id']); //调用支付成功事件 $data = ['order' => self::where("id",$order['id'])->find()]; \think\Hook::listen('activity_order_payed_after', $data); //更新订单数据 self::statisticsAndUpdateClassesNumber($order); return true; } public static function buildCode($order){ if(is_string($order)||is_numeric($order))$order = self::getNopayOrder($order); $num = $order['num']; for ($i=0;$i<$num;$i++){ $params = [ "activity_order_id"=>$order["id"], "status" =>'3', "activity_id"=>$order['activity_id'], ]; $orderCode = OrderCode::create($params); $orderCode["code"] = en_code($orderCode["id"]); $orderCode["miniurl"] = self::getMiniQrcodeLink(["order_id"=>$order['id'],"code"=>$orderCode["code"]]); //生成核销二维码和一维码 // //生成二维码和一维码 // //二维码 // $orderCode->codeimage = (Common::getQrcode([ // 'text' => $orderCode["miniurl"], // 'size' => 200, // ]))['url']; // //一维码 // $orderCode->codeoneimage = (Common::getBarcode([ // 'text' => $orderCode["miniurl"], // 'size' => 200, // ]))['url']; $orderCode->save(); } return $order; } public static function getPath($params=[],$expression = '{{KEYWORD}}'){ $path = config("site.activity_verification_url"); if(!$path) throw new \Exception("请先配置小程序路径"); // 参数替换 $template = Common::parsePrintTemplateString($path,$params,$expression); return $template; } public static function getMiniQrcodeLink($params=[],$expression = '{{KEYWORD}}',$qrcode=false){ $path = self::getPath($params,$expression); //如果路径开头有斜杠则去除 if(substr($path,0,1) == "/"){ $path = substr($path,1); } //解析该路径,获取url参数和除去参数的url $url_params = []; $url = ""; if(strpos($path,"?") !== false){ $url_params = explode("?",$path); $url = $url_params[0]; $url_params = $url_params[1]; }else{ $url = $path; } $q_params = []; //生成小程序二维码 $query = $url_params; $q_params["path"] = $url; if($query)$q_params["query"] = $query; // 实例对应的接口对象 $scheme = new \WeMini\Scheme(Service::wechatConfig()); $res= $scheme->urlLink($q_params); if(!isset($res["url_link"]))throw new \Exception("生成小程序二维码失败"); $url_link = $res["url_link"]; if(!$qrcode)return $url_link; //链接生成二维码 //二维码 $response = Common::getQrcode([ 'text' => $url_link, 'size' => 200, ],false,false,true); //全返回 return compact("url_link","response"); } /** * 根据缓存创建订单 */ public function cacheCreateOrder($order_no, $user_id,$remark="", $trans = false) { //得到缓存 $orderInfo = self::getOrderCache($user_id, $order_no); //得到下单信息 if (!$orderInfo) throw new \Exception('请您完善预约信息'); if ($trans) { self::beginTrans(); } try { //'classes_activity_id','classes_activity_item_id' //1订单执行创建 $order = $this->createOrder($user_id,$orderInfo['activity_id'],$orderInfo['num'],$order_no,$orderInfo['param'],$remark); $orderitem = $order->detail; //如果是免费订单,则直接调用支付完成 if ($orderitem['feel'] == '1' || $order['totalprice'] == 0) { //调用订单支付成功事件 $this->paySuccess($order_no,['platform'=>"miniapp",'pay_type'=>'yue']); } //5删除缓存 self::deleteOrderCache($user_id, $order_no); if ($trans) { self::commitTrans(); } } catch (\Exception $e) { if ($trans) { self::rollbackTrans(); } throw new \Exception($e->getMessage().$e->getFile() . $e->getLine()); } return self::showInfo($order_no); } public function createOrder($user_id,$activity_id,$num,$order_no,$param,$remark='',$other_params=[]){ $this->orderVaild($user_id,$activity_id,$num, $order_no, $param,true); //订单支付信息 $order_info = self::getCost($user_id,$activity_id,$num,$param,$other_params,true); //组装订单数据 compact('order_data','activity_info','user_data',"activity_info"); $order_data = $order_info['order_data']; $order_data["order_no"] = $order_no; $res1 = self::create($order_data); if (!$res1) throw new \Exception('创建订单失败'); $activity_info = $order_info["activity_info"]; if(!$activity_info)throw new \Exception('订单未选活动!'); //課程详情 $activity_info = $order_info['activity_info']; $order_detail_data = []; $order_detail_data = array_merge($order_detail_data,$activity_info); $order_detail_data["activity_id"] = $activity_info['id']; $order_detail_data["activity_order_id"] = $res1['id']; unset($order_detail_data['id']); unset($order_detail_data['createtime']); $orderDetail = (new OrderDetail()); $orderDetail->allowField(true)->save($order_detail_data); // $order_item_data = []; // $order_item_data = array_merge($order_item_data,$classes_activity_item->toArray()); // $order_item_data["classes_activity_item_id"] = $classes_activity_item['id']; // $order_item_data["classes_activity_order_id"] = $res1['id']; // // unset($order_item_data['id']); // unset($order_item_data['createtime']); // $orderItem = (new OrderItem()); // $orderItem->allowField(true)->save($order_item_data); //更新订单详情id $res1->activity_order_detail_id = $orderDetail->id; $res1->save(); //记录订单日志 OrderLog::log($res1['id'],"活动订单创建成功,等待下一步操作(如果付费需去支付)",'user',$user_id); //7事件 $data = ['order' => self::where("id",$res1['id'])->find()]; \think\Hook::listen('activity_order_create_after', $data); //更新订单数据 self::statisticsAndUpdateClassesNumber($res1); return $res1; } //统计并更新活动数等相关统计数据 public static function statisticsAndUpdateClassesNumber($order){ if(is_string($order)||is_numeric($order))$order = self::getOrder($order); // $detail = $order->detail; // if(!$detail)throw new \think\Exception("订单信息缺失!"); // // //课程下单时已核销人数更新 // $lib = $order->activity; // if($lib){ // // } //将课程信息和课时信息同步到所有已下单的订单信息中 (new Activity)->update_classes($order["activity_id"]); //如果有评价执行评价更新 return $order; } /** * 检测支付开关(待扩展) */ public static function checkPaySwitch($pay_type,$user_id){ } /**订单支付前判断 * @param $order_no * @param $pay_type * @return array|false|\PDOStatement|string|Model */ public function checkPay($order_no,$pay_type,$password = null){ $order = self::getNopayOrder($order_no); //支付前判断 //TODO通用判 $this->orderVaild($order['user_id'],$order['activity_id'],$order['num'],$order['order_no'], []); //... $user = User::where('id',$order['user_id'])->find(); if(!$user)throw new \Exception("支付用户异常"); //是否开通此支付方式 self::checkPaySwitch($pay_type,$order['user_id']); //各自币种判断 switch ($pay_type) { case "wechat": //微信支付 //.. break; case "alipay": //支付宝支付 //... break; case "offline": //线下支付 //.. break; case "yue": //余额支付 //支付密码判断 // $res = User::checkPayPassword($order['user_id'],$password,true); // if(!$res['is_setting'])throw new \Exception("您还未设置支付密码,请您先去设置支付密码"); // if(!$res['is_right'])throw new \Exception("支付密码错误"); //判断余额是否充足 //得到支付余额 $total_amount = $order['totalprice'] ?:0; //得到用户余额 $money = $user['money']; if(bccomp($money,$total_amount)==-1)throw new \Exception("当前余额不足以完成本次支付!"); break; default: throw new \Exception("不支持的支付类型验证"); } return true; } /** 课时订单操作权限检测 * @param $order 订单 * @param $oper_id 操作人id * @param $oper_type 操作人类型:user-用户或员工,admin-管理员 * @return void * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function checkOptionAuth($classes_order_id,$oper_id,$oper_type,$only_user=false,$only_shop=false,$only_admin=false,$only_shop_user=false){ //课程是否存在并上架 $classesOrder = Order::where("id",$classes_order_id)->find(); if(!$classesOrder)throw new \Exception("订单不存在!"); switch ($oper_type) { case 'user': if($only_admin)throw new \Exception("您无权操作该订单!"); //自己或操作员 if($oper_id != $classesOrder["user_id"]){ if($only_user) throw new \Exception("您无权操作该订单!"); //说明是操作员 $help_user_info = User::where('id',$oper_id)->find(); if(!$help_user_info) throw new \Exception("代下单员工不存在!"); $classes_activity_ids = (new Activity())->getActivityAuthIds($oper_id); //判断当前订单课程是否在此课程授权范围内 if(!in_array($classesOrder["activity_id"],$classes_activity_ids)) throw new \Exception("该活动不在您的授权范围内,无法代操作!"); }else{ if($only_shop_user) throw new \Exception("您无权操作该订单!"); $classes_activity_ids = (new Activity())->getActivityAuthIds($oper_id); //不是员工并且想操作只有机构能操作的单 if(!in_array($classesOrder["activity_id"],$classes_activity_ids) && $only_shop)throw new \Exception("您无权操作该订单!"); } break; case 'admin': $admin_info = Admin::where('id',$oper_id)->find(); if(!$admin_info) throw new \Exception("代下单管理员不存在!"); break; case 'shop': if($only_admin)throw new \Exception("您无权操作该订单!"); if($only_user) throw new \Exception("您无权操作该订单!"); $admin_info = Manystore::where('id',$oper_id)->find(); if(!$admin_info) throw new \Exception("代下单管理员不存在!"); $classes_activity_ids = (new Activity())->getActivityAuthIds($admin_info["user_id"]); //判断当前订单课程是否在此课程授权范围内 if(!in_array($classesOrder["activity_id"],$classes_activity_ids)) throw new \Exception("该活动不在您的授权范围内,无法代操作!"); break; default: throw new \Exception("请选择正确的代下单类型!"); } } /** 检测订单完成状态 * @param $order * @return void * @throws \think\Exception * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function statisticsAndUpdateOrderFinish($order){ if(is_string($order)||is_numeric($order))$order = self::getOrder($order); $detail = $order->detail; if(!$detail)throw new \think\Exception("订单信息缺失!"); //得到当前订单的所有已完成课时订单 $hourorderOrderCount = OrderCode::where("status","=",'6') ->where("activity_order_id","=",$order["id"]) ->count(); //可完成的订单 if(in_array($order["status"],["2",'3']) && (in_array($order["server_status"],['0'])) && !$order['finishtime']){ //判定是否达成完成条件 //条件:课程课时已全部完成 if($hourorderOrderCount >= $order->num){ //执行订单完成的更新逻辑 self::updateFinish($order); }elseif($order["status"] == "2"){ //更新订单状态成核销中 $order["status"] = '3'; $order["reservation_time"] = time(); $order->save(); } } } /** 更新订单成已完成 * @param $order * @return void */ public static function updateFinish($order){ if(is_string($order)||is_numeric($order))$order = self::getOrder($order); //检测未核销订单,强制核销 $orderCodes = OrderCode::where("status","=",'3') ->where("activity_order_id","=",$order["id"]) ->select(); foreach ($orderCodes as $k=>$orderCode){ //强制核销 (new OrderCode)->forceVerification($orderCode["code"],"admin",0,false); } //更新订单 $order["status"] = '9'; $order["finishtime"] = time(); $order->save(); //记录订单日志 OrderLog::log($order['id'],"活动订单完成,人数已全部核销",'user',$order['user_id']); //释放结算订单 // (new SettleLog)->generatorLog($order['id']); //调用支付成功事件 $data = ['order' => $order]; \think\Hook::listen('activity_order_finish_after', $data); } /**得到可取消的免费订单以及未支付取消 * @param $order_no * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getHaveCancelFreeOrder($order_no,$check=false){ // $where = [self::STATUS_NOPAY,self::STATUS_PAYED]; $order = self::where('order_no|id|pay_no',$order_no)->find(); $detail = $order->detail; if(!$detail) throw new \Exception("订单信息缺失!"); if(!$order)throw new \Exception("订单信息缺失!"); //非免费单进行中无法取消 if($order['totalprice'] != 0){ //非免费单只有未支付的单才可以取消 if($order['status'] != '0'){ throw new \Exception("只有未支付的单才可以取消!"); } }else{ if($check && !in_array($order['status'],['0','2'])){ throw new \Exception("免费单只有进行中订单可取消,已取消请忽重复取消!"); } } if(!$check) return $order; //活动开始后不可取消 $time = time(); if ($detail['start_time'] < $time) throw new \Exception("活动开始后不可取消!"); return $order; } /**更新免费订单取消状态以及未支付取消 * @param $order * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function updateFreeCancel($order,$check=false){ if(is_string($order))$order = self::getHaveCancelFreeOrder($order,$check); $order->status = "-3";//refund_status $order->canceltime = time(); $order->save(); return $order; } /**免费订单取消以及未支付取消 * @param $order_no * @param int $user_id * @param bool $check * @param bool $trans * @return bool * @throws \Exception */ public function freeCancel($order_no,$user_id=0,$check=false,$oper_type='user',$oper_id=0,$trans=false){ //得到可取消订单 $order = self::getHaveCancelFreeOrder($order_no,$check); if($check){ //用户操作权限检测 self::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type); } //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //事务逻辑 //更新订单取消状态 $order = self::updateFreeCancel($order,$check); //插入订单取消日志 if(!$user_id ||$order["user_id"] !=$user_id ){ OrderLog::log($order['id'],"[系统操作]活动订单取消成功",$oper_type ?: 'user', $oper_id ?: $order['user_id']); }else{ OrderLog::log($order['id'],"活动订单取消成功",$oper_type ?: 'user', $oper_id ?: $order['user_id']); } //调用订单取消事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id]; \think\Hook::listen('activity_order_cancel_after', $data); //执行课时数更新 $res1 = self::statisticsAndUpdateClassesNumber($order['id']); if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); } return $res1; } /** * 超时取消检测(未支付取消) */ public static function timeoutCheck($order_id=0,$trans = false){ $count = 0; $unpaid_order_expire_time = config("site.unpaid_activity_cancel_time"); if(!$unpaid_order_expire_time|| $unpaid_order_expire_time < 0)return $count; $unpaid_order_expire_time = time() - $unpaid_order_expire_time; //得到所有过期的队列 $model = self::where("status",'in',['0'])->where("createtime","<=",$unpaid_order_expire_time); if($order_id)$model = $model->where("id",$order_id); $list = $model->select(); if ($trans) { self::beginTrans(); } try { foreach ($list as $order) { //取消订单 (new self)->freeCancel($order['id'],0,false,'admin',0); $count++; } if ($trans) { self::commitTrans(); } } catch (\Exception $e) { if ($trans) { self::rollbackTrans(); } throw new \Exception($e->getMessage()); } return $count; } /**得到可取消的付费订单 * @param $order_no * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getHaveCancelPaidOrder($order_no,$check=true){ // $where = [self::STATUS_NOPAY,self::STATUS_PAYED]; $order = self::where('order_no|id|pay_no',$order_no)->find(); $detail = $order->detail; if(!$detail) throw new \Exception("订单信息缺失!"); if(!$order)throw new \Exception("订单信息缺失!"); //非免费单进行中无法取消 if($order['totalprice'] != 0){ //非免费单只有未退款可退 if(!in_array($order['status'],["2","3","9"])){ throw new \Exception("只有已报名和核销中的单才可以取消!"); } }else{ throw new \Exception("免费单请走免费订单取消接口!"); } if($check){ //根据不同退款策略判断当前时间点是否可取消并退款 $refund_status = $detail["refund_status"]; //活动开始后不可取消,只能走售后 $time = time(); switch ($refund_status){ case "1": //不退款 throw new \Exception("当前活动不支持取消退款,请走售后流程!"); break; case "3": //开始前退 case "5": //随时退 if ($detail['start_time'] < $time) throw new \Exception("活动开始后不可取消,请走售后流程!"); break; default: throw new \Exception("不支持的退款策略,请走售后流程!"); } } return $order; } /**更新付费订单取消状态 * @param $order * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function updatePaidCancel($order,$check=true){ if(is_string($order))$order = self::getHaveCancelPaidOrder($order,$check); $order->status = "-3";//refund_status $order->canceltime = time(); $order->save(); //将所有资金结算记录作废 $data = [ "status" =>"-1", "canceltime" => time(), ]; SettleLog::where("activity_order_id",$order["id"])->where("status","in",['1','2'])->update($data); return $order; } /**得到可审核订单 * @param $order_no * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getHaveExamineOrder($order_no){ // $where = [self::STATUS_NOPAY,self::STATUS_PAYED]; $order = self::where('order_no|id|pay_no',$order_no)->where("status","in",["-3",'4'])->find(); if(!$order)throw new \Exception("不是待审核的订单!"); return $order; } /**更新订单结算中状态 * @param $order * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function updateExamineFailSettlement($order,$reason="",$oper_id = 0,$oper_type='user'){ if(is_string($order))$order = self::getHaveExamineOrder($order); $order->before_status = $order->status; $order->status = "5"; $order->auth_status = "1"; $order->server_status = "6"; $order->auth_time = time(); $order->refundsendtime = time(); $order->reason = $reason; $order->auth_user_id = $oper_id; $order->auth_type = $oper_type; $order->save(); return $order; } /**付费订单取消 * @param $order_no * @param int $user_id * @param bool $check * @param bool $trans * @return bool * @throws \Exception */ public function paidCancel($order_no,$user_id=0,$check=false,$oper_type='user',$oper_id=0,$activity_cancel=false,$trans=false){ //得到可取消订单 $order = self::getHaveCancelPaidOrder($order_no,$check); if($check){ //用户操作权限检测 self::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type); } //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //事务逻辑 //更新订单取消状态 $order = self::updatePaidCancel($order,$check); //插入订单取消日志 if(!$user_id ||$order["user_id"] !=$user_id ){ OrderLog::log($order['id'],"[系统操作]活动订单取消成功,款项在一个工作日内将自动退还",$oper_type ?: 'user', $oper_id ?: $order['user_id']); }else{ OrderLog::log($order['id'],"活动订单取消成功,款项在一个工作日内将自动退还",$oper_type ?: 'user', $oper_id ?: $order['user_id']); } //调用订单取消事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id]; \think\Hook::listen('activity_order_cancel_after', $data); //执行课时数更新 $res1 = self::statisticsAndUpdateClassesNumber($order['id']); //执行退款 //调用退款发起 //更新订单状态 $reason = "活动取消自动退款"; $order = self::updateExamineFailSettlement($order['order_no'],"",$user_id ?: $oper_id,$oper_type); //审核失败逻辑 OrderLog::log($order['id'],"活动取消退款,原因;{$reason},如有退款额度,该活动单将自动退款以便重新下单(没有请忽略)",$oper_type ?: 'user', $oper_id ?: $order['user_id']); //调用订单事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id]; \think\Hook::listen('activity_order_auth_fail_after', $data); //计算自动退款额度 $refundprice = $this->countRefundAmount($order["id"],$activity_cancel); if($refundprice>0){ //执行退款 self::orderRefund($order['order_no'],$refundprice,$oper_type,$oper_id); } if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); } return $res1; } /** 如果是自动退款,根据退款策略计算应退款额 * @param $order_id * @param $trans * @return true * @throws \Exception */ public function countRefundAmount($order_id,$activity_cancel=false,$trans=false){ $price = 0; $order = self::where('id',$order_id)->find(); if(!$order) return $price; $detail = $order->detail; if(!$detail) return $price; // //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //根据不同退款策略判断当前时间点是否可取消并退款 $refund_status = $detail["refund_status"]; //活动开始后不可取消,只能走售后 $time = time(); if($activity_cancel){ //活动取消退全款 $price = $order["sub_refundprice"]; }else{ switch ($refund_status){ case "1": //不退款 throw new \Exception("当前活动不支持取消退款,请走售后流程!"); break; case "3": //开始前退 case "5": //随时退 退全款 $price = $order["sub_refundprice"]; break; default: throw new \Exception("不支持的退款策略,请走售后流程!"); } } if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); } return $price; } /**得到可直接退款订单(正常取消退全款非售后) * @param $order_no * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getHaveRefundOrder($refund_sn){ // $where = [self::STATUS_NOPAY,self::STATUS_PAYED]; $order = self::where('refund_no|order_no|id',$refund_sn)->where('status','5')->where("sub_refundprice",">",0)->find(); if(!$order)throw new \Exception("不是可直接退款的订单!"); return $order; } /**预约订单退款(退全款) * @param $order * @param $refund_money * @param $trans */ public static function orderRefund($order,$refund_money,$oper_type='user',$oper_id=0,$trans=false,$admin=false){ if(is_numeric($order))$order = self::getHaveRefundOrder($order); if(!$order)throw new \Exception("找不到订单"); //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //生成退款单号 if(!$order['refund_no']){ $order['refund_no'] = get_order_sn(); // $order->save(); } if(!$refund_money)$refund_money = $order["total_refundprice"] ?: $order['sub_refundprice']; if($refund_money<=0)$refund_money = 0; if(!$refund_money)throw new \Exception("退款金额异常!"); //保存应退款额 $order->total_refundprice = $refund_money; $order->save(); //事务逻辑 switch ($order['pay_type']) { case "wechat": //微信退款 self::wechatRefund($order,$refund_money,$oper_type,$oper_id); break; // case "alipay": //支付宝退款 // self::alipayRefund($order,$refund_money,$oper_type,$oper_id); // break; // case "wallet": //钱包支付退款 // self::walletRefund($order,$refund_money,$oper_type,$oper_id); // break; // case "offline": //线下支付退款 // // break; default: throw new \Exception("订单币种异常!"); } if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage()); } return $res; } /**微信退款 * @param $order * @param $refund_money */ public static function wechatRefund($order,$refund_money,$refund_desc="",$oper_type='user',$oper_id=0,$trans=false){ //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //事务逻辑 //创建订单退款订单 $classesorder = $order; $detail = $order->detail; //执行微信退款sdk $order_data = [ 'out_trade_no' => $classesorder->order_no ]; $total_fee = $classesorder->payprice * 100; $refund_fee = $refund_money * 100; $order_data = array_merge($order_data, [ 'out_refund_no' => $order->refund_no, 'total_fee' => $total_fee, 'refund_fee' => $refund_fee, 'refund_desc' => $refund_desc ?: "活动[{$detail["title"]}]订单[ID:{$classesorder['id']}]退款{$refund_money}已到账户", ]); $config = Service::getConfig('wechat',[],$classesorder->platform); $notify_url = request()->domain() . '/api/school.newactivity.pay/notifyr/payment/' . $classesorder->pay_type . '/platform/' . $classesorder->platform; $config['notify_url'] = $notify_url; $pay = Pay::wechat($config); // throw new \Exception($trans."111测试错误!".$order["status"]); $result = $pay->refund($order_data); \think\Log::write('refund-result' . json_encode($result)); if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') { $res = true; } else { throw new \Exception($result['return_msg']); } if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); } return $res; } /**同意并发放退款金额 * @param $order 退款单号 * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function updateRefundOrder($order,$price=0,$pay_json=[]){ if(is_string($order))$order = self::getHaveRefundOrder($order); // //如果$price为0,尝试从$pay_json退款回调中获取退款金额 // if(!$price && $pay_json){ // if(isset($pay_json['refund_fee']))$price = bcdiv($pay_json['refund_fee'],100,2); // } // $order->before_status = $order->status;//refund_status $order->status = "6"; $order->server_status = "6"; $order->real_refundprice = bcadd($order->real_refundprice ?:'0', $price?:$order->total_refundprice ,2); $order->sub_refundprice = bcsub($order->sub_refundprice ?:'0', $price?:$order->total_refundprice ,2); $order->refundtime = time(); $order->refund_json = json_encode($pay_json); $order->save(); //若存在剩余金额,结算给发布者 if($order->sub_refundprice > $order->fee_scale){ //报废旧的结算单 SettleLog::where("activity_order_id",$order['id']) ->where("status" ,"in",["1","2"]) ->update([ "status"=>"-1", "canceltime" => time() ]); //插入新结算单 (new SettleLog)->generatorLog($order['id']); } return $order; } /**退款成功处理逻辑(需修改) * @param $refund_sn * @param bool $trans * @return bool * @throws \Exception */ public static function refundSuccess($refund_sn,$refund_json=[],$price=0,$trans=false){ //得到机构售后提交确认订单 $order = self::getHaveRefundOrder($refund_sn); //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //事务逻辑 //更新订单状态为同意 $order = self::updateRefundOrder($refund_sn,$price,$refund_json); //插入订单日志 OrderLog::log($order['id'],"活动订单退款已原路退回", 'admin', 0); //执行课时数更新 $res1 = self::statisticsAndUpdateClassesNumber($order['id']); //调用订单取消事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$order['user_id'],"oper_type"=>'admin',"oper_id"=>0]; \think\Hook::listen('activity_order_refund_success_after', $data); if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage()); } return $res; } /**订单退款失败记录 * @param $order * @param $refund_money * @param $trans */ public static function orderRefundFail($order,$msg,$oper_type='user',$oper_id=0,$trans=false,$admin=false){ if(is_numeric($order))$order = self::getOrder($order); if(!$order)throw new \Exception("找不到订单"); //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //事务逻辑 $order->refund_error = $msg; $order->save(); //插入订单日志 OrderLog::log($order['id'],"活动订单退款失败:".$msg, $oper_type, $oper_id); //调用订单取消事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$order['user_id'],"oper_type"=>$oper_type,"oper_id"=>$oper_id]; \think\Hook::listen('activity_order_refund_fail_after', $data); if($trans){ self::commitTrans(); } }catch (\Throwable $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage()); } return $res; } /**得到可直接退款订单(正常取消退全款非售后) * @param $order_no * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function getHaveAfterSalesOrder($refund_sn,$check=true){ // $where = [self::STATUS_NOPAY,self::STATUS_PAYED]; $order = self::where('refund_no|order_no|id',$refund_sn)->where('status',"in",["2","3","9"])->where("sub_refundprice",">",0)->find(); if(!$order)throw new \Exception("不是可直接退款的订单!"); $detail = $order->detail; if(!$detail)throw new \Exception("找不到订单详情!"); if($check){ if(!in_array($order['status'],["2","3","9"])) throw new \Exception("不是可申请售后的订单!"); //申请售后必须是活动结束后n秒内 $last_time = $detail['end_time'] + config('site.activity_end_sales'); if(time() > $last_time) throw new \Exception("已超过发起售后的最后期限!"); } return $order; } public function updateAfterSales($order,$total_refundprice,$auth_reason,$check=true){ if(is_numeric($order))$order = self::getHaveAfterSalesOrder($order,$check); $order["before_status"] = $order["status"]; $order["status"] = "4"; $order["server_status"] = "3"; $order["auth_time"] = time(); $order["auth_status"] = 0; $order["auth_reason"] = $auth_reason; $order["total_refundprice"] = $total_refundprice; $order["first_refundprice"] = $total_refundprice; $order->save(); } /** 发起售后 * @param $order_no * @param $user_id * @param $trans * @return true * @throws \Exception */ public function afterSales($order,$num,$auth_reason,$user_id ,$check=true,$oper_type='user',$oper_id=0,$trans=false){ if(is_numeric($order))$order = self::getHaveAfterSalesOrder($order,$check); if(!$order)throw new \Exception("找不到订单"); if($check){ //用户操作权限检测 self::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type); } $detail = $order->detail; if(!$detail)throw new \Exception("找不到订单详情!"); if(!$auth_reason) throw new \Exception("请填写申请原因"); //$num至少要1,至多要订单数量 if($num < 1 || $num > $order['num']) throw new \Exception("数量错误"); $total_refundprice = bcmul($detail['price'],$num,2); if($total_refundprice < 0.01 || $total_refundprice > $order['sub_refundprice']) throw new \Exception("退款金额错误"); //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //设置订单申请状态 $this->updateAfterSales($order,$total_refundprice,$auth_reason,$check); //订单日志 OrderLog::log($order['id'],"活动订单申请售后:".$auth_reason, $oper_type, $oper_id); //调用订单取消事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$order['user_id'],"oper_type"=>$oper_type,"oper_id"=>$oper_id]; \think\Hook::listen('activity_order_after_sales_after', $data); if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); } return $res; } public static function getHaveShopConfirmationOrder($refund_sn,$check=true){ // $where = [self::STATUS_NOPAY,self::STATUS_PAYED]; $order = self::where('refund_no|order_no|id',$refund_sn)->where('status',"in",["4","7"])->where("sub_refundprice",">",0)->find(); if(!$order)throw new \Exception("不是可直接退款的订单!"); $detail = $order->detail; if(!$detail)throw new \Exception("找不到订单详情!"); if($check){ } return $order; } /**机构同意并确认价格 * @param $order * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function updateShopConfirmationOrder($order,$price){ if(is_string($order))$order = self::getHaveShopConfirmationOrder($order); $order->status = "5";//refund_status $order->service_stauts = "6"; $order->auth_status = 1; $order->total_refundprice = $price; $order->refundtime = time(); $order->save(); return $order; } /**机构拒绝售后 * @param $order * @return array|false|\PDOStatement|string|Model * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException */ public static function updateShopConfirmationFailOrder($order,$reason){ if(is_string($order))$order = self::getHaveShopConfirmationOrder($order); $order->status = "7";//refund_status $order->service_stauts = "6"; $order->auth_status = 2; $order->reason = $reason; $order->suspend_status = '1'; $order->save(); return $order; } /**发布者售后提交确认信息 * @param $order_no * @param int $user_id * @param string $status yes同意 no拒绝 *@param string $price 商定价格 *@param string $reject_reason 拒绝原因 * @param bool $check * @param bool $trans * @return bool * @throws \Exception */ public function shopConfirmation($order_no,$status,$price,$reject_reason,$user_id=0,$check=false,$oper_type='user',$oper_id=0,$trans=false){ //得到机构售后提交确认订单 $order = self::getHaveShopConfirmationOrder($order_no); if($check){ //用户操作权限检测 Order::checkOptionAuth($order['id'],$user_id ?: $oper_id,$oper_type,false,true); } $classesorder = $order; if(!$classesorder)throw new \Exception("订单不存在!"); switch ($status){ case 'yes': //同意 //验证价格正确性 //金额必须小于等于剩余未退全额 $price = floatval($price); if($price > $classesorder['sub_refundprice']){ throw new \Exception("价格必须小于等于剩余应退全额!"); } //数据修正大于剩余未退全额 则等于剩余未退 $price = $price > $classesorder['sub_refundprice'] ? $classesorder['sub_refundprice'] : $price; if($price<=0)throw new \Exception("同意退款必须大于0!"); //如果非全额退款,剩余金额必须大于订单支付时的手续费 if($price < $classesorder['sub_refundprice']){ $sub_price = bcsub($classesorder['sub_refundprice'] , $price,2); if($sub_price <= $classesorder['fee_price']){ throw new \Exception("如果未退全款,剩余未退金额必须大于订单支付时的手续费{$classesorder['fee_price']}!"); } } break; case 'no': //拒绝 //拒绝原因必填 if(!$reject_reason)throw new \Exception("拒绝原因必填!"); break; default: throw new \Exception("参数错误!"); } //判断逻辑 if($trans){ self::beginTrans(); } $res = true; try{ //事务逻辑 //更新售后为取消状态 switch ($status){ case 'yes': //同意 //更新订单状态为同意 $order = self::updateShopConfirmationOrder($order,$price); //插入订单日志 if(!$user_id ||$order["user_id"] !=$user_id ){ OrderLog::log($order['id'],"[系统操作]活动订单售后已处理,用户发起金额为{$order['first_refundprice']},确认退款金额为{$price}元,等待退款到账",$oper_type ?: 'user', $oper_id ?: $order['user_id']); }else{ OrderLog::log($order['id'],"活动订单售后已处理,用户发起金额为{$order['first_refundprice']},确认退款金额为{$price}元,等待退款到账",$oper_type ?: 'user', $oper_id ?: $order['user_id']); } //调用订单事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id]; \think\Hook::listen('activity_order_shop_confirm_after', $data); //执行退款 //执行退款 self::orderRefund($order['order_no'],$price,$oper_type,$oper_id); break; case 'no': //拒绝 //更新 //更新售后为机构驳回结单状态 $order = self::updateShopConfirmationFailOrder($order,$reject_reason); //插入订单日志 if(!$user_id ||$order["user_id"] !=$user_id ){ $order::log($order['id'],"[系统操作]活动订单售后驳回,订单挂起,等待官方处理",$oper_type ?: 'user', $oper_id ?: $order['user_id']); }else{ $order::log($order['id'],"活动订单售后驳回,订单挂起,等待官方处理",$oper_type ?: 'user', $oper_id ?: $order['user_id']); } //调用订单取消事件 $data = ['order' => self::where("id",$order['id'])->find(),"user_id"=>$user_id,"oper_type"=>$oper_type,"oper_id"=>$oper_id]; \think\Hook::listen('activity_order_shop_reject_after', $data); break; default: throw new \Exception("参数错误!"); } //执行课时数更新 $res1 = order::statisticsAndUpdateClassesNumber($order['classes_order_id']); if($trans){ self::commitTrans(); } }catch (\Exception $e){ if($trans){ self::rollbackTrans(); } throw new \Exception($e->getMessage()); } return $res1; } }