250 lines
7.2 KiB
Go
250 lines
7.2 KiB
Go
|
package user
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"crypto/rand"
|
||
|
"encoding/base64"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"github.com/qiniu/go-sdk/v7/storagev2/credentials"
|
||
|
"github.com/qiniu/go-sdk/v7/storagev2/http_client"
|
||
|
"github.com/qiniu/go-sdk/v7/storagev2/uploader"
|
||
|
"tianyuan-api/apps/gateway/internal/svc"
|
||
|
"tianyuan-api/apps/gateway/internal/types"
|
||
|
"io"
|
||
|
"math/big"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/zeromicro/go-zero/core/logx"
|
||
|
)
|
||
|
|
||
|
type UploadBusinessLicenseLogic struct {
|
||
|
logx.Logger
|
||
|
ctx context.Context
|
||
|
svcCtx *svc.ServiceContext
|
||
|
}
|
||
|
|
||
|
func NewUploadBusinessLicenseLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadBusinessLicenseLogic {
|
||
|
return &UploadBusinessLicenseLogic{
|
||
|
Logger: logx.WithContext(ctx),
|
||
|
ctx: ctx,
|
||
|
svcCtx: svcCtx,
|
||
|
}
|
||
|
}
|
||
|
func (l *UploadBusinessLicenseLogic) UploadBusinessLicense(r *http.Request) (resp *types.UploadBusinessLicenseResp, err error) {
|
||
|
// 1. 解析文件上传表单,限制文件大小
|
||
|
err = r.ParseMultipartForm(4 << 20) // 限制最大文件大小为4MB
|
||
|
if err != nil {
|
||
|
return nil, errors.New("图片不能超过4MB")
|
||
|
}
|
||
|
|
||
|
// 2. 获取上传的文件
|
||
|
file, handler, err := r.FormFile("file")
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer file.Close()
|
||
|
|
||
|
// 3. 创建临时文件保存上传的内容
|
||
|
tempFile, err := os.CreateTemp("", "upload-*.jpg")
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("创建临时文件失败: %v", err)
|
||
|
}
|
||
|
defer tempFile.Close()
|
||
|
|
||
|
// 4. 将文件内容保存到临时文件
|
||
|
_, err = io.Copy(tempFile, file)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("保存文件失败: %v", err)
|
||
|
}
|
||
|
|
||
|
// 5. 调用百度智能云进行营业执照识别
|
||
|
tempFilePath := tempFile.Name()
|
||
|
fileBytes, err := os.ReadFile(tempFilePath)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("读取临时文件失败: %v", err)
|
||
|
}
|
||
|
|
||
|
licenseInfo, err := l.RecognizeBusinessLicense(fileBytes)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("营业执照识别失败: %v", err)
|
||
|
}
|
||
|
|
||
|
// 6. 生成新的文件名
|
||
|
newFileName := l.GenerateFileName("business_license_", handler.Filename)
|
||
|
|
||
|
// 7. 确认是营业执照后,将图片上传到七牛云
|
||
|
imageUrl, err := l.UploadToQiniu(tempFilePath, newFileName)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("上传图片到七牛云失败: %v", err)
|
||
|
}
|
||
|
|
||
|
// 8. 返回百度智能云的识别信息和图片URL给前端
|
||
|
return &types.UploadBusinessLicenseResp{
|
||
|
Url: imageUrl,
|
||
|
EnterpriseName: licenseInfo["company_name"],
|
||
|
CreditCode: licenseInfo["credit_code"],
|
||
|
LegalPerson: licenseInfo["legal_person"],
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// 百度智能云营业执照识别
|
||
|
func (l *UploadBusinessLicenseLogic) RecognizeBusinessLicense(fileBytes []byte) (map[string]string, error) {
|
||
|
// 获取百度智能云Access Token
|
||
|
accessToken := l.GetAccessToken()
|
||
|
if accessToken == "" {
|
||
|
return nil, errors.New("获取百度智能云Access Token失败")
|
||
|
}
|
||
|
|
||
|
// 调用百度智能云营业执照识别接口
|
||
|
baiduUrl := "https://aip.baidubce.com/rest/2.0/ocr/v1/business_license?access_token=" + accessToken
|
||
|
|
||
|
// 将图片文件进行Base64编码
|
||
|
fileBase64 := base64.StdEncoding.EncodeToString(fileBytes)
|
||
|
fileBase64UrlEncoded := url.QueryEscape(fileBase64)
|
||
|
// 准备POST请求的Payload
|
||
|
payload := strings.NewReader(fmt.Sprintf("image=%s", fileBase64UrlEncoded))
|
||
|
|
||
|
req, err := http.NewRequest("POST", baiduUrl, payload)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||
|
client := &http.Client{}
|
||
|
res, err := client.Do(req)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer res.Body.Close()
|
||
|
|
||
|
body, err := io.ReadAll(res.Body)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// 解析响应体
|
||
|
var result map[string]interface{}
|
||
|
if err := json.Unmarshal(body, &result); err != nil {
|
||
|
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||
|
}
|
||
|
|
||
|
// 检查是否有错误码
|
||
|
if _, exists := result["error_code"]; exists {
|
||
|
return nil, fmt.Errorf("图片解析失败,请上传清晰正确的图片")
|
||
|
}
|
||
|
|
||
|
// 成功,提取所需的字段
|
||
|
wordsResult := result["words_result"].(map[string]interface{})
|
||
|
companyName := wordsResult["单位名称"].(map[string]interface{})["words"].(string)
|
||
|
socialCreditCode := wordsResult["社会信用代码"].(map[string]interface{})["words"].(string)
|
||
|
legalPerson := wordsResult["法人"].(map[string]interface{})["words"].(string)
|
||
|
|
||
|
// 返回提取的信息
|
||
|
return map[string]string{
|
||
|
"company_name": companyName,
|
||
|
"credit_code": socialCreditCode,
|
||
|
"legal_person": legalPerson,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// 获取百度智能云Access Token
|
||
|
func (l *UploadBusinessLicenseLogic) GetAccessToken() string {
|
||
|
apiKey := l.svcCtx.Config.Baidu.ApiKey
|
||
|
secretKey := l.svcCtx.Config.Baidu.SecretKey
|
||
|
|
||
|
baiduUrl := "https://aip.baidubce.com/oauth/2.0/token"
|
||
|
postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", apiKey, secretKey)
|
||
|
resp, err := http.Post(baiduUrl, "application/x-www-form-urlencoded", strings.NewReader(postData))
|
||
|
if err != nil {
|
||
|
return ""
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
body, err := io.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
accessTokenObj := map[string]interface{}{}
|
||
|
_ = json.Unmarshal(body, &accessTokenObj)
|
||
|
return accessTokenObj["access_token"].(string)
|
||
|
}
|
||
|
|
||
|
// 七牛云上传图片
|
||
|
func (l *UploadBusinessLicenseLogic) UploadToQiniu(localFilePath string, fileName string) (string, error) {
|
||
|
// 从配置中获取七牛云的AccessKey和SecretKey
|
||
|
accessKey := l.svcCtx.Config.Qiniu.AccessKey
|
||
|
secretKey := l.svcCtx.Config.Qiniu.SecretKey
|
||
|
bucket := l.svcCtx.Config.Qiniu.Bucket
|
||
|
domain := l.svcCtx.Config.Qiniu.Domain
|
||
|
|
||
|
// 1. 构建上传凭证
|
||
|
mac := credentials.NewCredentials(accessKey, secretKey)
|
||
|
|
||
|
// 2. 构建上传管理器
|
||
|
options := uploader.UploadManagerOptions{
|
||
|
Options: http_client.Options{
|
||
|
Credentials: mac, // 这里传入认证信息
|
||
|
},
|
||
|
}
|
||
|
|
||
|
uploadManager := uploader.NewUploadManager(&options)
|
||
|
|
||
|
objectOptions := &uploader.ObjectOptions{
|
||
|
BucketName: bucket,
|
||
|
ObjectName: &fileName,
|
||
|
FileName: fileName,
|
||
|
}
|
||
|
|
||
|
// 3. 执行文件上传
|
||
|
err := uploadManager.UploadFile(context.Background(), localFilePath, objectOptions, nil)
|
||
|
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
// 返回文件的URL地址
|
||
|
fileUrl := fmt.Sprintf("%s/%s", domain, fileName)
|
||
|
return fileUrl, nil
|
||
|
}
|
||
|
|
||
|
// 生成新的文件名,包含前缀、时间戳和随机数
|
||
|
func (l *UploadBusinessLicenseLogic) GenerateFileName(prefix, originalFileName string) string {
|
||
|
timestamp := time.Now().Format("20060102150405") // 生成时间戳
|
||
|
randomNumber := l.GenerateRandomNumber(4) // 生成4位随机数
|
||
|
fileExtension := l.GetFileExtension(originalFileName) // 获取原文件扩展名
|
||
|
|
||
|
// 返回生成的文件名
|
||
|
return fmt.Sprintf("%s%s_%s%s", prefix, timestamp, randomNumber, fileExtension)
|
||
|
}
|
||
|
|
||
|
// 生成指定长度的随机数
|
||
|
func (l *UploadBusinessLicenseLogic) GenerateRandomNumber(length int) string {
|
||
|
var digits = "0123456789"
|
||
|
result := make([]byte, length)
|
||
|
for i := range result {
|
||
|
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(digits))))
|
||
|
result[i] = digits[n.Int64()]
|
||
|
}
|
||
|
return string(result)
|
||
|
}
|
||
|
|
||
|
// 获取文件扩展名
|
||
|
func (l *UploadBusinessLicenseLogic) GetFileExtension(fileName string) string {
|
||
|
if len(fileName) > 0 {
|
||
|
for i := len(fileName) - 1; i >= 0 && fileName[i] != '.'; i-- {
|
||
|
if i == 0 {
|
||
|
return "" // 无扩展名
|
||
|
}
|
||
|
}
|
||
|
return fileName[strings.LastIndex(fileName, "."):]
|
||
|
}
|
||
|
return ""
|
||
|
}
|