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 "" }