From 68b3ab95b9fa4d9091a18314a120eb3fbd219c3d Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Fri, 30 Aug 2024 14:30:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5comment=20servicecrud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generate/protobuf/tweeting.proto | 174 +++++++++++++----- internal/domain/errors.go | 30 +++ .../commentservice/delete_comment_logic.go | 44 +++++ .../commentservice/get_comments_logic.go | 102 ++++++++++ .../logic/commentservice/new_comment_logic.go | 95 ++++++++++ .../commentservice/new_comment_logic_test.go | 1 + .../commentservice/update_comment_logic.go | 81 ++++++++ .../logic/postservice/update_post_logic.go | 15 +- internal/model/mongo/comment_model.go | 77 +++++++- internal/model/mongo/comment_model_gen.go | 6 +- internal/model/mongo/comment_types.go | 21 ++- internal/model/mongo/post_model.go | 4 +- internal/model/mongo/post_types.go | 4 +- .../commentservice/comment_service_server.go | 47 +++++ .../server/postservice/post_service_server.go | 8 +- internal/svc/init_mongo.go | 7 + internal/svc/service_context.go | 11 +- 17 files changed, 652 insertions(+), 75 deletions(-) create mode 100644 internal/logic/commentservice/delete_comment_logic.go create mode 100644 internal/logic/commentservice/get_comments_logic.go create mode 100644 internal/logic/commentservice/new_comment_logic.go create mode 100644 internal/logic/commentservice/new_comment_logic_test.go create mode 100644 internal/logic/commentservice/update_comment_logic.go create mode 100644 internal/server/commentservice/comment_service_server.go diff --git a/generate/protobuf/tweeting.proto b/generate/protobuf/tweeting.proto index 968a1aa..cbc1b2c 100644 --- a/generate/protobuf/tweeting.proto +++ b/generate/protobuf/tweeting.proto @@ -9,90 +9,101 @@ message OKResp {} message NoneReq {} // 分頁信息 -message Pager { - int64 total = 1; // 總數量 - int64 size = 2; // 每頁數量 - int64 index = 3; // 當前頁碼 +message Pager +{ + int64 total = 1; // 總數量 + int64 size = 2; // 每頁數量 + int64 index = 3; // 當前頁碼 } // ========== 貼文區 =========== // ------ NewPost 新增貼文-------- -message NewPostReq { - string uid = 1; // 發佈貼文的用戶ID - string content = 2; // 貼文內容 - repeated string tags = 3; // 貼文相關標籤 - repeated Media media = 4; // 這筆文章的所有 Media URL - bool is_ad = 5; // 是否為廣告 +message NewPostReq +{ + string uid = 1; // 發佈貼文的用戶ID + string content = 2; // 貼文內容 + repeated string tags = 3; // 貼文相關標籤 + repeated Media media = 4; // 這筆文章的所有 Media URL + bool is_ad = 5; // 是否為廣告 } -message Media { +message Media +{ string type = 1; string url = 2; } // 貼文回應 -message PostResp { - string post_id = 1; // 創建成功的貼文ID +message PostResp +{ + string post_id = 1; // 創建成功的貼文ID } // ------ DeletePost 刪除貼文 ------ // 刪除貼文的請求 -message DeletePostsReq { - repeated string post_id = 1; // 貼文ID +message DeletePostsReq +{ + repeated string post_id = 1; // 貼文ID } // ------ UpdatePost 更新貼文 ------ // 更新貼文的請求 -message UpdatePostReq { - string post_id = 1; // 貼文ID - repeated string tags = 2; // 新的標籤列表 - repeated Media media = 3; // 這筆文章的所有 Media URL - optional string content = 4; // 新的貼文內容 - optional int64 like_count = 5; // 喜歡數量 - optional int64 dislike_count = 6; // 不喜歡數量 +message UpdatePostReq +{ + string post_id = 1; // 貼文ID + repeated string tags = 2; // 新的標籤列表 + repeated Media media = 3; // 這筆文章的所有 Media URL + optional string content = 4; // 新的貼文內容 + optional int64 like_count = 5; // 喜歡數量 + optional int64 dislike_count = 6; // 不喜歡數量 } // ------ListPosts 查詢貼文 ------ // 查詢貼文的請求 -message QueryPostsReq { - repeated string uid = 1; // 可選:根據用戶ID篩選貼文 - repeated string post_id = 2; // 可選:根據貼文ID篩選貼文 - optional int32 only_ads = 3; // 可選:是否只顯示廣告 0 不篩選 1 只顯示廣告 2 不顯示廣告 - int32 page_index = 4; // 分頁的頁碼 - int32 page_size = 5; // 每頁顯示的數量 +message QueryPostsReq +{ + repeated string uid = 1; // 可選:根據用戶ID篩選貼文 + repeated string post_id = 2; // 可選:根據貼文ID篩選貼文 + optional int32 only_ads = 3; // 可選:是否只顯示廣告 0 不篩選 1 只顯示廣告 2 不顯示廣告 + int32 page_index = 4; // 分頁的頁碼 + int32 page_size = 5; // 每頁顯示的數量 } // 貼文詳情 -message PostDetailItem { - string post_id = 1; // 貼文ID - string uid = 2; // 發佈用戶ID - string content = 3; // 貼文內容 - repeated string tags = 4; // 標籤 - repeated Media media = 5; // 圖片URL - bool is_ad = 6; // 是否為廣告 - int64 created_at = 7; // 發佈時間 - int64 update_at = 8; // 更新時間 - int64 like_count = 9; // 讚數 - int64 dislike_count = 10; // 不喜歡數量 +message PostDetailItem +{ + string post_id = 1; // 貼文ID + string uid = 2; // 發佈用戶ID + string content = 3; // 貼文內容 + repeated string tags = 4; // 標籤 + repeated Media media = 5; // 圖片URL + bool is_ad = 6; // 是否為廣告 + int64 created_at = 7; // 發佈時間 + int64 update_at = 8; // 更新時間 + int64 like_count = 9; // 讚數 + int64 dislike_count = 10; // 不喜歡數量 } // 貼文列表回應 -message ListPostsResp { - repeated PostDetailItem posts = 1; // 貼文列表 +message ListPostsResp +{ + repeated PostDetailItem posts = 1; // 貼文列表 Pager page = 2; } -message ModifyLikeDislikeCountReq { - string post_id = 1; // 貼文的 ID - int64 reaction_type = 2; // 用戶的反應類型,可能是讚或不讚 - bool is_increment = 3; // 表示是否增加(true 表示增加,false 表示減少) - int64 count = 4; // 異動數量 +message ModifyLikeDislikeCountReq +{ + string post_id = 1; // 貼文的 ID + int64 reaction_type = 2; // 用戶的反應類型,可能是讚或不讚 + bool is_increment = 3; // 表示是否增加(true 表示增加,false 表示減少) + int64 count = 4; // 異動數量 } // ========== 定義貼文服務(最基本單位,不要把邏輯放進來,也考慮是否要做快取) ========== -service PostService { +service PostService +{ // CreatePost 新增貼文 rpc CreatePost(NewPostReq) returns (PostResp); // DeletePost 刪除貼文 @@ -102,3 +113,72 @@ service PostService { // ListPosts 查詢貼文 rpc ListPosts(QueryPostsReq) returns (ListPostsResp); } + +// ================================================================================================= + +// ------------ 評論貼文的請求 ------------ +message CommentPostReq +{ + string post_id = 1; // 貼文ID + string uid = 2; // 評論者ID + string content = 3; // 評論內容 +} + +message CommentPostResp +{ + string comment_id = 1; // 回應ID +} + +// ------------ 查詢評論的請求 ------------ +message GetCommentsReq +{ + string post_id = 1; // 貼文ID + int32 page_index = 2; // 分頁頁碼 + int32 page_size = 3; // 每頁顯示數量 +} + +// 評論詳情 +message CommentDetail +{ + string comment_id = 1; // 評論ID + string uid = 2; // 評論者ID + string content = 3; // 評論內容 + int64 created_at = 4; // 創建時間 + int64 like_count = 5; // 讚數 + int64 dislike_count = 6; // 不喜歡數量 +} + +// 評論列表回應 +message GetCommentsResp +{ + repeated CommentDetail comments = 1; // 評論列表 + Pager page = 2; +} + +// ------------ 刪除評論請求 ------------ +message DeleteCommentReq +{ + repeated string comment_id = 1; // 評論ID +} + +// 更新評論請求 +message UpdateCommentReq +{ + string comment_id = 1; // 評論ID + string content = 2; // 更新後的評論內容 + optional int64 like_count = 3; // 讚數 + optional int64 dislike_count = 4; // 不喜歡數量 +} + +// 定義評論服務 +service CommentService +{ + // NewComment 發表評論 + rpc NewComment(CommentPostReq) returns (CommentPostResp); + // GetComments 查詢評論 + rpc GetComments(GetCommentsReq) returns (GetCommentsResp); + // DeleteComment 刪除評論 + rpc DeleteComment(DeleteCommentReq) returns (OKResp); + // UpdateComment 更新評論 + rpc UpdateComment(UpdateCommentReq) returns (OKResp); +} \ No newline at end of file diff --git a/internal/domain/errors.go b/internal/domain/errors.go index 6c63b9d..d1432cb 100644 --- a/internal/domain/errors.go +++ b/internal/domain/errors.go @@ -1,5 +1,13 @@ package domain +import ( + ers "code.30cm.net/digimon/library-go/errs" + "code.30cm.net/digimon/library-go/errs/code" + "fmt" + "github.com/zeromicro/go-zero/core/logx" + "strings" +) + type ErrorCode uint32 func (e ErrorCode) ToUint32() uint32 { @@ -15,3 +23,25 @@ const ( UpdatePostError ListPostError ) + +const ( + CommentFoundErrorCode ErrorCode = iota + 10 + CommentInsertErrorCode + CommentDeleteErrorCode + CommentUpdateErrorCode + CommentListErrorCode +) + +func CommentError(ec ErrorCode, s ...string) *ers.LibError { + return ers.NewError(code.CloudEPTweeting, code.DBError, + ec.ToUint32(), + fmt.Sprintf("%s", strings.Join(s, " "))) +} + +func CommentErrorL(ec ErrorCode, + l logx.Logger, filed []logx.LogField, s ...string) *ers.LibError { + e := CommentError(ec, s...) + l.WithCallerSkip(1).WithFields(filed...).Error(e.Error()) + + return e +} diff --git a/internal/logic/commentservice/delete_comment_logic.go b/internal/logic/commentservice/delete_comment_logic.go new file mode 100644 index 0000000..467d027 --- /dev/null +++ b/internal/logic/commentservice/delete_comment_logic.go @@ -0,0 +1,44 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/internal/domain" + "context" + + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/svc" + + "github.com/zeromicro/go-zero/core/logx" +) + +type DeleteCommentLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewDeleteCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteCommentLogic { + return &DeleteCommentLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +// DeleteComment 刪除評論 +func (l *DeleteCommentLogic) DeleteComment(in *tweeting.DeleteCommentReq) (*tweeting.OKResp, error) { + _, err := l.svcCtx.CommentModel.DeleteMany(l.ctx, in.GetCommentId()...) + if err != nil { + e := domain.CommentErrorL( + domain.CommentDeleteErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "CommentModel.DeleteMany"}, + {Key: "err", Value: err}, + }, + "failed to del comment").Wrap(err) + return nil, e + } + + return &tweeting.OKResp{}, nil +} diff --git a/internal/logic/commentservice/get_comments_logic.go b/internal/logic/commentservice/get_comments_logic.go new file mode 100644 index 0000000..ec6b291 --- /dev/null +++ b/internal/logic/commentservice/get_comments_logic.go @@ -0,0 +1,102 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + ers "code.30cm.net/digimon/library-go/errs" + "context" + + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/svc" + + "github.com/zeromicro/go-zero/core/logx" +) + +type GetCommentsLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewGetCommentsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommentsLogic { + return &GetCommentsLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +// 只列出要驗證的資料 +type listReq struct { + PostID string `json:"post_id" validate:"required"` + PageSize int64 `json:"page_size" validate:"required"` + PageIndex int64 `json:"page_index" validate:"required"` +} + +// 將單個 Post 轉換為 PostDetailItem +func convertToCommentDetailItem(item *model.Comment) *tweeting.CommentDetail { + return &tweeting.CommentDetail{ + CommentId: item.ID.Hex(), + Uid: item.UID, + Content: item.Content, + CreatedAt: item.CreateAt, + LikeCount: int64(item.LikeCount), + DislikeCount: int64(item.DisLikeCount), + } +} + +// GetComments 查詢評論 +// 目前應該是沒有需求是要看 uid 在哪裡留過言,如果未來業務邏輯有再新增 +func (l *GetCommentsLogic) GetComments(in *tweeting.GetCommentsReq) (*tweeting.GetCommentsResp, error) { + // 將 PageSize 和 PageIndex 提前轉換為 int64 + pageSize := int64(in.GetPageSize()) + pageIndex := int64(in.GetPageIndex()) + + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&listReq{ + PageSize: pageSize, + PageIndex: pageIndex, + PostID: in.GetPostId(), + }); err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + + // 構建查詢條件 + query := &model.QueryCommentModelReq{ + PostID: in.GetPostId(), + PageSize: pageSize, + PageIndex: pageIndex, + } + + // 執行查詢 + find, count, err := l.svcCtx.CommentModel.Find(l.ctx, query) + if err != nil { + e := domain.CommentErrorL( + domain.CommentListErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "query", Value: query}, + {Key: "func", Value: "CommentModel.Find"}, + {Key: "err", Value: err}, + }, + "failed to find comment").Wrap(err) + return nil, e + } + + // 將查詢結果轉換為 API 回應格式 + result := make([]*tweeting.CommentDetail, 0, count) + for _, item := range find { + result = append(result, convertToCommentDetailItem(item)) + } + + // 返回結果 + return &tweeting.GetCommentsResp{ + Comments: result, + Page: &tweeting.Pager{ + Total: count, + Index: pageIndex, + Size: pageSize, + }, + }, nil +} diff --git a/internal/logic/commentservice/new_comment_logic.go b/internal/logic/commentservice/new_comment_logic.go new file mode 100644 index 0000000..07abc0c --- /dev/null +++ b/internal/logic/commentservice/new_comment_logic.go @@ -0,0 +1,95 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + "app-cloudep-tweeting-service/internal/svc" + ers "code.30cm.net/digimon/library-go/errs" + "context" + "errors" + + "github.com/zeromicro/go-zero/core/logx" +) + +type NewCommentLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewNewCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewCommentLogic { + return &NewCommentLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +// 輸入的定義 -> 檢查用 +type newCommentReq struct { + UID string `json:"uid" validate:"required"` + Content string `json:"content" validate:"required,lte=500"` // 貼文限制 500 字內 + PostID string `json:"post_id" validate:"required"` +} + +// NewComment 發表評論 +func (l *NewCommentLogic) NewComment(in *tweeting.CommentPostReq) (*tweeting.CommentPostResp, error) { + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&newCommentReq{ + UID: in.GetUid(), + Content: in.GetContent(), + PostID: in.GetPostId(), + }); err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + + // 檢查是否有這個文章 + _, err := l.svcCtx.PostModel.FindOne(l.ctx, in.GetPostId()) + if err != nil { + if errors.Is(model.ErrNotFound, err) { + // 錯誤代碼 05-031-00 + return nil, ers.ResourceNotFound("failed to find post: ", in.GetPostId()) + } + // 錯誤代碼 05-021-10 + e := domain.CommentErrorL( + domain.CommentFoundErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.FindOne"}, + {Key: "err", Value: err}, + }, + "failed to find post:", in.GetPostId()).Wrap(err) + + return nil, e + } + + data := &model.Comment{ + PostID: in.GetPostId(), + UID: in.GetUid(), + Content: in.GetContent(), + LikeCount: 0, + DisLikeCount: 0, + } + err = l.svcCtx.CommentModel.Insert(l.ctx, data) + if err != nil { + // 錯誤代碼 05-021-11 + e := domain.CommentErrorL( + domain.CommentInsertErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "CommentModel.Insert"}, + {Key: "err", Value: err}, + }, + "failed to insert comment:", in.GetPostId()).Wrap(err) + + return nil, e + } + + return &tweeting.CommentPostResp{ + CommentId: data.ID.Hex(), + }, nil +} diff --git a/internal/logic/commentservice/new_comment_logic_test.go b/internal/logic/commentservice/new_comment_logic_test.go new file mode 100644 index 0000000..40aa145 --- /dev/null +++ b/internal/logic/commentservice/new_comment_logic_test.go @@ -0,0 +1 @@ +package commentservicelogic diff --git a/internal/logic/commentservice/update_comment_logic.go b/internal/logic/commentservice/update_comment_logic.go new file mode 100644 index 0000000..5c64d66 --- /dev/null +++ b/internal/logic/commentservice/update_comment_logic.go @@ -0,0 +1,81 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + "app-cloudep-tweeting-service/internal/svc" + ers "code.30cm.net/digimon/library-go/errs" + "context" + "go.mongodb.org/mongo-driver/bson/primitive" + + "github.com/zeromicro/go-zero/core/logx" +) + +type UpdateCommentLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewUpdateCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateCommentLogic { + return &UpdateCommentLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +type checkCommentId struct { + CommentId string `validate:"required"` + Content string `json:"content,omitempty" validate:"lte=500"` +} + +// UpdateComment 更新評論 +func (l *UpdateCommentLogic) UpdateComment(in *tweeting.UpdateCommentReq) (*tweeting.OKResp, error) { + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&checkCommentId{ + CommentId: in.GetCommentId(), + Content: in.GetContent(), + }); err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + // 沒有就沒有,有就走全覆蓋 + update := model.Comment{} + oid, err := primitive.ObjectIDFromHex(in.GetCommentId()) + if err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat("failed to get correct comment id") + } + update.ID = oid + update.Content = in.GetContent() + // 因為 0 也有意義,所以如果是真的沒帶進來,用 -1 帶進去表示不作動 + if in.LikeCount == nil { + update.LikeCount = -1 + } else { + update.LikeCount = in.GetLikeCount() + } + if in.DislikeCount == nil { + update.DisLikeCount = -1 + } else { + update.DisLikeCount = in.GetDislikeCount() + } + + _, err = l.svcCtx.CommentModel.UpdateOptional(l.ctx, &update) + if err != nil { + // 錯誤代碼 05-021-13 + e := domain.CommentErrorL( + domain.CommentUpdateErrorCode, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "CommentModel.UpdateOptional"}, + {Key: "err", Value: err}, + }, + "failed to update comment:", in.CommentId).Wrap(err) + return nil, e + } + + return &tweeting.OKResp{}, nil +} diff --git a/internal/logic/postservice/update_post_logic.go b/internal/logic/postservice/update_post_logic.go index acdb501..4343e89 100644 --- a/internal/logic/postservice/update_post_logic.go +++ b/internal/logic/postservice/update_post_logic.go @@ -80,8 +80,19 @@ func (l *UpdatePostLogic) UpdatePost(in *tweeting.UpdatePostReq) (*tweeting.OKRe } update.MediaURL = media update.Content = in.GetContent() - update.Like = uint64(in.GetLikeCount()) - update.DisLike = uint64(in.GetDislikeCount()) + + // 因為 0 也有意義,所以如果是真的沒帶進來,用 -1 帶進去表示不作動 + if in.LikeCount == nil { + update.Like = -1 + } else { + update.Like = in.GetLikeCount() + } + + if in.DislikeCount == nil { + update.DisLike = -1 + } else { + update.DisLike = in.GetDislikeCount() + } _, err = l.svcCtx.PostModel.UpdateOptional(l.ctx, &update) if err != nil { diff --git a/internal/model/mongo/comment_model.go b/internal/model/mongo/comment_model.go index 30fe357..adb0d29 100644 --- a/internal/model/mongo/comment_model.go +++ b/internal/model/mongo/comment_model.go @@ -1,6 +1,14 @@ package model -import "github.com/zeromicro/go-zero/core/stores/mon" +import ( + "context" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/stores/mon" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "time" +) var _ CommentModel = (*customCommentModel)(nil) @@ -9,13 +17,27 @@ type ( // and implement the added methods in customCommentModel. CommentModel interface { commentModel + DeleteMany(ctx context.Context, id ...string) (int64, error) + UpdateOptional(ctx context.Context, data *Comment) (*mongo.UpdateResult, error) + Find(ctx context.Context, param *QueryCommentModelReq) ([]*Comment, int64, error) } customCommentModel struct { *defaultCommentModel } + + QueryCommentModelReq struct { + PostID string + PageSize int64 + PageIndex int64 + } ) +func (xw customCommentModel) Find(ctx context.Context, param *QueryCommentModelReq) ([]*Comment, int64, error) { + // TODO implement me + panic("implement me") +} + // NewCommentModel returns a model for the mongo. func NewCommentModel(url, db, collection string) CommentModel { conn := mon.MustNewModel(url, db, collection) @@ -23,3 +45,56 @@ func NewCommentModel(url, db, collection string) CommentModel { defaultCommentModel: newDefaultCommentModel(conn), } } + +func (m *defaultCommentModel) DeleteMany(ctx context.Context, id ...string) (int64, error) { + objectIDs := make([]primitive.ObjectID, 0, len(id)) + + // prepare + for _, item := range id { + oid, err := primitive.ObjectIDFromHex(item) + if err != nil { + logx.WithCallerSkip(1).WithFields( + logx.Field("func", "defaultPostModel.DeleteMany"), + logx.Field("id", item), + ).Error(err.Error()) + + continue + } + objectIDs = append(objectIDs, oid) + } + + // 檢查是否有有效的 ObjectIDs + if len(objectIDs) == 0 { + return 0, ErrNotFound + } + + // 刪除文檔 + res, err := m.conn.DeleteMany(ctx, bson.M{"_id": bson.M{"$in": objectIDs}}) + if err != nil { + return 0, err + } + + return res, err +} + +func (m *defaultCommentModel) UpdateOptional(ctx context.Context, data *Comment) (*mongo.UpdateResult, error) { + update := bson.M{"$set": bson.M{}} + + if data.Content != "" { + update["$set"].(bson.M)["content"] = data.Content + } + + if data.LikeCount != -1 { + update["$set"].(bson.M)["like_count"] = data.LikeCount + } + + if data.DisLikeCount != -1 { + update["$set"].(bson.M)["dis_like_count"] = data.DisLikeCount + } + + // UpdateAt 是每次都需要更新的,不用檢查 + update["$set"].(bson.M)["updateAt"] = time.Now().UTC().UnixNano() + res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, update) + + return res, err +} diff --git a/internal/model/mongo/comment_model_gen.go b/internal/model/mongo/comment_model_gen.go index 6c5609b..e575dd8 100644 --- a/internal/model/mongo/comment_model_gen.go +++ b/internal/model/mongo/comment_model_gen.go @@ -29,8 +29,8 @@ func newDefaultCommentModel(conn *mon.Model) *defaultCommentModel { func (m *defaultCommentModel) Insert(ctx context.Context, data *Comment) error { if data.ID.IsZero() { data.ID = primitive.NewObjectID() - data.CreateAt = time.Now() - data.UpdateAt = time.Now() + data.CreateAt = time.Now().UTC().UnixNano() + data.UpdateAt = time.Now().UTC().UnixNano() } _, err := m.conn.InsertOne(ctx, data) @@ -57,7 +57,7 @@ func (m *defaultCommentModel) FindOne(ctx context.Context, id string) (*Comment, } func (m *defaultCommentModel) Update(ctx context.Context, data *Comment) (*mongo.UpdateResult, error) { - data.UpdateAt = time.Now() + data.UpdateAt = time.Now().UTC().UnixNano() res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) return res, err diff --git a/internal/model/mongo/comment_types.go b/internal/model/mongo/comment_types.go index f82cabc..ae83fa4 100644 --- a/internal/model/mongo/comment_types.go +++ b/internal/model/mongo/comment_types.go @@ -1,14 +1,23 @@ package model import ( - "time" - "go.mongodb.org/mongo-driver/bson/primitive" ) type Comment struct { - ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` - // TODO: Fill your own fields - UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"` - CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"` + ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` + PostID string `bson:"post_id" json:"post_id"` + UID string `bson:"uid" json:"uid"` // 留下留言的人 + Content string `bson:"content" json:"content"` // 留言內容 + LikeCount int64 `bson:"like_count" json:"like_count"` // 喜歡這則留言的人 + DisLikeCount int64 `bson:"dis_like_count" json:"dis_like_count"` // 不喜歡這則留言的人 + UpdateAt int64 `bson:"updateAt,omitempty" json:"updateAt,omitempty"` + CreateAt int64 `bson:"createAt,omitempty" json:"createAt,omitempty"` } + +func (c Comment) CollectionName() string { + return "comment" +} + +// 照邏輯,應該需要建立的索引有 +// 複合索引:(PostID + CreateAt) diff --git a/internal/model/mongo/post_model.go b/internal/model/mongo/post_model.go index 9829de5..d97b772 100644 --- a/internal/model/mongo/post_model.go +++ b/internal/model/mongo/post_model.go @@ -106,11 +106,11 @@ func (m *defaultPostModel) UpdateOptional(ctx context.Context, data *Post) (*mon update["$set"].(bson.M)["media_url"] = data.MediaURL } - if data.Like != 0 { + if data.Like != -1 { update["$set"].(bson.M)["like"] = data.Like } - if data.DisLike != 0 { + if data.DisLike != -1 { update["$set"].(bson.M)["dislike"] = data.DisLike } diff --git a/internal/model/mongo/post_types.go b/internal/model/mongo/post_types.go index 3396ab9..7b50259 100644 --- a/internal/model/mongo/post_types.go +++ b/internal/model/mongo/post_types.go @@ -12,8 +12,8 @@ type Post struct { IsAd bool `bson:"is_ad" json:"is_ad"` // 此則貼文是否為廣告貼文 -> 過濾條件 Tags []string `bson:"tags" json:"tags"` // 本則貼文的標籤,不提供搜尋,僅提供顯示(存名字,ID 建立之後就不提供修改與刪除) MediaURL []Media `bson:"media_url" json:"media_url"` // 網址 - Like uint64 `bson:"like" json:"like"` // 讚數量 - DisLike uint64 `bson:"dislike" json:"dislike"` // 不讚數量 + Like int64 `bson:"like" json:"like"` // 讚數量 + DisLike int64 `bson:"dislike" json:"dislike"` // 不讚數量 UpdateAt int64 `bson:"updateAt,omitempty" json:"updateAt,omitempty"` CreateAt int64 `bson:"createAt,omitempty" json:"createAt,omitempty"` // -> 排序條件 } diff --git a/internal/server/commentservice/comment_service_server.go b/internal/server/commentservice/comment_service_server.go new file mode 100644 index 0000000..f434bad --- /dev/null +++ b/internal/server/commentservice/comment_service_server.go @@ -0,0 +1,47 @@ +// Code generated by goctl. DO NOT EDIT. +// Source: tweeting.proto + +package server + +import ( + "context" + + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/logic/commentservice" + "app-cloudep-tweeting-service/internal/svc" +) + +type CommentServiceServer struct { + svcCtx *svc.ServiceContext + tweeting.UnimplementedCommentServiceServer +} + +func NewCommentServiceServer(svcCtx *svc.ServiceContext) *CommentServiceServer { + return &CommentServiceServer{ + svcCtx: svcCtx, + } +} + +// NewComment 發表評論 +func (s *CommentServiceServer) NewComment(ctx context.Context, in *tweeting.CommentPostReq) (*tweeting.CommentPostResp, error) { + l := commentservicelogic.NewNewCommentLogic(ctx, s.svcCtx) + return l.NewComment(in) +} + +// GetComments 查詢評論 +func (s *CommentServiceServer) GetComments(ctx context.Context, in *tweeting.GetCommentsReq) (*tweeting.GetCommentsResp, error) { + l := commentservicelogic.NewGetCommentsLogic(ctx, s.svcCtx) + return l.GetComments(in) +} + +// DeleteComment 刪除評論 +func (s *CommentServiceServer) DeleteComment(ctx context.Context, in *tweeting.DeleteCommentReq) (*tweeting.OKResp, error) { + l := commentservicelogic.NewDeleteCommentLogic(ctx, s.svcCtx) + return l.DeleteComment(in) +} + +// UpdateComment 更新評論 +func (s *CommentServiceServer) UpdateComment(ctx context.Context, in *tweeting.UpdateCommentReq) (*tweeting.OKResp, error) { + l := commentservicelogic.NewUpdateCommentLogic(ctx, s.svcCtx) + return l.UpdateComment(in) +} diff --git a/internal/server/postservice/post_service_server.go b/internal/server/postservice/post_service_server.go index 27f442f..07d52d3 100644 --- a/internal/server/postservice/post_service_server.go +++ b/internal/server/postservice/post_service_server.go @@ -7,7 +7,7 @@ import ( "context" "app-cloudep-tweeting-service/gen_result/pb/tweeting" - postservicelogic "app-cloudep-tweeting-service/internal/logic/postservice" + "app-cloudep-tweeting-service/internal/logic/postservice" "app-cloudep-tweeting-service/internal/svc" ) @@ -45,9 +45,3 @@ func (s *PostServiceServer) ListPosts(ctx context.Context, in *tweeting.QueryPos l := postservicelogic.NewListPostsLogic(ctx, s.svcCtx) return l.ListPosts(in) } - -// ModifyLikeDislikeCount 調整讚或不讚數量 -func (s *PostServiceServer) ModifyLikeDislikeCount(ctx context.Context, in *tweeting.ModifyLikeDislikeCountReq) (*tweeting.OKResp, error) { - l := postservicelogic.NewModifyLikeDislikeCountLogic(ctx, s.svcCtx) - return l.ModifyLikeDislikeCount(in) -} diff --git a/internal/svc/init_mongo.go b/internal/svc/init_mongo.go index 980c67e..d815d98 100644 --- a/internal/svc/init_mongo.go +++ b/internal/svc/init_mongo.go @@ -14,7 +14,14 @@ func mustMongoConnectUrl(c config.Config) string { ) } +// TODO 思考快取做在那邊 + func MustPostModel(c config.Config) model.PostModel { postCollection := model.Post{} return model.NewPostModel(mustMongoConnectUrl(c), c.Mongo.Database, postCollection.CollectionName()) } + +func MustCommentModel(c config.Config) model.CommentModel { + m := model.Comment{} + return model.NewCommentModel(mustMongoConnectUrl(c), c.Mongo.Database, m.CollectionName()) +} diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index ec86c98..6dff22c 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -11,14 +11,15 @@ type ServiceContext struct { Config config.Config Validate vi.Validate - PostModel model.PostModel + PostModel model.PostModel + CommentModel model.CommentModel } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ - Config: c, - Validate: vi.MustValidator(), - - PostModel: MustPostModel(c), + Config: c, + Validate: vi.MustValidator(), + PostModel: MustPostModel(c), + CommentModel: MustCommentModel(c), } }