♻️ use minio instead of mongodb

This commit is contained in:
2025-02-05 18:08:29 +08:00
parent a3d4f2c8d1
commit d2b0d7b42e
53 changed files with 2446 additions and 702 deletions

View File

@@ -255,35 +255,35 @@ service auth {
type (
// 评论提交请求参数
CommentRequest {
Content string `json:"content"`
Images []string `json:"images,optional"`
TopicId string `json:"topic_id"`
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
Content string `json:"content"`
Images string `json:"images,optional"`
TopicId string `json:"topic_id"`
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
}
// 回复评论提交请求参数
ReplyCommentRequest {
Content string `json:"content"`
Images []string `json:"images,optional"`
TopicId string `json:"topic_id" `
ReplyId int64 `json:"reply_id" `
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
Content string `json:"content"`
Images string `json:"images,optional"`
TopicId string `json:"topic_id" `
ReplyId int64 `json:"reply_id" `
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
}
// 回复回复请求参数
ReplyReplyRequest {
Content string `json:"content"`
Images []string `json:"images,optional"`
TopicId string `json:"topic_id"`
ReplyTo int64 `json:"reply_to"`
ReplyId int64 `json:"reply_id"`
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
Content string `json:"content"`
Images string `json:"images,optional"`
TopicId string `json:"topic_id"`
ReplyTo int64 `json:"reply_to"`
ReplyId int64 `json:"reply_id"`
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
}
// 评论列表请求参数
CommentListRequest {
@@ -314,26 +314,26 @@ type (
type (
// CommentContent 评论内容
CommentContent {
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
Level int64 `json:"level,omitempty" default:"0"`
Id int64 `json:"id"`
UserId string `json:"user_id"`
TopicId string `json:"topic_id"`
Content string `json:"content"`
ReplyTo int64 `json:"reply_to,omitempty"`
ReplyId int64 `json:"reply_id,omitempty"`
ReplyUser string `json:"reply_user,omitempty"`
ReplyNickname string `json:"reply_nickname,omitempty"`
IsAuthor int64 `json:"is_author"`
Likes int64 `json:"likes"`
ReplyCount int64 `json:"reply_count"`
CreatedTime string `json:"created_time"`
Location string `json:"location"`
Browser string `json:"browser"`
OperatingSystem string `json:"operating_system"`
IsLiked bool `json:"is_liked" default:"false"`
Images []string `json:"images,omitempty"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
Level int64 `json:"level,omitempty" default:"0"`
Id int64 `json:"id"`
UserId string `json:"user_id"`
TopicId string `json:"topic_id"`
Content string `json:"content"`
ReplyTo int64 `json:"reply_to,omitempty"`
ReplyId int64 `json:"reply_id,omitempty"`
ReplyUser string `json:"reply_user,omitempty"`
ReplyNickname string `json:"reply_nickname,omitempty"`
IsAuthor int64 `json:"is_author"`
Likes int64 `json:"likes"`
ReplyCount int64 `json:"reply_count"`
CreatedTime string `json:"created_time"`
Location string `json:"location"`
Browser string `json:"browser"`
OperatingSystem string `json:"operating_system"`
IsLiked bool `json:"is_liked" default:"false"`
Images string `json:"images,omitempty"`
}
// CommentListPageResponse 评论返回值
CommentListPageResponse {
@@ -365,7 +365,7 @@ type (
timeout: 10s // 超时时间
maxBytes: 1048576 // 最大请求大小
signature: false // 是否开启签名验证
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,AuthorizationMiddleware,NonceMiddleware // 注册中间件
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware // 注册中间件
MaxConns: true // 是否开启最大连接数限制
Recover: true // 是否开启自动恢复
jwt: Auth // 是否开启jwt验证
@@ -427,6 +427,28 @@ type (
Bucket string `json:"bucket"`
Region string `json:"region"`
}
FaceSampleLibrary {
ID int64 `json:"id"`
FaceName string `json:"face_name"`
FaceImage string `json:"face_image"`
}
FaceSampleLibraryListRequest {
Type int64 `json:"type"`
}
FaceSampleLibraryListResponse {
faces []FaceSampleLibrary `json:"faces"`
}
ModifyFaceNameRequestAndResponse {
ID int64 `json:"id"`
FaceName string `json:"face_name"`
}
ModifyFaceTypeRequest {
IDs []int64 `json:"ids"`
FaceType int64 `json:"face_type"`
}
ModifyFaceTypeResponse {
result string `json:"result"`
}
)
// 文件上传
@@ -436,7 +458,7 @@ type (
timeout: 20s // 超时时间
maxBytes: 104857600 // 最大请求大小
signature: false // 是否开启签名验证
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,AuthorizationMiddleware,NonceMiddleware // 注册中间件
middleware: SecurityHeadersMiddleware,CasbinVerifyMiddleware,NonceMiddleware // 注册中间件
MaxConns: true // 是否开启最大连接数限制
Recover: true // 是否开启自动恢复
jwt: Auth // 是否开启jwt验证
@@ -449,5 +471,17 @@ service auth {
// 设置存储配置
@handler setStorageConfig
post /config (StorageConfigRequest) returns (string)
// 获取人脸样本库列表
@handler getFaceSampleLibraryList
post /face/sample/list (FaceSampleLibraryListRequest) returns (FaceSampleLibraryListResponse)
// 修改人脸样本名称
@handler modifyFaceLibraryName
post /face/sample/modify/name (ModifyFaceNameRequestAndResponse) returns (ModifyFaceNameRequestAndResponse)
// 修改人脸样本类型
@handler modifyFaceLibraryType
post /face/sample/modify/type (ModifyFaceTypeRequest) returns (ModifyFaceTypeResponse)
}

View File

@@ -170,6 +170,17 @@ SMS:
Username: landaiqing
# 短信宝用户密码
Password: $LDQ20020618xxx$
# 高德地图配置
Map:
# 高德地图API Key
Key: 54823a494909959a9c8cd8af101bbc32
Key: 54823a494909959a9c8cd8af101bbc32
# Minio配置
Minio:
# Minio 地址
Endpoint: 1.95.0.111:9000
# Minio 访问密钥
AccessKeyID: JNLVxMGro1XXwajodLBX
# Minio 访问密钥
SecretAccessKey: XEHkwExqQdAlEPfpRk36xpc0Sie8hZkcmlhXQJXw
# Minio 使用SSL
UseSSL: false

View File

@@ -74,4 +74,10 @@ type Config struct {
Map struct {
Key string
}
Minio struct {
Endpoint string
AccessKeyID string
SecretAccessKey string
UseSSL bool
}
}

View File

@@ -62,7 +62,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.AuthorizationMiddleware, serverCtx.NonceMiddleware},
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
@@ -186,13 +186,28 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.AuthorizationMiddleware, serverCtx.NonceMiddleware},
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.NonceMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/config",
Handler: storage.SetStorageConfigHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/face/sample/list",
Handler: storage.GetFaceSampleLibraryListHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/face/sample/modify/name",
Handler: storage.ModifyFaceLibraryNameHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/face/sample/modify/type",
Handler: storage.ModifyFaceLibraryTypeHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/uploads",

View File

@@ -0,0 +1,29 @@
package storage
import (
"github.com/zeromicro/go-zero/rest/httpx"
"net/http"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/common/xhttp"
)
func GetFaceSampleLibraryListHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.FaceSampleLibraryListRequest
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := storage.NewGetFaceSampleLibraryListLogic(r.Context(), svcCtx)
resp, err := l.GetFaceSampleLibraryList(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,29 @@
package storage
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/common/xhttp"
)
func ModifyFaceLibraryNameHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ModifyFaceNameRequestAndResponse
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := storage.NewModifyFaceLibraryNameLogic(r.Context(), svcCtx)
resp, err := l.ModifyFaceLibraryName(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -0,0 +1,29 @@
package storage
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"schisandra-album-cloud-microservices/app/auth/api/internal/logic/storage"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/common/xhttp"
)
func ModifyFaceLibraryTypeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.ModifyFaceTypeRequest
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := storage.NewModifyFaceLibraryTypeLogic(r.Context(), svcCtx)
resp, err := l.ModifyFaceLibraryType(&req)
if err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}

View File

@@ -34,7 +34,7 @@ func (l *GenerateClientIdLogic) GenerateClientId(clientIP string) (resp string,
return clientId, nil
}
simpleUuid := kgo.SimpleUuid()
if err = l.svcCtx.RedisClient.SetEx(l.ctx, constant.UserClientPrefix+clientIP, simpleUuid, time.Hour*24*7).Err(); err != nil {
if err = l.svcCtx.RedisClient.SetEx(l.ctx, constant.UserClientPrefix+clientIP, simpleUuid, time.Hour*24*3).Err(); err != nil {
return "", errors.New(http.StatusInternalServerError, err.Error())
}
return simpleUuid, nil

View File

@@ -2,17 +2,14 @@ package comment
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/url"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
"schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/common/utils"
"sync"
"time"
"github.com/chenmingyong0423/go-mongox/v2/builder/query"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gen/field"
)
@@ -60,6 +57,7 @@ func (l *GetCommentListLogic) GetCommentList(req *types.CommentListRequest) (res
comment.Browser,
comment.OperatingSystem,
comment.Location,
comment.ImagePath,
user.Avatar,
user.Nickname,
).LeftJoin(user, comment.UserID.EqCol(user.UID)).
@@ -82,7 +80,7 @@ func (l *GetCommentListLogic) GetCommentList(req *types.CommentListRequest) (res
for _, commentList := range commentQueryList {
commentIds = append(commentIds, commentList.ID)
}
l.wg.Add(2)
l.wg.Add(1)
// *************** 获取评论点赞状态 **********
likeMap := make(map[int64]bool)
@@ -102,36 +100,22 @@ func (l *GetCommentListLogic) GetCommentList(req *types.CommentListRequest) (res
likeMap[like.CommentID] = true
}
}()
// ***************获取评论图片 **********
commentImageMap := make(map[int64][]string)
go func() {
defer l.wg.Done()
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
commentImages, err := newCollection.Finder().
Filter(query.Eq("topic_id", req.TopicId)).
Filter(query.In("comment_id", commentIds...)).
Find(l.ctx)
if err != nil {
logx.Error(err)
return
}
for _, image := range commentImages {
if len(image.Images) == 0 {
continue
}
imagesBase64 := make([]string, len(image.Images))
for i, img := range image.Images {
imagesBase64[i] = fmt.Sprintf("data:%s;base64,%s", utils.GetMimeType(img), base64.StdEncoding.EncodeToString(img))
}
commentImageMap[image.CommentId] = imagesBase64
}
}()
l.wg.Wait()
// *************** 组装数据 **********
result := make([]types.CommentContent, 0, len(commentQueryList))
for _, commentData := range commentQueryList {
var imagePath string
if commentData.ImagePath != "" {
reqParams := make(url.Values)
presignedURL, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.CommentImagesBucketName, commentData.ImagePath, time.Hour*24, reqParams)
if err != nil {
logx.Error(err)
continue
}
imagePath = presignedURL.String()
}
commentContent := types.CommentContent{
Avatar: commentData.Avatar,
NickName: commentData.Nickname,
@@ -148,7 +132,7 @@ func (l *GetCommentListLogic) GetCommentList(req *types.CommentListRequest) (res
Browser: commentData.Browser,
OperatingSystem: commentData.OperatingSystem,
IsLiked: likeMap[commentData.ID],
Images: commentImageMap[commentData.ID],
Images: imagePath,
}
result = append(result, commentContent)
}

View File

@@ -2,17 +2,13 @@ package comment
import (
"context"
"encoding/base64"
"errors"
"fmt"
"net/url"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
"schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/common/utils"
"sync"
"github.com/chenmingyong0423/go-mongox/v2/builder/query"
"time"
"github.com/zeromicro/go-zero/core/logx"
)
@@ -59,6 +55,7 @@ func (l *GetReplyListLogic) GetReplyList(req *types.ReplyListRequest) (resp *typ
reply.ReplyUser,
reply.ReplyTo,
reply.ReplyID,
reply.ImagePath,
commentUser.Avatar,
commentUser.Nickname,
replyUser.Nickname.As("reply_nickname"),
@@ -82,7 +79,7 @@ func (l *GetReplyListLogic) GetReplyList(req *types.ReplyListRequest) (resp *typ
for _, commentList := range replyQueryList {
commentIds = append(commentIds, commentList.ID)
}
l.wg.Add(2)
l.wg.Add(1)
// *************** 获取评论点赞状态 **********
likeMap := make(map[int64]bool)
@@ -102,36 +99,22 @@ func (l *GetReplyListLogic) GetReplyList(req *types.ReplyListRequest) (resp *typ
likeMap[like.CommentID] = true
}
}()
// ***************获取评论图片 **********
commentImageMap := make(map[int64][]string)
go func() {
defer l.wg.Done()
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
commentImages, err := newCollection.Finder().
Filter(query.Eq("topic_id", req.TopicId)).
Filter(query.In("comment_id", commentIds...)).
Find(l.ctx)
if err != nil {
logx.Error(err)
return
}
for _, image := range commentImages {
if len(image.Images) == 0 {
continue
}
imagesBase64 := make([]string, len(image.Images))
for i, img := range image.Images {
imagesBase64[i] = fmt.Sprintf("data:%s;base64,%s", utils.GetMimeType(img), base64.StdEncoding.EncodeToString(img))
}
commentImageMap[image.CommentId] = imagesBase64
}
}()
l.wg.Wait()
// *************** 组装数据 **********
result := make([]types.CommentContent, 0, len(replyQueryList))
for _, replyData := range replyQueryList {
var imagePath string
if replyData.ImagePath != "" {
reqParams := make(url.Values)
presignedURL, err := l.svcCtx.MinioClient.PresignedGetObject(l.ctx, constant.CommentImagesBucketName, replyData.ImagePath, time.Hour*24, reqParams)
if err != nil {
logx.Error(err)
continue
}
imagePath = presignedURL.String()
}
commentContent := types.CommentContent{
Avatar: replyData.Avatar,
NickName: replyData.Nickname,
@@ -148,7 +131,7 @@ func (l *GetReplyListLogic) GetReplyList(req *types.ReplyListRequest) (resp *typ
Browser: replyData.Browser,
OperatingSystem: replyData.OperatingSystem,
IsLiked: likeMap[replyData.ID],
Images: commentImageMap[replyData.ID],
Images: imagePath,
ReplyUser: replyData.ReplyUser,
ReplyTo: replyData.ReplyTo,
ReplyId: replyData.ReplyId,

View File

@@ -1,13 +1,18 @@
package comment
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/ccpwcn/kgo"
"github.com/minio/minio-go/v7"
"net/http"
"path"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"strconv"
"schisandra-album-cloud-microservices/common/captcha/verify"
"schisandra-album-cloud-microservices/common/constant"
@@ -41,9 +46,6 @@ func (l *SubmitCommentLogic) SubmitComment(r *http.Request, req *types.CommentRe
if !res {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "captcha.verificationFailure"))
}
if len(req.Images) > 3 {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.tooManyImages"))
}
userAgent := r.Header.Get("User-Agent")
if userAgent == "" {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
@@ -74,6 +76,7 @@ func (l *SubmitCommentLogic) SubmitComment(r *http.Request, req *types.CommentRe
commentContent := l.svcCtx.Sensitive.Replace(xssFilterContent, '*')
topicType := constant.CommentTopicType
commentType := constant.COMMENT
commentReply := l.svcCtx.DB.ScaCommentReply
comment := &model.ScaCommentReply{
Content: commentContent,
UserID: uid,
@@ -87,28 +90,52 @@ func (l *SubmitCommentLogic) SubmitComment(r *http.Request, req *types.CommentRe
OperatingSystem: operatingSystem,
Agent: userAgent,
}
err = l.svcCtx.DB.ScaCommentReply.Create(comment)
err = commentReply.Create(comment)
if err != nil {
return nil, err
}
if len(req.Images) > 0 {
imagesData, err := utils.ProcessImages(req.Images)
if req.Images != "" {
imagesData, err := utils.Base64ToBytes(req.Images)
if err != nil {
return nil, err
}
commentImages := &types.CommentImages{
UserId: uid,
TopicId: req.TopicId,
CommentId: comment.ID,
Images: imagesData,
objectKey := path.Join(
req.TopicId,
time.Now().Format("2006/01"), // 按年/月划分目录
strconv.FormatInt(comment.ID, 10),
fmt.Sprintf("%s_%s.jpg", uid, kgo.SimpleUuid()),
)
exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, constant.CommentImagesBucketName)
if err != nil || !exists {
err = l.svcCtx.MinioClient.MakeBucket(l.ctx, constant.CommentImagesBucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
if err != nil {
logx.Errorf("Failed to create MinIO bucket: %v", err)
return nil, err
}
}
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
_, err = newCollection.Creator().InsertOne(l.ctx, commentImages)
// 上传到MinIO
_, err = l.svcCtx.MinioClient.PutObject(
l.ctx,
constant.CommentImagesBucketName,
objectKey,
bytes.NewReader(imagesData),
int64(len(imagesData)),
minio.PutObjectOptions{
ContentType: "image/jpeg",
},
)
if err != nil {
logx.Errorf("Failed to upload image to MinIO: %v", err)
return nil, err
}
info, err := commentReply.Where(commentReply.ID.Eq(comment.ID)).Update(commentReply.ImagePath, objectKey)
if err != nil || info.RowsAffected == 0 {
return nil, errors.New("update image path failed")
}
}
commentResponse := &types.CommentResponse{
Id: comment.ID,

View File

@@ -1,18 +1,23 @@
package comment
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/ccpwcn/kgo"
"github.com/minio/minio-go/v7"
"net/http"
"path"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"schisandra-album-cloud-microservices/common/captcha/verify"
"schisandra-album-cloud-microservices/common/constant"
errors2 "schisandra-album-cloud-microservices/common/errors"
"schisandra-album-cloud-microservices/common/i18n"
"schisandra-album-cloud-microservices/common/utils"
"strconv"
"time"
"github.com/mssola/useragent"
@@ -39,9 +44,6 @@ func (l *SubmitReplyCommentLogic) SubmitReplyComment(r *http.Request, req *types
if !res {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "captcha.verificationFailure"))
}
if len(req.Images) > 3 {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.tooManyImages"))
}
userAgent := r.Header.Get("User-Agent")
if userAgent == "" {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
@@ -105,24 +107,47 @@ func (l *SubmitReplyCommentLogic) SubmitReplyComment(r *http.Request, req *types
}
if len(req.Images) > 0 {
imagesData, err := utils.ProcessImages(req.Images)
if req.Images != "" {
imagesData, err := utils.Base64ToBytes(req.Images)
if err != nil {
return nil, err
}
objectKey := path.Join(
req.TopicId,
time.Now().Format("2006/01"), // 按年/月划分目录
strconv.FormatInt(reply.ID, 10),
fmt.Sprintf("%s_%s.jpg", uid, kgo.SimpleUuid()),
)
commentImages := &types.CommentImages{
UserId: uid,
TopicId: req.TopicId,
CommentId: reply.ID,
Images: imagesData,
exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, constant.CommentImagesBucketName)
if err != nil || !exists {
err = l.svcCtx.MinioClient.MakeBucket(l.ctx, constant.CommentImagesBucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
if err != nil {
logx.Errorf("Failed to create MinIO bucket: %v", err)
return nil, err
}
}
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
_, err = newCollection.Creator().InsertOne(l.ctx, commentImages)
// 上传到MinIO
_, err = l.svcCtx.MinioClient.PutObject(
l.ctx,
constant.CommentImagesBucketName,
objectKey,
bytes.NewReader(imagesData),
int64(len(imagesData)),
minio.PutObjectOptions{
ContentType: "image/jpeg",
},
)
if err != nil {
logx.Errorf("Failed to upload image to MinIO: %v", err)
return nil, err
}
info, err := commentReply.Where(commentReply.ID.Eq(reply.ID)).Update(commentReply.ImagePath, objectKey)
if err != nil || info.RowsAffected == 0 {
return nil, errors.New("update image path failed")
}
}
commentResponse := &types.CommentResponse{

View File

@@ -1,18 +1,23 @@
package comment
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/ccpwcn/kgo"
"github.com/minio/minio-go/v7"
"net/http"
"path"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
"schisandra-album-cloud-microservices/app/auth/model/mysql/model"
"schisandra-album-cloud-microservices/common/captcha/verify"
"schisandra-album-cloud-microservices/common/constant"
errors2 "schisandra-album-cloud-microservices/common/errors"
"schisandra-album-cloud-microservices/common/i18n"
"schisandra-album-cloud-microservices/common/utils"
"strconv"
"time"
"github.com/mssola/useragent"
@@ -39,9 +44,6 @@ func (l *SubmitReplyReplyLogic) SubmitReplyReply(r *http.Request, req *types.Rep
if !res {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "captcha.verificationFailure"))
}
if len(req.Images) > 3 {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.tooManyImages"))
}
userAgent := r.Header.Get("User-Agent")
if userAgent == "" {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
@@ -112,24 +114,47 @@ func (l *SubmitReplyReplyLogic) SubmitReplyReply(r *http.Request, req *types.Rep
}
// 处理图片
if len(req.Images) > 0 {
imagesData, err := utils.ProcessImages(req.Images)
if req.Images != "" {
imagesData, err := utils.Base64ToBytes(req.Images)
if err != nil {
return nil, err
}
commentImages := &types.CommentImages{
UserId: uid,
TopicId: req.TopicId,
CommentId: replyReply.ID,
Images: imagesData,
objectKey := path.Join(
req.TopicId,
time.Now().Format("2006/01"), // 按年/月划分目录
strconv.FormatInt(replyReply.ID, 10),
fmt.Sprintf("%s_%s.jpg", uid, kgo.SimpleUuid()),
)
exists, err := l.svcCtx.MinioClient.BucketExists(l.ctx, constant.CommentImagesBucketName)
if err != nil || !exists {
err = l.svcCtx.MinioClient.MakeBucket(l.ctx, constant.CommentImagesBucketName, minio.MakeBucketOptions{Region: "us-east-1", ObjectLocking: true})
if err != nil {
logx.Errorf("Failed to create MinIO bucket: %v", err)
return nil, err
}
}
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
_, err = newCollection.Creator().InsertOne(l.ctx, commentImages)
// 上传到MinIO
_, err = l.svcCtx.MinioClient.PutObject(
l.ctx,
constant.CommentImagesBucketName,
objectKey,
bytes.NewReader(imagesData),
int64(len(imagesData)),
minio.PutObjectOptions{
ContentType: "image/jpeg",
},
)
if err != nil {
logx.Errorf("Failed to upload image to MinIO: %v", err)
return nil, err
}
info, err := commentReply.Where(commentReply.ID.Eq(replyReply.ID)).Update(commentReply.ImagePath, objectKey)
if err != nil || info.RowsAffected == 0 {
return nil, errors.New("update image path failed")
}
}
// 构建响应

View File

@@ -0,0 +1,48 @@
package storage
import (
"context"
"errors"
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type GetFaceSampleLibraryListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewGetFaceSampleLibraryListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetFaceSampleLibraryListLogic {
return &GetFaceSampleLibraryListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetFaceSampleLibraryListLogic) GetFaceSampleLibraryList(req *types.FaceSampleLibraryListRequest) (resp *types.FaceSampleLibraryListResponse, err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
faceLibrary, err := l.svcCtx.AiSvcRpc.QueryFaceLibrary(l.ctx, &pb.QueryFaceLibraryRequest{UserId: uid, Type: req.Type})
if err != nil {
return nil, err
}
var faceSampleLibraries []types.FaceSampleLibrary
for _, face := range faceLibrary.GetFaces() {
faceSampleLibraries = append(faceSampleLibraries, types.FaceSampleLibrary{
ID: face.GetId(),
FaceName: face.GetFaceName(),
FaceImage: face.GetFaceImage(),
})
}
return &types.FaceSampleLibraryListResponse{
Faces: faceSampleLibraries,
}, nil
}

View File

@@ -0,0 +1,38 @@
package storage
import (
"context"
"errors"
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ModifyFaceLibraryNameLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewModifyFaceLibraryNameLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ModifyFaceLibraryNameLogic {
return &ModifyFaceLibraryNameLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ModifyFaceLibraryNameLogic) ModifyFaceLibraryName(req *types.ModifyFaceNameRequestAndResponse) (resp *types.ModifyFaceNameRequestAndResponse, err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
faceInfo, err := l.svcCtx.AiSvcRpc.ModifyFaceName(l.ctx, &pb.ModifyFaceNameRequest{UserId: uid, FaceId: req.ID, FaceName: req.FaceName})
if err != nil {
return nil, err
}
return &types.ModifyFaceNameRequestAndResponse{ID: faceInfo.GetFaceId(), FaceName: faceInfo.GetFaceName()}, nil
}

View File

@@ -0,0 +1,46 @@
package storage
import (
"context"
"errors"
"schisandra-album-cloud-microservices/app/aisvc/rpc/pb"
"schisandra-album-cloud-microservices/app/auth/api/internal/svc"
"schisandra-album-cloud-microservices/app/auth/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type ModifyFaceLibraryTypeLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewModifyFaceLibraryTypeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ModifyFaceLibraryTypeLogic {
return &ModifyFaceLibraryTypeLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *ModifyFaceLibraryTypeLogic) ModifyFaceLibraryType(req *types.ModifyFaceTypeRequest) (resp *types.ModifyFaceTypeResponse, err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
faceInfo, err := l.svcCtx.AiSvcRpc.ModifyFaceType(l.ctx, &pb.ModifyFaceTypeRequest{UserId: uid, FaceId: req.IDs, Type: req.FaceType})
if err != nil {
return nil, err
}
storageInfo := l.svcCtx.DB.ScaStorageInfo
resultInfo, err := storageInfo.Where(storageInfo.FaceID.In(req.IDs...)).Update(storageInfo.Hide, req.FaceType)
if err != nil {
return nil, err
}
if resultInfo.RowsAffected != int64(len(req.IDs)) {
return nil, errors.New("update failed")
}
return &types.ModifyFaceTypeResponse{Result: faceInfo.Result}, nil
}

View File

@@ -1,20 +0,0 @@
package middleware
import (
"net/http"
"schisandra-album-cloud-microservices/common/middleware"
)
type AuthorizationMiddleware struct {
}
func NewAuthorizationMiddleware() *AuthorizationMiddleware {
return &AuthorizationMiddleware{}
}
func (m *AuthorizationMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
middleware.AuthorizationMiddleware(w, r)
next(w, r)
}
}

View File

@@ -4,23 +4,23 @@ import (
"github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount"
"github.com/casbin/casbin/v2"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/minio/minio-go/v7"
"github.com/redis/go-redis/v9"
"github.com/wenlng/go-captcha/v2/rotate"
"github.com/wenlng/go-captcha/v2/slide"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
sensitive "github.com/zmexing/go-sensitive-word"
"go.mongodb.org/mongo-driver/v2/mongo"
"schisandra-album-cloud-microservices/app/aisvc/rpc/client/aiservice"
"schisandra-album-cloud-microservices/app/auth/api/internal/config"
"schisandra-album-cloud-microservices/app/auth/api/internal/middleware"
"schisandra-album-cloud-microservices/app/auth/model/mongodb"
"schisandra-album-cloud-microservices/app/auth/model/mysql"
"schisandra-album-cloud-microservices/app/auth/model/mysql/query"
"schisandra-album-cloud-microservices/common/captcha/initialize"
"schisandra-album-cloud-microservices/common/casbinx"
"schisandra-album-cloud-microservices/common/gao_map"
"schisandra-album-cloud-microservices/common/ip2region"
"schisandra-album-cloud-microservices/common/miniox"
"schisandra-album-cloud-microservices/common/redisx"
"schisandra-album-cloud-microservices/common/sensitivex"
"schisandra-album-cloud-microservices/common/storage"
@@ -33,19 +33,18 @@ type ServiceContext struct {
AiSvcRpc aiservice.AiService
SecurityHeadersMiddleware rest.Middleware
CasbinVerifyMiddleware rest.Middleware
AuthorizationMiddleware rest.Middleware
NonceMiddleware rest.Middleware
DB *query.Query
RedisClient *redis.Client
Ip2Region *xdb.Searcher
CasbinEnforcer *casbin.SyncedCachedEnforcer
WechatOfficial *officialAccount.OfficialAccount
MongoClient *mongo.Database
RotateCaptcha rotate.Captcha
SlideCaptcha slide.Captcha
Sensitive *sensitive.Manager
StorageManager *manager.Manager
GaoMap *gao_map.AmapClient
MinioClient *minio.Client
}
func NewServiceContext(c config.Config) *ServiceContext {
@@ -56,7 +55,6 @@ func NewServiceContext(c config.Config) *ServiceContext {
Config: c,
SecurityHeadersMiddleware: middleware.NewSecurityHeadersMiddleware().Handle,
CasbinVerifyMiddleware: middleware.NewCasbinVerifyMiddleware(casbinEnforcer).Handle,
AuthorizationMiddleware: middleware.NewAuthorizationMiddleware().Handle,
NonceMiddleware: middleware.NewNonceMiddleware(redisClient).Handle,
DB: queryDB,
RedisClient: redisClient,
@@ -65,10 +63,10 @@ func NewServiceContext(c config.Config) *ServiceContext {
WechatOfficial: wechat_official.NewWechatPublic(c.Wechat.AppID, c.Wechat.AppSecret, c.Wechat.Token, c.Wechat.AESKey, c.Redis.Host, c.Redis.Pass, c.Redis.DB),
RotateCaptcha: initialize.NewRotateCaptcha(),
SlideCaptcha: initialize.NewSlideCaptcha(),
MongoClient: mongodb.NewMongoDB(c.Mongo.Uri, c.Mongo.Username, c.Mongo.Password, c.Mongo.AuthSource, c.Mongo.Database),
Sensitive: sensitivex.NewSensitive(),
StorageManager: storage.InitStorageManager(),
GaoMap: gao_map.NewAmapClient(c.Map.Key, ""),
AiSvcRpc: aiservice.NewAiService(zrpc.MustNewClient(c.AiSvcRpc)),
MinioClient: miniox.NewMinio(c.Minio.Endpoint, c.Minio.AccessKeyID, c.Minio.SecretAccessKey, c.Minio.UseSSL),
}
}

View File

@@ -2,19 +2,8 @@ package types
import (
"time"
"github.com/chenmingyong0423/go-mongox/v2"
)
// CommentImages 评论 图片
type CommentImages struct {
mongox.Model `bson:",inline"`
TopicId string `json:"topic_id" bson:"topic_id"`
CommentId int64 `json:"comment_id" bson:"comment_id"`
UserId string `json:"user_id" bson:"user_id"`
Images [][]byte `json:"images" bson:"images"`
}
// CommentListQueryResult 评论列表查询结果
type CommentListQueryResult struct {
ID int64 `json:"id"`
@@ -30,6 +19,7 @@ type CommentListQueryResult struct {
Location string `json:"location"`
Avatar string `json:"avatar"`
Nickname string `json:"nickname"`
ImagePath string `json:"image_path"`
}
// ReplyListQueryResult 回复列表查询结果
@@ -51,4 +41,5 @@ type ReplyListQueryResult struct {
ReplyId int64 `json:"reply_id"`
ReplyTo int64 `json:"reply_to"`
ReplyNickname string `json:"reply_nickname"`
ImagePath string `json:"image_path"`
}

View File

@@ -12,26 +12,26 @@ type AccountLoginRequest struct {
}
type CommentContent struct {
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
Level int64 `json:"level,omitempty" default:"0"`
Id int64 `json:"id"`
UserId string `json:"user_id"`
TopicId string `json:"topic_id"`
Content string `json:"content"`
ReplyTo int64 `json:"reply_to,omitempty"`
ReplyId int64 `json:"reply_id,omitempty"`
ReplyUser string `json:"reply_user,omitempty"`
ReplyNickname string `json:"reply_nickname,omitempty"`
IsAuthor int64 `json:"is_author"`
Likes int64 `json:"likes"`
ReplyCount int64 `json:"reply_count"`
CreatedTime string `json:"created_time"`
Location string `json:"location"`
Browser string `json:"browser"`
OperatingSystem string `json:"operating_system"`
IsLiked bool `json:"is_liked" default:"false"`
Images []string `json:"images,omitempty"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
Level int64 `json:"level,omitempty" default:"0"`
Id int64 `json:"id"`
UserId string `json:"user_id"`
TopicId string `json:"topic_id"`
Content string `json:"content"`
ReplyTo int64 `json:"reply_to,omitempty"`
ReplyId int64 `json:"reply_id,omitempty"`
ReplyUser string `json:"reply_user,omitempty"`
ReplyNickname string `json:"reply_nickname,omitempty"`
IsAuthor int64 `json:"is_author"`
Likes int64 `json:"likes"`
ReplyCount int64 `json:"reply_count"`
CreatedTime string `json:"created_time"`
Location string `json:"location"`
Browser string `json:"browser"`
OperatingSystem string `json:"operating_system"`
IsLiked bool `json:"is_liked" default:"false"`
Images string `json:"images,omitempty"`
}
type CommentDisLikeRequest struct {
@@ -59,12 +59,12 @@ type CommentListRequest struct {
}
type CommentRequest struct {
Content string `json:"content"`
Images []string `json:"images,optional"`
TopicId string `json:"topic_id"`
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
Content string `json:"content"`
Images string `json:"images,optional"`
TopicId string `json:"topic_id"`
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
}
type CommentResponse struct {
@@ -82,6 +82,20 @@ type CommentResponse struct {
ReplyTo int64 `json:"reply_to,omitempty"`
}
type FaceSampleLibrary struct {
ID int64 `json:"id"`
FaceName string `json:"face_name"`
FaceImage string `json:"face_image"`
}
type FaceSampleLibraryListRequest struct {
Type int64 `json:"type"`
}
type FaceSampleLibraryListResponse struct {
Faces []FaceSampleLibrary `json:"faces"`
}
type LoginResponse struct {
AccessToken string `json:"access_token"`
ExpireAt int64 `json:"expire_at"`
@@ -92,6 +106,20 @@ type LoginResponse struct {
Status int64 `json:"status"`
}
type ModifyFaceNameRequestAndResponse struct {
ID int64 `json:"id"`
FaceName string `json:"face_name"`
}
type ModifyFaceTypeRequest struct {
IDs []int64 `json:"ids"`
FaceType int64 `json:"face_type"`
}
type ModifyFaceTypeResponse struct {
Result string `json:"result"`
}
type OAuthCallbackRequest struct {
Code string `form:"code"`
}
@@ -116,14 +144,14 @@ type RefreshTokenResponse struct {
}
type ReplyCommentRequest struct {
Content string `json:"content"`
Images []string `json:"images,optional"`
TopicId string `json:"topic_id" `
ReplyId int64 `json:"reply_id" `
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
Content string `json:"content"`
Images string `json:"images,optional"`
TopicId string `json:"topic_id" `
ReplyId int64 `json:"reply_id" `
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
}
type ReplyListRequest struct {
@@ -134,15 +162,15 @@ type ReplyListRequest struct {
}
type ReplyReplyRequest struct {
Content string `json:"content"`
Images []string `json:"images,optional"`
TopicId string `json:"topic_id"`
ReplyTo int64 `json:"reply_to"`
ReplyId int64 `json:"reply_id"`
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
Content string `json:"content"`
Images string `json:"images,optional"`
TopicId string `json:"topic_id"`
ReplyTo int64 `json:"reply_to"`
ReplyId int64 `json:"reply_id"`
ReplyUser string `json:"reply_user" `
Author string `json:"author"`
Key string `json:"key"`
Point []int64 `json:"point"`
}
type ResetPasswordRequest struct {