order-food-admin/app/Services/OrderService.php

658 lines
25 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Services;
use DB;
use Carbon\Carbon;
use App\Models\{User,Good,GoodsAttr,Order,OrderGoods,PayLog,ActivityOption,PointsLog,ActivityDayGood,ActivityDayNum,ReserveOrderLog,UserTicket};
use App\Traits\{WechatPay, JiaBoPrint};
use App\Jobs\{TimingCancelOrderJob,PrintOrderJob};
use App\Services\GoodsService;
use Illuminate\Support\Facades\Redis;
/**
* Class OrderService
*
* @package App\Services
*/
class OrderService
{
protected $status;
protected $message;
protected $number_hash_prefix = "milk_tea_order_number_at_";
protected $notice_hash_prefix = "milk_tea_number_notice_at_";
private function toRes(){
return [
'status'=>$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);
}
}