🏗️ microservice fabric splitting

This commit is contained in:
2024-12-24 00:38:41 +08:00
parent 462e811742
commit 89d64336f5
311 changed files with 18384 additions and 2428 deletions

View File

@@ -0,0 +1,27 @@
package config
import "github.com/zeromicro/go-zero/rest"
type Config struct {
rest.RestConf
Auth struct {
AccessSecret string
}
Mysql struct {
DataSource string
MaxOpenConn int
MaxIdleConn int
}
Redis struct {
Host string
Pass string
DB int
}
Mongo struct {
Uri string
Username string
Password string
AuthSource string
Database string
}
}

View File

@@ -0,0 +1,24 @@
package comment
import (
"github.com/zeromicro/go-zero/rest/httpx"
"net/http"
"schisandra-album-cloud-microservices/app/community/api/internal/logic/comment"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/common/xhttp"
)
func DislikeCommentHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CommentDisLikeRequest
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := comment.NewDislikeCommentLogic(r.Context(), svcCtx)
err := l.DislikeComment(&req)
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
}
}

View File

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

View File

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

View File

@@ -0,0 +1,24 @@
package comment
import (
"github.com/zeromicro/go-zero/rest/httpx"
"net/http"
"schisandra-album-cloud-microservices/app/community/api/internal/logic/comment"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/common/xhttp"
)
func LikeCommentHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.CommentLikeRequest
if err := httpx.Parse(r, &req); err != nil {
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
return
}
l := comment.NewLikeCommentLogic(r.Context(), svcCtx)
err := l.LikeComment(&req)
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,63 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.7.3
package handler
import (
"net/http"
"time"
comment "schisandra-album-cloud-microservices/app/community/api/internal/handler/comment"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.SecurityHeadersMiddleware, serverCtx.CasbinVerifyMiddleware, serverCtx.AuthorizationMiddleware, serverCtx.NonceMiddleware},
[]rest.Route{
{
Method: http.MethodPost,
Path: "/dislike",
Handler: comment.DislikeCommentHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/like",
Handler: comment.LikeCommentHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/list",
Handler: comment.GetCommentListHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/reply/list",
Handler: comment.GetReplyListHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/reply/reply/submit",
Handler: comment.SubmitReplyReplyHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/reply/submit",
Handler: comment.SubmitReplyCommentHandler(serverCtx),
},
{
Method: http.MethodPost,
Path: "/submit",
Handler: comment.SubmitCommentHandler(serverCtx),
},
}...,
),
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
rest.WithPrefix("/api/auth/comment"),
rest.WithTimeout(10000*time.Millisecond),
rest.WithMaxBytes(1048576),
)
}

View File

