Kaynağa Gözat

额外添加阿里云ocr识别接口

shizhongqi 1 ay önce
ebeveyn
işleme
1b0fed00e6

+ 16 - 0
app-ghs/controllers/LakalaAccountController.php

@@ -2,8 +2,10 @@
 
 namespace ghs\controllers;
 
+use bizGhs\lakala\classes\AliyunOcrClass;
 use bizGhs\lakala\services\LakalaAccountService;
 use common\components\util;
+use ghs\models\lakala\AliyunOcrForm;
 use ghs\models\lakala\ApplicationIdForm;
 use ghs\models\lakala\CreateForm;
 use ghs\models\lakala\FormDataForm;
@@ -98,6 +100,20 @@ class LakalaAccountController extends BaseController
         ));
     }
 
+    public function actionAliyunOcr()
+    {
+        $form = new AliyunOcrForm();
+        $form->loadAndValidate();
+
+        util::success(AliyunOcrClass::recognize(
+            (int)$form->id,
+            $this->mainId,
+            $this->shopId,
+            $form->imgType,
+            $form->imageUrl
+        ));
+    }
+
     public function actionOption()
     {
         $form = new OptionForm();

+ 33 - 0
app-ghs/models/lakala/AliyunOcrForm.php

@@ -0,0 +1,33 @@
+<?php
+namespace ghs\models\lakala;
+
+use ghs\models\BaseForm;
+
+/**
+ * 阿里云 OCR 识别表单模型
+ */
+class AliyunOcrForm extends BaseForm
+{
+    public $id;
+    public $imgType;
+    public $imageUrl;
+
+    public function rules()
+    {
+        return [
+            [['id', 'imgType', 'imageUrl'], 'required', 'message' => '请填写{attribute}'],
+            ['id', 'integer', 'min' => 1, 'message' => '申请ID格式错误', 'tooSmall' => '申请ID格式错误'],
+            ['imgType', 'string', 'max' => 64],
+            ['imageUrl', 'string', 'max' => 1024],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'id' => '申请ID',
+            'imgType' => '图片类型',
+            'imageUrl' => '图片地址',
+        ];
+    }
+}

+ 248 - 0
biz-ghs/lakala/classes/AliyunOcrClass.php

@@ -0,0 +1,248 @@
+<?php
+
+namespace bizGhs\lakala\classes;
+
+use AlibabaCloud\Client\AlibabaCloud;
+use AlibabaCloud\Client\Exception\ClientException;
+use AlibabaCloud\Client\Exception\ServerException;
+use common\components\util;
+use Yii;
+
+class AliyunOcrClass
+{
+    public static function recognize($id, $mainId, $shopId, $imgType, $imageUrl)
+    {
+        $application = LakalaApplicationClass::getByCondition([
+            'id' => $id,
+            'mainId' => $mainId,
+            'shopId' => $shopId,
+        ]);
+        if (empty($application)) {
+            util::fail('没有找到申请记录');
+        }
+
+        try {
+            $response = self::request($imgType, self::formatImageUrl($imageUrl));
+        } catch (ClientException $e) {
+            Yii::warning('阿里云OCR客户端异常:' . $e->getErrorMessage(), __METHOD__);
+            util::fail('OCR识别失败,请稍后重试');
+        } catch (ServerException $e) {
+            Yii::warning('阿里云OCR服务端异常:' . $e->getErrorMessage(), __METHOD__);
+            util::fail('OCR识别失败,请稍后重试');
+        } catch (\Exception $e) {
+            Yii::warning('阿里云OCR异常:' . $e->getMessage(), __METHOD__);
+            util::fail('OCR识别失败,请稍后重试');
+        }
+
+        return [
+            'status' => '00',
+            'result' => self::formatResult($imgType, $response),
+        ];
+    }
+
+    private static function request($imgType, $imageUrl)
+    {
+        $accessKeyId = getenv('OCR_ACCESS_KEY_ID');
+        $accessKeySecret = getenv('OCR_ACCESS_KEY_SECRET');
+        AlibabaCloud::accessKeyClient(
+            $accessKeyId,
+            $accessKeySecret
+        )->regionId('cn-shanghai')->asDefaultClient();
+
+        $action = 'RecognizeCharacter';
+        $query = [
+            'RegionId' => 'cn-shanghai',
+            'ImageURL' => $imageUrl,
+        ];
+        if ($imgType === 'ID_CARD_FRONT') {
+            $action = 'RecognizeIdentityCard';
+            $query['Side'] = 'face';
+        } elseif ($imgType === 'ID_CARD_BEHIND') {
+            $action = 'RecognizeIdentityCard';
+            $query['Side'] = 'back';
+        } elseif ($imgType === 'BANK_CARD') {
+            $action = 'RecognizeBankCard';
+        }
+
+        return AlibabaCloud::rpc()
+            ->product('ocr')
+            ->version('2019-12-30')
+            ->action($action)
+            ->method('POST')
+            ->options([
+                'query' => $query,
+            ])
+            ->request()
+            ->toArray();
+    }
+
+    private static function formatImageUrl($imageUrl)
+    {
+        $imageUrl = trim((string)$imageUrl);
+        $host = parse_url($imageUrl, PHP_URL_HOST);
+        if ($host && strpos($host, '.aliyuncs.com') !== false) {
+            return $imageUrl;
+        }
+
+        $objectPath = $imageUrl;
+        if (strpos($imageUrl, 'http://') === 0 || strpos($imageUrl, 'https://') === 0) {
+            $objectPath = parse_url($imageUrl, PHP_URL_PATH);
+        }
+        $objectPath = ltrim((string)$objectPath, '/');
+        if ($objectPath === '') {
+            util::fail('图片地址无效');
+        }
+
+        return sprintf(
+            'https://%s.%s/%s',
+            Yii::$app->params['ossBucket'],
+            Yii::$app->params['endpoint'],
+            $objectPath
+        );
+    }
+
+    private static function formatResult($imgType, array $response)
+    {
+        $data = self::decodeData($response);
+        $lines = self::extractLines($data);
+        $text = implode("\n", $lines);
+        $result = [
+            'text' => $text,
+            'lines' => $lines,
+        ];
+
+        if ($imgType === 'ID_CARD_FRONT') {
+            $result = array_merge($result, self::parseIdCardFront($text, $data));
+        } elseif ($imgType === 'ID_CARD_BEHIND') {
+            $result = array_merge($result, self::parseIdCardBehind($text, $data));
+        } elseif ($imgType === 'BANK_CARD') {
+            $result = array_merge($result, self::parseBankCard($text, $lines, $data));
+        }
+
+        return $result;
+    }
+
+    private static function decodeData(array $response)
+    {
+        $data = isset($response['Data']) ? $response['Data'] : $response;
+        if (is_string($data)) {
+            $decoded = json_decode($data, true);
+            return is_array($decoded) ? $decoded : ['text' => $data];
+        }
+        return $data;
+    }
+
+    private static function extractLines($data)
+    {
+        $lines = [];
+        self::collectLines($data, $lines);
+        $lines = array_values(array_unique(array_filter(array_map('trim', $lines))));
+        return $lines;
+    }
+
+    private static function collectLines($data, array &$lines)
+    {
+        if (is_string($data)) {
+            $lines[] = $data;
+            return;
+        }
+        if (!is_array($data)) {
+            return;
+        }
+
+        foreach (['word', 'words', 'content', 'text', 'value', 'textContent'] as $key) {
+            if (isset($data[$key]) && is_string($data[$key])) {
+                $lines[] = $data[$key];
+            }
+        }
+        foreach ($data as $value) {
+            self::collectLines($value, $lines);
+        }
+    }
+
+    private static function parseIdCardFront($text, array $data)
+    {
+        $result = [];
+        $name = self::findValue($data, ['Name', 'name', '姓名']);
+        if ($name !== '') {
+            $result['name'] = $name;
+        }
+        $idNumber = self::findValue($data, ['IDNumber', 'IdNumber', 'idNumber', 'CardNumber', '公民身份号码', '身份证号']);
+        if ($idNumber !== '') {
+            $result['id_number'] = strtoupper($idNumber);
+        }
+        if (preg_match('/姓名\s*([\x{4e00}-\x{9fa5}·]{2,20})/u', $text, $match)) {
+            $result['name'] = $result['name'] ?? $match[1];
+        }
+        if (preg_match('/\d{17}[\dXx]/', $text, $match)) {
+            $result['id_number'] = $result['id_number'] ?? strtoupper($match[0]);
+        }
+        return $result;
+    }
+
+    private static function parseIdCardBehind($text, array $data)
+    {
+        $result = [];
+        $startDate = self::findValue($data, ['StartDate', 'startDate', 'ValidStartDate', '有效期限起始日期']);
+        $endDate = self::findValue($data, ['EndDate', 'endDate', 'ValidEndDate', '有效期限结束日期']);
+        if ($startDate !== '' && $endDate !== '') {
+            $result['validity'] = self::normalizeDate($startDate) . '-' . self::normalizeDate($endDate);
+        }
+        if (preg_match_all('/\d{4}[.年-]\d{1,2}[.月-]\d{1,2}日?/', $text, $matches) && count($matches[0]) >= 2) {
+            $result['validity'] = $result['validity'] ?? (self::normalizeDate($matches[0][0]) . '-' . self::normalizeDate($matches[0][1]));
+        } elseif (strpos($text, '长期') !== false && preg_match('/\d{4}[.年-]\d{1,2}[.月-]\d{1,2}日?/', $text, $match)) {
+            $result['validity'] = $result['validity'] ?? (self::normalizeDate($match[0]) . '-9999-12-31');
+        }
+        return $result;
+    }
+
+    private static function parseBankCard($text, array $lines, array $data)
+    {
+        $result = [];
+        $cardNumber = self::findValue($data, ['CardNumber', 'cardNumber', 'BankCardNumber', 'bankCardNumber', '银行卡号']);
+        if ($cardNumber !== '') {
+            $result['card_number'] = preg_replace('/\s+/', '', $cardNumber);
+            $result['bank_card_number'] = $result['card_number'];
+        }
+        $bankName = self::findValue($data, ['BankName', 'bankName', '银行名称', '银行']);
+        if ($bankName !== '') {
+            $result['bank_name'] = $bankName;
+        }
+        $numberText = preg_replace('/\s+/', '', $text);
+        if (preg_match('/\d{12,30}/', $numberText, $match)) {
+            $result['card_number'] = $result['card_number'] ?? $match[0];
+            $result['bank_card_number'] = $result['bank_card_number'] ?? $match[0];
+        }
+        foreach ($lines as $line) {
+            if (strpos($line, '银行') !== false) {
+                $result['bank_name'] = $result['bank_name'] ?? $line;
+                break;
+            }
+        }
+        return $result;
+    }
+
+    private static function normalizeDate($date)
+    {
+        if (preg_match('/(\d{4})[.年-](\d{1,2})[.月-](\d{1,2})日?/', $date, $match)) {
+            return sprintf('%04d-%02d-%02d', $match[1], $match[2], $match[3]);
+        }
+        return $date;
+    }
+
+    private static function findValue(array $data, array $keys)
+    {
+        foreach ($data as $key => $value) {
+            if (in_array($key, $keys, true) && (is_string($value) || is_numeric($value))) {
+                return trim((string)$value);
+            }
+            if (is_array($value)) {
+                $found = self::findValue($value, $keys);
+                if ($found !== '') {
+                    return $found;
+                }
+            }
+        }
+        return '';
+    }
+}