讓我們來用php編寫一個搶購商品指令碼

admin⁠發表於2020-12-11

主題列表:juejin, github, smartblue, cyanosis, channing-cyan

貢獻主題:https://github.com/xitu/juejin-markdown-themes

theme: juejin
highlight: juejin

專案原始碼

== 還沒上傳呢

https://juejin.im/editor/drafts/6894061101374734343

環境

  • php7.2
  • guzzle
  • thinkphp5.1

說明

0. git clone xxxxxx
1. composer create-project topthink/think=5.1.* fxbox
2. cd fxbox
3. composer require guzzlehttp/guzzle:~6.0  安裝 guzzle 
4. composer update

如何使用

如果是github那邊下載原始碼的話,看這裡

1. cd fxbox
2. php think fcbox:run
3. ....後面這裡就可以指令碼的安裝提示進行下一步驟了

效果圖

核心程式碼


<?php

namespace app\command;

use GuzzleHttp\Cookie\CookieJar;
use think\console\Command;
use think\console\Input;
use think\console\Output;

class Fcbox extends Command
{

    private $userInfo = [];
    private $cookies = '';
    private $skus = '';
    private $address = [];

    //數量
    private $count = 0;
    // 使用者列表的下標
    private $current_address = 0;
    // sku列表的下標
    private $current_sku = 0;
    // 搶購開始時間
    private $start_time = 0;
    //生成訂單的token
    private $orderToken = 0;


    public $client = null;
    public $jar = null;
    public $output = null;

    public function __construct($name = null)
    {

        parent::__construct($name);

        $this->client = new \GuzzleHttp\Client(); //初始化客戶端
        $this->jar = new CookieJar();
        $this->output = new Output();


        //獲取使用者資訊
        $this->cookies = $this->inputCookie();

        // 訂單結束


    }

    protected function configure()
    {
        // 指令配置
        $this->setName('fcbox:run');
        // 設定引數

    }

    protected function execute(Input $input, Output $output)
    {

        $this->output->writeln("-------------正在獲取個人資訊中-------------");
        $this->userInfo = $this->getUserInfo(); // 使用者資訊
        $this->address = $this->getUserAddress(); // 使用者地址
        $this->output->writeln("----------------------------------------------------------------------");


        // 獲取產品sku資訊 價格 描述  產品名稱
        $this->skus = $this->getProduct(); // 產品資訊
        $this->output->writeln("----------------------------------------------------------------------");

        // 選擇sku資訊
        $this->selectProductSku();
        $this->output->writeln("----------------------------------------------------------------------");

        // 選擇數量
        $this->getInputCount();
        $this->output->writeln("----------------------------------------------------------------------");

        // 選擇地址
        $this->selectUserAddress();
        $this->output->writeln("----------------------------------------------------------------------");


        // 搶購開始的時間
        $this->getInputStartTime();
        $this->output->writeln("----------------------------------------------------------------------");


        /**
         *
         * 這裡需要迴圈進行下單
         *
         *
         */

        // 提交訂單

        while (true) {
            sleep(5);
        }


        //生成提交訂單的資訊  認證token
        $this->getConfirmToken();
        $this->output->writeln("----------------------------------------------------------------------");

        //提交訂單
        $this->submitOrder();
        $this->output->writeln("----------------------------------------------------------------------");


        // 指令碼執行結束
        $this->output->writeln("----------------------------------------------------------------------");
        $this->output->writeln("------------------------------指令碼執行完成------------------------------");
        $this->output->writeln("----------------------------------------------------------------------");

    }


