模擬 easywechat 開發騰訊的 ocr 包

lixueyuan發表於2021-04-21

前言

眾所周知 easywechat 非常強大,裡面包含微信平臺的所有功能例如 公眾號、小程式、開放平臺、企業號等功能。
正好我這邊接到需求要開發一個身份證識別的功能,考慮到擴充套件性,決定參考 easywechat 的管理模式開發一個自己的包。下面是主要步驟和程式碼包結構如下:

模擬 easywechat 開發騰訊的 ocr 包
因為是公司內部專案所以放棄用composer管理包,至於如何用composer管理可以看教程

LX2 PHP 擴充套件包實戰教程 - 從入門到釋出

1、 主要檔案和目錄說明:
目錄:
src/Ai 表示騰訊的識別模組,可以新建其它模組(類似 easywechat 的 miniProgram、OfficialAccount 等模組)
src/Ai/Ocr 表示騰訊識別的Ocr模組(類似 OfficialAccount 授權登入、選單、訊息等模組)
src/config 表示配置檔案位置
src/Kernel 表示容器管理,核心模組
檔案:
src/Ai/Ocr/Client.php 表示具體業務處理類程式碼如下:

<?php

namespace Tecent\src\Ai\Ocr;

// 騰訊ocr認證
use Pimple\Container;

/**
 * 處理具體業務邏輯
  * Class Client
 * @package Tencent\src\Ai\Ocr 
 */
class Client
{
    private $idCardUrl = 'https://recognition.image.myqcloud.com/ocr/idcard';
    private $appid;
    private $secret_id;
    private $secret_key;

    public function __construct($app)
    {
        // container 讀取配置檔案
  $config           = $app->getConfig();
        $this->appid      = $config['app_id'];
        $this->secret_id  = $config['secret_id'];
        $this->secret_key = $config['secret_key'];
    }

    /**
  * 身份證識別
  * @Author lxy
  * @Date 2021/4/20 * @param $cardType
  * @param $urlList
  * @return string
  * @throws \GuzzleHttp\Exception\GuzzleException */  public function idCard($cardType, $urlList)
    {
        $client   = new \GuzzleHttp\Client();
        $signArr  = $this->getSign();
        $options  = [
            'headers' => [
                'authorization' => $signArr['signStr'],
                'content-type' => 'application/json'
  ],
            'json' => [
                'appid' => $this->appid,
                'card_type' => $cardType,
                'url_list' => $urlList,
            ]
        ];
        $response = $client->request('POST', $this->idCardUrl, $options);
        return $response->getBody()->getContents();
    }

    /**
 * 行駛證駕駛證識別
  * @Author lxy
 * @Date 2021/4/20 */  public function drivinglicence($type, $imageurl)
    {
        $signArr  = $this->getSign();
        $client = new \GuzzleHttp\Client();
        $options = [
            'headers' => [
                'content-type'=>'application/json',
                'authorization' => $signArr['signStr'],
            ],
            'json' => [
                'appid' => $this->appid,
                'type' => $type,
                'url' => $imageurl,
            ],
        ];
        $result = $client->request('POST','https://recognition.image.myqcloud.com/ocr/drivinglicence', $options);
        return $result->getBody()->getContents();
    }
    /**
     * 簽名
     * @Author lxy
     * @Date 2021/4/20 * @return array
     */  
    private function getSign()
    {
        $appid       = $this->appid;
        $bucket      = "tencentyun";
        $secret_id   = $this->secret_id;
        $secret_key  = $this->secret_key;
        $expired     = time() + 2592000;
        $onceExpired = 0;
        $current     = time();
        $rdm         = rand();
        $userid      = "0";
        $fileid      = "tencentyunSignTest";
        $srcStr      = 'a=' . $appid . '&b=' . $bucket . '&k=' . $secret_id . '&e=' . $expired . '&t=' . $current . '&r=' . $rdm . '&f=';
        $srcWithFile = 'a=' . $appid . '&b=' . $bucket . '&k=' . $secret_id . '&e=' . $expired . '&t=' . $current . '&r=' . $rdm . '&f=' . $fileid;
        $srcStrOnce  = 'a=' . $appid . '&b=' . $bucket . '&k=' . $secret_id . '&e=' . $onceExpired . '&t=' . $current . '&r=' . $rdm
            . '&f=' . $fileid;
        $signStr     = base64_encode(hash_hmac('SHA1', $srcStr, $secret_key, true) . $srcStr);
        $srcWithFile = base64_encode(hash_hmac('SHA1', $srcWithFile, $secret_key, true) . $srcWithFile);
        $signStrOnce = base64_encode(hash_hmac('SHA1', $srcStrOnce, $secret_key, true) . $srcStrOnce);
        return compact('signStr', 'srcWithFile', 'signStrOnce');
    }
}

src/Ai/Ocr/ServiceProvider.php 表示註冊一個 ocr 服務

<?php

namespace Tecent\src\Ai\Ocr;

use Pimple\Container;
use Pimple\ServiceProviderInterface;

/**
 * Register any application services.
 * 參考:https://github.com/silexphp/Pimple
 * Class ServiceProvider
 * @package Tencent\src\Ai\Ocr
 */
class ServiceProvider implements ServiceProviderInterface
{
    public function register(Container $app)
    {
        !isset($app['ocr']) && $app['ocr'] = function ($app) {
            return new Client($app);
        };
    }
}

