|
|
@@ -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 '';
|
|
|
+ }
|
|
|
+}
|