From 90e942e61f59694981a9a606afb4fbfa7b2a8b58 Mon Sep 17 00:00:00 2001 From: workabee <2950914812@qq.com> Date: Mon, 19 Feb 2024 13:54:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 + internal/model/ocr.go | 264 +++++++++++++++++++++++++++++++++ pkg/router/router.go | 2 +- pkg/service/common/common.go | 2 - pkg/service/register_record.go | 66 +++++++-- 5 files changed, 322 insertions(+), 14 deletions(-) create mode 100644 internal/model/ocr.go diff --git a/go.mod b/go.mod index 9c99695..47e1d8d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/exhibition-main go 1.20 replace ( + github.com/fonchain/utils/baidu => ../utils/baidu github.com/fonchain_enterprise/utils/aes => ../utils/aes github.com/fonchain_enterprise/utils/chain => ../utils/chain github.com/fonchain_enterprise/utils/feie => ../utils/feie @@ -22,6 +23,7 @@ require ( github.com/dubbogo/gost v1.13.2 github.com/dubbogo/grpc-go v1.42.10 github.com/dubbogo/triple v1.2.2-rc2 + github.com/fonchain/utils/baidu v0.0.0-00010101000000-000000000000 github.com/fonchain_enterprise/utils/objstorage v0.0.0-00010101000000-000000000000 github.com/gin-contrib/gzip v0.0.6 github.com/gin-contrib/pprof v1.4.0 diff --git a/internal/model/ocr.go b/internal/model/ocr.go new file mode 100644 index 0000000..4e50867 --- /dev/null +++ b/internal/model/ocr.go @@ -0,0 +1,264 @@ +package model + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strconv" + "time" + "unicode/utf8" +) + +const ( + grant_type = "client_credentials" +) + +var accessToken string +var expires_in uint64 + +type OcrQuery struct { + IdCardUrl string `json:"idCardUrl"` + Side int `json:"side"` // 1 正面 2 反面 +} + +type OcrRes struct { + RealName string `json:"realName"` + IDNum string `json:"iDNum"` + Path string `json:"path"` + Age int `json:"age"` + Birthday string `json:"birthday"` + Sex string `json:"sex"` + ExpirationDate string `json:"expirationDate"` +} + +const ( + ocr_client_id = "cLg2dUH1pqopsj22tShw8FQU" + ocr_client_secret = "diriz5PmhLOB8hwE4KnEEiaBMV6WfBR1" +) + +type OrcRes struct { + Path string `json:"path"` + Name string `json:"name"` + IdCard string `json:"idCard"` + Age int `json:"age"` + Birthday string `json:"birthday"` + Sex string `json:"sex"` + ExpirationDate string `json:"expirationDate"` +} + +type OcrGetIdCardRes struct { + WordsResult WordsResult `json:"words_result"` + IdcardNumberType int `json:"idcard_number_type"` + WordsResultNum int `json:"words_result_num"` + ImageStatus string `json:"image_status"` + LogId uint64 `json:"log_id"` +} +type WordsResult struct { + Name WordsResultDetail `json:"姓名"` + Address WordsResultDetail `json:"住址"` + IdCard WordsResultDetail `json:"公民身份号码"` + Birthday WordsResultDetail `json:"出生"` + Sex WordsResultDetail `json:"性别"` + ExpirationDate WordsResultDetail `json:"失效日期"` +} +type WordsResultDetail struct { + Words string +} + +type AccessToken struct { + Refresh_token string `json:"refresh_token"` + Expires_in uint64 `json:"expires_in"` + Scope string `json:"scope"` + Session_key string `json:"session_key"` + Access_token string `json:"access_token"` + Session_secret string `json:"session_secret"` + Error string `json:"error"` + Error_description string `json:"error_description"` +} + +func OcrGetIdCard(image string, side string) (*OrcRes, error) { + var ( + accesstoken string + response string + err error + ) + if accesstoken, err = GetOcrAccessToken(); err != nil { + fmt.Println(err.Error()) + return nil, err + } + if response, err = PostForm("https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token=["+accesstoken+"]", url.Values{"image": {image}, "id_card_side": {side}}); err != nil { + fmt.Println(err.Error()) + return nil, err + } + var res OcrGetIdCardRes + if err = json.Unmarshal([]byte(response), &res); err != nil { + return nil, err + } + fmt.Printf("%+v\n", res) + switch res.ImageStatus { + case "normal": + + case "reversed_side": + return nil, errors.New("身份证正反面颠倒") + case "non_idcard": + return nil, errors.New("上传的图片中不包含身份证") + case "blurred": + return nil, errors.New("身份证模糊") + case "other_type_card": + return nil, errors.New("其他类型证照") + case "over_exposure": + return nil, errors.New("身份证关键字段反光或过曝") + case "over_dark": + return nil, errors.New("身份证欠曝(亮度过低)") + case "unknown": + return nil, errors.New("未知状态") + default: + return nil, errors.New("未知状态") + } + switch res.IdcardNumberType { + case -1: + return nil, errors.New("身份证正面所有字段全为空") + case 0: + return nil, errors.New(" 身份证证号不合法,此情况下不返回身份证证号") + case 1: + case 2: + return nil, errors.New("身份证证号和性别、出生信息都不一致") + case 3: + return nil, errors.New("身份证证号和出生信息不一致") + case 4: + return nil, errors.New("身份证证号和性别信息不一致") + default: + return nil, errors.New("未知状态") + } + var result OrcRes + fmt.Println(res.WordsResult.Name.Words) + if side == "front" { + result.Name = res.WordsResult.Name.Words + result.Birthday = res.WordsResult.Birthday.Words + result.IdCard = res.WordsResult.IdCard.Words + result.Sex = res.WordsResult.Sex.Words + result.Path = res.WordsResult.Address.Words + var age int + + birYear, _ := strconv.Atoi(result.Birthday[0:4]) + birMonth, _ := strconv.Atoi(result.Birthday[4:6]) + fmt.Println(birYear) + fmt.Println(time.Now().Year()) + age = time.Now().Year() - birYear + + if int(time.Now().Month()) < birMonth { + age-- + } + + result.Age = age + } else { + expirationDate := res.WordsResult.ExpirationDate.Words[:4] + "-" + res.WordsResult.ExpirationDate.Words[4:6] + "-" + res.WordsResult.ExpirationDate.Words[6:8] + result.ExpirationDate = expirationDate + } + + return &result, nil +} + +func (m *OcrRes) CheckIdAndName() { + if utf8.RuneCountInString(m.RealName) == 18 && utf8.RuneCountInString(m.IDNum) != 18 { + m.IDNum, m.RealName = m.RealName, m.IDNum + } +} + +func GetOcrAccessToken() (string, error) { + var ( + resObj AccessToken + err error + // daoAccessToken entity.AccessToken + ) + if expires_in == 0 || expires_in < uint64(time.Now().Unix()) { + fmt.Println(1) + // if daoAccessToken, err = dao.GetAccessToken("baidu", "accesstoken"); err != nil { //查询是否有记录 + // return "", err + // } + // fmt.Println(2) + if resObj, err = getOcrAccessToken(); err != nil { //从链上获取 + return "", err + } + // var tmp = entity.AccessToken{ + // Platform: "baidu", + // Types: "accesstoken", + // Txt: resObj.Access_token, + // Expires_in: uint64(time.Now().Unix()) + resObj.Expires_in, + // } + // if daoAccessToken.Uid == 0 { + // if err = dao.AddAccessToken(tmp); err != nil { + // return "", err + // } + // } else { + // if _, err = dao.UpdateAccessToken(tmp); err != nil { + // return "", err + // } + // } + accessToken = resObj.Access_token + expires_in = resObj.Expires_in + } + return accessToken, nil + +} + +func getOcrAccessToken() (AccessToken, error) { + var ( + resObj AccessToken + err error + ) + url := "https://aip.baidubce.com/oauth/2.0/token" + urlReq := "?grant_type=" + grant_type + "&client_id=" + ocr_client_id + "&client_secret=" + ocr_client_secret + fmt.Println(urlReq) + res := Get(url + urlReq) + if err = json.Unmarshal([]byte(res), &resObj); err != nil { + return resObj, err + } + if resObj.Error != "" { + return resObj, errors.New(resObj.Error_description) + } + return resObj, err +} + +func PostForm(urlStr string, data url.Values) (string, error) { + resp, err := http.PostForm(urlStr, data) + + if err != nil { + // handle error + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + // handle error + } + return string(body), err +} + +func Get(url string) string { + + // 超时时间:5秒 + client := &http.Client{Timeout: 5 * time.Second} + resp, err := client.Get(url) + if err != nil { + panic(err) + } + defer resp.Body.Close() + var buffer [512]byte + result := bytes.NewBuffer(nil) + for { + n, err := resp.Body.Read(buffer[0:]) + result.Write(buffer[0:n]) + if err != nil && err == io.EOF { + break + } else if err != nil { + panic(err) + } + } + + return result.String() +} diff --git a/pkg/router/router.go b/pkg/router/router.go index 279ef42..be8db2f 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -37,7 +37,7 @@ func NewRouter() *gin.Engine { registerAuth.POST("check_by_phone", service.CheckByPhone) //检索手机号 registerAuth.POST("save_register_info", service.SaveRegister) //保存 registerAuth.POST("export_register", service.ExportRegister) //导出 - registerAuth.POST("scan_id_card", service.GetCardIdWithImg) //扫描身份证图片获取信息 + registerAuth.POST("scan_id_card", service.OcrBase64) //扫描身份证图片获取信息 } //静态文件 diff --git a/pkg/service/common/common.go b/pkg/service/common/common.go index 6f2d87e..eb5e2f2 100644 --- a/pkg/service/common/common.go +++ b/pkg/service/common/common.go @@ -242,7 +242,6 @@ func PutBos(filePath string, mediaType string, needRemove bool) (url string, err err = errors.New(msg.ErrorUploadBos) return } - //url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName) url = fmt.Sprintf("%s/%s", config.Data.Bos.CdnHost, objectName) return } @@ -265,7 +264,6 @@ func quickBos(file *multipart.FileHeader, mediaType string, mask string, source logger.Errorf("quickBos err", err) return } - //url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName) url = fmt.Sprintf("%s/%s", config.Data.Bos.CdnHost, objectName) return } diff --git a/pkg/service/register_record.go b/pkg/service/register_record.go index 7dcfd8a..6f8fa73 100644 --- a/pkg/service/register_record.go +++ b/pkg/service/register_record.go @@ -2,9 +2,9 @@ package service import ( "context" + "encoding/base64" "fmt" "github.com/dubbogo/gost/log/logger" - "github.com/exhibition-main/api/artist" "github.com/exhibition-main/api/exhibition" "github.com/exhibition-main/internal/config" "github.com/exhibition-main/internal/model" @@ -13,6 +13,9 @@ import ( "github.com/exhibition-main/pkg/logic" "github.com/exhibition-main/pkg/utils" "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "io/ioutil" + "net/http" "time" ) @@ -131,23 +134,64 @@ func ExportRegister(c *gin.Context) { } -func GetCardIdWithImg(c *gin.Context) { - var listReq artist.GetCardIdWithImgReq - if err := c.ShouldBind(&listReq); err != nil { - logger.Errorf("GetCardIdWithImg ShouldBind err", err) - response.ResponseQuickMsg(c, msg.Fail, err.Error(), nil) +func OcrBase64(c *gin.Context) { + + var req model.OcrQuery + + if err := c.ShouldBindBodyWith(&req, binding.JSON); err != nil { + logger.Errorf("OcrBase64 ShouldBind err", err) + response.ResponseQuickMsg(c, msg.Fail, msg.INVALID_PARAMS, nil) return } - if err := listReq.Validate(); err != nil { - err = utils.SubstrError(err) - response.ResponseQuickMsg(c, msg.Fail, err.Error(), nil) + + img := req.IdCardUrl + + response1, err := http.Get(img) + if err != nil { + fmt.Println("网络请求错误:", err) + return + } + + defer response1.Body.Close() + + // 读取图片数据 + imageData, err := ioutil.ReadAll(response1.Body) + if err != nil { + fmt.Println("读取图片数据错误:", err) return } - resp, err := GrpcArtistImpl.GetCardIdWithImg(context.Background(), &listReq) + + // 将图片数据转换为base64编码 + base64Data := base64.StdEncoding.EncodeToString(imageData) + fmt.Println(base64Data) + + side := "" + if req.Side == 1 { + side = "front" + } else { + side = "back" + } + result, err := model.OcrGetIdCard(base64Data, side) if err != nil { response.ResponseQuickMsg(c, msg.Fail, err.Error(), nil) return } - response.ResponseQuickMsg(c, msg.Ok, resp.Msg, resp) + + res := model.OcrRes{} + if side == "front" { + res.IDNum = result.IdCard + res.RealName = result.Name + res.Path = result.Path + res.Age = result.Age + res.Birthday = result.Birthday + res.Sex = result.Sex + fmt.Println("身份证和名字", res.IDNum, res.RealName) + res.CheckIdAndName() + fmt.Println("身份证和名字", res.IDNum, res.RealName) + } else { + res.ExpirationDate = result.ExpirationDate + } + + response.ResponseQuickMsg(c, msg.Ok, "操作成功", res) return }