This commit is contained in:
2025-07-07 20:11:59 +08:00
parent ab0fdbc447
commit 06e3aa2eb3
2009 changed files with 193082 additions and 0 deletions

View File

@ -0,0 +1,491 @@
package coryCommon
import (
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/os/gctx"
"github.com/tiger1103/gfast-cache/cache"
commonService "github.com/tiger1103/gfast/v3/internal/app/common/service"
"github.com/tiger1103/gfast/v3/library/liberr"
tool "github.com/tiger1103/gfast/v3/utility/coryUtils"
"golang.org/x/net/context"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
/**
功能:
文字识别百度API识别身份证、银行卡内容、人脸检测、人脸对比
*/
// OcrReq 请求体参数 身份证和银行卡都是此结构体,
type OcrReq struct {
Image string `json:"image"` // 二选一图像数据base64编码后进行urlencode需去掉编码头data:image/jpeg;base64, 要求base64编码和urlencode后大小不超过4M最短边至少15px最长边最大4096px,支持jpg/jpeg/png/bmp格式
Url string `json:"url"` // 二选一图片完整URLURL长度不超过1024字节URL对应的图片base64编码后大小不超过4M最短边至少15px最长边最大4096px,支持jpg/jpeg/png/bmp格式当image字段存在时url字段失效 请注意关闭URL防盗链
IdCardSide string `json:"id_card_side"` // 身份证需要此参数人头面填写front国徽面填写back 银行卡不需要
DetectPhoto bool `json:"detect_photo"` // 是否检测身份证进行裁剪默认不检测。可选值true-检测身份证并返回证照的 base64 编码及位置信息
}
/*
1、clientId 必须参数应用的APIKey
2、clientSecret 必须参数应用的Secret Key
3、存在redis的数据前缀
*/
var clientId = ""
var clientSecret = ""
var cacheRedis = "zmGoBaiDuOcrAccessToken"
func init() {
one, err := g.Cfg().Get(gctx.New(), "baiDuYun.clientId")
if err != nil {
fmt.Println("百度云API未找到")
}
two, err := g.Cfg().Get(gctx.New(), "baiDuYun.clientSecret")
if err != nil {
fmt.Println("百度云Secret未找到")
}
clientId = one.String()
clientSecret = two.String()
}
/*
IDCardInfo 获取身份证相关数据
*/
type IDCardInfo struct {
WordsResult WordsResult `json:"words_result"`
WordsResultNum int `json:"words_result_num"`
IDCardNumberType int `json:"idcard_number_type"`
ImageStatus string `json:"image_status"`
LogID int64 `json:"log_id"`
Photo string `json:"photo"`
}
type WordsResult struct {
Name Field `json:"姓名"`
Nation Field `json:"民族"`
Address Field `json:"住址"`
CitizenIdentification Field `json:"公民身份号码"`
Birth Field `json:"出生"`
Gender Field `json:"性别"`
ExpirationDate Field `json:"失效日期"`
IssuingAuthority Field `json:"签发机关"`
IssueDate Field `json:"签发日期"`
}
type Field struct {
Location Location `json:"location"`
Words string `json:"words"`
}
type Location struct {
Top int `json:"top"`
Left int `json:"left"`
Width int `json:"width"`
Height int `json:"height"`
}
func ImgOCR(vr OcrReq) (m map[string]interface{}) {
//请求路径+token
baseUrl := "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard"
//先从缓存里面捞取token,如果没得就重新获取token
var atStr = redisCacheStr()
// 构造 URL 参数
params := url.Values{}
params.Set("access_token", atStr)
// 构造完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseUrl, params.Encode())
response, err := gclient.New().ContentJson().ContentType("application/x-www-form-urlencoded").Post(gctx.New(), requestURL, vr)
if err != nil {
return
}
var dataInfo = strings.ReplaceAll(response.ReadAllString(), " ", "")
//解析数据
bodyData := []byte(dataInfo)
var idCardInfo IDCardInfo
err = json.Unmarshal(bodyData, &idCardInfo)
if err != nil {
fmt.Println("Failed to parse JSON data:", err)
return
}
m = make(map[string]interface{})
//身份证正反面颠倒了,直接返回空
if idCardInfo.ImageStatus == "reversed_side" {
return
}
result := idCardInfo.WordsResult
//if idCardInfo.Photo != "" {
// m["pacePhoto"] = idCardInfo.Photo
//}
if result.Name.Words != "" {
m["userName"] = result.Name.Words
}
if result.Nation.Words != "" {
m["sfzNation"] = result.Nation.Words
}
if result.Address.Words != "" {
m["sfzSite"] = result.Address.Words
}
if result.CitizenIdentification.Words != "" {
m["sfzNumber"] = result.CitizenIdentification.Words
}
if result.Birth.Words != "" {
str, _ := tool.New().TimeCycle(result.Birth.Words)
m["sfzBirth"] = str
}
if result.Gender.Words != "" {
var se = result.Gender.Words
if se == "男" {
se = "1"
} else if se == "女" {
se = "2"
} else {
se = "3"
}
m["sex"] = se
}
if result.ExpirationDate.Words != "" {
str, err := tool.New().TimeCycle(result.ExpirationDate.Words)
if err == nil {
m["sfzEnd"] = str
} else {
str := result.ExpirationDate.Words
m["sfzEnd"] = str
}
}
if result.IssuingAuthority.Words != "" {
m["IssuingAuthority"] = result.IssuingAuthority.Words
}
if result.IssueDate.Words != "" {
str, _ := tool.New().TimeCycle(result.IssueDate.Words)
m["sfzStart"] = str
}
return m
}
/*
BankData 获取银行卡相关数据
针对卡号、有效期、发卡行、卡片类型、持卡人5个关键字段进行结构化识别识别准确率超过99%
*/
type BankData struct {
ValidDate string `json:"valid_date"` //有效期
BankCardNumber string `json:"bank_card_number"` //银行卡卡号
BankName string `json:"bank_name"` //银行名,不能识别时为空
BankCardType int `json:"bank_card_type"` //银行卡类型0不能识别; 1借记卡; 2贷记卡原信用卡大部分为贷记卡; 3准贷记卡; 4预付费卡
HolderName string `json:"holder_name"` //持卡人姓名,不能识别时为空
}
type Result struct {
Res BankData `json:"result"` //具体数据
Direction int `json:"direction"` //图像方向。 - - 1未定义; - 0正向; - 1逆时针90度; - 2逆时针180度; - 3逆时针270度
LogID int64 `json:"log_id"` //请求标识码,随机数,唯一。
}
func ImgYhkOCR(vr OcrReq) (m map[string]interface{}) {
m = make(map[string]interface{})
//请求路径+token
baseUrl := "https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard"
//先从缓存里面捞取token
var atStr = redisCacheStr()
// 构造 URL 参数
params := url.Values{}
params.Set("access_token", atStr)
// 构造完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseUrl, params.Encode())
response, err := gclient.New().ContentJson().ContentType("application/x-www-form-urlencoded").Post(gctx.New(), requestURL, vr)
if err != nil {
return
}
//解析数据
allString := response.ReadAllString()
bodyData := []byte(allString)
var result Result
err = json.Unmarshal(bodyData, &result)
if err != nil {
fmt.Println("Failed to parse JSON data:", err)
return
}
if result.Res.ValidDate != "" {
m["ValidDate"] = result.Res.ValidDate
}
if result.Res.BankCardNumber != "" {
m["yhkNumber"] = strings.ReplaceAll(result.Res.BankCardNumber, " ", "")
}
if result.Res.BankName != "" {
m["yhkOpeningBank"] = result.Res.BankName
}
if result.Res.BankCardType >= 0 {
m["BankCardType"] = result.Res.BankCardType
}
if result.Res.HolderName != "" {
m["yhkCardholder"] = result.Res.HolderName
}
return m
}
/*
HumanFaceReq 请求参数 人脸识别+人脸检测
*/
type HumanFaceReq struct {
Image string `json:"image"` //图片此处填写base64
ImageType string `json:"image_type" gf:"default:BASE64" ` //图片类型-BASE64-URL-BASE64此封装固定用base64
FaceField string `json:"face_field" gf:"default:face_type,quality"` //包括age,expression,face_shape,gender,glasses,landmark,landmark150, quality,eye_status,emotion,face_type,mask,spoofing信息 逗号分隔. 默认只返回face_token、人脸框、概率和旋转角度
}
/*
HumanFaceRep 返回参数 人脸识别
*/
type HumanFaceRep struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
LogId int `json:"log_id"`
Timestamp int `json:"timestamp"`
Cached int `json:"cached"`
Result struct {
FaceNum int `json:"face_num"`
FaceList []struct {
FaceToken string `json:"face_token"`
Location struct {
Left float64 `json:"left"`
Top float64 `json:"top"`
Width int `json:"width"`
Height int `json:"height"`
Rotation int `json:"rotation"`
} `json:"location"`
FaceProbability float64 `json:"face_probability"`
Angle struct {
Yaw float64 `json:"yaw"`
Pitch float64 `json:"pitch"`
Roll float64 `json:"roll"`
} `json:"angle"`
FaceType struct {
Type string `json:"type"`
Probability float64 `json:"probability"`
} `json:"face_type"`
Quality struct {
Occlusion struct {
LeftEye float64 `json:"left_eye"`
RightEye float64 `json:"right_eye"`
Nose float64 `json:"nose"`
Mouth float64 `json:"mouth"`
LeftCheek float64 `json:"left_cheek"`
RightCheek float64 `json:"right_cheek"`
ChinContour float64 `json:"chin_contour"`
} `json:"occlusion"`
Blur float64 `json:"blur"`
Illumination float64 `json:"illumination"`
Completeness int64 `json:"completeness"`
} `json:"quality"`
} `json:"face_list"`
} `json:"result"`
}
func HumanFace(hf *HumanFaceReq) (err error) {
ctx := gctx.New()
err = g.Try(ctx, func(ctx context.Context) {
err = nil
//1、请求地址+token
url := "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=" + redisCacheStr()
marshal, err := json.Marshal(hf)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
//3、准备请求
payload := strings.NewReader(string(marshal))
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
//4、设置请求头
req.Header.Add("Content-Type", "application/json")
//5、发送请求
res, err := client.Do(req)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
defer res.Body.Close()
//6、返回数据
body, err := io.ReadAll(res.Body)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
var aaa = body
//解析数据
var result HumanFaceRep
err = json.Unmarshal(aaa, &result)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
if result.ErrorCode != 0 {
if result.ErrorMsg == "pic not has face" {
err = errors.New("这张照片没有人脸")
liberr.ErrIsNil(ctx, err)
} else {
err = errors.New(result.ErrorMsg)
liberr.ErrIsNil(ctx, err)
}
return
}
//1、人脸置信度范围【0~1】代表这是一张人脸的概率0最小、1最大。其中返回0或1时数据类型为Integer
dataInfo := result.Result.FaceList[0]
reliabilityOne := dataInfo.FaceProbability
if reliabilityOne != 1.0 {
err = errors.New("识别不清晰!")
}
//2、判断是否真是人脸 human: 真实人脸 cartoon: 卡通人脸
reliabilityTwo := dataInfo.FaceType.Type
if reliabilityTwo == "cartoon" {
err = errors.New("请传入真实人脸!")
} else {
//判断可信度 置信度范围0~1
reliabilityThree := dataInfo.FaceType.Probability
if reliabilityThree < 0.8 {
err = errors.New("请勿化妆太过夸张!")
}
}
//3、人脸模糊程度范围[0~1]0表示清晰1表示模糊
reliabilityFour := dataInfo.Quality.Blur
if reliabilityFour >= 0.1 {
err = errors.New("人脸过于模糊!")
}
//4、光线太暗 0~255 值越大光线越好
reliabilityFive := dataInfo.Quality.Illumination
if reliabilityFive < 80.0 {
err = errors.New("光线太暗!")
}
//5、人脸是否完整 1完整 0不完整
reliabilitySix := dataInfo.Quality.Completeness
if reliabilitySix != 1 {
err = errors.New("请确定人脸在图框内!")
}
return
})
return
}
// ComparisonRep 人脸检测返回数据
type ComparisonRep struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
LogId int `json:"log_id"`
Timestamp int `json:"timestamp"`
Cached int `json:"cached"`
Result struct {
Score float64 `json:"score"` //人脸相似度得分推荐阈值80分
FaceList []struct {
FaceToken string `json:"face_token"`
} `json:"face_list"`
} `json:"result"`
}
func Comparison(arrObject []*HumanFaceReq) (score float64, err error) {
//1、请求地址+token
url := "https://aip.baidubce.com/rest/2.0/face/v3/match?access_token=" + redisCacheStr()
//2、请求参数转字符串json
marshal, err := json.Marshal(arrObject)
if err != nil {
return
}
payload := strings.NewReader(string(marshal))
//3、准备post请求
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)
if err != nil {
fmt.Println(err)
return
}
//4、设置请求头
req.Header.Add("Content-Type", "application/json")
//5、发送请求关闭连接
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
//6、数据结果
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
//7、解析数据
var result ComparisonRep
err = json.Unmarshal(body, &result)
if err != nil {
fmt.Println("Failed to parse JSON data:", err)
return
}
score = result.Result.Score
return score, err
}
/*
AccessTokenResponse 获取Access_token,有效期(秒为单位有效期30天)
*/
type AccessTokenResponse struct {
AccessToken string `json:"access_token"`
}
func AccessTokenFunc() (str string) {
url := "https://aip.baidubce.com/oauth/2.0/token?client_id=" + clientId + "&client_secret=" + clientSecret + "&grant_type=client_credentials"
payload := strings.NewReader(``)
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var tokenResponse AccessTokenResponse
json.Unmarshal(body, &tokenResponse)
if err != nil {
fmt.Println(err)
return
}
return tokenResponse.AccessToken
}
// 缓存捞取token,如果没得就重新获取token
func redisCacheStr() (atStr string) {
atStr = ""
ctx := gctx.New()
prefix := g.Cfg().MustGet(ctx, "system.cache.prefix").String()
gfCache := cache.New(prefix)
at := commonService.Cache().Get(ctx, gfCache.CachePrefix+cacheRedis)
if at == nil || at.String() == "" {
atStr = AccessTokenFunc()
//存储到redis时间为29天
commonService.Cache().Set(ctx, cacheRedis, atStr, time.Hour*24*29)
} else {
atStr = at.String()
}
//atStr = "24.c8814d3fc7961820f0e23ee9d80cf96c.2592000.1696671067.282335-38777216"
return atStr
}