// Code scaffolded by goctl. Safe to edit. // goctl 1.10.1 package ocr import ( "context" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" "sundynix-micro-go/app/plant/api/internal/svc" "sundynix-micro-go/app/plant/api/internal/types" plantModel "sundynix-micro-go/app/plant/model" "github.com/zeromicro/go-zero/core/logx" ) type OcrClassifyLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } // OCR植物识别 func NewOcrClassifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *OcrClassifyLogic { return &OcrClassifyLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } // baiduTokenResp 百度 token 响应 type baiduTokenResp struct { AccessToken string `json:"access_token"` } // OcrClassify 调用百度 AI 植物识别接口 func (l *OcrClassifyLogic) OcrClassify(req *types.OcrReq) (interface{}, error) { // 1. 获取百度 access_token apiKey := l.svcCtx.Config.BaiduOcr.ApiKey secretKey := l.svcCtx.Config.BaiduOcr.SecretKey if apiKey == "" || secretKey == "" { return nil, fmt.Errorf("百度 OCR 未配置 ApiKey/SecretKey") } tokenURL := fmt.Sprintf( "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s", apiKey, secretKey, ) tokenResp, err := http.Post(tokenURL, "application/x-www-form-urlencoded", nil) if err != nil { return nil, fmt.Errorf("获取百度 token 失败: %w", err) } defer tokenResp.Body.Close() tokenBody, _ := io.ReadAll(tokenResp.Body) var tokenObj baiduTokenResp if err = json.Unmarshal(tokenBody, &tokenObj); err != nil || tokenObj.AccessToken == "" { return nil, fmt.Errorf("解析百度 token 失败") } // 2. 调用植物识别接口(传入图片 URL) apiURL := "https://aip.baidubce.com/rest/2.0/image-classify/v1/plant?access_token=" + tokenObj.AccessToken payload := strings.NewReader("url=" + url.QueryEscape(req.ImageUrl) + "&baike_num=1") classifyReq, err := http.NewRequest("POST", apiURL, payload) if err != nil { return nil, fmt.Errorf("创建请求失败: %w", err) } classifyReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") client := &http.Client{} classifyResp, err := client.Do(classifyReq) if err != nil { return nil, fmt.Errorf("调用百度植物识别接口失败: %w", err) } defer classifyResp.Body.Close() body, err := io.ReadAll(classifyResp.Body) if err != nil { return nil, fmt.Errorf("读取识别结果失败: %w", err) } // 3. 解析为结构化识别结果并写入 ClassifyRecord 表 var baiduResp struct { LogId uint64 `json:"log_id"` Result []struct { Score float64 `json:"score"` Name string `json:"name"` BaikeInfo *struct { BaikeUrl string `json:"baike_url"` ImageUrl string `json:"image_url"` Description string `json:"description"` } `json:"baike_info"` } `json:"result"` } _ = json.Unmarshal(body, &baiduResp) if baiduResp.LogId > 0 { var dbResults plantModel.ResultsArray = make(plantModel.ResultsArray, 0, len(baiduResp.Result)) for _, item := range baiduResp.Result { var baikeInfo *plantModel.BaikeInfo if item.BaikeInfo != nil { baikeInfo = &plantModel.BaikeInfo{ BaikeUrl: item.BaikeInfo.BaikeUrl, ImageUrl: item.BaikeInfo.ImageUrl, Description: item.BaikeInfo.Description, } } dbResults = append(dbResults, plantModel.ResultItem{ Score: item.Score, Name: item.Name, BaikeInfo: baikeInfo, }) } userID := fmt.Sprintf("%v", l.ctx.Value("userId")) record := plantModel.ClassifyRecord{ UserID: userID, LogID: baiduResp.LogId, AllResults: dbResults, } if errDb := l.svcCtx.DB.Create(&record).Error; errDb != nil { l.Logger.Errorf("植物识别记录写入数据库失败: %v", errDb) } } // 4. 直接返回百度原始结果 var result interface{} if err = json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("解析识别结果失败: %w", err) } return result, nil }