| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- <?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 '';
- }
- }
|