微信小程序電商實戰(六)

微信支付

Scope權限作用域的應用

1模擬枚舉
在這裏插入圖片描述
重構service/UserToken下的prepareCachedValue方法

 //準備緩存中value的數據
    private function prepareCachedValue($wxResult,$uid){
        $cachedValue = $wxResult;
        $cachedValue['uid'] = $uid;
        //scope=16代表App用戶的權限數值
        $cachedValue['scope']=ScopeEnum::User;//作用域 數字越大,權限越大
        //scope = 32代表CMS用戶(管理員)的權限數值
        // $cachedValue['scope']=32;
        return $cachedValue;
    }

前置方法

在這裏插入圖片描述
在這裏插入圖片描述

對Address接口做權限控制

controller/Address:

protected $beforeActionList = [
        'checkPrimaryScope' => ['only' => 'createOrUpdateAddress']//關聯數組
    ];

    protected function checkPrimaryScope(){
        $scope =TokenService::getCurrentTokenVar('scope');//從緩存中獲取scope
        if($scope){//判空
            if($scope>=ScopeEnum::User){
                return true;
            }
            else{
                throw new ForbiddenException();//拋出異常就終止 不會再執行createOrUpdateAddress()
            }
        }
        else{
            throw new TokenException();
        }
    }

下單與支付的業務流程

微信支付

class Order
{
    //用戶在選擇商品後,向API提供包含他所選擇商品的相關信息
    //API在接收到信息後,需要檢測訂單商品的庫存量(檢測的原因主要是客戶端和服務端信息不是實時同步)
    //服務器如果檢測到有庫存,把訂單數據存入數據庫中=下單成功了,再返回客戶端消息,告訴客戶端可以支付了
    //客戶端調用支付接口,進行支付
    //還需要再次檢測商品的庫存量
    //服務器就可以調用微信的支付接口進行支付
    //微信會返回給我們一個支付的結果(異步調用)
    //即使成功,也需要再進行一次庫存量檢測
    //根據結果 成功。進行庫存量扣除。
    //返回支付是否成功的結果(不是服務器返回,因爲是異步,無法實時同步,所以是微信會返回客戶端一個結果)
}

在這裏插入圖片描述

重構權限控制前置方法

面向對象編程:
將業務邏封裝到service/Token中

//需要用戶和CMS管理員都可以訪問的權限
    public static function needPrimaryScope(){
        $scope =self::getCurrentTokenVar('scope');//從緩存中獲取scope
        if($scope){//判空
            if($scope>=ScopeEnum::User){
                return true;
            }
            else{
                throw new ForbiddenException();//拋出異常就終止 不會再執行createOrUpdateAddress()
            }
        }
        else{
            throw new TokenException();
        }
    }

    //只有用戶纔可以訪問的權限
    public static function needExclusiveScope(){
        $scope =self::getCurrentTokenVar('scope');//從緩存中獲取scope
        if($scope){//判空
            if($scope==ScopeEnum::User){
                return true;
            }
            else{
                throw new ForbiddenException();//拋出異常就終止 不會再執行createOrUpdateAddress()
            }
        }
        else{
            throw new TokenException();
        }
    }

創建控制器的基類BaseController:

class BaseController extends Controller
{
    protected function checkPrimaryScope(){
       TokenService::needPrimaryScope();
    }

    protected function checkExclusiveScope(){
        TokenService::needExclusiveScope();
    }
}

控制器Address和Order分別使用前置方法:
Address:

    protected $beforeActionList = [//檢查權限 用戶以上級別都可以管理地址信息
        'checkPrimaryScope' => ['only' => 'createOrUpdateAddress']//關聯數組
    ];

Order:

    protected $beforeActionList = [//檢查權限 只有用戶纔可以下訂單
        'checkExclusiveScope' => ['only' => 'placeOrder']
    ];

OrderPlace驗證器

客戶端傳入的下單商品數據結構如下:
在這裏插入圖片描述
OrderPlace(驗證二維數組的方法):

class OrderPlace extends BaseValidate
{
    protected $rule = [//整個數組驗證規則
        'products' => 'checkProducts'
    ];

    protected $singleRule = [//每個子項的驗證規則(裏層數組)
        'product_id' => 'require|isPositiveInteger',//下單商品id
        'count' => 'require|isPositiveInteger',//下單商品數量
    ];