@@ -0,0 +1,59 @@
package comment
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
"net/http"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"schisandra-album-cloud-microservices/app/community/api/internal/types"
errors2 "schisandra-album-cloud-microservices/common/errors"
"schisandra-album-cloud-microservices/common/i18n"
)
type DislikeCommentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewDislikeCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DislikeCommentLogic {
return &DislikeCommentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *DislikeCommentLogic) DislikeComment(req *types.CommentDisLikeRequest) (err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return errors.New("user_id not found")
}
tx := l.svcCtx.DB.Begin()
commentLike := l.svcCtx.DB.ScaCommentLike
resultInfo, err := tx.ScaCommentLike.Where(commentLike.TopicID.Eq(req.TopicId), commentLike.CommentID.Eq(req.CommentId), commentLike.UserID.Eq(uid)).Delete()
if err != nil {
_ = tx.Rollback()
return err
}
if resultInfo.RowsAffected == 0 {
_ = tx.Rollback()
return errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.LikeError"))
}
comment := l.svcCtx.DB.ScaCommentReply
updates, err := tx.ScaCommentReply.Where(comment.TopicID.Eq(req.TopicId), comment.ID.Eq(req.CommentId)).Update(comment.Likes, comment.Likes.Sub(1))
if err != nil {
_ = tx.Rollback()
return err
}
if updates.RowsAffected == 0 {
_ = tx.Rollback()
return errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.LikeError"))
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,162 @@
package comment
import (
"context"
"encoding/base64"
"errors"
"fmt"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
types2 "schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/app/community/api/model/mongodb"
constant2 "schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/common/utils"
"sync"
"github.com/chenmingyong0423/go-mongox/v2/builder/query"
"github.com/zeromicro/go-zero/core/logx"
"gorm.io/gen/field"
)
type GetCommentListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
wg sync.WaitGroup
}
func NewGetCommentListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommentListLogic {
return &GetCommentListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetCommentListLogic) GetCommentList(req *types2.CommentListRequest) (resp *types2.CommentListPageResponse, err error) {
// 获取用户ID
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
var commentQueryList []types2.CommentListQueryResult
comment := l.svcCtx.DB.ScaCommentReply
user := l.svcCtx.DB.ScaAuthUser
var orderConditions []field.Expr
if req.IsHot {
orderConditions = append(orderConditions, comment.Likes.Desc(), comment.ReplyCount.Desc())
} else {
orderConditions = append(orderConditions, comment.CreatedAt.Desc())
}
count, err := comment.Select(
comment.ID,
comment.UserID,
comment.TopicID,
comment.Content,
comment.CreatedAt,
comment.Author,
comment.Likes,
comment.ReplyCount,
comment.Browser,
comment.OperatingSystem,
comment.Location,
user.Avatar,
user.Nickname,
).LeftJoin(user, comment.UserID.EqCol(user.UID)).
Where(comment.TopicID.Eq(req.TopicId), comment.CommentType.Eq(constant2.COMMENT)).
Order(orderConditions...).
ScanByPage(&commentQueryList, (req.Page-1)*req.Size, req.Size)
if err != nil {
return nil, err
}
if count == 0 || len(commentQueryList) == 0 {
return &types2.CommentListPageResponse{
Total: count,
Size: req.Size,
Current: req.Page,
Comments: []types2.CommentContent{},
}, nil
}
// **************** 获取评论Id和用户Id ************
commentIds := make([]int64, 0, len(commentQueryList))
for _, commentList := range commentQueryList {
commentIds = append(commentIds, commentList.ID)
}
l.wg.Add(2)
// *************** 获取评论点赞状态 **********
likeMap := make(map[int64]bool)
go func() {
defer l.wg.Done()
commentLike := l.svcCtx.DB.ScaCommentLike
likeList, err := commentLike.Where(
commentLike.TopicID.Eq(req.TopicId),
commentLike.UserID.Eq(uid),
commentLike.CommentID.In(commentIds...)).
Find()
if err != nil {
logx.Error(err)
return
}
for _, like := range likeList {
likeMap[like.CommentID] = true
}
}()
// ***************获取评论图片 **********
commentImageMap := make(map[int64][]string)
go func() {
defer l.wg.Done()
newCollection := mongodb.MustNewCollection[types2.CommentImages](l.svcCtx.MongoClient, constant2.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([]types2.CommentContent, 0, len(commentQueryList))
for _, commentData := range commentQueryList {
commentContent := types2.CommentContent{
Avatar: commentData.Avatar,
NickName: commentData.Nickname,
Content: commentData.Content,
CreatedTime: commentData.CreatedAt.Format(constant2.TimeFormat),
Level: 0,
Id: commentData.ID,
UserId: commentData.UserID,
TopicId: commentData.TopicID,
IsAuthor: commentData.Author,
Likes: commentData.Likes,
ReplyCount: commentData.ReplyCount,
Location: commentData.Location,
Browser: commentData.Browser,
OperatingSystem: commentData.OperatingSystem,
IsLiked: likeMap[commentData.ID],
Images: commentImageMap[commentData.ID],
}
result = append(result, commentContent)
}
commentListPageResponse := &types2.CommentListPageResponse{
Total: count,
Size: req.Size,
Current: req.Page,
Comments: result,
}
return commentListPageResponse, nil
}

View File

@@ -0,0 +1,166 @@
package comment
import (
"context"
"encoding/base64"
"errors"
"fmt"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
types2 "schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/app/community/api/model/mongodb"
constant2 "schisandra-album-cloud-microservices/common/constant"
"schisandra-album-cloud-microservices/common/utils"
"sync"
"github.com/chenmingyong0423/go-mongox/v2/builder/query"
"github.com/zeromicro/go-zero/core/logx"
)
type GetReplyListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
wg sync.WaitGroup
}
func NewGetReplyListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetReplyListLogic {
return &GetReplyListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetReplyListLogic) GetReplyList(req *types2.ReplyListRequest) (resp *types2.CommentListPageResponse, err error) {
// 获取用户ID
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found")
}
var replyQueryList []types2.ReplyListQueryResult
reply := l.svcCtx.DB.ScaCommentReply
user := l.svcCtx.DB.ScaAuthUser
commentUser := user.As("comment_user")
replyUser := user.As("reply_user")
count, err := reply.Select(
reply.ID,
reply.UserID,
reply.TopicID,
reply.Content,
reply.CreatedAt,
reply.Author,
reply.ReplyCount,
reply.Likes,
reply.Browser,
reply.OperatingSystem,
reply.Location,
reply.ReplyUser,
reply.ReplyTo,
reply.ReplyID,
commentUser.Avatar,
commentUser.Nickname,
replyUser.Nickname.As("reply_nickname"),
).LeftJoin(commentUser, reply.UserID.EqCol(commentUser.UID)).
LeftJoin(replyUser, reply.ReplyUser.EqCol(replyUser.UID)).
Where(reply.TopicID.Eq(req.TopicId), reply.ReplyID.Eq(req.CommentId), reply.CommentType.Eq(constant2.REPLY)).
Order(reply.Likes.Desc(), reply.CreatedAt.Desc()).
ScanByPage(&replyQueryList, (req.Page-1)*req.Size, req.Size)
if err != nil {
return nil, err
}
if count == 0 || len(replyQueryList) == 0 {
return &types2.CommentListPageResponse{
Total: count,
Size: req.Size,
Current: req.Page,
}, nil
}
// **************** 获取评论Id和用户Id ************
commentIds := make([]int64, 0, len(replyQueryList))
for _, commentList := range replyQueryList {
commentIds = append(commentIds, commentList.ID)
}
l.wg.Add(2)
// *************** 获取评论点赞状态 **********
likeMap := make(map[int64]bool)
go func() {
defer l.wg.Done()
commentLike := l.svcCtx.DB.ScaCommentLike
likeList, err := commentLike.Where(
commentLike.TopicID.Eq(req.TopicId),
commentLike.UserID.Eq(uid),
commentLike.CommentID.In(commentIds...)).
Find()
if err != nil {
logx.Error(err)
return
}
for _, like := range likeList {
likeMap[like.CommentID] = true
}
}()
// ***************获取评论图片 **********
commentImageMap := make(map[int64][]string)
go func() {
defer l.wg.Done()
newCollection := mongodb.MustNewCollection[types2.CommentImages](l.svcCtx.MongoClient, constant2.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([]types2.CommentContent, 0, len(replyQueryList))
for _, replyData := range replyQueryList {
commentContent := types2.CommentContent{
Avatar: replyData.Avatar,
NickName: replyData.Nickname,
Content: replyData.Content,
CreatedTime: replyData.CreatedAt.Format(constant2.TimeFormat),
Level: 0,
Id: replyData.ID,
UserId: replyData.UserID,
TopicId: replyData.TopicID,
IsAuthor: replyData.Author,
Likes: replyData.Likes,
ReplyCount: replyData.ReplyCount,
Location: replyData.Location,
Browser: replyData.Browser,
OperatingSystem: replyData.OperatingSystem,
IsLiked: likeMap[replyData.ID],
Images: commentImageMap[replyData.ID],
ReplyUser: replyData.ReplyUser,
ReplyTo: replyData.ReplyTo,
ReplyId: replyData.ReplyId,
ReplyNickname: replyData.ReplyNickname,
}
result = append(result, commentContent)
}
commentListPageResponse := &types2.CommentListPageResponse{
Total: count,
Size: req.Size,
Current: req.Page,
Comments: result,
}
return commentListPageResponse, nil
}

View File

@@ -0,0 +1,61 @@
package comment
import (
"context"
"errors"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/app/community/api/model/mysql/model"
"schisandra-album-cloud-microservices/common/i18n"
"time"
"github.com/zeromicro/go-zero/core/logx"
)
type LikeCommentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLikeCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeCommentLogic {
return &LikeCommentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LikeCommentLogic) LikeComment(req *types.CommentLikeRequest) (err error) {
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return errors.New("user_id not found")
}
tx := l.svcCtx.DB.Begin()
commentLike := &model.ScaCommentLike{
CommentID: req.CommentId,
TopicID: req.TopicId,
UserID: uid,
LikeTime: time.Now(),
}
err = tx.ScaCommentLike.Create(commentLike)
if err != nil {
_ = tx.Rollback()
return err
}
comment := l.svcCtx.DB.ScaCommentReply
updates, err := tx.ScaCommentReply.Where(comment.TopicID.Eq(req.TopicId), comment.ID.Eq(req.CommentId)).Update(comment.Likes, comment.Likes.Add(1))
if err != nil {
_ = tx.Rollback()
return err
}
if updates.RowsAffected == 0 {
_ = tx.Rollback()
return errors.New(i18n.FormatText(l.ctx, "comment.LikeError"))
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,124 @@
package comment
import (
"context"
"errors"
"net/http"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/app/community/api/model/mongodb"
"schisandra-album-cloud-microservices/app/community/api/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"
"time"
"github.com/mssola/useragent"
"github.com/zeromicro/go-zero/core/logx"
)
type SubmitCommentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSubmitCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubmitCommentLogic {
return &SubmitCommentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SubmitCommentLogic) SubmitComment(r *http.Request, req *types.CommentRequest) (resp *types.CommentResponse, err error) {
res := verify.VerifySlideCaptcha(l.ctx, l.svcCtx.RedisClient, req.Point, req.Key)
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"))
}
ua := useragent.New(userAgent)
ip := utils.GetClientIP(r)
location, err := l.svcCtx.Ip2Region.SearchByStr(ip)
if err != nil {
return nil, err
}
location = utils.RemoveZeroAndAdjust(location)
browser, _ := ua.Browser()
operatingSystem := ua.OS()
var isAuthor int64 = 0
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found in context")
}
if uid == req.Author {
isAuthor = 1
}
xssFilterContent := utils.XssFilter(req.Content)
if xssFilterContent == "" {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
}
commentContent := l.svcCtx.Sensitive.Replace(xssFilterContent, '*')
topicType := constant.CommentTopicType
commentType := constant.COMMENT
comment := &model.ScaCommentReply{
Content: commentContent,
UserID: uid,
TopicID: req.TopicId,
TopicType: topicType,
CommentType: commentType,
Author: isAuthor,
CommentIP: ip,
Location: location,
Browser: browser,
OperatingSystem: operatingSystem,
Agent: userAgent,
}
err = l.svcCtx.DB.ScaCommentReply.Create(comment)
if err != nil {
return nil, err
}
if len(req.Images) > 0 {
imagesData, err := utils.ProcessImages(req.Images)
if err != nil {
return nil, err
}
commentImages := &types.CommentImages{
UserId: uid,
TopicId: req.TopicId,
CommentId: comment.ID,
Images: imagesData,
}
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
_, err = newCollection.Creator().InsertOne(l.ctx, commentImages)
if err != nil {
return nil, err
}
}
commentResponse := &types.CommentResponse{
Id: comment.ID,
Content: commentContent,
UserId: uid,
TopicId: req.TopicId,
Author: isAuthor,
Location: location,
Browser: browser,
OperatingSystem: operatingSystem,
CreatedTime: time.Now().Format(constant.TimeFormat),
}
return commentResponse, nil
}

View File

@@ -0,0 +1,146 @@
package comment
import (
"context"
"errors"
"net/http"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/app/community/api/model/mongodb"
"schisandra-album-cloud-microservices/app/community/api/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"
"time"
"github.com/mssola/useragent"
"github.com/zeromicro/go-zero/core/logx"
)
type SubmitReplyCommentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSubmitReplyCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubmitReplyCommentLogic {
return &SubmitReplyCommentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SubmitReplyCommentLogic) SubmitReplyComment(r *http.Request, req *types.ReplyCommentRequest) (resp *types.CommentResponse, err error) {
res := verify.VerifySlideCaptcha(l.ctx, l.svcCtx.RedisClient, req.Point, req.Key)
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"))
}
ua := useragent.New(userAgent)
ip := utils.GetClientIP(r)
location, err := l.svcCtx.Ip2Region.SearchByStr(ip)
if err != nil {
return nil, err
}
location = utils.RemoveZeroAndAdjust(location)
browser, _ := ua.Browser()
operatingSystem := ua.OS()
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found in context")
}
var isAuthor int64 = 0
if uid == req.Author {
isAuthor = 1
}
xssFilterContent := utils.XssFilter(req.Content)
if xssFilterContent == "" {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
}
commentContent := l.svcCtx.Sensitive.Replace(xssFilterContent, '*')
tx := l.svcCtx.DB.Begin()
topicType := constant.CommentTopicType
commentType := constant.REPLY
reply := &model.ScaCommentReply{
Content: commentContent,
UserID: uid,
TopicID: req.TopicId,
TopicType: topicType,
CommentType: commentType,
Author: isAuthor,
CommentIP: ip,
Location: location,
Browser: browser,
OperatingSystem: operatingSystem,
Agent: userAgent,
ReplyID: req.ReplyId,
ReplyUser: req.ReplyUser,
}
err = tx.ScaCommentReply.Create(reply)
if err != nil {
return nil, err
}
commentReply := l.svcCtx.DB.ScaCommentReply
update, err := tx.ScaCommentReply.Where(commentReply.ID.Eq(req.ReplyId)).Update(commentReply.ReplyCount, commentReply.ReplyCount.Add(1))
if err != nil {
return nil, err
}
if update.RowsAffected == 0 {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
}
if len(req.Images) > 0 {
imagesData, err := utils.ProcessImages(req.Images)
if err != nil {
return nil, err
}
commentImages := &types.CommentImages{
UserId: uid,
TopicId: req.TopicId,
CommentId: reply.ID,
Images: imagesData,
}
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
_, err = newCollection.Creator().InsertOne(l.ctx, commentImages)
if err != nil {
return nil, err
}
}
commentResponse := &types.CommentResponse{
Id: reply.ID,
Content: commentContent,
UserId: uid,
TopicId: reply.TopicID,
Author: isAuthor,
Location: location,
Browser: browser,
OperatingSystem: operatingSystem,
CreatedTime: time.Now().Format(constant.TimeFormat),
ReplyId: reply.ReplyID,
ReplyUser: reply.ReplyUser,
}
err = tx.Commit()
if err != nil {
return nil, err
}
return commentResponse, nil
}

