$this->status, 'message'=>$this->message, ]; } public function createOrderToExchange(User $user, $goods_info){ $pay_type = 2;//默认兑换支付; //检测商品有效性 $attr_data['goods_info'] = $goods_info; $info_name = []; $attr_price = 0; $goods = Good::findOrFail($goods_info['goods_id']); if(!$goods->is_sell){ $this->status = false; $this->message = '商品已下架'; return $this->toRes(); } $goods_attrs = GoodsAttr::with('attr')->where('is_use', 1)->whereIn('id', $goods_info['goods_attr'])->get(); //判断属性是否均可用 if($goods_attrs->count() != count($goods_info['goods_attr'])){ $this->status = false; $this->message = '商品属性错误'; return $this->toRes(); } foreach($goods_attrs as $goods_attr){ //判断属性是否有重复选择 if($goods_attr->attr->attr_type == 1 && isset($info_name[$goods_attr->attr_id])){ $this->status = false; $this->message = '商品属性错误'; return $this->toRes(); } if(isset($info_name[$goods_attr->attr_id])){ $info_name[$goods_attr->attr_id] .= '|'.$goods_attr->attr_value; }else{ $info_name[$goods_attr->attr_id] = $goods_attr->attr_name.":".$goods_attr->attr_value; } $attr_price += $goods_attr->value_price; } $attr_data['info_name'] = $info_name; $goods_info['goods_name'] = $goods->goods_name; $goods_info['goods_cover'] = $goods->goods_cover; $goods_info['goods_attrs'] = json_encode($attr_data, JSON_UNESCAPED_UNICODE);; $goods_info['goods_price'] = $goods->goods_price + $attr_price; $goods_info['goods_num'] = 1; $goods_infos[] =$goods_info; //检测是否满足兑换 $need_echange = []; $goods_service = new GoodsService(); if(!($need_echange = $goods_service->canExchange($goods->id, $user->id))){ $this->status = false; $this->message = '兑换奖品不足'; return $this->toRes(); } $order = $this->createOrder($user->id, $goods_infos, $pay_type); //丢入延时队列,定时取消 $closed_time = now()->addMinutes(15); TimingCancelOrderJob::dispatch($order)->delay($closed_time); $order->closed_time = $closed_time; $order->save(); //记录兑换需要的图标; PayLog::where(['order_sn'=>$order->order_sn, 'status'=>0])->update(['pay_ext'=>json_encode($need_echange)]); return $order; } public function createOrderToCart(User $user){ $pay_type = 1;//默认微信支付; //从购物车中获取商品 $cart_goods = $user->getUserCart(); $goods_infos = $cart_goods->toArray(); if(count($goods_infos) < 1){ $this->status = false; $this->message = '购物车为空,无法下单'; return $this->toRes(); } //插入需要的商品信息 foreach($goods_infos as $key => $goods_info){ $goods_infos[$key]['goods_cover'] = $goods_info['goods']['goods_cover']; $goods_infos[$key]['is_too'] = $goods_info['goods']['is_too'];//小程序下单,第二杯半价 } //创建订单 $order = $this->createOrder($user->id, $goods_infos, $pay_type); if($order){ //清空购物车; User::findOrFail($user->id)->cleanUserCart(); //丢入延时队列,定时取消 $closed_time = now()->addMinutes(15); TimingCancelOrderJob::dispatch($order)->delay($closed_time); $order->closed_time = $closed_time; $order->save(); switch($pay_type){ case 1://微信支付 $wechat_pay = new WechatPay(); $res = $wechat_pay->createOrder($order); if($res['return_code'] == 'SUCCESS'){ //记录perpay_id; PayLog::where(['order_sn'=>$order->order_sn, 'status'=>0])->update(['pay_ext'=>$res['prepay_id']]); }else{ $this->status = false; $this->message = $res['return_msg']; return $this->toRes(); } break; case 3://线下支付 break; } return $order; }else{ $this->status = false; // $this->message = '创建订单失败, 请稍后再试'; return $this->toRes(); } } /** * 预约下单 */ public function createOrderToReserve(User $user, $goods_info, $reserve_data){ $pay_type = 1;//默认微信支付; $now_time = Carbon::now(); $attr_data['goods_info'] = $goods_info; $info_name = []; $attr_price = 0; $goods = Good::findOrFail($goods_info['goods_id']); $activity = ActivityDayGood::where('id', $reserve_data['activity_id']) ->where('start_time', '<=', $now_time) ->where('end_time', '>=', $now_time) ->first(); if(!$activity){ $this->status = false; $this->message = '活动已关闭'; return $this->toRes(); } //检测商品和活动是否匹配 if($activity->goods_id != $goods->id){ $this->status = false; $this->message = '商品与活动不符'; return $this->toRes(); } //检测库存 $today = $now_time->toDateString(); $hash_key = 'reserve_goods_' . $activity->id. '_' . $today; if(Redis::command('exists', [$hash_key]) == 0){//今日库存是否存入redis //获取今日指定库存 $num = $activity->goods_num; $day_num = ActivityDayNum::where(['activity_day_id'=>$activity->id, 'time'=>$today])->first(); if($day_num){ $num = $day_num->goods_num; } Redis::command('set', [$hash_key, $num]); } $num = Redis::command('get', [$hash_key]); if ($num <= 0) { $this->status = false; $this->message = '来晚了,下次早点吧'; return $this->toRes(); } //检测限购 $start_time = Carbon::now()->startOfDay(); $end_time = Carbon::now()->endOfDay(); $times = ReserveOrderLog::where(['activity_day_id'=>$activity->id, 'user_id'=>$user->id])->whereBetween('created_at', [$start_time, $end_time])->count(); $buy_nums = 0; if($times > 0){ if($times >= $activity->buy_times){ $this->status = false; $this->message = '已到当日限购次数,明天再来吧'; return $this->toRes(); } $buy_nums = ReserveOrderLog::where(['activity_day_id'=>$activity->id, 'pay_status'=>1, 'user_id'=>$user->id])->whereBetween('created_at', [$start_time, $end_time])->sum('goods_num'); if($buy_nums >= $activity->buy_num){ $this->status = false; $this->message = '已到当日限购数量,明天再来吧'; return $this->toRes(); } } if(($reserve_data['goods_num'] + $buy_nums) > $activity->buy_num){ $this->status = false; $this->message = '超出当日限购数量,请减少购买数量'; return $this->toRes(); } $goods_attrs = GoodsAttr::with('attr')->where('is_use', 1)->whereIn('id', $goods_info['goods_attr'])->get(); //判断属性是否均可用 if($goods_attrs->count() != count($goods_info['goods_attr'])){ $this->status = false; $this->message = '商品属性错误'; return $this->toRes(); } foreach($goods_attrs as $goods_attr){ //判断属性是否有重复选择 if($goods_attr->attr->attr_type == 1 && isset($info_name[$goods_attr->attr_id])){ $this->status = false; $this->message = '商品属性错误'; return $this->toRes(); } if(isset($info_name[$goods_attr->attr_id])){ $info_name[$goods_attr->attr_id] .= '|'.$goods_attr->attr_value; }else{ $info_name[$goods_attr->attr_id] = $goods_attr->attr_name.":".$goods_attr->attr_value; } $attr_price += $goods_attr->value_price; } $attr_data['info_name'] = $info_name; $goods_info['goods_name'] = $goods->goods_name; $goods_info['goods_cover'] = $goods->goods_cover; $goods_info['goods_attrs'] = json_encode($attr_data, JSON_UNESCAPED_UNICODE);; $goods_info['goods_price'] = $goods->goods_price + $attr_price; $goods_info['goods_num'] = $reserve_data['goods_num']; $goods_info['is_too'] = $goods->is_too; $goods_infos[] =$goods_info; //减库存 $re = Redis::command('decrby', [$hash_key, $reserve_data['goods_num']]); //减多了回滚 if ($re < 0) { Redis::command('incrby', [$hash_key, $reserve_data['goods_num']]); $this->status = false; $this->message = '剩余库存不足'; return $this->toRes(); } //创建订单 $order = $this->createOrder($user->id, $goods_infos, $pay_type); if($order){ //丢入延时队列,定时取消 $closed_time = now()->addMinutes(15); TimingCancelOrderJob::dispatch($order)->delay($closed_time); $order->closed_time = $closed_time; $order->is_reserve = 1; $order->reserve_time = $reserve_data['reserve_time'].' 00:00:00'; $order->save(); //插入预约记录 ReserveOrderLog::create([ 'activity_day_id' => $activity->id, 'user_id' => $user->id, 'order_sn' => $order->order_sn, 'goods_num' => $reserve_data['goods_num'], 'pay_status' => 0, ]); //微信支付 $wechat_pay = new WechatPay(); $res = $wechat_pay->createOrder($order); if($res['return_code'] == 'SUCCESS'){ //记录perpay_id; PayLog::where(['order_sn'=>$order->order_sn, 'status'=>0])->update(['pay_ext'=>$res['prepay_id']]); }else{ $this->status = false; $this->message = $res['return_msg']; return $this->toRes(); } return $order; }else{ $this->status = false; $this->message = '创建订单失败, 请稍后再试'; Redis::command('incrby', [$hash_key, $reserve_data['goods_num']]);//回滚库存 return $this->toRes(); } } /** * 后台下单 */ public function createOrderToAdmin($a_goods_infos){ $pay_type = 3;//默认线下点单; //处理商品 $goods_infos = []; foreach($a_goods_infos as $goods_info){ $attr_data['goods_info'] = $goods_info; $info_name = []; $attr_price = 0; $goods = Good::findOrFail($goods_info['goods_id']); if(!$goods->is_sell){ $this->status = false; $this->message = '商品已下架'; return $this->toRes(); } $goods_attrs = GoodsAttr::with('attr')->where('is_use', 1)->whereIn('id', $goods_info['goods_attr'])->get(); //判断属性是否均可用 if($goods_attrs->count() != count($goods_info['goods_attr'])){ $this->status = false; $this->message = '商品属性错误'; return $this->toRes(); } foreach($goods_attrs as $goods_attr){ //判断属性是否有重复选择 if($goods_attr->attr->attr_type == 1 && isset($info_name[$goods_attr->attr_id])){ $this->status = false; $this->message = '商品属性错误'; return $this->toRes(); } if(isset($info_name[$goods_attr->attr_id])){ $info_name[$goods_attr->attr_id] .= '|'.$goods_attr->attr_value; }else{ $info_name[$goods_attr->attr_id] = $goods_attr->attr_name.":".$goods_attr->attr_value; } $attr_price += $goods_attr->value_price; } $attr_data['info_name'] = $info_name; $goods_info['goods_name'] = $goods->goods_name; $goods_info['goods_cover'] = $goods->goods_cover; $goods_info['goods_attrs'] = json_encode($attr_data, JSON_UNESCAPED_UNICODE);; $goods_info['goods_price'] = $goods->goods_price + $attr_price; $goods_info['goods_num'] = $goods_info['goods_num']; $goods_infos[] =$goods_info; } $order = $this->createOrder(0, $goods_infos, $pay_type); if($order){ $this->payOrder($order); // //丢入延时队列,定时取消 // $closed_time = now()->addMinutes(15); // TimingCancelOrderJob::dispatch($order)->delay($closed_time); // $order->closed_time = $closed_time; // $order->save(); return $order; }else{ $this->status = false; return $this->toRes(); } } private function createOrder($user_id, $goods_infos = [], $pay_type){ try { DB::beginTransaction(); $order = new Order(); $order->order_sn = $this->createOrderSn($user_id); $order->user_id = $user_id; $order->order_phone = $user_id ? (User::findOrFail($user_id)->phone):''; $order->pay_type = $pay_type; $order->order_status = 1; //处理商品,计算订单价格 $order_goods = []; $order_price = 0; foreach($goods_infos as $goods){ $_goods = []; $_goods['goods_id'] = $goods['goods_id']; $_goods['goods_name'] = $goods['goods_name']; $_goods['goods_cover'] = $goods['goods_cover']; $_goods['goods_attrs'] = $goods['goods_attrs']; $_goods['goods_price'] = $goods['goods_price']; $_goods['goods_num'] = $goods['goods_num']; $_goods['is_too'] = isset($goods['is_too'])? $goods['is_too']:0; $_goods['special_value'] = 0; $_goods && $order_goods[] = $_goods; $order_price += $_goods['goods_price']*$_goods['goods_num']; } if($order->pay_type == 1 && env('APP_ENV', 'local') == 'local'){ $pay_price = 0.01;//测试环境,默认1分 }else{ if($order->pay_type == 2){ $pay_price = 0; }else{ $pay_price = $order_price; } } /** 处理第二杯半价商品start **/ $too_goods_arr = []; foreach($order_goods as $t_goods){ if($t_goods['is_too'] == 1){ if(isset($too_goods_arr[$t_goods['goods_id']])){ $too_goods_arr[$t_goods['goods_id']]['goods_num'] += $t_goods['goods_num']; }else{ $too_goods_arr[$t_goods['goods_id']]['goods_num'] = $t_goods['goods_num']; } if(!isset($too_goods_arr[$t_goods['goods_id']]['goods_price']) || $too_goods_arr[$t_goods['goods_id']]['goods_price'] > $t_goods['goods_price']) { $too_goods_arr[$t_goods['goods_id']]['goods_price'] = $t_goods['goods_price']; } } } $order_special_value = 0; if(count($too_goods_arr) > 0){ foreach($order_goods as $key=> $o_goods){ if(isset($too_goods_arr[$o_goods['goods_id']]) && $too_goods_arr[$o_goods['goods_id']]['goods_num'] >=2){ if($o_goods['goods_price'] == $too_goods_arr[$o_goods['goods_id']]['goods_price']){ $order_goods[$key]['special_value'] = number_format(round(intval($o_goods['goods_price']*100)/2)/100, 2); $order_special_value += $order_goods[$key]['special_value']; } } } } if($order_special_value){ $order->ticket_value = $order_special_value; $pay_price -= $order_special_value; if($pay_price <=0){ $pay_price = 0.01; } } /** 处理第二杯半价商品end **/ $order->order_price = $order_price; $order->pay_price = $pay_price; //保存订单 $order->save(); //插入订单商品; OrderGoods::insert(array_map(function($goods) use ($order){ $goods['order_id'] = $order->id; $goods['created_at'] = Carbon::now(); $goods['updated_at'] = Carbon::now(); return $goods; }, $order_goods)); //插入支付记录 PayLog::create([ 'type' => $order->pay_type, 'order_sn' => $order->order_sn, 'status' => 0, ]); DB::commit(); } catch (\Throwable $th) { DB::rollback(); $this->message = $th->getMessage(); return false; } return $order; } /** * 创建唯一订单编号 */ public function createOrderSn($user_id = 0){ list($msec, $sec) = explode(' ', microtime()); $msectime_num = sprintf('%d', floatval($msec) * 1000); //共16位:日期时间2106021613+用户ID序号格式化001+毫秒后3位210 return Carbon::now()->isoFormat('YYMMDDHHmm').str_pad($user_id, 3,'0',STR_PAD_LEFT).str_pad($msectime_num, 3,'0',STR_PAD_LEFT); } /** * 支付订单 */ public function payOrder(Order $order){ $order->pay_status = 1; $order->pay_time = now(); PayLog::where([ 'order_sn'=>$order->order_sn, 'status'=>0 ])->update(['status'=>1]); //非预约单 if(!$order->is_reserve){ //生成取餐号 $order->order_number = $this->createOrderNumber(); // //处理订单吹牛逻辑,是否幸运号 // $luck_number = ActivityOption::findOrFail(11)->key_value; // $luck_number = explode(',', $luck_number); // if(in_array($order->order_number, $luck_number)){ // $order->is_niu = 1; // } // $order->number_time = now(); }else{ //更新预约订单记录支付状态 ReserveOrderLog::where(['order_sn'=>$order->order_sn])->update(['pay_status'=>1]); } $order->closed_time = null; //如果是微信支付,则赠送积分 if($order->pay_type == 1){ $order_point_price = $order->ticket_value >0 ? number_format((intval($order->order_price*100) - intval($order->ticket_value*100))/100, 2):number_format($order->order_price, 2); $points = floor(floor($order_point_price)*(ActivityOption::findOrFail(12)->key_value)); $user = User::findOrFail($order->user_id); if($user){ $user->increment('points', $points); //记录积分消耗记录 PointsLog::create([ 'user_id'=> $user->id, 'type'=>1 , 'points'=> $points, 'now_points'=> $user->points, 'desc' => '支付订单:'.$order->order_sn, ]); } } //增加商品对应销量 $order_goods = OrderGoods::where('order_id', $order->id)->get(); if($order_goods->count() > 0){//处理不同数量的销量 foreach($order_goods as $goods){ Good::where('id', $goods->goods_id)->increment('sell_num', $goods->goods_num); } } $order->save(); //填入待取餐大屏 if($order->order_number){ // $this->insertBigScreenNumber($order->order_number); if(env('APP_ENV', 'local') == 'production'){ //打印订单 PrintOrderJob::dispatch($order); } } return $order; } /** * 创建取餐号 */ public function createOrderNumber(){ $start_time = Carbon::now()->startOfDay(); $end_time = Carbon::now()->endOfDay(); $num = Order::where([ 'order_status'=>1, 'pay_status' =>1, ])->whereBetween('number_time', [$start_time, $end_time])->count()+1; $oneArray = [ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' ]; $one = $oneArray[$num/100]; $two = $num%100; return $one.str_pad($two, 2,'0',STR_PAD_LEFT); } /** * 订单取餐 */ public function shippingOrder(Order $order){ $order->shipping_status = 1; $order->shipping_time = now(); $order->save(); //消除大屏待取取餐号 $this->deleteBigScreenNumber($order->order_number); //如果是吹牛订单,填入大屏吹牛成功提醒 if($order->is_niu){ $this->insertBigScreenNotice($order->order_number); } return true; } /** * 取消订单 */ public function cancleOrder(Order $order){ // if($order->pay_status == 1){//已支付订单无法取消 // return false; // } $order->order_status = -1; $order->closed_time = null; PayLog::where([ 'order_sn'=>$order->order_sn, 'status'=>0 ])->update(['status'=>-1]); if($order->is_reserve){//恢复取消订单时对应库存 $serve_order_log = ReserveOrderLog::where('order_sn', $order->order_sn)->first(); $today = Carbon::parse($order->created_at)->toDateString(); $hash_key = 'reserve_goods_' . $serve_order_log->activity_day_id. '_' . $today; Redis::command('incrby', [$hash_key, $serve_order_log->goods_num]);//回滚库存 $serve_order_log->delete(); } //如果使用优惠券,恢复对应优惠券 if($order->ticket_id){ UserTicket::where('id', $order->ticket_id)->update(['ticket_status'=>0]); // foreach ($order->goods as $key =>$goods){ // $order->goods[$key]->special_value = 0; // $order->goods[$key]->ticket_id = 0; // } // $order->ticket_id = 0; // $order->pay_price = number_format((intval($order->pay_price*100) + intval($order->ticket_value*100))/100, 2); // if($order->pay_price <= 0 || env('APP_ENV', 'local') == 'local'){ // $order->pay_price = 0.01; // } // $order->ticket_value = 0; } $order->push(); return true; } /** * 填入大屏待取餐号 */ public function insertBigScreenNumber($number){ // 获取今天的日期 $date = Carbon::now()->toDateString(); // Redis 哈希表的命名,如:larabbs_last_actived_at_2017-10-21 $hash = $this->number_hash_prefix . $date; Redis::lpush($hash, $number); } /** * 取出大屏待取餐号 */ public function deleteBigScreenNumber($number){ // lrem $date = Carbon::now()->toDateString(); // Redis 哈希表的命名,如:larabbs_last_actived_at_2017-10-21 $hash = $this->number_hash_prefix . $date; Redis::lrem($hash, 0, $number); } public function insertBigScreenNotice($number){ // 获取今天的日期 $date = Carbon::now()->toDateString(); // Redis 哈希表的命名,如:larabbs_last_actived_at_2017-10-21 $hash = $this->notice_hash_prefix . $date; $notice = "恭喜{$number}号吹牛成功!"; Redis::lpush($hash, $notice); } }