feat: 植物识别
This commit is contained in:
@@ -8,6 +8,7 @@ type ApiGroup struct {
|
|||||||
PostApi
|
PostApi
|
||||||
WikiClassApi
|
WikiClassApi
|
||||||
WikiApi
|
WikiApi
|
||||||
|
OcrApi
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -16,4 +17,5 @@ var (
|
|||||||
postService = service.GroupApp.PlantServiceGroup.PostService
|
postService = service.GroupApp.PlantServiceGroup.PostService
|
||||||
wikiClassService = service.GroupApp.PlantServiceGroup.WikiClassService
|
wikiClassService = service.GroupApp.PlantServiceGroup.WikiClassService
|
||||||
wikiService = service.GroupApp.PlantServiceGroup.WikiService
|
wikiService = service.GroupApp.PlantServiceGroup.WikiService
|
||||||
|
ocrService = service.GroupApp.PlantServiceGroup.OcrService
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sundynix-go/global"
|
||||||
|
"sundynix-go/model/commom/response"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OcrApi struct{}
|
||||||
|
|
||||||
|
// ClassifyPlant
|
||||||
|
// @tags 识别相关
|
||||||
|
// @Summary base64植物识别
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @accept multipart/form-data
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param file formData file true "植物识别"
|
||||||
|
// @Success 200 {object} response.Response{msg=string} "文件OCR"
|
||||||
|
// @router /ocr/base64 [post]
|
||||||
|
func (o *OcrApi) ClassifyPlant(c *gin.Context) {
|
||||||
|
multipartFile, header, err := c.Request.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("接收文件失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg("接收文件失败!", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := ocrService.ClassifyPlant(multipartFile, header)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("植物识别识别!", zap.Error(err))
|
||||||
|
response.FailWithMsg("植物识别失败!", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(res, c)
|
||||||
|
}
|
||||||
@@ -70,6 +70,37 @@ func (a *PostApi) PostPage(c *gin.Context) {
|
|||||||
}, c)
|
}, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MyPost 我的发布
|
||||||
|
// @Tags 帖子
|
||||||
|
// @Summary 我的发布
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @accept json
|
||||||
|
// @Produce application/json
|
||||||
|
// @Param data body plantReq.PostPage true "分页获取帖子列表"
|
||||||
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||||
|
// @Router /post/myPost [post]
|
||||||
|
func (a *PostApi) MyPost(c *gin.Context) {
|
||||||
|
var req plantReq.PostPage
|
||||||
|
err := c.ShouldBindJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
response.FailWithMsg("请求参数错误", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
posts, total, err := postService.MyPost(req, userId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取帖子列表失败", zap.Error(err))
|
||||||
|
response.FailWithMsg("获取帖子列表失败", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(response.PageResult{
|
||||||
|
List: posts,
|
||||||
|
Total: total,
|
||||||
|
Page: req.Current,
|
||||||
|
PageSize: req.PageSize,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
// LikePost 点赞帖子
|
// LikePost 点赞帖子
|
||||||
// @Tags 帖子
|
// @Tags 帖子
|
||||||
// @Summary 点赞帖子
|
// @Summary 点赞帖子
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func (a *WikiClassApi) AddClass(c *gin.Context) {
|
|||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @accept json
|
// @accept json
|
||||||
// @Produce application/json
|
// @Produce application/json
|
||||||
// @Param data body lantReq.UpdateWikiClass true "修改分类"
|
// @Param data body plantReq.UpdateWikiClass true "修改分类"
|
||||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发布成功"}"
|
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发布成功"}"
|
||||||
// @Router /wiki-class/update [post]
|
// @Router /wiki-class/update [post]
|
||||||
func (a *WikiClassApi) UpdateClass(c *gin.Context) {
|
func (a *WikiClassApi) UpdateClass(c *gin.Context) {
|
||||||
|
|||||||
@@ -137,7 +137,14 @@ func (m *MenuApi) GetAllMenuTree(c *gin.Context) {
|
|||||||
// @Success 200 {object} response.Response{data=[]system.Menu,msg=string} "用户菜单数据"
|
// @Success 200 {object} response.Response{data=[]system.Menu,msg=string} "用户菜单数据"
|
||||||
// @Router /menu/getUserMenuTree [get]
|
// @Router /menu/getUserMenuTree [get]
|
||||||
func (m *MenuApi) GetUserMenuTree(c *gin.Context) {
|
func (m *MenuApi) GetUserMenuTree(c *gin.Context) {
|
||||||
|
userId := auth.GetUserId(c)
|
||||||
|
routes, err := menuService.GetUserRoutes(userId)
|
||||||
|
if err != nil {
|
||||||
|
global.Logger.Error("获取用户菜单失败!", zap.Error(err))
|
||||||
|
response.FailWithMsg(err.Error(), c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response.OkWithData(routes, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route
|
// Route
|
||||||
|
|||||||
+13
-5
@@ -1,5 +1,5 @@
|
|||||||
system:
|
system:
|
||||||
addr: 8888
|
addr: 8889
|
||||||
db-type: mysql
|
db-type: mysql
|
||||||
router-prefix: ""
|
router-prefix: ""
|
||||||
enable-captcha: 0
|
enable-captcha: 0
|
||||||
@@ -14,9 +14,14 @@ jwt:
|
|||||||
|
|
||||||
|
|
||||||
# 植趣微信小程序
|
# 植趣微信小程序
|
||||||
|
#mini-program:
|
||||||
|
# app-id: wxb463820bf36dd5d6
|
||||||
|
# app-secret: 731784a74c76c6d31fa00bb847af2c7d
|
||||||
|
|
||||||
|
# 植遇微信小程序
|
||||||
mini-program:
|
mini-program:
|
||||||
app-id: wxb463820bf36dd5d6
|
app-id: wx52dfc635739a9c19
|
||||||
app-secret: 731784a74c76c6d31fa00bb847af2c7d
|
app-secret: 5aaed22f05352b7cd991870de6600bef
|
||||||
|
|
||||||
# 植趣服务号
|
# 植趣服务号
|
||||||
service-account:
|
service-account:
|
||||||
@@ -32,6 +37,9 @@ wechat-pay:
|
|||||||
public-key-path: /Users/blizzard/privateFolder/cert/pub_key.pem # 商户APIv3密钥对应的公钥
|
public-key-path: /Users/blizzard/privateFolder/cert/pub_key.pem # 商户APIv3密钥对应的公钥
|
||||||
notify-url: https://prod.sundynix.cn/api/wechatpay/notify # 微信支付结果通知回调地址
|
notify-url: https://prod.sundynix.cn/api/wechatpay/notify # 微信支付结果通知回调地址
|
||||||
|
|
||||||
|
baidu-img-classify:
|
||||||
|
api-key: hpBfjwy8ifv3qswYGYjUCNKN
|
||||||
|
secret-key: i5aXZdM4XZVuDroBslL0f3uIuwbAyXFS
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
access-key-id: qP5QXP3g6Axw1hkwX21Y
|
access-key-id: qP5QXP3g6Axw1hkwX21Y
|
||||||
@@ -74,8 +82,8 @@ redis:
|
|||||||
- 172.21.0.4:7001
|
- 172.21.0.4:7001
|
||||||
- 172.21.0.2:7002
|
- 172.21.0.2:7002
|
||||||
db: 1
|
db: 1
|
||||||
# name: ""
|
# name: ""
|
||||||
# password: "sundynix"
|
# password: "sundynix"
|
||||||
cluster: false
|
cluster: false
|
||||||
|
|
||||||
zap:
|
zap:
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
type BaiduImgClassify struct {
|
||||||
|
ApiKey string `mapstructure:"api-key" json:"api-key" yaml:"api-key"`
|
||||||
|
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
|
||||||
|
}
|
||||||
+4
-3
@@ -13,7 +13,8 @@ type Config struct {
|
|||||||
RocketMQConfig RocketMQConfig `mapstructure:"rocket-mq" json:"rocket-mq" yaml:"rocket-mq"`
|
RocketMQConfig RocketMQConfig `mapstructure:"rocket-mq" json:"rocket-mq" yaml:"rocket-mq"`
|
||||||
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
|
TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
|
||||||
|
|
||||||
MiniProgram MiniProgram `mapstructure:"mini-program" json:"mini-program" yaml:"mini-program"` //小程序
|
MiniProgram MiniProgram `mapstructure:"mini-program" json:"mini-program" yaml:"mini-program"` //小程序
|
||||||
ServiceAccount ServiceAccount `mapstructure:"service-account" json:"service-account" yaml:"service-account"` //服务号
|
ServiceAccount ServiceAccount `mapstructure:"service-account" json:"service-account" yaml:"service-account"` //服务号
|
||||||
WechatPay WechatPay `mapstructure:"wechat-pay" json:"wechat-pay" yaml:"wechat-pay"` //微信支付
|
WechatPay WechatPay `mapstructure:"wechat-pay" json:"wechat-pay" yaml:"wechat-pay"` //微信支付
|
||||||
|
BaiduImgClassify BaiduImgClassify `mapstructure:"baidu-img-classify" json:"baidu-img-classify" yaml:"baidu-img-classify"` // 百度植物识别
|
||||||
}
|
}
|
||||||
|
|||||||
+920
-4347
File diff suppressed because it is too large
Load Diff
+920
-4347
File diff suppressed because it is too large
Load Diff
+636
-2738
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,7 @@ func Routers() {
|
|||||||
plantGroup.InitPostRouter(NeedAuthGroup) // 帖子相关
|
plantGroup.InitPostRouter(NeedAuthGroup) // 帖子相关
|
||||||
plantGroup.InitWikiClassRouter(NeedAuthGroup) //百科分类
|
plantGroup.InitWikiClassRouter(NeedAuthGroup) //百科分类
|
||||||
plantGroup.InitWikiRouter(NeedAuthGroup) //百科
|
plantGroup.InitWikiRouter(NeedAuthGroup) //百科
|
||||||
|
plantGroup.InitOcrRouter(NeedAuthGroup) // ocr识别
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
// @title Swagger API接口文档
|
// @title Swagger API接口文档
|
||||||
// @version v1.0.0
|
// @version v1.0.0
|
||||||
// @description 使用gin + gorm进行极速开发的全栈开发基础平台
|
// @description 使用gin + gorm进行极速开发的全栈开发基础平台
|
||||||
// @securityDefinitions.apikey ApiKeyAuth
|
// @securityDefinitions.apikey BearerAuth
|
||||||
// @in header
|
// @in header
|
||||||
// @name Authorization
|
// @name Authorization
|
||||||
// @BasePath /
|
// @BasePath /
|
||||||
|
|||||||
@@ -12,6 +12,4 @@ type Oss struct {
|
|||||||
Key string `json:"key" form:"key" gorm:"column:key;comment:文件key"`
|
Key string `json:"key" form:"key" gorm:"column:key;comment:文件key"`
|
||||||
Suffix string `json:"suffix" form:"suffix" gorm:"column:suffix;comment:文件后缀"`
|
Suffix string `json:"suffix" form:"suffix" gorm:"column:suffix;comment:文件后缀"`
|
||||||
MD5 string `json:"md5" form:"md5" gorm:"column:md5;comment:文件md5"`
|
MD5 string `json:"md5" form:"md5" gorm:"column:md5;comment:文件md5"`
|
||||||
Height int `json:"height" form:"height" gorm:"column:height;comment:图片高度"`
|
|
||||||
Width int `json:"width" form:"width" gorm:"column:width;comment:图片宽度"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type RouterGroup struct {
|
|||||||
PostRouter
|
PostRouter
|
||||||
WikiClassRouter
|
WikiClassRouter
|
||||||
WikiRouter
|
WikiRouter
|
||||||
|
OcrRouter
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化路由
|
// 初始化路由
|
||||||
@@ -17,4 +18,5 @@ var (
|
|||||||
postApi = v1.ApiGroupApp.PlantApiGroup.PostApi
|
postApi = v1.ApiGroupApp.PlantApiGroup.PostApi
|
||||||
wikiClassApi = v1.ApiGroupApp.PlantApiGroup.WikiClassApi
|
wikiClassApi = v1.ApiGroupApp.PlantApiGroup.WikiClassApi
|
||||||
wikiApi = v1.ApiGroupApp.PlantApiGroup.WikiApi
|
wikiApi = v1.ApiGroupApp.PlantApiGroup.WikiApi
|
||||||
|
ocrApi = v1.ApiGroupApp.PlantApiGroup.OcrApi
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package plant
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
type OcrRouter struct{}
|
||||||
|
|
||||||
|
func (c *OcrRouter) InitOcrRouter(Router *gin.RouterGroup) {
|
||||||
|
badgeRouter := Router.Group("classify")
|
||||||
|
{
|
||||||
|
badgeRouter.POST("/plant", ocrApi.ClassifyPlant)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ func (p *PostRouter) InitPostRouter(Router *gin.RouterGroup) {
|
|||||||
// 帖子
|
// 帖子
|
||||||
postRouter.POST("publish", postApi.PublishPost) // 发布帖子
|
postRouter.POST("publish", postApi.PublishPost) // 发布帖子
|
||||||
postRouter.POST("page", postApi.PostPage) // 帖子列表
|
postRouter.POST("page", postApi.PostPage) // 帖子列表
|
||||||
|
postRouter.POST("myPost", postApi.MyPost) // 我的发布
|
||||||
postRouter.GET("like", postApi.LikePost) // 点赞或者取消点赞
|
postRouter.GET("like", postApi.LikePost) // 点赞或者取消点赞
|
||||||
postRouter.POST("comment", postApi.CommentPost) // 评论
|
postRouter.POST("comment", postApi.CommentPost) // 评论
|
||||||
//postRouter.POST("deleteComment", postApi.delementComment) // 取消评论
|
//postRouter.POST("deleteComment", postApi.delementComment) // 取消评论
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ type ServiceGroup struct {
|
|||||||
PostService
|
PostService
|
||||||
WikiClassService
|
WikiClassService
|
||||||
WikiService
|
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
|
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 点赞帖或取消赞
|
// LikePost 点赞帖或取消赞
|
||||||
func (s *PostService) LikePost(userId, postId, class string) error {
|
func (s *PostService) LikePost(userId, postId, class string) error {
|
||||||
// class = 1点赞
|
// class = 1点赞
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ func (s *WikiService) WikiPage(req plantReq.WikiPage) (list interface{}, total i
|
|||||||
limit := req.PageSize
|
limit := req.PageSize
|
||||||
offset := req.PageSize * (req.Current - 1)
|
offset := req.PageSize * (req.Current - 1)
|
||||||
db := global.DB.Model(&plant.Wiki{}).Preload("ImgList", func(db *gorm.DB) *gorm.DB {
|
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 {
|
}).Preload("Classes", func(db *gorm.DB) *gorm.DB {
|
||||||
return db.Order("created_at desc")
|
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)
|
db = db.Where("is_hot = ?", *req.IsHot)
|
||||||
}
|
}
|
||||||
if len(req.ClassIdIs) > 0 {
|
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)
|
Where("sundynix_wiki_class.class_id IN (?)", req.ClassIdIs)
|
||||||
}
|
}
|
||||||
err = db.Count(&total).Error
|
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) {
|
if !errors.Is(global.DB.Where("name = ?", req.Name).First(&plant.Class{}).Error, gorm.ErrRecordNotFound) {
|
||||||
return errors.New("存在重复分类名称,请修改名称")
|
return errors.New("存在重复分类名称,请修改名称")
|
||||||
}
|
}
|
||||||
if errors.Is(global.DB.Where("id = ?", req.OssId).First(&system.Oss{}).Error, gorm.ErrRecordNotFound) {
|
if req.OssId != "" {
|
||||||
return errors.New("不存在此图片")
|
if errors.Is(global.DB.Where("id = ?", req.OssId).First(&system.Oss{}).Error, gorm.ErrRecordNotFound) {
|
||||||
|
return errors.New("不存在此图片")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return global.DB.Create(&plant.Class{
|
return global.DB.Create(&plant.Class{
|
||||||
Name: req.Name,
|
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) {
|
func (s *WikiClassService) ClassPage(req common.PageInfo) (list interface{}, total int64, err error) {
|
||||||
limit := req.PageSize
|
limit := req.PageSize
|
||||||
offset := req.PageSize * (req.Current - 1)
|
offset := req.PageSize * (req.Current - 1)
|
||||||
db := global.DB.Model(&plant.Class{}).Preload("Oss")
|
db := global.DB.Model(&plant.Class{})
|
||||||
var classes []*plant.Class
|
var classes []plant.Class
|
||||||
err = db.Count(&total).Error
|
err = db.Count(&total).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -65,7 +67,7 @@ func (s *WikiClassService) DeleteClass(req common.IdsReq) error {
|
|||||||
// ClassList 列表
|
// ClassList 列表
|
||||||
func (s *WikiClassService) ClassList() (list interface{}, err error) {
|
func (s *WikiClassService) ClassList() (list interface{}, err error) {
|
||||||
var classes []plant.Class
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"io"
|
"io"
|
||||||
@@ -58,24 +57,7 @@ func (o *OssService) Upload(multipartFile multipart.File, header *multipart.File
|
|||||||
}
|
}
|
||||||
//文件后缀
|
//文件后缀
|
||||||
s := strings.Split(header.Filename, ".")
|
s := strings.Split(header.Filename, ".")
|
||||||
height := 0
|
|
||||||
width := 0
|
|
||||||
//mime类型
|
|
||||||
contentType := header.Header.Get("Content-Type")
|
|
||||||
allowedImageTypes := map[string]bool{
|
|
||||||
"image/jpeg": true,
|
|
||||||
"image/png": true,
|
|
||||||
}
|
|
||||||
isPossibleImage := allowedImageTypes[contentType]
|
|
||||||
//仅当可能是图片时 才计算图片宽高
|
//仅当可能是图片时 才计算图片宽高
|
||||||
if isPossibleImage {
|
|
||||||
img, _, err1 := image.Decode(multipartFile)
|
|
||||||
if err1 != nil {
|
|
||||||
return file, err
|
|
||||||
}
|
|
||||||
height = img.Bounds().Max.Y
|
|
||||||
width = img.Bounds().Max.X
|
|
||||||
}
|
|
||||||
f := system.Oss{
|
f := system.Oss{
|
||||||
Key: key, // uploads/2025-09-17/
|
Key: key, // uploads/2025-09-17/
|
||||||
Name: header.Filename,
|
Name: header.Filename,
|
||||||
@@ -83,8 +65,6 @@ func (o *OssService) Upload(multipartFile multipart.File, header *multipart.File
|
|||||||
Tag: s[len(s)-1],
|
Tag: s[len(s)-1],
|
||||||
Url: filepath, // http://127.0.0.1:9000/planting-fun/uploads/2025-09-17/211476f3837fc7acbaebf0f901c1bd68.png
|
Url: filepath, // http://127.0.0.1:9000/planting-fun/uploads/2025-09-17/211476f3837fc7acbaebf0f901c1bd68.png
|
||||||
MD5: hashString,
|
MD5: hashString,
|
||||||
Height: height,
|
|
||||||
Width: width,
|
|
||||||
}
|
}
|
||||||
return f, global.DB.Create(&f).Error
|
return f, global.DB.Create(&f).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func (s *MenuService) GetUserRoutes(userId string) (menus []*system.Menu, err er
|
|||||||
var menuIds []string
|
var menuIds []string
|
||||||
err = global.DB.Model(&system.RoleMenu{}).Where("role_id in ?", roleIds).Pluck("menu_id", &menuIds).Error
|
err = global.DB.Model(&system.RoleMenu{}).Where("role_id in ?", roleIds).Pluck("menu_id", &menuIds).Error
|
||||||
var menuList []*system.Menu
|
var menuList []*system.Menu
|
||||||
err = global.DB.Model(&system.Menu{}).Where("id in ?", menuIds).Order("sort asc").Find(&menuList).Error
|
err = global.DB.Model(&system.Menu{}).Where("id in ? and category = 1 ", menuIds).Order("sort asc").Find(&menuList).Error
|
||||||
return buildMenuTree(menuList), nil
|
return buildMenuTree(menuList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user