模拟 easywechat 开发腾讯的 ocr 包

手机APP/开发
418
0
0
2022-04-09

前言

众所周知 easywechat 非常强大,里面包含微信平台的所有功能例如 公众号、小程序、开放平台、企业号等功能。

正好我这边接到需求要开发一个身份证识别的功能,考虑到扩展性,决定参考 easywechat 的管理模式开发一个自己的包。下面是主要步骤和代码包结构如下:

模拟 easywechat 开发腾讯的 ocr 包

因为是公司内部项目所以放弃用composer管理包,至于如何用composer管理可以看教程


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!

以后其它类似的功能也可以这么做