laravel+redis 附近的車輛 + 釋出訂單 + 司機搶單

會尿尿的大鯊魚發表於2022-07-14

路由

Route::get('post-moment-import', [\App\Http\Controllers\IndexController::class, 'postMomentImport']);
Route::get('post-moment', [\App\Http\Controllers\IndexController::class, 'postMoment']);
Route::get('edit-moment/{moment}', [\App\Http\Controllers\IndexController::class, 'editMoment']);
Route::get('get-moments', [\App\Http\Controllers\IndexController::class, 'getMoments']);
// 釋出搶單
Route::get('issue-order', [\App\Http\Controllers\IndexController::class, 'IssueOrder']);
// 搶單
Route::get('grab-single', [\App\Http\Controllers\IndexController::class, 'GrabSingle']);

Collection

<?php

namespace App\Http\Controllers;

use App\Helps\api\redisLbs;
use App\Models\Moment;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;

class IndexController extends Controller
{
    public function postMoment (Request $request, Moment $moment)
    {
        if (!$request->longitude){
            return response()->json(['status' => 0, 'msg' => '缺少經度']);
        }
        if (!$request->latitude){
            return response()->json(['status' => 0, 'msg' => '缺少緯度']);
        }

        //依賴注入,隨時用
        $moment->name = $request->name;
        $moment->longitude = $request->input('longitude');
        $moment->latitude = $request->input('latitude');

        if ($moment->save()){
            //連線redis加點
            $lbs = new redisLbs();
            $result = $lbs->geoAdd($moment->id, $request->input('longitude'), $request->input('latitude'));
            if ($result){
                return response()->json(['status' => 1, 'msg' => '新增動態成功']);
            }
            return response()->json(['status' => 0, 'msg' => '動態新增成功,redis同步失敗']);
        }
        return response()->json(['status' => 0, 'msg' => '動態新增失敗']);
    }

    public function postMomentImport ()
    {
        $json = '[{"name":"xxx","poi":"118.96,42.2737"}]';
        $json_arr = json_decode($json, JSON_UNESCAPED_UNICODE);

        foreach (collect($json_arr)->chunk(300) ?? [] as $chunk)
        {
            foreach ($chunk ?? [] as $arr)
            {
                list($longitude, $latitude) = explode(',', $arr['poi']);
                $moment = Moment::create([
                    'name' => $arr['name'],
                    'longitude' => $longitude,
                    'latitude' => $latitude,
                ]);

//            $lbs = new redisLbs();
//            $result = $lbs->geoAdd($moment->id, $longitude, $latitude);
            }
        }

        return response()->json(['status' => 1, 'msg' => '更新動態成功']);
    }

    public function editMoment (Request $request, Moment $moment)
    {
        if (!$request->longitude){
            return response()->json(['status' => 0, 'msg' => '缺少經度']);
        }
        if (!$request->latitude){
            return response()->json(['status' => 0, 'msg' => '缺少緯度']);
        }

        //依賴注入,隨時用
        $moment->name = $request->name;
        $moment->longitude = $request->input('longitude');
        $moment->latitude = $request->input('latitude');

        if ($moment->save()){
            //連線redis加點
            $lbs = new redisLbs();
            $result = $lbs->geoAdd($moment->id, $request->input('longitude'), $request->input('latitude'));
            if ($result){
                return response()->json(['status' => 1, 'msg' => '更新動態成功']);
            }
            return response()->json(['status' => 0, 'msg' => '動態更新成功,redis同步失敗']);
        }
        return response()->json(['status' => 0, 'msg' => '動態更新失敗']);
    }

    public function getMoments (Request $request)
    {
        $lbs = new redisLbs();
        $result = $lbs->geoNearFind($request->longitude,$request->latitude,1000); //獲取附近距離3千米的
        /* 返回的兩個座標點,mid為76和12,距離,還有經緯度
        [
            "76",
            "1112.2630",
            [
                "100.19999831914902",
                "39.999999910849162"
            ]
        ],
        [
            "12",
            "2213.4033",
            [
                "100.19999831914902",
                "10.100000747410967"
            ]
        ],
          */
        $mid = $dist=[];
        for($i=0;$i<count($result);$i++){
            $dist[$i]['mid'] = $mid[$i] = $result[$i][0];  //點的id,也就是moment的主鍵
            $dist[$i]['distance'] = round($result[$i][1], 2);
        }
//        $moments = Moment::where([
//            ['created_at','>',date('Y-m-d H:m:i',time()-1800)],
//        ])->whereIn('id', $mid)->orderBy('created_at', 'desc')->get()->toArray();
        $moments = Moment::whereIn('id', $mid)->orderBy('created_at', 'desc')->select('id', 'name')->get()->toArray();

        // 合併陣列
        foreach ($dist ?? [] as $item)
        {
            foreach ($moments ?? [] as $k => $moment)
            {
                if ($item['mid'] == $moment['id'])
                {
                     $moments[$k]['distance'] = $item['distance'];
                }
            }
        }

        // 根據distance進行排序
        $datas = collect($moments)->sortBy('distance')->values()->all();

        return response()->json(['status' => 1, 'msg' => '成功獲取附近動態', 'data' => $datas]);
    }

