[Amazon] SPAPI订单下载流程, 使用RabbitMQ

66 阅读2分钟
  • 1.在店铺管理中, 点击下载 image.png

  • 2.编写Store控制器, 用于下载订单, 并将下载结果传输到MQ中

<?php

namespace app\admin\controller;

use app\admin\model\Order;
use app\admin\model\OrderItems;
use app\common\controller\Backend;
use app\admin\model\Store as StoreModel;
use SellingPartnerApi\Api\CatalogItemsV0Api;
use SellingPartnerApi\Configuration;
use SellingPartnerApi\Endpoint;
use SellingPartnerApi\Api\SellersV1Api as SellersApi;
use SellingPartnerApi\Api\OrdersV0Api;
use SellingPartnerApi\Api\TokensV20210301Api;
use SellingPartnerApi\Model\TokensV20210301\CreateRestrictedDataTokenRequest;
use app\common\service\RabbitMQService;

// 店铺管理
class Store extends Backend
{
    protected $model = null;
    protected $preExcludeFields = ['id', 'create_time', 'update_time'];
    protected $quickSearchField = ['id', 'title'];
    protected $limit = 10;

    public function initialize()
    {
        parent::initialize();
        $this->model = new \app\admin\model\Store;
    }

    /**
     * 下载订单
     */
    public function getOrder()
    {
        $store_id = request()->get('store_id');
        $storeModel = new StoreModel();
        $store = $storeModel->find($store_id)->toArray();
        if ($store['type'] != 2 && $store['type'] != 3) {
            dd("非亚马逊账号");
        }
        
        $conf = [
            'access_key'    => $store['spapi_amazon_access_key'], 
            'access_secret' => $store['spapi_amazon_access_secret'],
            'role_arn'      => $store['spapi_amazon_role_arn'], 
            'refresh_token' => $store['spapi_amazon_refresh_token'],
            'client_id'     => $store['spapi_amazon_client_id'], 
            'client_secret' => $store['spapi_amazon_client_secret'],
            'endpoint'      => $store['spapi_amazon_endpoint'],
            'marketplaceId' => $store['api_marketplaceid'],
        ];

        switch ($conf['endpoint']) {
            case 'NA':
                $endpoint = Endpoint::NA;
                break;
            case 'EU':
                $endpoint = Endpoint::EU;
                break;
            case 'FE':
                $endpoint = Endpoint::FE;
                break;
        }

        $connect = new Configuration([
            "lwaClientId" => $conf['client_id'],
            "lwaClientSecret" => $conf['client_secret'],
            "lwaRefreshToken" => $conf['refresh_token'],
            "awsAccessKeyId" => $conf['access_key'],
            "awsSecretAccessKey" => $conf['access_secret'],
            "endpoint" => $endpoint,
            "roleArn" => $conf['role_arn'],
        ]);
        
        if ($store['last_time']) {
            $stime = $store['last_time'];
        } else {
            $stime = date('Y-m-d H:i:s', strtotime("-15 days"));
        }

        $this->downloadOrder($connect, $store,  $stime);
        dd("下载完成");
    }


