feat: 植物识别
This commit is contained in:
@@ -6,4 +6,5 @@ type ServiceGroup struct {
|
||||
PostService
|
||||
WikiClassService
|
||||
WikiService
|
||||
OcrService
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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点赞
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user