feat: 植物识别

This commit is contained in:
Blizzard
2026-02-10 12:35:46 +08:00
parent e612234c91
commit 556ab6baff
24 changed files with 2745 additions and 11473 deletions
+1
View File
@@ -6,4 +6,5 @@ type ServiceGroup struct {
PostService
WikiClassService
WikiService
OcrService
}
+78
View File
@@ -0,0 +1,78 @@
package plant
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"strings"
"sundynix-go/global"
"sundynix-go/model/plant/response"
"sundynix-go/pkg/httpclient"
"go.uber.org/zap"
)
type OcrService struct{}
// ClassifyPlant 植物识别
func (s *OcrService) ClassifyPlant(file multipart.File, header *multipart.FileHeader) (response.PlantRecognitionResponse, error) {
reqUrl := "https://aip.baidubce.com/rest/2.0/image-classify/v1/plant?access_token=" + getAccessToken()
// 3. 读取文件的全部字节
fileBytes, err := io.ReadAll(file)
if err != nil {
global.Logger.Error("读取文件失败!", zap.Error(err))
}
// 4. 将字节流编码为 Base64 字符串(可选 URLEncoding,根据场景选择)
// 去掉编码头进行urlencode
base64Str := base64.StdEncoding.EncodeToString(fileBytes)
escapedBase64 := url.QueryEscape(base64Str)
payload := strings.NewReader("image=" + escapedBase64 + "&baike_num=1")
myHttpClient := httpclient.GetClient()
req, err := http.NewRequest("POST", reqUrl, payload)
if err != nil {
global.Logger.Error("创建请求失败!", zap.Error(err))
return response.PlantRecognitionResponse{}, err
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Accept", "application/json")
resp, err := myHttpClient.Do(req)
if err != nil {
global.Logger.Error("请求百度接口失败!", zap.Error(err))
return response.PlantRecognitionResponse{}, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
global.Logger.Error("解析百度接口响应失败!", zap.Error(err))
return response.PlantRecognitionResponse{}, err
}
// 3. 解析JSON到结构体
var plantResp response.PlantRecognitionResponse
if err = json.Unmarshal(body, &plantResp); err != nil {
global.Logger.Error("解析识别JSON失败!", zap.Error(err))
}
return plantResp, err
}
func getAccessToken() string {
rpcUrl := "https://aip.baidubce.com/oauth/2.0/token"
postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", global.Config.BaiduImgClassify.ApiKey, global.Config.BaiduImgClassify.SecretKey)
resp, err := http.Post(rpcUrl, "application/x-www-form-urlencoded", strings.NewReader(postData))
if err != nil {
fmt.Println(err)
return ""
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return ""
}
accessTokenObj := map[string]any{}
_ = json.Unmarshal([]byte(body), &accessTokenObj)
return accessTokenObj["access_token"].(string)
}
+61
View File
@@ -111,6 +111,67 @@ func (s *PostService) PostPage(req plantReq.PostPage, userId string) (list inter
return posts, total, err
}
// MyPost 我的帖子
func (s *PostService) MyPost(req plantReq.PostPage, userId string) (list interface{}, total int64, err error) {
limit := req.PageSize
offset := req.PageSize * (req.Current - 1)
db := global.DB.Model(&plant.Post{}).
Preload("ImgList", func(db *gorm.DB) *gorm.DB {
return db.Order("created_at desc")
}).
Preload("Publisher", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
}).
Preload("LikeList", func(db *gorm.DB) *gorm.DB {
return db.Preload("Liker", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
})
}).
Preload("CommentList", func(db *gorm.DB) *gorm.DB {
return db.Preload("Commentator", func(db *gorm.DB) *gorm.DB {
return db.Preload("Avatar")
})
})
var posts []plant.Post
db = db.Where("user_id = ?", userId)
if req.Title != "" {
db = db.Where("title like ?", "%"+req.Title+"%")
}
//todo 审核帖子
err = db.Count(&total).Error
if err != nil {
return
}
err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&posts).Error
// 优化 N+1 查询
var postIds []string
for _, v := range posts {
postIds = append(postIds, v.Id)
}
// 批量查询当前用户点赞的记录
var postLikeList []*plant.PostLike
err = global.DB.Where("user_id = ? and post_id in ?", userId, postIds).Find(&postLikeList).Error
if err != nil {
return
}
// 构建id映射
likesMap := make(map[string]bool)
for _, v := range postLikeList {
likesMap[v.PostId] = true
}
// 是否点赞
for i := range posts {
if likesMap[posts[i].Id] {
posts[i].HasLiked = 1
} else {
posts[i].HasLiked = 0
}
}
return posts, total, err
}
// LikePost 点赞帖或取消赞
func (s *PostService) LikePost(userId, postId, class string) error {
// class = 1点赞
+2 -2
View File
@@ -153,7 +153,7 @@ func (s *WikiService) WikiPage(req plantReq.WikiPage) (list interface{}, total i
limit := req.PageSize
offset := req.PageSize * (req.Current - 1)
db := global.DB.Model(&plant.Wiki{}).Preload("ImgList", func(db *gorm.DB) *gorm.DB {
return db.Order("created_at desc").Limit(1)
return db.Order("created_at desc")
}).Preload("Classes", func(db *gorm.DB) *gorm.DB {
return db.Order("created_at desc")
})
@@ -165,7 +165,7 @@ func (s *WikiService) WikiPage(req plantReq.WikiPage) (list interface{}, total i
db = db.Where("is_hot = ?", *req.IsHot)
}
if len(req.ClassIdIs) > 0 {
db = db.Joins("inner join sundynix_wiki_class on sundynix_wiki_class.class.id = sundynix_wiki.id").
db = db.Joins("inner join sundynix_wiki_class on sundynix_wiki_class.wiki_id = sundynix_wiki.id").
Where("sundynix_wiki_class.class_id IN (?)", req.ClassIdIs)
}
err = db.Count(&total).Error
+7 -5
View File
@@ -21,8 +21,10 @@ func (s *WikiClassService) AddClass(req plantReq.CreateWikiClass) error {
if !errors.Is(global.DB.Where("name = ?", req.Name).First(&plant.Class{}).Error, gorm.ErrRecordNotFound) {
return errors.New("存在重复分类名称,请修改名称")
}
if errors.Is(global.DB.Where("id = ?", req.OssId).First(&system.Oss{}).Error, gorm.ErrRecordNotFound) {
return errors.New("不存在此图片")
if req.OssId != "" {
if errors.Is(global.DB.Where("id = ?", req.OssId).First(&system.Oss{}).Error, gorm.ErrRecordNotFound) {
return errors.New("不存在此图片")
}
}
return global.DB.Create(&plant.Class{
Name: req.Name,
@@ -42,8 +44,8 @@ func (s *WikiClassService) UpdateClass(req plantReq.UpdateWikiClass) error {
func (s *WikiClassService) ClassPage(req common.PageInfo) (list interface{}, total int64, err error) {
limit := req.PageSize
offset := req.PageSize * (req.Current - 1)
db := global.DB.Model(&plant.Class{}).Preload("Oss")
var classes []*plant.Class
db := global.DB.Model(&plant.Class{})
var classes []plant.Class
err = db.Count(&total).Error
if err != nil {
return
@@ -65,7 +67,7 @@ func (s *WikiClassService) DeleteClass(req common.IdsReq) error {
// ClassList 列表
func (s *WikiClassService) ClassList() (list interface{}, err error) {
var classes []plant.Class
err = global.DB.Order("created_at desc").Find(&classes).Error
err = global.DB.Preload("Oss").Order("created_at desc").Find(&classes).Error
if err != nil {
return
}