    // 下载订单, 并放到队列中
    public function downloadOrder($connect, $store, $stime)
    {
        $apiInstance = new OrdersV0Api($connect);
        // $apiInstance = new \SellingPartnerApi\Api\OrdersApi($connect);

        $format = "Y-m-d\TH:i:s.\\0\\0\\0\\Z";
        $created_after = gmdate($format, strtotime($stime));
        $data_elements = null;
        if ($store['type'] == 3) {
            if ($store['is_pii'])  $data_elements = ['buyerInfo', 'shippingAddress'];
            $fulfillment_channels = 'MFN';
        } else {
            $fulfillment_channels = 'AFN';
        }

        $result = $apiInstance->getOrders([$store['api_marketplaceid']], $created_after, null, null, null, null, $fulfillment_channels, null, null, null, $this->limit, null, null, null, null, null, null, $data_elements);
        $result = $result['payload']['orders'];

        $list = [];
        $code = uniqid();
        $time = date("Y-m-d H:i:s");
        foreach ($result as $key=>$item) {

            $orderData = $this->objectToArray($item); // 订单对象转数组
            $orderData = $this->formatOrder(end($orderData)); // 格式化订单数组

            $orderItem = $apiInstance->getOrderItems($item['amazon_order_id']);
            $orderItem = $this->objectToArray($orderItem['payload']['order_items']);

            foreach ($orderItem as $vo) {
                $orderData['order_items'] = $this->formatOrder(end($vo));
            }

            // 将其他信息保存到数组中
            $orderData['info'] = [
                'store_id'  => $store['id'],
                'store_name'  => $store['title'],
                'code'      => $code,
                'type'      => $fulfillment_channels,
                'download_time'  => $time
            ];
            
            // $list[] = $orderData; 
            // 直接推送到队列中, 不查询数据库
            $rabbitMQService = new RabbitMQService();
            $rabbitMQService->sendMessage(json_encode($orderData), 'order');
        }
    }


    /**
     * 特殊的对象转数组方法
     */
    function objectToArray($obj) {
        $obj = (array)$obj;
        $result = [];
        foreach ($obj as $key => $value) {
            if (gettype($value) == 'object' || gettype($value) == 'array') {
                $result[$key] = $this->objectToArray($value); // 递归调用
            } else {
                $result[$key] = $value;
            }
        }
        return $result;
    }

   // 格式化订单结果(包括订单和订单商品)
    function formatOrder($items, $test = 0) {
        $list = [];
        foreach ($items as $key=>$item) {
            if ($test && preg_match('/container/', $key)) {
                return $item;
            }
            if (is_array($item)) {
                $list[$key] = $this->formatOrder($item, 1);
            } else {
                if (preg_match('/container/', $key)) {
                    $list[$key] = end($item);
                } else {
                    $list[$key] = $item;
                }
            }
        }
        return $list;
    }
    
    // 将null改为空白
    public function formatNull($array, $replacement = '')
    {
        foreach ($array as &$value) {
            if (is_array($value)) {
                $value = $this->formatNull($value, $replacement);
            } elseif ($value === null) {
                $value = $replacement;
            }
        }
        return $array;
    }

    // 检查插入的数据中, 是否有数组, 如果有, 设为空白
    public function checkItems($data)
    {
        foreach ($data as &$item) {
            if (is_array($item)) {
                $item = '';
            }
        }
        return $data;
    }
}
  • 3.在MQ消费者中读取队列, 进行订单入库
<?php
declare (strict_types = 1);
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use app\common\service\RabbitMQService;
use app\admin\model\Order;
use app\admin\model\OrderItems;
use app\admin\model\Store;

class GetMqOrder extends Command
{
    
    private $command = 'get_mq_order';

    public $queueOrder = 'order';
    public $queueOrderFail = 'order_fail';
    
        
    protected function configure()
    {
        $this->setName($this->command)->setDescription('从MQ中获取订单');
    }

    protected function execute(Input $input, Output $output)
    {
        $output->writeln('开始获取订单');
        $rabbitMQService = new RabbitMQService();
        $callback = function ($msg) {

            $this->log("您有新的订单");
            $result = json_decode($msg->getBody(), true);

            if (is_array($result)) {
                $res = $this->saveOrder($result);
                if ($res !== true) {
                    $this->resend($result);    
                }
            } else {
                $this->resend($result);    
            }
        };
        $rabbitMQService->consumeQueue($callback, $this->queueOrder);
    }