src/Ai/Application.php 應用服務管理包括自動註冊服務、服務發現等功能

<?php

namespace Tecent\src\Ai;

use Tecent\src\Ai\Ocr\ServiceProvider;
use Tecent\src\Kernel\ServiceContainer;

/**
 * 定義一個application(裡面包含很多application services)
 * application services 在建構函式裡面自動註冊
 * Class Application
 * @package Tencent\src\Ai
 */
class Application extends ServiceContainer
{
    protected $providers = [
        ServiceProvider::class,// 提供一個ocr圖片識別的服務
        //... 可以註冊無數個,想寫多少就寫多少
    ];
}

src/config/tencent_cloud.php 配置檔案(如果有多個就不能這麼寫了,參考laravel-wechat)

<?php

return [
    'app_id' => env('APP_ID','xxx'),
    'secret_id' => env('SECRET_ID','xxx'),
    'secret_key' => env('SECRET_KEY', 'xxx'),
];

src/Kernel/ServiceContainer.php 核心模組,服務註冊、服務發現

<?php


namespace Tecent\src\Kernel;


use Pimple\Container;

class ServiceContainer extends Container
{

    /**
     * @var string
     */
    protected $id;

    /**
     * @var array
     */
    protected $providers = [];

    /**
     * @var array
     */
    protected $defaultConfig = [];

    /**
     * @var array
     */
    protected $userConfig = [];

    /**
     * Constructor.
     * 建構函式註冊服務(載入配置檔案)都是通過https://github.com/silexphp/Pimple這個包來完成的
     * @param array $config
     * @param array $prepends
     * @param string|null $id
     */
    public function __construct(array $config = [], array $prepends = [], string $id = null)
    {
        $this->registerProviders($this->getProviders());

        parent::__construct($prepends);

        $this->userConfig = $config;

        $this->id = $id;

    }

    /**
     * @return string
     */
    public function getId()
    {
        return $this->id ?? $this->id = md5(json_encode($this->userConfig));
    }

    /**
     * @return array
     */
    public function getConfig()
    {
        $base = [
            'http' => [
                'timeout' => 30.0,
            ],
        ];
        return array_replace_recursive($base, $this->defaultConfig, $this->userConfig);
    }

    /**
     * Return all providers.
     *
     * @return array
     */
    public function getProviders()
    {
        return  $this->providers;
    }

    /**
     * @param string $id
     * @param mixed $value
     */
    public function rebind($id, $value)
    {
        $this->offsetUnset($id);
        $this->offsetSet($id, $value);
    }

    /**
     * Magic get access.
     * 說人話:這個魔術方法在呼叫時自動執行,從而例項化物件【Pimple的Using the defined services is also very easy:】
     * @param string $id
     *
     * @return mixed
     */
    public function __get($id)
    {
        return $this->offsetGet($id);
    }

    /**
     * Magic set access.
     * 魔術方法:設定一個例項或Defining Services,臥槽!
     * @param string $id
     * @param mixed $value
     */
    public function __set($id, $value)
    {
        $this->offsetSet($id, $value);
    }

    /**
     * 手動註冊物件
     * @param array $providers
     */
    public function registerProviders(array $providers)
    {
        foreach ($providers as $provider) {
            parent::register(new $provider());
        }
    }
}

src/Facade.php

<?php

namespace Tencent\src;

class Facade extends \Illuminate\Support\Facades\Facade
{
    protected static function getFacadeAccessor()
    {
       return 'ocr';
    }
}

src/ServiceProvider.php

<?php
namespace Tencent\src;

use Dingo\Api\Provider\LaravelServiceProvider;
use Tecent\src\Ai\Application;

class ServiceProvider extends LaravelServiceProvider {

    public function boot()
    {
        $this->publishes([realpath(__DIR__.'/config/tencent_cloud.php') => config_path('tencent_cloud.php')]);
    }
    /***
    * 這裡我寫簡單了,可以寫一個陣列迴圈註冊服務。配置檔案也一樣,參考laravel-wechat的方式
    */
    public function register()
    {
        $config = config('tencent_cloud');
        $this->app->singleton('tencent.ocr', function ($app) use($config){
            return new Application($config);
        });
    }

    public function provides()
    {
        return ['ocr'];
    }
}

到此全部完成,是不是很簡單。

下面是使用方法:
composer.json 裡新增一下自動載入

...
"autoload": {
    "classmap": [
        "database/seeds",
        "database/factories",
        "app/Libs/RongCloud",
        "app/Libs/laravel-baidu",
        "app/Libs/baidu",
        "app/Libs/Tencent"
  ],
    "psr-4": {
        "App\\": "app/"
  },
    "files": [
        "app/Include/fun_common.php"
  ]
  ...
},

app.php裡面providers陣列裡面加上服務

...
'providers' =>[
    // 騰訊雲
    Tencent\src\ServiceProvider::class,
],
...

釋出配置檔案

php artisan vendor:publish //選擇你要的釋出

使用:

public function tecentIdcard(Request $request)
{
    $cardType = $request->card_type;
    $cardUrlList = $request->url_list;
    $result = app('tencent.ocr')->ocr->idCard($cardType, $cardUrlList);
    return $this->response->array(json_decode($result,true));
}

.env 新增配置檔案
so easy!
以後其它類似的功能也可以這麼做

本作品採用《CC 協議》,轉載必須註明作者和本文連結
程式設計兩年半,喜歡ctrl(唱、跳、rap、籃球)

相關文章