View File

@@ -0,0 +1,156 @@
package comment
import (
"context"
"errors"
"net/http"
"schisandra-album-cloud-microservices/app/community/api/internal/svc"
"schisandra-album-cloud-microservices/app/community/api/internal/types"
"schisandra-album-cloud-microservices/app/community/api/model/mongodb"
"schisandra-album-cloud-microservices/app/community/api/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"
"time"
"github.com/mssola/useragent"
"github.com/zeromicro/go-zero/core/logx"
)
type SubmitReplyReplyLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewSubmitReplyReplyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SubmitReplyReplyLogic {
return &SubmitReplyReplyLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *SubmitReplyReplyLogic) SubmitReplyReply(r *http.Request, req *types.ReplyReplyRequest) (resp *types.CommentResponse, err error) {
res := verify.VerifySlideCaptcha(l.ctx, l.svcCtx.RedisClient, req.Point, req.Key)
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"))
}
ua := useragent.New(userAgent)
// 获取客户端IP及位置信息
ip := utils.GetClientIP(r)
location, err := l.svcCtx.Ip2Region.SearchByStr(ip)
if err != nil {
return nil, err
}
location = utils.RemoveZeroAndAdjust(location)
// 获取浏览器与操作系统信息
browser, _ := ua.Browser()
operatingSystem := ua.OS()
uid, ok := l.ctx.Value("user_id").(string)
if !ok {
return nil, errors.New("user_id not found in context")
}
// 判断作者身份
var isAuthor int64 = 0
if uid == req.Author {
isAuthor = 1
}
// XSS过滤
xssFilterContent := utils.XssFilter(req.Content)
if xssFilterContent == "" {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
}
commentContent := l.svcCtx.Sensitive.Replace(xssFilterContent, '*')
tx := l.svcCtx.DB.Begin()
topicType := constant.CommentTopicType
commentType := constant.REPLY
replyReply := &model.ScaCommentReply{
Content: commentContent,
UserID: uid,
TopicID: req.TopicId,
TopicType: topicType,
CommentType: commentType,
Author: isAuthor,
CommentIP: ip,
Location: location,
Browser: browser,
OperatingSystem: operatingSystem,
Agent: userAgent,
ReplyID: req.ReplyId,
ReplyUser: req.ReplyUser,
ReplyTo: req.ReplyTo,
}
err = tx.ScaCommentReply.Create(replyReply)
if err != nil {
return nil, err
}
commentReply := l.svcCtx.DB.ScaCommentReply
update, err := tx.ScaCommentReply.Where(commentReply.ID.Eq(req.ReplyId)).Update(commentReply.ReplyCount, commentReply.ReplyCount.Add(1))
if err != nil {
return nil, err
}
if update.RowsAffected == 0 {
return nil, errors2.New(http.StatusInternalServerError, i18n.FormatText(l.ctx, "comment.commentError"))
}
// 处理图片
if len(req.Images) > 0 {
imagesData, err := utils.ProcessImages(req.Images)
if err != nil {
return nil, err
}
commentImages := &types.CommentImages{
UserId: uid,
TopicId: req.TopicId,
CommentId: replyReply.ID,
Images: imagesData,
}
newCollection := mongodb.MustNewCollection[types.CommentImages](l.svcCtx.MongoClient, constant.COMMENT_IMAGES)
_, err = newCollection.Creator().InsertOne(l.ctx, commentImages)
if err != nil {
return nil, err
}
}
// 构建响应
commentResponse := &types.CommentResponse{
Id: replyReply.ID,
Content: commentContent,
UserId: uid,
TopicId: replyReply.TopicID,
Author: isAuthor,
Location: location,
Browser: browser,
OperatingSystem: operatingSystem,
CreatedTime: time.Now().Format(constant.TimeFormat),
ReplyId: replyReply.ReplyID,
ReplyUser: replyReply.ReplyUser,
ReplyTo: replyReply.ReplyTo,
}
// 提交事务
if err = tx.Commit(); err != nil {
return nil, err
}
return commentResponse, nil
}