    /**
     * 保存订单
     */
    protected function saveOrder($order)
    {
        // 保存到数据库中
        $orderModel = new Order();
        $itemModel = new OrderItems();
        $storeModel = new Store();

        $isExists = $orderModel->where('amazon_order_id', $order['amazon_order_id'])->count();
        if ($isExists) {
            $this->log("订单已存在: {$order['amazon_order_id']}");
            return true;
        }
        $time = date("Y-m-d H:i:s");

        // 创建要插入的数据数组
        $insertData = [];
        $insertData['store_id'] = $order['info']['store_id'];
        $insertData['store_name'] = $order['info']['store_name'];
        $insertData['code'] = $order['info']['code'];
        $insertData['type'] = $order['info']['type'];
        $insertData['download_time'] = $order['info']['download_time'];

        $insertData['amazon_order_id'] = $order['amazon_order_id'];
        $insertData['seller_order_id'] = $order['seller_order_id'];
        $insertData['purchase_date'] = $order['purchase_date'];
        $insertData['last_update_date'] = $order['last_update_date'];
        $insertData['order_status'] = $order['order_status'];
        $insertData['fulfillment_channel'] = $order['fulfillment_channel'];
        $insertData['sales_channel'] = $order['sales_channel'];
        $insertData['order_channel'] = $order['order_channel'];
        $insertData['ship_service_level'] = $order['ship_service_level'];
        $insertData['order_total_currency_code'] = $order['order_total']['currency_code'];
        $insertData['order_total_amount'] = $order['order_total']['amount'];
        $insertData['number_of_items_shipped'] = $order['number_of_items_shipped'];
        $insertData['number_of_items_unshipped'] = $order['number_of_items_unshipped'];
        $insertData['payment_execution_detail'] = $order['payment_execution_detail'];
        $insertData['payment_method'] = $order['payment_method'];
        $insertData['payment_method_details'] = implode(', ', $order['payment_method_details']);
        $insertData['marketplace_id'] = $order['marketplace_id'];
        $insertData['shipment_service_level_category'] = $order['shipment_service_level_category'];
        $insertData['easy_ship_shipment_status'] = $order['easy_ship_shipment_status'];
        $insertData['cba_displayable_shipping_label'] = $order['cba_displayable_shipping_label'];
        $insertData['order_type'] = $order['order_type'];
        $insertData['earliest_ship_date'] = $order['earliest_ship_date'];
        $insertData['latest_ship_date'] = $order['latest_ship_date'];
        $insertData['earliest_delivery_date'] = $order['earliest_delivery_date'];
        $insertData['latest_delivery_date'] = $order['latest_delivery_date'];
        $insertData['is_business_order'] = $order['is_business_order'];
        $insertData['is_prime'] = $order['is_prime'];
        $insertData['is_premium_order'] = $order['is_premium_order'];
        $insertData['is_global_express_enabled'] = $order['is_global_express_enabled'];
        $insertData['replaced_order_id'] = $order['replaced_order_id'];
        $insertData['is_replacement_order'] = $order['is_replacement_order'];
        $insertData['promise_response_due_date'] = $order['promise_response_due_date'];
        $insertData['is_estimated_ship_date_set'] = $order['is_estimated_ship_date_set'];
        $insertData['is_sold_by_ab'] = $order['is_sold_by_ab'];
        $insertData['is_iba'] = $order['is_iba'];
        $insertData['default_ship_from_location_address'] = $order['default_ship_from_location_address'];
        $insertData['buyer_invoice_preference'] = $order['buyer_invoice_preference'];
        $insertData['buyer_tax_information'] = $order['buyer_tax_information'];
        $insertData['fulfillment_instruction'] = $order['fulfillment_instruction'];
        $insertData['is_ispu'] = $order['is_ispu'];
        $insertData['is_access_point_order'] = $order['is_access_point_order'];
        $insertData['marketplace_tax_info'] = $order['marketplace_tax_info'];
        $insertData['seller_display_name'] = $order['seller_display_name'];
        $insertData['shipping_address_name'] = $order['shipping_address']['name'];
        $insertData['shipping_address_line1'] = $order['shipping_address']['address_line1'];
        $insertData['shipping_address_line2'] = $order['shipping_address']['address_line2'];
        $insertData['shipping_address_line3'] = $order['shipping_address']['address_line3'];
        $insertData['shipping_address_city'] = $order['shipping_address']['city'];
        $insertData['shipping_address_county'] = $order['shipping_address']['county'];
        $insertData['shipping_address_district'] = $order['shipping_address']['district'];
        $insertData['shipping_address_state_or_region'] = $order['shipping_address']['state_or_region'];
        $insertData['shipping_address_municipality'] = $order['shipping_address']['municipality'];
        $insertData['shipping_address_postal_code'] = $order['shipping_address']['postal_code'];
        $insertData['shipping_address_country_code'] = $order['shipping_address']['country_code'];
        $insertData['shipping_address_phone'] = $order['shipping_address']['phone'];
        $insertData['shipping_address_type'] = $order['shipping_address']['address_type'];
        $insertData['buyer_email'] = $order['buyer_info']['buyer_email'];
        $insertData['buyer_name'] = $order['buyer_info']['buyer_name'];
        $insertData['buyer_county'] = $order['buyer_info']['buyer_county'];
        $insertData['buyer_tax_info'] = $order['buyer_info']['buyer_tax_info'];
        $insertData['purchase_order_number'] = $order['buyer_info']['purchase_order_number'];
        $insertData['automated_shipping_settings'] = $order['automated_shipping_settings'];
        $insertData['has_regulated_items'] = $order['has_regulated_items'];
        $insertData['electronic_invoice_status'] = $order['electronic_invoice_status'];
        $insertData['item_approval_types'] = $order['item_approval_types'];
        $insertData['item_approval_status'] = $order['item_approval_status'];

        $orderId = $orderModel->create($insertData);

        // 插入订单商品
        if ($orderId->id) {

            $this->log("订单入库成功:" .$order['amazon_order_id']);
            
            $items = $order['order_items'];
            $items = $this->formatNull($items);

            $data = [];
            $data['order_id'] = $orderId->id;
            $data['order_sn'] = $order['amazon_order_id']; // 来自订单
            $data['asin'] = $items['asin'];
            $data['seller_sku'] = $items['seller_sku'];
            $data['order_item_id'] = $items['order_item_id'];
            $data['title'] = $items['title'];
            $data['quantity_ordered'] = $items['quantity_ordered'];
            $data['quantity_shipped'] = $items['quantity_shipped'];
            $data['product_info_number_of_items'] = $items['product_info']['number_of_items'];
            $data['points_granted'] = $items['points_granted'];
            $data['item_price_currency_code'] = $items['item_price']['currency_code'];
            $data['item_price_amount'] = $items['item_price']['amount'];

            $data['shipping_price_currency_code'] = $data['shipping_price_amount'] = '';
            if ($items['shipping_price']) {
                $data['shipping_price_currency_code'] = $items['shipping_price']['currency_code'];
                $data['shipping_price_amount'] = $items['shipping_price']['amount'];
            } 

            $data['item_tax_currency_code'] = $items['item_tax']['currency_code'];
            $data['item_tax_amount'] = $items['item_tax']['amount'];

            $data['shipping_tax_currency_code'] = $data['shipping_tax_amount'] = '';
            if ($items['shipping_tax']) {
                $data['shipping_tax_currency_code'] = $items['shipping_tax']['currency_code'];
                $data['shipping_tax_amount'] = $items['shipping_tax']['amount'];
            } 

            $data['shipping_discount_currency_code'] = $data['shipping_discount_amount'] = '';
            if ($items['shipping_discount']) {
                $data['shipping_discount_currency_code'] = $items['shipping_discount']['currency_code'];
                $data['shipping_discount_amount'] = $items['shipping_discount']['amount'];
            }

            $data['shipping_discount_tax_currency_code'] = $data['shipping_discount_tax_amount'] = '';
            if ($items['shipping_discount_tax']) {
                $data['shipping_discount_tax_currency_code'] = $items['shipping_discount_tax']['currency_code'];
                $data['shipping_discount_tax_amount'] = $items['shipping_discount_tax']['amount'];
            }

            $data['promotion_discount_currency_code'] = $items['promotion_discount']['currency_code'];
            $data['promotion_discount_amount'] = $items['promotion_discount']['amount'];
            $data['promotion_discount_tax_currency_code'] = $items['promotion_discount_tax']['currency_code'];
            $data['promotion_discount_tax_amount'] = $items['promotion_discount_tax']['amount'];

            $data['promotion_ids'] = '';
            if ($data['promotion_ids']) {
                $data['promotion_ids'] = implode(',', $items['promotion_ids']);
            }

            $data['cod_fee'] = $items['cod_fee'];
            $data['cod_fee_discount'] = $items['cod_fee_discount'];
            $data['is_gift'] = $items['is_gift'];
            $data['condition_note'] = $items['condition_note'];
            $data['condition_id'] = $items['condition_id'];
            $data['condition_subtype_id'] = $items['condition_subtype_id'];
            $data['scheduled_delivery_start_date'] = $items['scheduled_delivery_start_date'];
            $data['scheduled_delivery_end_date'] = $items['scheduled_delivery_end_date'];
            $data['price_designation'] = $items['price_designation'];
            $data['tax_collection'] = $items['tax_collection'];
            $data['serial_number_required'] = $items['serial_number_required'];
            $data['is_transparency'] = $items['is_transparency'];
            $data['ioss_number'] = $items['ioss_number'];
            $data['store_chain_store_id'] = $items['store_chain_store_id'];
            $data['deemed_reseller_category'] = $items['deemed_reseller_category'];
            $data['buyer_info_buyer_customized_info'] = $items['buyer_info']['buyer_customized_info'];
            $data['buyer_info_gift_wrap_price'] = $items['buyer_info']['gift_wrap_price'];
            $data['buyer_info_gift_wrap_tax'] = $items['buyer_info']['gift_wrap_tax'];
            $data['buyer_info_gift_message_text'] = $items['buyer_info']['gift_message_text'];
            $data['buyer_info_gift_wrap_level'] = $items['buyer_info']['gift_wrap_level'];
            $data['buyer_requested_cancel'] = $items['buyer_requested_cancel'];
            $data['item_approval_context'] = $items['item_approval_context'];
            $data['serial_numbers'] = $items['serial_numbers'];
            $data['create_time'] = $time;
            $data = $this->checkItems($data);
            $itemId = $itemModel->create($data);

            // 更新店铺的最后下载时间
            $store = $storeModel::find($order['info']['store_id']);
            $lastTime = date("Y-m-d H:i:s", strtotime($order['purchase_date'])); // 注意这里要转换时间格式
            if ($lastTime > $store->last_time) {
                $store->last_time = $lastTime; // 最后下载时间是采购时间
                $store->save();
            }
        }
    }
    
    /**
     * 处理失败的数据, 放回到失败队列中
     */
    protected function resend($result)
    {
        $rabbitMQService = new RabbitMQService();
        $rabbitMQService->sendMessage($result, $this->queueOrderFail);
    }

    // 将null改为空白
    public function formatNull($array, $replacement = '')
    {
        foreach ($array as &$value) {
            if (is_array($value)) {
                $value = $this->formatNull($value, $replacement);
            } elseif ($value === null) {
                $value = $replacement;
            }
        }
        return $array;
    }

    // 检查插入的数据中, 是否有数组, 如果有, 设为空白
    public function checkItems($data)
    {
        foreach ($data as &$item) {
            if (is_array($item)) {
                $item = '';
            }
        }
        return $data;
    }

    public function log($content)
    {
        echo "[" . date("Y-m-d H:i:s") . "] ". $content . "\n";
    }

}