微信小程序电商实战(六)

微信支付

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表中存储快照信息:
在这里插入图片描述

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