View File

@@ -0,0 +1,20 @@
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

@@ -0,0 +1,24 @@
package middleware
import (
"github.com/casbin/casbin/v2"
"net/http"
"schisandra-album-cloud-microservices/common/middleware"
)
type CasbinVerifyMiddleware struct {
casbin *casbin.SyncedCachedEnforcer
}
func NewCasbinVerifyMiddleware(casbin *casbin.SyncedCachedEnforcer) *CasbinVerifyMiddleware {
return &CasbinVerifyMiddleware{
casbin: casbin,
}
}
func (m *CasbinVerifyMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
middleware.CasbinMiddleware(w, r, m.casbin)
next(w, r)
}
}

View File

@@ -0,0 +1,24 @@
package middleware
import (
"github.com/redis/go-redis/v9"
"net/http"
"schisandra-album-cloud-microservices/common/middleware"
)
type NonceMiddleware struct {
RedisClient *redis.Client
}
func NewNonceMiddleware(redisClient *redis.Client) *NonceMiddleware {
return &NonceMiddleware{
RedisClient: redisClient,
}
}
func (m *NonceMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
middleware.NonceMiddleware(w, r, m.RedisClient)
next(w, r)
}
}

View File

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

View File

@@ -0,0 +1,52 @@
package svc
import (
"github.com/casbin/casbin/v2"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/redis/go-redis/v9"
"github.com/zeromicro/go-zero/rest"
sensitive "github.com/zmexing/go-sensitive-word"
"go.mongodb.org/mongo-driver/v2/mongo"
"schisandra-album-cloud-microservices/app/community/api/internal/config"
"schisandra-album-cloud-microservices/app/community/api/internal/middleware"
"schisandra-album-cloud-microservices/app/community/api/model/mongodb"
"schisandra-album-cloud-microservices/app/community/api/model/mysql"
"schisandra-album-cloud-microservices/app/community/api/model/mysql/query"
"schisandra-album-cloud-microservices/common/casbinx"
"schisandra-album-cloud-microservices/common/ip2region"
"schisandra-album-cloud-microservices/common/redisx"
"schisandra-album-cloud-microservices/common/sensitivex"
)
type ServiceContext struct {
Config config.Config
SecurityHeadersMiddleware rest.Middleware
CasbinVerifyMiddleware rest.Middleware
AuthorizationMiddleware rest.Middleware
NonceMiddleware rest.Middleware
MongoClient *mongo.Database
DB *query.Query
CasbinEnforcer *casbin.SyncedCachedEnforcer
RedisClient *redis.Client
Sensitive *sensitive.Manager
Ip2Region *xdb.Searcher
}
func NewServiceContext(c config.Config) *ServiceContext {
redisClient := redisx.NewRedis(c.Redis.Host, c.Redis.Pass, c.Redis.DB)
db, queryDB := mysql.NewMySQL(c.Mysql.DataSource, c.Mysql.MaxOpenConn, c.Mysql.MaxIdleConn, redisClient)
casbinEnforcer := casbinx.NewCasbin(db)
return &ServiceContext{
Config: c,
SecurityHeadersMiddleware: middleware.NewSecurityHeadersMiddleware().Handle,
CasbinVerifyMiddleware: middleware.NewCasbinVerifyMiddleware(casbinEnforcer).Handle,
AuthorizationMiddleware: middleware.NewAuthorizationMiddleware().Handle,
NonceMiddleware: middleware.NewNonceMiddleware(redisClient).Handle,
MongoClient: mongodb.NewMongoDB(c.Mongo.Uri, c.Mongo.Username, c.Mongo.Password, c.Mongo.AuthSource, c.Mongo.Database),
DB: queryDB,
CasbinEnforcer: casbinEnforcer,
RedisClient: redisClient,
Sensitive: sensitivex.NewSensitive(),
Ip2Region: ip2region.NewIP2Region(),
}
}