    /**
     * 對使用者輸入的cookie進行處理
     * @return CookieJar
     */
    private function inputCookie()
    {
        try {
            fwrite(STDOUT, '請輸入你的Cookies:');
            $cookies = trim(fgets(STDIN));
            $cookies = explode(';', $cookies);
            $base_url = 'mall.fcbox.com';
            $data = [];
            foreach ($cookies as $cookie) {
                if ($cookie == '') continue;
                [$name, $value] = explode('=', $cookie);
                $data[trim($name)] = trim($value);
            }
            $resp = $this->jar->fromArray($data, $base_url);
            return $resp;

        } catch (\Exception $e) {
            $this->output->writeln('驗證使用者Cookies發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }
    }


    /**
     * 獲取產品的資訊
     * @return array
     */
    private function getProduct()
    {
        try {
            fwrite(STDOUT, '請輸入你的產品的URL路徑:');
            $url = trim(fgets(STDIN));
            $args = explode('/', $url);
            $product_id = array_pop($args);

            $sku = $this->getProductSku($product_id);
            $this->output->writeln("-------------獲取產品SKU資訊成功-------------");
            return $sku;

        } catch (\Exception $e) {
            $this->output->writeln('獲取產品資訊發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }
    }

    /**
     * 獲取產品的sku 列表
     * @param $product_id
     * @return array
     */
    private function getProductSku($product_id)
    {
        try {
            $skuInfoUrl = "https://mall.fcbox.com/item/skuInfo?itemCode={$product_id}&province=&city=&area=&latitude=&longitude=";

            $response = $this->client->request('GET', $skuInfoUrl, [
                'timeout' => 3.14,
                'verify' => false,
            ]);

            $content = $response->getBody()->getContents(); //獲取響應體,物件
            $content = json_decode($content, JSON_UNESCAPED_UNICODE);

            $skus = [];
            foreach ($content['data']['skus'] as $sku) {

                $item['skuCode'] = $sku['skuCode'];
                $item['stock'] = $sku['stock'];
                $item['salePrice'] = $sku['salePrice'];
                $item['saleSiteId'] = $sku['saleSiteId'];
                $item['saleSiteType'] = $sku['saleSiteType'];
                $item['itemCode'] = $sku['itemCode'];
                $item['description'] = $sku['saleAttributes'];
                $item['activityPrice'] = $sku['activityPrice'];

                $skus[] = $item;
            }
            return $skus;

        } catch (\Exception $e) {
            $this->output->writeln('獲取產品SKU發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }

    }


    /**
     * 獲取cookies 的賬號資訊以及個人地址
     * @param $cookies
     * @return mixed
     */
    public function getUserInfo()
    {
        try {
            // 個人中心
            $centerUrl = 'https://mall.fcbox.com/member/center?';
            $response = $this->client->request('GET', $centerUrl, [
                'timeout' => 3.14,
                'verify' => false,
                'cookies' => $this->cookies,
//            'debug' => true, //除錯
            ]);

            $content = $response->getBody()->getContents(); //獲取響應體,物件
            $content = json_decode($content, JSON_UNESCAPED_UNICODE);
            $this->output->writeln("-------獲取使用者資訊成功-------");
            return $content['data'];

        } catch (\Exception $e) {
            $this->output->writeln('獲取使用者資訊發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }

    }


    /**
     * 獲取使用者地址
     */
    private function getUserAddress()
    {
        try {

            $url = 'https://mall.fcbox.com/addressBook/list';
            $response = $this->client->request('POST', $url, [
                'timeout' => 3.14,
                'verify' => false,
                'cookies' => $this->cookies,
//            'debug' => true, //除錯
            ]);
            $content = $response->getBody()->getContents(); //獲取響應體,物件
            $centerContent = json_decode($content, JSON_UNESCAPED_UNICODE);
            $this->output->writeln("-------獲取使用者地址成功-------");

            return $centerContent['data'];
        } catch (\Exception $e) {
            $this->output->writeln('獲取使用者地址列表發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }
    }

    /**
     * 設定需要購買的數量
     * @return int
     */
    private function getInputCount()
    {
        try {
            $result = false;
            do {

                fwrite(STDOUT, '請輸入你需要購買的數量:');
                $count = trim(fgets(STDIN));
                if (is_numeric($count) && $count != 0 && $count != '') {
                    $result = true;
                    $this->count = intval($count);
                    return intval($count);
                }
                $this->output->writeln('請輸入一個正確的數字');

            } while ($result == false);

        } catch (\Exception $e) {
            $this->output->writeln('搶購數量發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }
    }


    /**
     * 選擇使用者的地址
     * @return int|string
     */
    private function selectUserAddress()
    {
        try {
            $this->output->writeln('-------------使用者地址列表-------------');

            foreach ($this->address as $key => $address) {
                $item = [];
                if ($address['isDefault'] == 1) $item[] = '《預設地址》';

                unset($address['id']);
                unset($address['latitude']);
                unset($address['longitude']);
                unset($address['source']);
                unset($address['uicUserId']);
                unset($address['is_default']);

                foreach ($address as $name => $value) {
                    $item[] = $value;
                }
                $item = implode(' ', $item);
                $this->output->writeln("地址-序號:{$key}  ->   {$item}");
            }

            // 驗證輸入的序號是否合法
            $result = false;
            do {

                fwrite(STDOUT, '請選擇使用者序號的地址:');
                $input = trim(fgets(STDIN));
                // 非法輸入
                if ($input > count($this->address) || $input < 0) continue;

                $result = true;
                $this->current_address = $input;
                return $this->current_address;

            } while ($result == false);

        } catch (\Exception $e) {
            $this->output->writeln('選擇使用者地址發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }
    }


    /**
     * 選擇產品的sku
     * @return int|string
     */
    private function selectProductSku()
    {
        try {
            $this->output->writeln('-------------SKU列表-------------');

            $lists[] = [
                '序號', 'SKU', '庫存', '價格', '描述',
            ];
            foreach ($this->skus as $key => $sku) {
                $item = [];
                $item['key'] = $key; //sku
                $item['skuCode'] = $sku['skuCode']; //sku
                $item['stock'] = $sku['stock']; // 庫存
                $item['salePrice'] = $sku['salePrice'] / 100; // 價格
                foreach ($sku['description'] as $description) {
                    $item['description'] = implode(',', [$description['key'], $description['value']]);  // sku 描述
                }
                $lists[] = $item;
            }

            foreach ($lists as $rows) {
                foreach ($rows as $row) {
                    $this->output->write($row . "\t\t");
                }
                $this->output->write("\n");
            }

            // 驗證輸入的序號是否合法
            $result = false;
            do {

                fwrite(STDOUT, '請選擇產品SKU序號的序號:');
                $input = trim(fgets(STDIN));
                // 非法輸入
                if ($input > count($this->address) || $input < 0) continue;

                $result = true;
                $this->current_sku = $input;
                return $this->current_sku;

            } while ($result == false);

        } catch (\Exception $e) {
            $this->output->writeln('發生錯誤,程式即將退出,錯誤原因:' . $e->getMessage());
            exit;
        }

    }


    /**
     * 設定開始搶購的時間
     * @return false|int
     */
    private function getInputStartTime()
    {
        try {

            $result = false;
            do {
                fwrite(STDOUT, '請輸入開始搶購的時間:');
                $time = trim(fgets(STDIN));
                $time = strtotime($time);
                if (time() < $time) {
                    $result = true;
                    $this->start_time = $time;
                    return $time;
                }
                $this->output->writeln('請輸入一個正確的時間範圍');

            } while ($result == false);

        } catch (\Exception $e) {
            $this->output->writeln('搶購開始時間發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }

    }


    /**
     * 生成提交訂單的order
     * @return mixed
     */
    private function getConfirmToken()
    {
//        addressId: 2891060
//itemReqStr: [{"count":1,"saleSiteId":403,"skuCode":"127025488939648"}]
        try {
            $this->output->writeln('-------------正在生成訂單Token中-------------');

            $url = 'https://mall.fcbox.com/order/confirm';
            $response = $this->client->request('POST', $url, [
                'timeout' => 3.14,
                'verify' => false,
                'cookies' => $this->cookies,
                'form_params' => [
                    'addressId' => $this->address[$this->current_address]['id'],
                    'itemReqStr' => json_encode([[
                        'count' => $this->count,
                        'saleSiteId' => $this->skus[$this->current_sku]['saleSiteId'],
                        'skuCode' => $this->skus[$this->current_sku]['skuCode'],
                    ]]),
                ]

            ]);

            $content = $response->getBody()->getContents();
            $content = json_decode($content, JSON_UNESCAPED_UNICODE);
            $token = $content['data']['orderToken'];

            $this->orderToken = $token;
            $this->output->writeln("-------------訂單Token生成成功,Token值為:{$token}-------------");

            return $token;

        } catch (\Exception $e) {
            $this->output->writeln('生成訂單Token發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }

    }

    /**
     * 提交訂單資訊
     */
    private function submitOrder()
    {
        try {

            $this->output->writeln('-------------提交訂單中-------------');

            $url = 'https://mall.fcbox.com/order/submit';

            $data['token'] = $this->orderToken;
            $data['addressId'] = $this->address[$this->current_address]['id'];
            $data['deliveryRemark'] = $this->orderToken;
            $data['coupons'] = [];
            $data['orderFee'] = $this->skus[$this->current_sku]['activityPrice']; //價格
            $data['orderFrom'] = 1;
            $data['itemList'] = [
                [
                    'skuCode' => $this->skus[$this->current_sku]['skuCode'],
                    'siteId' => $this->skus[$this->current_sku]['saleSiteId'],
                    'count' => $this->count,
                    'remark' => '[]',
                ]
            ];
            $data['siteRemarks'] = [
                [
                    'siteId' => 13,
                    'remark' => '',
                ]
            ];

            $response = $this->client->request('POST', $url, [
                'timeout' => 3.14,
                'verify' => false,
                'cookies' => $this->cookies,
                'json' => $data,
            ]);

            $content = $response->getBody()->getContents();
            $content = json_decode($content, JSON_UNESCAPED_UNICODE);
            $this->output->writeln("-------------提交訂單完成,當前訂單狀態:{$content['msg']}-------------");

        } catch (\Exception $e) {
            $this->output->writeln('提交訂單發生錯誤,錯誤原因:' . $e->getMessage());
            exit;
        }
    }
}


相關文章