    public function IssueOrder ()
    {
        $orderNum = 2000; // 釋出了20個簽單訂單
        for ($i=1; $i <= $orderNum; $i++) {
            $grab_orders = "grab_orders".$i;
            Redis::lpush($grab_orders, 1);
        }
        return response()->json(['status' => 200, 'msg' => '釋出成功!']);
    }

    public function GrabSingle (Request $request)
    {
        // 使用者id
        $userId = rand(1, 9999);
        // 搶單訂單的id
        $orderId = rand(1, 2000);
        //對應商品庫存佇列
        $goods = "grab_orders".$orderId;
        //對應商品搶購成功使用者集合 {1,3,4}
        $robSuccessUser = "success_user".$orderId;
        //進行判斷當前使用者是否在搶成功的佇列裡面
        $result = Redis::sismember($robSuccessUser,$userId);

        //如果你在這裡面,就搶完了
        if ($result) {
            //如果搶購成功 返回狀態碼,進行下單
            Log::info("使用者:{$userId},已經搶過了,訂單:{$goods}");
            return response()->json(['status' => 20000, 'data' => '', 'msg' => '您已經搶購過了']);
        }
        //減庫存,把佇列裡面的資料從左邊 頭
        $count = Redis::lpop($goods);

        if (!$count) {
            //如果搶購成功 返回狀態碼,進行下單
            Log::info("使用者:{$userId},已經搶光了哦,訂單:{$goods},手慢了");
            return response()->json(['status' => 20001, 'data' => '', 'msg' => '已經搶光了哦']);
        }
        //把當前這個秒殺的uid儲存到中獎的佇列裡set
        $success = Redis::sadd($robSuccessUser, $userId);
        if(!$success){
            //已經在成功佇列裡了,加回庫存,防止的是同個使用者併發請求
            Redis::lpush($goods, 1);
            //如果搶購成功 返回狀態碼,進行下單
            Log::info("使用者:{$userId},已經搶購過了,訂單:{$goods},無需在槍");
            return response()->json(['status' => 20002, 'data' => '', 'msg' => '已經搶購過了']);
        }

        //如果搶購成功 返回狀態碼,進行下單
        Log::info("使用者:{$userId},搶單成功,訂單:{$goods}");
        return response()->json(['status' => 200, 'data' => '', 'msg' => '搶單成功']);
    }
}

RedisLbs

<?php


namespace App\Helps\api;


class redisLbs
{
    /** @var Redis */
    private $redis;
    public function __construct($config = array())
    {
        $host = isset($config['host']) ? $config['host'] : '127.0.0.1';
        $port = isset($config['port']) ? $config['port'] : '6379';
        $redis = new \Redis();
        $redis->connect($host, $port);
        if (env('APP_ENV') != 'local'){
            $redis->auth('myRedis');
            $redis ->set( "root" , "myRedis");
        }
        $this->setRedis($redis);
    }
    public function getRedis()
    {
        return $this->redis;
    }
    public function setRedis($redis)
    {
        $this->redis = $redis;
    }

    //新增點
    public function geoAdd($uin, $lon, $lat)
    {
        $redis = $this->getRedis();
        $redis->geoAdd('moments', $lon, $lat, $uin);
        return true;
    }

    //獲取點
    public function geoNearFind($longitude , $latitude , $maxDistance = 0, $unit = 'm')
    {
        $redis = $this->getRedis();
        $options = ['WITHDIST','ASC','WITHCOORD']; //顯示距離
        $list = $redis->geoRadius('moments', $longitude, $latitude , $maxDistance, $unit, $options);
        return $list;
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章