    protected function checkProducts($values)
    {
        if(!is_array($values)){//參數需要是一個數組(外層數組)
            throw new ParameterException([
                'msg' => '商品參數不正確'
            ]);
        }
        if(empty($values)){
            throw new ParameterException([
                'msg' => '商品列表不能爲空'
            ]);
        }

        foreach ($values as $value)//循環子元素(裏層數組)
        {
            $this->checkProduct($value);
        }
        return true;
    }

    private function checkProduct($value)
    {
        $validate = new BaseValidate($this->singleRule);//手動傳入自己定義的驗證規則
        $result = $validate->check($value);
        if(!$result){
            throw new ParameterException([
                'msg' => '商品列表參數錯誤',
            ]);
        }
    }
}

最後在controller/Order中調用

下單接口業務模型

service/Order:

<?php


namespace app\api\service;


use app\api\model\Product;
use app\lib\exception\OrderException;

class Order
{
    //訂單的商品列表,也就是客戶端傳入的products參數
    protected $oProducts;
    //數據表中真實的商品信息(包括庫存量)  用前兩個變量做對比來檢測庫存量是否足夠
    protected $products;
    protected $uid;

    public function place($uid, $oProducts)
    {
        //oProducts和products做對比
        //products從數據庫查出來
        $this->oProducts = $oProducts;
        $this->products = $this->getProductsByOrder($oProducts);
        $this->uid = $uid;
    }

    //根據訂單信息查找真實的商品信息(product_id)
    private function getProductsByOrder($oProducts)
    {
//        foreach ($oProducts as $product){
//            //循環地查詢數據庫 很容易搞崩數據庫 所以要避免循環
//        }
        $oPIDs = [];//存儲product_id
        foreach ($oProducts as $item) {
            array_push($oPIDs, $item['product_id']);
        }
        // 爲了避免循環查詢數據庫
        $products = Product::all($oPIDs)
            ->visible(['id', 'price', 'stock', 'name', 'main_img_url'])
            ->toArray();//將默認的數據集轉爲數組 因爲$oProducts是數組 將$products轉爲數組可以更好地對比
        return $products;
    }

    //做對比  數組對比  判斷Order裏所有Product狀態
    private function getOrderStatus()//返回訂單的最終狀態
    {
        $status = [
            'pass' => true,//庫存是否足夠 足夠則通過 某一個商品庫存不足就不通過
            'orderPrice' => 0,//所有商品價格總和
            'pStatusArray' => []//訂單詳情 查看歷史訂單用
        ];
        foreach ($this->oProducts as $oProduct) {
            $pStatus =
                $this->getProductStatus(
                    $oProduct['product_id'], $oProduct['count'], $this->products);
            if (!$pStatus['haveStock']) {
                $status['pass'] = false;
            }
            $status['orderPrice'] += $pStatus['totalPrice'];
            array_push($status['pStatusArray'], $pStatus);
        }
        return $status;
    }

    //判斷一個Product狀態
    private function getProductStatus($oPID, $oCount, $products)
    {
        $pIndex = -1;
        //保存訂單裏某一個商品的詳細信息 pStatusArray的子元素
        $pStatus = [
            'id' => null,
            'haveStock' => false,
            'count' => 0,
            'name' => '',
            //一類商品的總價:單價*數量
            'totalPrice' => 0
        ];
        //根據$oPID查詢products裏對應的序號(數組下標)
        for ($i = 0; $i < count($products); $i++) {
            if ($oPID == $products[$i]['id']) {
                $pIndex = $i;
            }
        }

        if ($pIndex == -1) {
            // 客戶端傳遞的productid有可能根本不存在
            throw new OrderException(
                [
                    'msg' => 'id爲' . $oPID . '的商品不存在,訂單創建失敗'
                ]);
        } else {
            $product = $products[$pIndex];
            $pStatus['id'] = $product['id'];
            $pStatus['name'] = $product['name'];
            $pStatus['count'] = $oCount;
            $pStatus['totalPrice'] = $product['price'] * $oCount;

            if ($product['stock'] - $oCount >= 0) {
                $pStatus['haveStock'] = true;
            }
        }
        return $pStatus;
    }
}

訂單快照

記錄用戶的下單情況,不能動態存儲,要用快照思想,訂單已經完成就不能再更改參數了。
order表和order_product表是多對多關係。
order表中存儲快照信息:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章