View File

@@ -0,0 +1,54 @@
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"`
UserID string `json:"user_id"`
TopicID string `json:"topic_id"`
Content string `json:"content"`
CreatedAt time.Time `json:"created_at"`
Author int64 `json:"author"`
Likes int64 `json:"likes"`
ReplyCount int64 `json:"reply_count"`
Browser string `json:"browser"`
OperatingSystem string `json:"operating_system"`
Location string `json:"location"`
Avatar string `json:"avatar"`
Nickname string `json:"nickname"`
}
// ReplyListQueryResult 回复列表查询结果
type ReplyListQueryResult struct {
ID int64 `json:"id"`
UserID string `json:"user_id"`
TopicID string `json:"topic_id"`
Content string `json:"content"`
CreatedAt time.Time `json:"created_at"`
Author int64 `json:"author"`
Likes int64 `json:"likes"`
ReplyCount int64 `json:"reply_count"`
Browser string `json:"browser"`
OperatingSystem string `json:"operating_system"`
Location string `json:"location"`
Avatar string `json:"avatar"`
Nickname string `json:"nickname"`
ReplyUser string `json:"reply_user"`
ReplyId int64 `json:"reply_id"`
ReplyTo int64 `json:"reply_to"`
ReplyNickname string `json:"reply_nickname"`
}

