最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 使用策略模式和简单工厂模式重写支付模块

    正文概述 转载于:掘金(zxr615)   2021-03-19   642

    问题所在

    • 全部支付走统一的二维码生成接口,导致需要通过 type 区分接收不同的字段,随着支付方式越来越多,参数判断越来越多,难以维护
    • 代码解构混乱,一个 $data 变量贯通整个方法,导致最后不知道 $data 变量里面什么数据,开发、排错越来越复杂
    • 异常处理,业务代码处处抛出 \Exception 和捕获 \Exception ,导致如果程序遇到了系统异常也不能及时的通知错误

    改造前的一段伪代码

    1. 所有业务逻辑错误也抛出 \Exception 异常,捕获 \Exception 后返回 下单失败 导致如果程序遇到真正错误时,无法及时排查错误
    2. 单看 checkVerifyType() 方法名会认为只是检查支付 type 是否正确, 但却不是,这个方法把所有该干不该干的事都干完了
    3. 传参用 01 也不能明确知道是代表什么东西
    4. qrcode 接口参数也很复杂,例:type = 1时,必须要 code 参数; type = 2 时,必须要 price 参数;type = 3 时 ....
    5. $data 里面各种数据,有:请求数据,订单临时数据,订单预览数据,根据购买商品的不同又放入不同的数据,结果 $data 就是个大杂烩,修改起来实在一言难尽
    // 所有购买入口获取二维码的入口
    public function qrcode(Request $request)
    {
        try {
          	// ...
            $key = $this->checkVerifyType(0, 1);
          	// ...
            return $key;
        } catch (\Exception $e) {
            return '下单失败';
        }
    }
    

    */Service/PayService.php

    public function checkVerifyType($payType1 = 0, $payType2 = 0)
    {
        $data = request()->all();
    
        if (!ctype_digit(strval($data['type']))) {
            throw new \Exception('type err');
        }
      
      	// ..... 还有一堆的参数验证
    
        switch ($data['type']) {
            case 'vip':
                // ... 验证
                $data['vip_info'] = Vip::where('code', $data['code'])->first();
                break;
            case 'recharge':
                // ... 验证
                $data['money'] = $data['money'];
                break;
            // case...
        }
    
        // 优惠券判断
        if ($data['coupon_id']) {
            $money = Coupon::where('id', $data['coupon_id'])->value("money");
            $data['reduce'] = $money;
            // ....
        }
    
        // 订单预览信息
        $data['show_title'] = "购买一个会员";
        $data['show_money'] = 100;
      
        $key = "abcdefg";
        Redis::set($key, $data);
    
        return $key;
    }
    

    着手改造

    前期准备

    原来的返回格式:

    public function json($code, $msg, $data)
    {
        return ['status' => $code, 'message' => $msg, 'data' => $data];
    }
    // 调用
    json(200, "Ok", []);
    

    虽然没什么大问题,但调用起来不太方便,也不直观,每次还需要传入一些不必要的参数,这里增加一些常用的返回方法

    BaseContrller 中增加几个返回数据的方法,方便调用

    const SUCCESS_CODE = 200;
    const SUCCESS_FAIL = 100;
    
    protected function success($msg = 'ok', $data = [], $code = self::SUCCESS_CODE)
    {
        return ['status' => $code, 'message' => $msg, 'data' => $data];
    }
    
    protected function data($data = [], $msg = 'ok', $code = self::SUCCESS_CODE)
    {
        return ['status' => $code, 'message' => $msg, 'data' => $data];
    }
    
    protected function fail($msg = 'ok', $data = [], $code = self::SUCCESS_FAIL)
    {
        return ['status' => $code, 'message' => $msg, 'data' => $data];
    }
    

    按模块区分不同的下单链接

    1. 开通会员: /buy/vip
    2. 充值:/buy/recharge
    3. 购买商品:/buy/goods

    创建临时订单策略

    1. 创建一个订单的 抽象策略,定义算法的接口,所有策略必须实现临时订单的接口,

      app/Http/Services/PayOrder/PayOrderStrategy.php

      abstract class PayOrderStrategy
      {
          abstract function createTemporaryOrder($request);
      }
      
    2. 创建一个 Context

      app/Http/Services/PayOrder/PayOrderStrategy.php

      class PayOrderContext
      {
          private $strategy;
      
          public function __construct(PayOrderStrategy $payOrderStrategy)
          {
              return $this->strategy = $payOrderStrategy;
          }
      
          public function createOrder(Request $request)
          {
              return $this->strategy->createTemporaryOrder($request);
          }
      }
      
    3. 基础的策略框架已经搭建好,现在就需要具体的策略了

      $request 是开通 vip 接口中传入的 $request

      app/Http/Services/PayOrder/Strategy/VipStrategy.php

      // 开通 vip
      class VipStrategy extends PayOrderStrategy
      {
        	// 组装临时订单的数据,然后存入 redis
        	// 这里是 vip 策略,所以只专注 vip 需要的数据就好
          function createTemporaryOrder(Request $request)
          {
              $packageCode = $request['code'];
              $package     = app(PayOrderService::class)->getVipByCode($packageCode);
      
              // 临时订单数据
             	$tmpOrder = [
                  'package_cope' => $package->toArray(),
                  'type'         => PayOrderService::TYPE_VIP,
                  'uid'          => 1,
                  'ip'           => $request->ip(),
                  // ....
              ];
            
              return app(PayOrderService::class)->saveTemporaryOrder($tmpOrder);
          }
      }
      
    4. 创建一个订单服务类,写一些创建订单的公共方法

      app/Http/Services/PayOrderService.php

      use Ramsey\Uuid\Uuid;
      
      class PayOrderService
      {
          const TYPE_VIP      = 1; // 购买 vip
          const TYPE_RECHARGE = 2; // 充值
          const TYPE_GOODS    = 3; // 购买商品
      
          // 通过 code 查询 vip 套餐信息
          public function getVipByCode(string $code)
          {
              // 这里应是从数据库获取数据返回
              return collect(['id' => 1, 'code' => 'vip1', 'price' => 100, 'vip_day' => 30]);
          }
      
          // 保存临时订单
          public function saveTemporaryOrder(array $tmpOrder)
          {
              $key = Uuid::uuid4()->toString();
              Cache::set($key, $tmpOrder, 3);
      
              return $key;
          }
      }
      

      目前的目录解构

      app/Http/Services/

      ├── PayOrder
      │   ├── PayOrderContext.php
      │   ├── PayOrderStrategy.php
      │   └── Strategy
      │       └── VipStrategy.php
      └── PayOrderService.php
      

    实现开通vip接口

    所有接口的数据都是通过 laravel 表单请求验证

    路由:routes/web.php

    Route::get('/buy/vip', "PayController@vip")->name('vip');
    

    app/Http/Controllers/PayController.php

    public function vip(Request $request)
    {
        $strategy = new VipStrategy();
        $tmpOrderKey = (new PayOrderContext($strategy))->createOrder($request);
    
        return $this->data(['key' => $tmpOrderKey]);
    }
    
    curl http://127.0.0.1:8000/buy/vip?code=vip1 | json
    {
      "status": 200,
      "message": "ok",
      "data": {
        "key": "35349845-0e76-4973-b240-67e7b3cdda42"
      }
    }
    

    临时订单已生成,现在需要需要开发手机扫码后的预览接口

    预览订单

    正常来说预览订单是每个支付都需要有的功能,所以增加一个抽象方法

    1. app/Http/Services/PayOrder/PayOrderStrategy.php 新增一个 preview 的抽象方法

      abstract class PayOrderStrategy
      {
          // 创建临时订单
          abstract function createTemporaryOrder(Request $request);
        
        	 // 预览订单
          protected function preview(array $tmpOrder)
          {
              throw new UnsupportedOperationException("不支持的方法");
          }
      }
      

      你可能会好奇,这里预览订单为什么要抛出一个异常呢?因为有些第三方支付没有手机支付,只能 pc 端跳转,所以就不会涉及预览这一说

      如果定义成 abstract 下面继承的方法有必须实现,这个非必须的就直接定义成 protected 并抛出一个异常,开发的时候如果错误的调用了这个方法就会知道,当前支付方式不支持订单的预览

    2. 开通 vip 策略实现 preview 方法,参数是临时订单的信息

      app/Http/Services/PayOrder/Strategy/VipStrategy.php

      function createTemporaryOrder(Request $request){ /*...*/ }
      
      function preview(array $tmpOrder)
      {
          $preview = [
              'title'   => '开通会员',
              'price'   => $tmpOrder['price'],
              'vip_day' => $tmpOrder['vip_day']
          ];
      
          return $preview;
      }
      
    3. 预览订单接口

      这个接口返回一个页面,手机扫码收可以预览并且有下单按钮

      路由:routes/web.php

      Route::get('/buy/preview', "PayController@preview")->name('preview');
      

      app/Http/Controllers/PayController.php

      // 预览订单接口
      public function preview(Request $request)
      {
          // 请求下单接口后返回的临时订单 key
          $tmpOrderKey = $request->get('key');
      
          // 获取临时订单
          $tmpOrder = app(PayOrderService::class)->getTemporaryOrder($tmpOrderKey);
      
          if (!$tmpOrder) {
              throw new TemporaryOrderException("订单已过期");
          }
      
          $strategy = new VipStrategy();
          $preview = (new PayOrderContext($strategy))->preview($tmpOrder);
      
          return view('preview', $preview);
      }
      
    4. 生成二维码

      前端请求 vip 接口之后使用返回的临时订单 key,作为 query 参数请求 预览订单 接口

      http://127.0.0.1:8000/buy/preview?key=35349845-0e76-4973-b240-67e7b3cdda42

      使用策略模式和简单工厂模式重写支付模块 使用策略模式和简单工厂模式重写支付模块

    起源地下载网 » 使用策略模式和简单工厂模式重写支付模块

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元