View File

@@ -0,0 +1,105 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.7.3
package types
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"`
}
type CommentDisLikeRequest struct {
TopicId string `json:"topic_id"`
CommentId int64 `json:"comment_id"`
}
type CommentLikeRequest struct {
TopicId string `json:"topic_id"`
CommentId int64 `json:"comment_id"`
}
type CommentListPageResponse struct {
Size int `json:"size"`
Total int64 `json:"total"`
Current int `json:"current"`
Comments []CommentContent `json:"comments"`
}
type CommentListRequest struct {
TopicId string `json:"topic_id"`
Page int `json:"page,default=1,optional"`
Size int `json:"size,default=5,optional"`
IsHot bool `json:"is_hot,default=true,optional"`
}
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"`
}
type CommentResponse struct {
Id int64 `json:"id"`
Content string `json:"content"`
UserId string `json:"user_id"`
TopicId string `json:"topic_id"`
Author int64 `json:"author"`
Location string `json:"location"`
Browser string `json:"browser"`
OperatingSystem string `json:"operating_system"`
CreatedTime string `json:"created_time"`
ReplyId int64 `json:"reply_id,omitempty"`
ReplyUser string `json:"reply_user,omitempty"`
ReplyTo int64 `json:"reply_to,omitempty"`
}
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"`
}
type ReplyListRequest struct {
TopicId string `json:"topic_id"`
CommentId int64 `json:"comment_id"`
Page int `json:"page,default=1,optional"`
Size int `json:"size,default=5,optional"`
}
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"`
}