From 5d22b9c575c8ea176c5c3ba269d41149c4943caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=80=A7=E9=A9=8A?= Date: Fri, 30 Aug 2024 07:08:43 +0000 Subject: [PATCH] feature/post_v2 (#2) Co-authored-by: daniel.w Reviewed-on: https://code.30cm.net/digimon/app-cloudep-tweeting-service/pulls/2 --- Makefile | 24 +- etc/tweeting.yaml | 8 + .../mongodb/20240829054501_post.up.js | 9 + generate/protobuf/tweeting.proto | 251 +++++++++--------- go.mod | 20 ++ internal/config/config.go | 8 + internal/domain/const.go | 13 + internal/domain/errors.go | 48 ++++ internal/domain/status.go | 12 + .../logic/commentservice/count_like_logic.go | 31 --- .../commentservice/delete_comment_logic.go | 15 +- .../delete_comment_logic_test.go | 85 ++++++ .../commentservice/get_comments_logic.go | 82 +++++- .../commentservice/get_comments_logic_test.go | 120 +++++++++ .../commentservice/get_like_status_logic.go | 31 --- .../commentservice/like_comment_logic.go | 31 --- .../logic/commentservice/like_list_logic.go | 31 --- .../logic/commentservice/new_comment_logic.go | 79 +++++- .../commentservice/new_comment_logic_test.go | 124 +++++++++ .../commentservice/update_comment_logic.go | 57 +++- .../update_comment_logic_test.go | 124 +++++++++ .../logic/postservice/count_like_logic.go | 31 --- .../logic/postservice/create_post_logic.go | 94 +++++++ .../postservice/create_post_logic_test.go | 107 ++++++++ .../logic/postservice/delete_post_logic.go | 19 +- .../postservice/delete_post_logic_test.go | 81 ++++++ .../postservice/get_like_status_logic.go | 31 --- internal/logic/postservice/like_list_logic.go | 31 --- internal/logic/postservice/like_logic.go | 31 --- .../logic/postservice/list_posts_logic.go | 102 ++++++- .../postservice/list_posts_logic_test.go | 164 ++++++++++++ internal/logic/postservice/new_post_logic.go | 31 --- .../logic/postservice/update_post_logic.go | 66 ++++- .../postservice/update_post_logic_test.go | 118 ++++++++ internal/mock/lib/validate.go | 73 +++++ internal/mock/model/comment_model.go | 152 +++++++++++ internal/mock/model/comment_model_gen.go | 101 +++++++ internal/mock/model/post_model.go | 152 +++++++++++ internal/mock/model/post_model_gen.go | 101 +++++++ internal/model/mongo/comment_likes_model.go | 25 -- .../model/mongo/comment_likes_model_gen.go | 74 ------ internal/model/mongo/comment_likes_types.go | 14 - internal/model/mongo/comment_model.go | 81 +++++- internal/model/mongo/comment_model_gen.go | 31 +-- internal/model/mongo/comment_types.go | 21 +- internal/model/mongo/post_likes_model.go | 25 -- internal/model/mongo/post_likes_model_gen.go | 74 ------ internal/model/mongo/post_likes_types.go | 14 - internal/model/mongo/post_model.go | 163 +++++++++++- internal/model/mongo/post_model_gen.go | 31 +-- internal/model/mongo/post_types.go | 30 ++- internal/model/mongo/tags_model.go | 25 -- internal/model/mongo/tags_model_gen.go | 74 ------ internal/model/mongo/tags_types.go | 14 - .../commentservice/comment_service_server.go | 28 +- .../server/postservice/post_service_server.go | 34 +-- internal/svc/init_mongo.go | 27 ++ internal/svc/service_context.go | 18 +- tweeting.go | 2 - 59 files changed, 2599 insertions(+), 894 deletions(-) create mode 100644 generate/database/mongodb/20240829054501_post.up.js create mode 100644 internal/domain/const.go create mode 100644 internal/domain/errors.go create mode 100644 internal/domain/status.go delete mode 100644 internal/logic/commentservice/count_like_logic.go create mode 100644 internal/logic/commentservice/delete_comment_logic_test.go create mode 100644 internal/logic/commentservice/get_comments_logic_test.go delete mode 100644 internal/logic/commentservice/get_like_status_logic.go delete mode 100644 internal/logic/commentservice/like_comment_logic.go delete mode 100644 internal/logic/commentservice/like_list_logic.go create mode 100644 internal/logic/commentservice/new_comment_logic_test.go create mode 100644 internal/logic/commentservice/update_comment_logic_test.go delete mode 100644 internal/logic/postservice/count_like_logic.go create mode 100644 internal/logic/postservice/create_post_logic.go create mode 100644 internal/logic/postservice/create_post_logic_test.go create mode 100644 internal/logic/postservice/delete_post_logic_test.go delete mode 100644 internal/logic/postservice/get_like_status_logic.go delete mode 100644 internal/logic/postservice/like_list_logic.go delete mode 100644 internal/logic/postservice/like_logic.go create mode 100644 internal/logic/postservice/list_posts_logic_test.go delete mode 100644 internal/logic/postservice/new_post_logic.go create mode 100644 internal/logic/postservice/update_post_logic_test.go create mode 100644 internal/mock/lib/validate.go create mode 100644 internal/mock/model/comment_model.go create mode 100644 internal/mock/model/comment_model_gen.go create mode 100644 internal/mock/model/post_model.go create mode 100644 internal/mock/model/post_model_gen.go delete mode 100644 internal/model/mongo/comment_likes_model.go delete mode 100644 internal/model/mongo/comment_likes_model_gen.go delete mode 100644 internal/model/mongo/comment_likes_types.go delete mode 100644 internal/model/mongo/post_likes_model.go delete mode 100644 internal/model/mongo/post_likes_model_gen.go delete mode 100644 internal/model/mongo/post_likes_types.go delete mode 100644 internal/model/mongo/tags_model.go delete mode 100644 internal/model/mongo/tags_model_gen.go delete mode 100644 internal/model/mongo/tags_types.go create mode 100644 internal/svc/init_mongo.go diff --git a/Makefile b/Makefile index 5a391e5..20dc350 100644 --- a/Makefile +++ b/Makefile @@ -49,9 +49,21 @@ build-docker: gen-mongo-model: # 建立 rpc 資料庫 # 只產生 Model 剩下的要自己撰寫,連欄位名稱也是 - goctl model mongo -c no -t post --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) - goctl model mongo -c no -t comment --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) - goctl model mongo -t tags --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) - goctl model mongo -t post_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) - goctl model mongo -t comment_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) - @echo "Generate mongo model files successfully" \ No newline at end of file + goctl model mongo -t post --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) + goctl model mongo -t comment --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) +# goctl model mongo -t tags --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) +# goctl model mongo -t post_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) +# goctl model mongo -t comment_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE) + @echo "Generate mongo model files successfully" + +.PHONY: mock-gen +mock-gen: # 建立 mock 資料 + mockgen -source=./internal/model/mongo/post_model_gen.go -destination=./internal/mock/model/post_model_gen.go -package=mock + mockgen -source=./internal/model/mongo/post_model.go -destination=./internal/mock/model/post_model.go -package=mock + mockgen -source=./internal/model/mongo/comment_model_gen.go -destination=./internal/mock/model/comment_model_gen.go -package=mock + mockgen -source=./internal/model/mongo/comment_model.go -destination=./internal/mock/model/comment_model.go -package=mock + @echo "Generate mock files successfully" + +.PHONY: migrate-database +migrate-database: + migrate -source file://generate/database/migrations/mongodb -database 'mongodb://127.0.0.1:27017/digimon_tweeting' up diff --git a/etc/tweeting.yaml b/etc/tweeting.yaml index c611c48..279f3cb 100644 --- a/etc/tweeting.yaml +++ b/etc/tweeting.yaml @@ -4,3 +4,11 @@ Etcd: Hosts: - 127.0.0.1:2379 Key: tweeting.rpc + +Mongo: + Schema: mongodb + Host: 127.0.0.1 + User: "" + Password: "" + Port: "27017" + Database: digimon_tweeting \ No newline at end of file diff --git a/generate/database/mongodb/20240829054501_post.up.js b/generate/database/mongodb/20240829054501_post.up.js new file mode 100644 index 0000000..d2dabbb --- /dev/null +++ b/generate/database/mongodb/20240829054501_post.up.js @@ -0,0 +1,9 @@ +use digimon_tweeting; +db.post.createIndex({ "uid": 1}); +db.post.createIndex({ "status": 1}); +db.post.createIndex({ "is_ad": 1}); +db.post.createIndex({ "createAt": 1 }); +db.post.createIndex({ "uid": 1,"status": 1, "createAt": 1 }); +db.post.createIndex({ "uid": 1, "createAt": 1 }); + +// TODO 看是否有要刪除過多的索引,要在測試一下 \ No newline at end of file diff --git a/generate/protobuf/tweeting.proto b/generate/protobuf/tweeting.proto index be6b914..cbc1b2c 100644 --- a/generate/protobuf/tweeting.proto +++ b/generate/protobuf/tweeting.proto @@ -1,136 +1,147 @@ syntax = "proto3"; package tweeting; -option go_package="./tweeting"; +option go_package = "./tweeting"; -// 基本回應 -message OKResp {} +// ========== 基本回應 =========== +message OKResp {} // 空的請求 -message NoneReq {} +message NoneReq {} // 分頁信息 -message Pager { - int64 total =1; // 總數量 - int64 size=2; // 每頁數量 - int64 index=3; // 當前頁碼 +message Pager +{ + int64 total = 1; // 總數量 + int64 size = 2; // 每頁數量 + int64 index = 3; // 當前頁碼 } -// 新增貼文的請求 -message NewPostReq { - int64 user_id = 1; // 發佈貼文的用戶ID - string content = 2; // 貼文內容 - repeated string tags = 3; // 貼文相關標籤 - repeated string media_url = 4; // 這筆文章的所有 Media URL - bool is_ad = 5; // 是否為廣告 +// ========== 貼文區 =========== + +// ------ 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 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 string media_url = 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 int64 user_id = 1; // 可選:根據用戶ID篩選貼文 - repeated int64 id = 2; // 可選:根據貼文ID篩選貼文 - repeated string tags = 3; // 可選:根據標籤篩選貼文 - optional bool only_ads = 4; // 可選:是否只顯示廣告 - int32 page_index = 5; // 分頁的頁碼 - int32 page_size = 6; // 每頁顯示的數量 +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 - int64 user_id = 2; // 發佈用戶ID - string content = 3; // 貼文內容 - repeated string tags = 4; // 標籤 - repeated string media_url = 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; // 貼文列表 - Pager page =2; -} - -// 讚/不讚請求 -message LikeReq { - string target_id = 1; // 目標ID(可以是貼文ID或評論ID) - int64 user_id = 2; // 點讚的用戶ID - int64 like_type = 3; // 讚或爛的類型 -} - -// 讚/不讚項目 -message LikeItem { - string target_id = 1; // 目標ID(可以是貼文ID或評論ID) - int64 user_id = 2; // 點讚的用戶ID - int64 like_type = 3; // 讚或爛的類型 -} - -// 讚/不讚列表請求 -message LikeListReq { - string target_id = 1; // 目標ID(可以是貼文ID或評論ID) - int64 like_type = 2; // 讚或爛的類型 - int32 page_index = 3; // 當前頁碼 - int32 page_size = 4; // 每頁顯示數量 -} - -// 讚/不讚列表回應 -message LikeListResp { - repeated LikeItem list = 1; // 讚/不讚列表 +message ListPostsResp +{ + repeated PostDetailItem posts = 1; // 貼文列表 Pager page = 2; } -// 讚/不讚數量請求 -message LikeCountReq { - string target_id = 1; // 目標ID(可以是貼文ID或評論ID) - int64 like_type = 2; // 讚或爛的類型 +message ModifyLikeDislikeCountReq +{ + string post_id = 1; // 貼文的 ID + int64 reaction_type = 2; // 用戶的反應類型,可能是讚或不讚 + bool is_increment = 3; // 表示是否增加(true 表示增加,false 表示減少) + int64 count = 4; // 異動數量 } -// 讚/不讚數量回應 -message LikeCountResp { - string count = 1; // 總共按讚數量 +// ========== 定義貼文服務(最基本單位,不要把邏輯放進來,也考慮是否要做快取) ========== +service PostService +{ + // CreatePost 新增貼文 + rpc CreatePost(NewPostReq) returns (PostResp); + // DeletePost 刪除貼文 + rpc DeletePost(DeletePostsReq) returns (OKResp); + // UpdatePost 更新貼文 + rpc UpdatePost(UpdatePostReq) returns (OKResp); + // ListPosts 查詢貼文 + rpc ListPosts(QueryPostsReq) returns (ListPostsResp); } -// 評論貼文的請求 -message CommentPostReq { - string post_id = 1; // 貼文ID - int64 user_id = 2; // 評論者ID - string content = 3; // 評論內容 +// ================================================================================================= + +// ------------ 評論貼文的請求 ------------ +message CommentPostReq +{ + string post_id = 1; // 貼文ID + string uid = 2; // 評論者ID + string content = 3; // 評論內容 } -// 查詢評論的請求 -message GetCommentsReq { - string post_id = 1; // 貼文ID - int32 page_index = 2; // 分頁頁碼 - int32 page_size = 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 { +message CommentDetail +{ string comment_id = 1; // 評論ID - int64 user_id = 2; // 評論者ID + string uid = 2; // 評論者ID string content = 3; // 評論內容 int64 created_at = 4; // 創建時間 int64 like_count = 5; // 讚數 @@ -138,60 +149,36 @@ message CommentDetail { } // 評論列表回應 -message GetCommentsResp { +message GetCommentsResp +{ repeated CommentDetail comments = 1; // 評論列表 Pager page = 2; } -// 刪除評論請求 -message DeleteCommentReq { - string comment_id = 1; // 評論ID +// ------------ 刪除評論請求 ------------ +message DeleteCommentReq +{ + repeated string comment_id = 1; // 評論ID } // 更新評論請求 -message UpdateCommentReq { - string comment_id = 1; // 評論ID - string content = 2; // 更新後的評論內容 -} - -// 定義貼文服務 -service PostService { - // NewPost 新增貼文 - rpc NewPost(NewPostReq) returns(PostResp); - // DeletePost 刪除貼文 - rpc DeletePost(DeletePostsReq) returns (OKResp); - // UpdatePost 更新貼文 - rpc UpdatePost(UpdatePostReq) returns (OKResp); - // ListPosts 查詢貼文 - rpc ListPosts(QueryPostsReq) returns (ListPostsResp); - - // Like 點讚/取消讚 貼文 - rpc Like(LikeReq) returns (OKResp); - // GetLikeStatus 取得讚/不讚狀態 - rpc GetLikeStatus(LikeReq) returns (OKResp); - // LikeList 取得讚/不讚列表 - rpc LikeList(LikeListReq) returns (LikeListResp); - // CountLike 取得讚/不讚數量 - rpc CountLike(LikeCountReq) returns (LikeCountResp); +message UpdateCommentReq +{ + string comment_id = 1; // 評論ID + string content = 2; // 更新後的評論內容 + optional int64 like_count = 3; // 讚數 + optional int64 dislike_count = 4; // 不喜歡數量 } // 定義評論服務 -service CommentService { +service CommentService +{ // NewComment 發表評論 - rpc NewComment(CommentPostReq) returns (OKResp); + rpc NewComment(CommentPostReq) returns (CommentPostResp); // GetComments 查詢評論 rpc GetComments(GetCommentsReq) returns (GetCommentsResp); // DeleteComment 刪除評論 rpc DeleteComment(DeleteCommentReq) returns (OKResp); // UpdateComment 更新評論 rpc UpdateComment(UpdateCommentReq) returns (OKResp); - - // LikeComment 點讚/取消讚 評論 - rpc LikeComment(LikeReq) returns (OKResp); - // GetLikeStatus 取得讚/不讚評論狀態 - rpc GetLikeStatus(LikeReq) returns (OKResp); - // LikeList 取得讚/不讚評論列表 - rpc LikeList(LikeListReq) returns (LikeListResp); - // CountLike 取得讚/不讚評論數量 - rpc CountLike(LikeCountReq) returns (LikeCountResp); } \ No newline at end of file diff --git a/go.mod b/go.mod index 1be6bfb..ea41a6a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,12 @@ module app-cloudep-tweeting-service go 1.22.3 require ( + code.30cm.net/digimon/library-go/errs v1.2.4 + code.30cm.net/digimon/library-go/validator v1.0.0 + github.com/stretchr/testify v1.9.0 github.com/zeromicro/go-zero v1.7.0 + go.mongodb.org/mongo-driver v1.16.0 + go.uber.org/mock v0.4.0 google.golang.org/grpc v1.66.0 google.golang.org/protobuf v1.34.2 ) @@ -18,14 +23,19 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fatih/color v1.17.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.22.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect @@ -33,20 +43,28 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.8 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/redis/go-redis/v9 v9.6.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.etcd.io/etcd/api/v3 v3.5.15 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect go.etcd.io/etcd/client/v3 v3.5.15 // indirect @@ -65,8 +83,10 @@ require ( go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/term v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect diff --git a/internal/config/config.go b/internal/config/config.go index c1f85b9..73cf966 100755 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,4 +4,12 @@ import "github.com/zeromicro/go-zero/zrpc" type Config struct { zrpc.RpcServerConf + Mongo struct { + Schema string + User string + Password string + Host string + Port string + Database string + } } diff --git a/internal/domain/const.go b/internal/domain/const.go new file mode 100644 index 0000000..0e7211b --- /dev/null +++ b/internal/domain/const.go @@ -0,0 +1,13 @@ +package domain + +type AdType int32 + +func (a AdType) ToInt32() int32 { + return int32(a) +} + +const ( + AdTypeAll AdType = iota + AdTypeOnlyAd + AdTypeOnlyNotAd +) diff --git a/internal/domain/errors.go b/internal/domain/errors.go new file mode 100644 index 0000000..d16b1ab --- /dev/null +++ b/internal/domain/errors.go @@ -0,0 +1,48 @@ +package domain + +import ( + "fmt" + "strings" + + ers "code.30cm.net/digimon/library-go/errs" + "code.30cm.net/digimon/library-go/errs/code" + "github.com/zeromicro/go-zero/core/logx" +) + +type ErrorCode uint32 + +func (e ErrorCode) ToUint32() uint32 { + return uint32(e) +} + +// Error Code 統一這邊改 +const ( + _ = iota + PostMongoErrorCode ErrorCode = iota + CreatePostError + DelPostError + 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/domain/status.go b/internal/domain/status.go new file mode 100644 index 0000000..abe9593 --- /dev/null +++ b/internal/domain/status.go @@ -0,0 +1,12 @@ +package domain + +type TweetingStatus int8 + +func (t TweetingStatus) ToInt8() int8 { + return int8(t) +} + +const ( + TweetingStatusNotReviewedYet TweetingStatus = iota + 1 + TweetingStatusPass +) diff --git a/internal/logic/commentservice/count_like_logic.go b/internal/logic/commentservice/count_like_logic.go deleted file mode 100644 index c656dc8..0000000 --- a/internal/logic/commentservice/count_like_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package commentservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type CountLikeLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewCountLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountLikeLogic { - return &CountLikeLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// CountLike 取得讚/不讚評論數量 -func (l *CountLikeLogic) CountLike(in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.LikeCountResp{}, nil -} diff --git a/internal/logic/commentservice/delete_comment_logic.go b/internal/logic/commentservice/delete_comment_logic.go index 665cf06..467d027 100644 --- a/internal/logic/commentservice/delete_comment_logic.go +++ b/internal/logic/commentservice/delete_comment_logic.go @@ -1,6 +1,7 @@ package commentservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" "context" "app-cloudep-tweeting-service/gen_result/pb/tweeting" @@ -25,7 +26,19 @@ func NewDeleteCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Del // DeleteComment 刪除評論 func (l *DeleteCommentLogic) DeleteComment(in *tweeting.DeleteCommentReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + _, 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/delete_comment_logic_test.go b/internal/logic/commentservice/delete_comment_logic_test.go new file mode 100644 index 0000000..00fa86c --- /dev/null +++ b/internal/logic/commentservice/delete_comment_logic_test.go @@ -0,0 +1,85 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + mockmodel "app-cloudep-tweeting-service/internal/mock/model" +) + +func TestDeleteComment(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockCommentModel := mockmodel.NewMockCommentModel(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + CommentModel: mockCommentModel, + } + + // 測試數據 + commentReq := &tweeting.DeleteCommentReq{ + CommentId: []string{"12345", "67890"}, + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.DeleteCommentReq + prepare func() + expectErr bool + }{ + { + name: "成功刪除評論", + input: commentReq, + prepare: func() { + // 模擬 DeleteMany 成功 + mockCommentModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(2), nil).Times(1) + }, + expectErr: false, + }, + { + name: "刪除評論失敗", + input: commentReq, + prepare: func() { + // 模擬 DeleteMany 失敗 + mockCommentModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(0), errors.New("delete failed")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 DeleteCommentLogic + logic := DeleteCommentLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 DeleteComment + resp, err := logic.DeleteComment(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + } + }) + } +} diff --git a/internal/logic/commentservice/get_comments_logic.go b/internal/logic/commentservice/get_comments_logic.go index 5f54081..d22a2cf 100644 --- a/internal/logic/commentservice/get_comments_logic.go +++ b/internal/logic/commentservice/get_comments_logic.go @@ -1,8 +1,12 @@ package commentservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" @@ -23,9 +27,77 @@ func NewGetCommentsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCo } } -// GetComments 查詢評論 -func (l *GetCommentsLogic) GetComments(in *tweeting.GetCommentsReq) (*tweeting.GetCommentsResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.GetCommentsResp{}, nil +// 只列出要驗證的資料 +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/get_comments_logic_test.go b/internal/logic/commentservice/get_comments_logic_test.go new file mode 100644 index 0000000..be66c91 --- /dev/null +++ b/internal/logic/commentservice/get_comments_logic_test.go @@ -0,0 +1,120 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + model "app-cloudep-tweeting-service/internal/model/mongo" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.uber.org/mock/gomock" + + mocklib "app-cloudep-tweeting-service/internal/mock/lib" + mockmodel "app-cloudep-tweeting-service/internal/mock/model" +) + +func TestGetComments(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockCommentModel := mockmodel.NewMockCommentModel(ctrl) + mockValidate := mocklib.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + CommentModel: mockCommentModel, + Validate: mockValidate, + } + + // 測試數據 + getCommentsReq := &tweeting.GetCommentsReq{ + PostId: "12345", + PageSize: 10, + PageIndex: 1, + } + + mockComments := []*model.Comment{ + { + ID: primitive.NewObjectID(), + PostID: "12345", + UID: "54321", + Content: "This is a comment", + LikeCount: 10, + DisLikeCount: 2, + }, + { + ID: primitive.NewObjectID(), + PostID: "12345", + UID: "67890", + Content: "This is another comment", + LikeCount: 5, + DisLikeCount: 1, + }, + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.GetCommentsReq + prepare func() + expectErr bool + }{ + { + name: "成功查詢評論", + input: getCommentsReq, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + mockCommentModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(mockComments, int64(len(mockComments)), nil).Times(1) + }, + expectErr: false, + }, + { + name: "查詢評論失敗", + input: getCommentsReq, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + mockCommentModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("find failed")).Times(1) + }, + expectErr: true, + }, + { + name: "驗證失敗", + input: getCommentsReq, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 GetCommentsLogic + logic := GetCommentsLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 GetComments + resp, err := logic.GetComments(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + assert.Len(t, resp.Comments, len(mockComments)) + } + }) + } +} diff --git a/internal/logic/commentservice/get_like_status_logic.go b/internal/logic/commentservice/get_like_status_logic.go deleted file mode 100644 index 6dbce00..0000000 --- a/internal/logic/commentservice/get_like_status_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package commentservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type GetLikeStatusLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewGetLikeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLikeStatusLogic { - return &GetLikeStatusLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// GetLikeStatus 取得讚/不讚評論狀態 -func (l *GetLikeStatusLogic) GetLikeStatus(in *tweeting.LikeReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.OKResp{}, nil -} diff --git a/internal/logic/commentservice/like_comment_logic.go b/internal/logic/commentservice/like_comment_logic.go deleted file mode 100644 index 60cd2c4..0000000 --- a/internal/logic/commentservice/like_comment_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package commentservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type LikeCommentLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewLikeCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeCommentLogic { - return &LikeCommentLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// LikeComment 點讚/取消讚 評論 -func (l *LikeCommentLogic) LikeComment(in *tweeting.LikeReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.OKResp{}, nil -} diff --git a/internal/logic/commentservice/like_list_logic.go b/internal/logic/commentservice/like_list_logic.go deleted file mode 100644 index 351a367..0000000 --- a/internal/logic/commentservice/like_list_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package commentservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type LikeListLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewLikeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeListLogic { - return &LikeListLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// LikeList 取得讚/不讚評論列表 -func (l *LikeListLogic) LikeList(in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.LikeListResp{}, nil -} diff --git a/internal/logic/commentservice/new_comment_logic.go b/internal/logic/commentservice/new_comment_logic.go index 5dcc84f..df084bb 100644 --- a/internal/logic/commentservice/new_comment_logic.go +++ b/internal/logic/commentservice/new_comment_logic.go @@ -1,10 +1,14 @@ package commentservicelogic import ( - "context" - "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" + "context" + "errors" + + ers "code.30cm.net/digimon/library-go/errs" "github.com/zeromicro/go-zero/core/logx" ) @@ -23,9 +27,70 @@ func NewNewCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewCom } } -// NewComment 發表評論 -func (l *NewCommentLogic) NewComment(in *tweeting.CommentPostReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.OKResp{}, nil +// 輸入的定義 -> 檢查用 +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..a7873ce --- /dev/null +++ b/internal/logic/commentservice/new_comment_logic_test.go @@ -0,0 +1,124 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + model "app-cloudep-tweeting-service/internal/model/mongo" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.uber.org/mock/gomock" + + mocklib "app-cloudep-tweeting-service/internal/mock/lib" + mockmodel "app-cloudep-tweeting-service/internal/mock/model" +) + +func TestNewComment(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockPostModel := mockmodel.NewMockPostModel(ctrl) + mockCommentModel := mockmodel.NewMockCommentModel(ctrl) + mockValidate := mocklib.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + PostModel: mockPostModel, + CommentModel: mockCommentModel, + Validate: mockValidate, + } + + // 測試數據 + postID := primitive.NewObjectID().Hex() + commentReq := &tweeting.CommentPostReq{ + Uid: "12345", + Content: "This is a comment", + PostId: postID, + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.CommentPostReq + prepare func() + expectErr bool + }{ + { + name: "成功發表評論", + input: commentReq, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬 FindOne 成功找到文章 + mockPostModel.EXPECT().FindOne(gomock.Any(), postID).Return(&model.Post{}, nil).Times(1) + // 模擬 Insert 成功 + mockCommentModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + expectErr: false, + }, + { + name: "驗證失敗", + input: commentReq, + prepare: func() { + // 模擬 Validate 失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + { + name: "文章不存在", + input: commentReq, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬 FindOne 找不到文章 + mockPostModel.EXPECT().FindOne(gomock.Any(), postID).Return(nil, model.ErrNotFound).Times(1) + }, + expectErr: true, + }, + { + name: "插入評論失敗", + input: commentReq, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬 FindOne 成功找到文章 + mockPostModel.EXPECT().FindOne(gomock.Any(), postID).Return(&model.Post{}, nil).Times(1) + // 模擬 Insert 失敗 + mockCommentModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(errors.New("insert failed")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 NewCommentLogic + logic := NewCommentLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 NewComment + resp, err := logic.NewComment(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + assert.NotEmpty(t, resp.CommentId) + } + }) + } +} diff --git a/internal/logic/commentservice/update_comment_logic.go b/internal/logic/commentservice/update_comment_logic.go index f38e6de..bc24ef0 100644 --- a/internal/logic/commentservice/update_comment_logic.go +++ b/internal/logic/commentservice/update_comment_logic.go @@ -1,10 +1,14 @@ 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" "context" - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" + ers "code.30cm.net/digimon/library-go/errs" + "go.mongodb.org/mongo-driver/bson/primitive" "github.com/zeromicro/go-zero/core/logx" ) @@ -23,9 +27,56 @@ func NewUpdateCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Upd } } +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) { - // todo: add your logic here and delete this line + // 驗證資料 + 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/commentservice/update_comment_logic_test.go b/internal/logic/commentservice/update_comment_logic_test.go new file mode 100644 index 0000000..ec4973a --- /dev/null +++ b/internal/logic/commentservice/update_comment_logic_test.go @@ -0,0 +1,124 @@ +package commentservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + mocklib "app-cloudep-tweeting-service/internal/mock/lib" + mockmodel "app-cloudep-tweeting-service/internal/mock/model" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/mock/gomock" + "google.golang.org/protobuf/proto" +) + +func TestUpdateComment(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockCommentModel := mockmodel.NewMockCommentModel(ctrl) + mockValidate := mocklib.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + CommentModel: mockCommentModel, + Validate: mockValidate, + } + + // 測試數據 + commentID := primitive.NewObjectID().Hex() + updateCommentReq := &tweeting.UpdateCommentReq{ + CommentId: commentID, + Content: "Updated content", + LikeCount: proto.Int64(5), + DislikeCount: proto.Int64(2), + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.UpdateCommentReq + prepare func() + expectErr bool + }{ + { + name: "成功更新評論", + input: updateCommentReq, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬 UpdateOptional 成功 + mockCommentModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return(&mongo.UpdateResult{ + ModifiedCount: 1, + }, nil).Times(1) + }, + expectErr: false, + }, + { + name: "驗證失敗", + input: updateCommentReq, + prepare: func() { + // 模擬 Validate 失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + { + name: "更新評論失敗", + input: updateCommentReq, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬 UpdateOptional 失敗 + mockCommentModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return( + &mongo.UpdateResult{ + ModifiedCount: 0, + }, errors.New("update failed")).Times(1) + }, + expectErr: true, + }, + { + name: "無效的評論ID", + input: &tweeting.UpdateCommentReq{ + CommentId: "invalid_id", + Content: "Updated content", + }, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 UpdateCommentLogic + logic := UpdateCommentLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 UpdateComment + resp, err := logic.UpdateComment(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, resp) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + } + }) + } +} diff --git a/internal/logic/postservice/count_like_logic.go b/internal/logic/postservice/count_like_logic.go deleted file mode 100644 index 4fb4e67..0000000 --- a/internal/logic/postservice/count_like_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package postservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type CountLikeLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewCountLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountLikeLogic { - return &CountLikeLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// CountLike 取得讚/不讚數量 -func (l *CountLikeLogic) CountLike(in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.LikeCountResp{}, nil -} diff --git a/internal/logic/postservice/create_post_logic.go b/internal/logic/postservice/create_post_logic.go new file mode 100644 index 0000000..e3fbb0d --- /dev/null +++ b/internal/logic/postservice/create_post_logic.go @@ -0,0 +1,94 @@ +package postservicelogic + +import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/svc" + "context" + + ers "code.30cm.net/digimon/library-go/errs" + "github.com/zeromicro/go-zero/core/logx" +) + +type CreatePostLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewCreatePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreatePostLogic { + return &CreatePostLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +// TODO 要調查一下內容如果存 html 是否有需要Encode +// 輸入的定義 -> 檢查用 +type newTweetingReq struct { + UID string `json:"uid" validate:"required"` + Content string `json:"content" validate:"required,lte=500"` // 貼文限制 500 字內 + Tags []string `json:"tags"` + MediaUrl []string `json:"media_url"` + IsAd bool `json:"is_ad"` // default false +} + +// CreatePost 新增貼文 +func (l *CreatePostLogic) CreatePost(in *tweeting.NewPostReq) (*tweeting.PostResp, error) { + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&newTweetingReq{ + UID: in.GetUid(), + Content: in.GetContent(), + }); err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + // ============ prepare ============ + // 新增資料 + tweet := &model.Post{ + UID: in.GetUid(), + Content: in.GetContent(), + Status: domain.TweetingStatusNotReviewedYet.ToInt8(), + IsAd: in.IsAd, + } + if len(in.GetTags()) > 0 { + // 存在貼文內的不提供搜尋,純顯示用,只不過在原始的tag 發生變動的時候,並不會一起改變 + // 搜尋會貼文與Tag 的表會再另外一邊做關聯 + // 暫時業務邏輯上tag 只提供新增,不提供修改以及刪除,故目前版本可行 + tweet.Tags = in.GetTags() + } + + if len(in.Media) > 0 { + // 將 Media 存入 + var media []model.Media + for _, item := range in.GetMedia() { + media = append(media, model.Media{ + Links: item.Url, + Type: item.Type, + }) + } + tweet.MediaURL = media + } + // ============ insert ============ + err := l.svcCtx.PostModel.Insert(l.ctx, tweet) + if err != nil { + // 錯誤代碼 05-021-02 + e := domain.CommentErrorL( + domain.CreatePostError, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.Insert"}, + {Key: "err", Value: err}, + }, + "failed to add new post").Wrap(err) + return nil, e + } + + return &tweeting.PostResp{ + PostId: tweet.ID.Hex(), + }, nil +} diff --git a/internal/logic/postservice/create_post_logic_test.go b/internal/logic/postservice/create_post_logic_test.go new file mode 100644 index 0000000..d7deaf5 --- /dev/null +++ b/internal/logic/postservice/create_post_logic_test.go @@ -0,0 +1,107 @@ +package postservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + "testing" + + mocklib "app-cloudep-tweeting-service/internal/mock/lib" + mockmodel "app-cloudep-tweeting-service/internal/mock/model" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestCreatePost(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockPostModel := mockmodel.NewMockPostModel(ctrl) + mockValidate := mocklib.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + PostModel: mockPostModel, + Validate: mockValidate, + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.NewPostReq + prepare func() + expectErr bool + }{ + { + name: "成功創建貼文", + input: &tweeting.NewPostReq{ + Uid: "12345", + Content: "Test content", + IsAd: false, + Tags: []string{"tag1", "tag2"}, + Media: []*tweeting.Media{ + { + Url: "http://example.com/image.png", + Type: "image", + }, + }, + }, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + mockPostModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1) + }, + expectErr: false, + }, + { + name: "驗證失敗", + input: &tweeting.NewPostReq{ + Uid: "", + Content: "", + }, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + { + name: "插入貼文失敗", + input: &tweeting.NewPostReq{ + Uid: "12345", + Content: "Test content", + IsAd: false, + }, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + mockPostModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(errors.New("insert failed")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 CreatePostLogic + logic := CreatePostLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 CreatePost + _, err := logic.CreatePost(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/logic/postservice/delete_post_logic.go b/internal/logic/postservice/delete_post_logic.go index 714e1f5..ffa6bda 100644 --- a/internal/logic/postservice/delete_post_logic.go +++ b/internal/logic/postservice/delete_post_logic.go @@ -1,10 +1,10 @@ package postservicelogic import ( - "context" - "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/domain" "app-cloudep-tweeting-service/internal/svc" + "context" "github.com/zeromicro/go-zero/core/logx" ) @@ -25,7 +25,20 @@ func NewDeletePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete // DeletePost 刪除貼文 func (l *DeletePostLogic) DeletePost(in *tweeting.DeletePostsReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + _, err := l.svcCtx.PostModel.DeleteMany(l.ctx, in.GetPostId()...) + if err != nil { + // 錯誤代碼 05-021-03 + e := domain.CommentErrorL( + domain.DelPostError, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.DeleteMany"}, + {Key: "err", Value: err}, + }, + "failed to del post").Wrap(err) + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/logic/postservice/delete_post_logic_test.go b/internal/logic/postservice/delete_post_logic_test.go new file mode 100644 index 0000000..adb2cbe --- /dev/null +++ b/internal/logic/postservice/delete_post_logic_test.go @@ -0,0 +1,81 @@ +package postservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + mockmodel "app-cloudep-tweeting-service/internal/mock/model" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" +) + +func TestDeletePost(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockPostModel := mockmodel.NewMockPostModel(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + PostModel: mockPostModel, + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.DeletePostsReq + prepare func() + expectErr bool + }{ + { + name: "成功刪除貼文", + input: &tweeting.DeletePostsReq{ + PostId: []string{"12345", "67890"}, + }, + prepare: func() { + // 模擬 DeleteMany 成功 + mockPostModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(2), nil).Times(1) + }, + expectErr: false, + }, + { + name: "刪除貼文失敗", + input: &tweeting.DeletePostsReq{ + PostId: []string{"12345", "67890"}, + }, + prepare: func() { + // 模擬 DeleteMany 失敗 + mockPostModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(0), errors.New("delete failed")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 DeletePostLogic + logic := DeletePostLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 DeletePost + _, err := logic.DeletePost(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/logic/postservice/get_like_status_logic.go b/internal/logic/postservice/get_like_status_logic.go deleted file mode 100644 index ba92101..0000000 --- a/internal/logic/postservice/get_like_status_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package postservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type GetLikeStatusLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewGetLikeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLikeStatusLogic { - return &GetLikeStatusLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// GetLikeStatus 取得讚/不讚狀態 -func (l *GetLikeStatusLogic) GetLikeStatus(in *tweeting.LikeReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.OKResp{}, nil -} diff --git a/internal/logic/postservice/like_list_logic.go b/internal/logic/postservice/like_list_logic.go deleted file mode 100644 index 6f4023b..0000000 --- a/internal/logic/postservice/like_list_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package postservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type LikeListLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewLikeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeListLogic { - return &LikeListLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// LikeList 取得讚/不讚列表 -func (l *LikeListLogic) LikeList(in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.LikeListResp{}, nil -} diff --git a/internal/logic/postservice/like_logic.go b/internal/logic/postservice/like_logic.go deleted file mode 100644 index 3965b38..0000000 --- a/internal/logic/postservice/like_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package postservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type LikeLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeLogic { - return &LikeLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// Like 點讚/取消讚 貼文 -func (l *LikeLogic) Like(in *tweeting.LikeReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.OKResp{}, nil -} diff --git a/internal/logic/postservice/list_posts_logic.go b/internal/logic/postservice/list_posts_logic.go index abba88d..544327d 100644 --- a/internal/logic/postservice/list_posts_logic.go +++ b/internal/logic/postservice/list_posts_logic.go @@ -1,8 +1,13 @@ package postservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" "context" + ers "code.30cm.net/digimon/library-go/errs" + "google.golang.org/protobuf/proto" + "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" @@ -23,9 +28,96 @@ func NewListPostsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListPos } } -// ListPosts 查詢貼文 -func (l *ListPostsLogic) ListPosts(in *tweeting.QueryPostsReq) (*tweeting.ListPostsResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.ListPostsResp{}, nil +// 只列出要驗證的資料 +type listReq struct { + OnlyAdds int32 `json:"only_adds" validate:"oneof=0 1 2 3"` + PageSize int64 `json:"page_size" validate:"required"` + PageIndex int64 `json:"page_index" validate:"required"` +} + +// 將單個 Post 轉換為 PostDetailItem +func convertToPostDetailItem(item *model.Post) *tweeting.PostDetailItem { + media := make([]*tweeting.Media, 0, len(item.MediaURL)) + for _, subItem := range item.MediaURL { + media = append(media, &tweeting.Media{ + Type: subItem.Type, + Url: subItem.Links, + }) + } + + return &tweeting.PostDetailItem{ + PostId: item.ID.Hex(), + Uid: item.UID, + Content: item.Content, + Tags: item.Tags, + Media: media, + IsAd: item.IsAd, + CreatedAt: item.CreateAt, + UpdateAt: item.UpdateAt, + LikeCount: int64(item.Like), + DislikeCount: int64(item.DisLike), + } +} + +// ListPosts 查詢貼文 -> 主流程 +func (l *ListPostsLogic) ListPosts(in *tweeting.QueryPostsReq) (*tweeting.ListPostsResp, error) { + // 將 PageSize 和 PageIndex 提前轉換為 int64 + pageSize := int64(in.GetPageSize()) + pageIndex := int64(in.GetPageIndex()) + + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&listReq{ + PageSize: pageSize, + PageIndex: pageIndex, + OnlyAdds: in.GetOnlyAds(), + }); err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + + // 構建查詢條件 + query := &model.QueryPostModelReq{ + UID: in.GetUid(), + Id: in.GetPostId(), + PageSize: pageSize, + PageIndex: pageIndex, + } + + // 處理 OnlyAds 條件 + if in.OnlyAds != nil { + onlyAds := in.GetOnlyAds() + query.OnlyAds = proto.Bool(onlyAds == domain.AdTypeOnlyAd.ToInt32()) + } + + // 執行查詢 + find, count, err := l.svcCtx.PostModel.Find(l.ctx, query) + if err != nil { + // 錯誤代碼 05-021-05 + e := domain.CommentErrorL( + domain.ListPostError, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "query", Value: query}, + {Key: "func", Value: "PostModel.Find"}, + {Key: "err", Value: err}, + }, + "failed to find posts").Wrap(err) + return nil, e + } + + // 將查詢結果轉換為 API 回應格式 + result := make([]*tweeting.PostDetailItem, 0, count) + for _, item := range find { + result = append(result, convertToPostDetailItem(item)) + } + + // 返回結果 + return &tweeting.ListPostsResp{ + Posts: result, + Page: &tweeting.Pager{ + Total: count, + Index: pageIndex, + Size: pageSize, + }, + }, nil } diff --git a/internal/logic/postservice/list_posts_logic_test.go b/internal/logic/postservice/list_posts_logic_test.go new file mode 100644 index 0000000..9e233df --- /dev/null +++ b/internal/logic/postservice/list_posts_logic_test.go @@ -0,0 +1,164 @@ +package postservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + mocklib "app-cloudep-tweeting-service/internal/mock/lib" + mockmodel "app-cloudep-tweeting-service/internal/mock/model" + model "app-cloudep-tweeting-service/internal/model/mongo" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + "testing" + "time" + + "google.golang.org/protobuf/proto" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.uber.org/mock/gomock" +) + +func TestConvertToPostDetailItem(t *testing.T) { + // 構建測試數據 + postID := primitive.NewObjectID() + post := &model.Post{ + ID: postID, + UID: "12345", + Content: "Test Content", + Tags: []string{"tag1", "tag2"}, + MediaURL: []model.Media{ + {Type: "image", Links: "http://example.com/image.png"}, + {Type: "video", Links: "http://example.com/video.mp4"}, + }, + IsAd: true, + CreateAt: time.Now().Unix(), + UpdateAt: time.Now().Unix(), + Like: 10, + DisLike: 2, + } + + // 執行轉換 + result := convertToPostDetailItem(post) + + // 驗證結果 + assert.Equal(t, postID.Hex(), result.PostId) + assert.Equal(t, "12345", result.Uid) + assert.Equal(t, "Test Content", result.Content) + assert.Equal(t, []string{"tag1", "tag2"}, result.Tags) + assert.Equal(t, true, result.IsAd) + assert.Equal(t, int64(10), result.LikeCount) + assert.Equal(t, int64(2), result.DislikeCount) + + // 驗證 Media 的轉換 + assert.Len(t, result.Media, 2) + assert.Equal(t, "image", result.Media[0].Type) + assert.Equal(t, "http://example.com/image.png", result.Media[0].Url) + assert.Equal(t, "video", result.Media[1].Type) + assert.Equal(t, "http://example.com/video.mp4", result.Media[1].Url) +} + +func TestListPosts(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockPostModel := mockmodel.NewMockPostModel(ctrl) + mockValidate := mocklib.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + PostModel: mockPostModel, + Validate: mockValidate, + } + + // 構建測試數據 + queryReq := &tweeting.QueryPostsReq{ + PageSize: 10, + PageIndex: 1, + OnlyAds: proto.Int32(1), + } + + mockPosts := []*model.Post{ + { + ID: primitive.NewObjectID(), + UID: "12345", + Content: "Test Content 1", + Tags: []string{"tag1", "tag2"}, + MediaURL: []model.Media{ + {Type: "image", Links: "http://example.com/image1.png"}, + }, + IsAd: false, + CreateAt: time.Now().Unix(), + UpdateAt: time.Now().Unix(), + Like: 5, + DisLike: 1, + }, + { + ID: primitive.NewObjectID(), + UID: "67890", + Content: "Test Content 2", + Tags: []string{"tag3", "tag4"}, + MediaURL: []model.Media{ + {Type: "video", Links: "http://example.com/video1.mp4"}, + }, + IsAd: true, + CreateAt: time.Now().Unix(), + UpdateAt: time.Now().Unix(), + Like: 3, + DisLike: 0, + }, + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.QueryPostsReq + prepare func() + expectErr bool + }{ + { + name: "成功查詢貼文", + input: queryReq, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + mockPostModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(mockPosts, int64(len(mockPosts)), nil).Times(1) + }, + expectErr: false, + }, + { + name: "查詢貼文失敗", + input: queryReq, + prepare: func() { + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + mockPostModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("find failed")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 ListPostsLogic + logic := ListPostsLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 ListPosts + resp, err := logic.ListPosts(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, resp) + assert.Len(t, resp.Posts, len(mockPosts)) + } + }) + } +} diff --git a/internal/logic/postservice/new_post_logic.go b/internal/logic/postservice/new_post_logic.go deleted file mode 100644 index a755c05..0000000 --- a/internal/logic/postservice/new_post_logic.go +++ /dev/null @@ -1,31 +0,0 @@ -package postservicelogic - -import ( - "context" - - "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/svc" - - "github.com/zeromicro/go-zero/core/logx" -) - -type NewPostLogic struct { - ctx context.Context - svcCtx *svc.ServiceContext - logx.Logger -} - -func NewNewPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewPostLogic { - return &NewPostLogic{ - ctx: ctx, - svcCtx: svcCtx, - Logger: logx.WithContext(ctx), - } -} - -// NewPost 新增貼文 -func (l *NewPostLogic) NewPost(in *tweeting.NewPostReq) (*tweeting.PostResp, error) { - // todo: add your logic here and delete this line - - return &tweeting.PostResp{}, nil -} diff --git a/internal/logic/postservice/update_post_logic.go b/internal/logic/postservice/update_post_logic.go index 454844b..2275fcc 100644 --- a/internal/logic/postservice/update_post_logic.go +++ b/internal/logic/postservice/update_post_logic.go @@ -1,8 +1,13 @@ package postservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" "context" + ers "code.30cm.net/digimon/library-go/errs" + "go.mongodb.org/mongo-driver/bson/primitive" + "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" @@ -23,9 +28,68 @@ func NewUpdatePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Update } } +type checkPostId struct { + PostID string `validate:"required"` + Content string `json:"content,omitempty" validate:"lte=500"` +} + // UpdatePost 更新貼文 func (l *UpdatePostLogic) UpdatePost(in *tweeting.UpdatePostReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&checkPostId{ + PostID: in.GetPostId(), + Content: in.GetContent(), + }); err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat(err.Error()) + } + // 沒有就沒有,有就走全覆蓋 + update := model.Post{} + oid, err := primitive.ObjectIDFromHex(in.GetPostId()) + if err != nil { + // 錯誤代碼 05-011-00 + return nil, ers.InvalidFormat("failed to get correct post id") + } + update.ID = oid + update.Tags = in.GetTags() + // 將 Media 存入 + var media []model.Media + for _, item := range in.GetMedia() { + media = append(media, model.Media{ + Links: item.Url, + Type: item.Type, + }) + } + update.MediaURL = media + update.Content = in.GetContent() + + // 因為 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 { + // 錯誤代碼 05-021-04 + e := domain.CommentErrorL( + domain.UpdatePostError, + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.UpdateOptional"}, + {Key: "err", Value: err}, + }, + "failed to update post", in.PostId).Wrap(err) + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/logic/postservice/update_post_logic_test.go b/internal/logic/postservice/update_post_logic_test.go new file mode 100644 index 0000000..1858572 --- /dev/null +++ b/internal/logic/postservice/update_post_logic_test.go @@ -0,0 +1,118 @@ +package postservicelogic + +import ( + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + mocklib "app-cloudep-tweeting-service/internal/mock/lib" + mockmodel "app-cloudep-tweeting-service/internal/mock/model" + "app-cloudep-tweeting-service/internal/svc" + "context" + "errors" + + "go.mongodb.org/mongo-driver/mongo" + "google.golang.org/protobuf/proto" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + + "testing" +) + +func TestUpdatePost(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + // 初始化 mock 依賴 + mockPostModel := mockmodel.NewMockPostModel(ctrl) + mockValidate := mocklib.NewMockValidate(ctrl) + + // 初始化服務上下文 + svcCtx := &svc.ServiceContext{ + PostModel: mockPostModel, + Validate: mockValidate, + } + + // 測試數據集 + tests := []struct { + name string + input *tweeting.UpdatePostReq + prepare func() + expectErr bool + }{ + { + name: "成功更新貼文", + input: &tweeting.UpdatePostReq{ + PostId: "66cfdc1d6f8fe7eac1e52523", + Content: proto.String("Updated content"), + Tags: []string{"tag1", "tag2"}, + Media: []*tweeting.Media{ + { + Url: "http://example.com/image.png", + Type: "image", + }, + }, + LikeCount: proto.Int64(10), + DislikeCount: proto.Int64(2), + }, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬 UpdateOptional 成功 + mockPostModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return(&mongo.UpdateResult{ + ModifiedCount: 1, + }, nil).Times(1) + }, + expectErr: false, + }, + { + name: "驗證失敗", + input: &tweeting.UpdatePostReq{ + PostId: "", + }, + prepare: func() { + // 模擬 Validate 失敗 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1) + }, + expectErr: true, + }, + { + name: "更新貼文失敗", + input: &tweeting.UpdatePostReq{ + PostId: "66cfdc1d6f8fe7eac1e52523", + Content: proto.String("Updated content"), + }, + prepare: func() { + // 模擬 Validate 成功 + mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1) + // 模擬 UpdateOptional 失敗 + mockPostModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return(&mongo.UpdateResult{ + ModifiedCount: 0, + }, errors.New("update failed")).Times(1) + }, + expectErr: true, + }, + } + + // 執行測試 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // 設置測試環境 + tt.prepare() + + // 初始化 UpdatePostLogic + logic := UpdatePostLogic{ + svcCtx: svcCtx, + ctx: context.TODO(), + } + + // 執行 UpdatePost + _, err := logic.UpdatePost(tt.input) + + // 驗證結果 + if tt.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/mock/lib/validate.go b/internal/mock/lib/validate.go new file mode 100644 index 0000000..dd852f8 --- /dev/null +++ b/internal/mock/lib/validate.go @@ -0,0 +1,73 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./validate.go +// +// Generated by this command: +// +// mockgen -source=./validate.go -destination=../../mock/lib/validate.go -package=lib +// + +// Package lib is a generated GoMock package. +package lib + +import ( + reflect "reflect" + + required "code.30cm.net/digimon/library-go/validator" + + gomock "go.uber.org/mock/gomock" +) + +// MockValidate is a mock of Validate interface. +type MockValidate struct { + ctrl *gomock.Controller + recorder *MockValidateMockRecorder +} + +// MockValidateMockRecorder is the mock recorder for MockValidate. +type MockValidateMockRecorder struct { + mock *MockValidate +} + +// NewMockValidate creates a new mock instance. +func NewMockValidate(ctrl *gomock.Controller) *MockValidate { + mock := &MockValidate{ctrl: ctrl} + mock.recorder = &MockValidateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockValidate) EXPECT() *MockValidateMockRecorder { + return m.recorder +} + +// BindToValidator mocks base method. +func (m *MockValidate) BindToValidator(opts ...required.Option) error { + m.ctrl.T.Helper() + varargs := []any{} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "BindToValidator", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// BindToValidator indicates an expected call of BindToValidator. +func (mr *MockValidateMockRecorder) BindToValidator(opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindToValidator", reflect.TypeOf((*MockValidate)(nil).BindToValidator), opts...) +} + +// ValidateAll mocks base method. +func (m *MockValidate) ValidateAll(obj any) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateAll", obj) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateAll indicates an expected call of ValidateAll. +func (mr *MockValidateMockRecorder) ValidateAll(obj any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAll", reflect.TypeOf((*MockValidate)(nil).ValidateAll), obj) +} diff --git a/internal/mock/model/comment_model.go b/internal/mock/model/comment_model.go new file mode 100644 index 0000000..db69a18 --- /dev/null +++ b/internal/mock/model/comment_model.go @@ -0,0 +1,152 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/model/mongo/comment_model.go +// +// Generated by this command: +// +// mockgen -source=./internal/model/mongo/comment_model.go -destination=./internal/mock/model/comment_model.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + model "app-cloudep-tweeting-service/internal/model/mongo" + context "context" + reflect "reflect" + + mongo "go.mongodb.org/mongo-driver/mongo" + gomock "go.uber.org/mock/gomock" +) + +// MockCommentModel is a mock of CommentModel interface. +type MockCommentModel struct { + ctrl *gomock.Controller + recorder *MockCommentModelMockRecorder +} + +// MockCommentModelMockRecorder is the mock recorder for MockCommentModel. +type MockCommentModelMockRecorder struct { + mock *MockCommentModel +} + +// NewMockCommentModel creates a new mock instance. +func NewMockCommentModel(ctrl *gomock.Controller) *MockCommentModel { + mock := &MockCommentModel{ctrl: ctrl} + mock.recorder = &MockCommentModelMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCommentModel) EXPECT() *MockCommentModelMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockCommentModel) Delete(ctx context.Context, id string) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockCommentModelMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCommentModel)(nil).Delete), ctx, id) +} + +// DeleteMany mocks base method. +func (m *MockCommentModel) DeleteMany(ctx context.Context, id ...string) (int64, error) { + m.ctrl.T.Helper() + varargs := []any{ctx} + for _, a := range id { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteMany", varargs...) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteMany indicates an expected call of DeleteMany. +func (mr *MockCommentModelMockRecorder) DeleteMany(ctx any, id ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx}, id...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMany", reflect.TypeOf((*MockCommentModel)(nil).DeleteMany), varargs...) +} + +// Find mocks base method. +func (m *MockCommentModel) Find(ctx context.Context, param *model.QueryCommentModelReq) ([]*model.Comment, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Find", ctx, param) + ret0, _ := ret[0].([]*model.Comment) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Find indicates an expected call of Find. +func (mr *MockCommentModelMockRecorder) Find(ctx, param any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockCommentModel)(nil).Find), ctx, param) +} + +// FindOne mocks base method. +func (m *MockCommentModel) FindOne(ctx context.Context, id string) (*model.Comment, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOne", ctx, id) + ret0, _ := ret[0].(*model.Comment) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindOne indicates an expected call of FindOne. +func (mr *MockCommentModelMockRecorder) FindOne(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockCommentModel)(nil).FindOne), ctx, id) +} + +// Insert mocks base method. +func (m *MockCommentModel) Insert(ctx context.Context, data *model.Comment) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockCommentModelMockRecorder) Insert(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockCommentModel)(nil).Insert), ctx, data) +} + +// Update mocks base method. +func (m *MockCommentModel) Update(ctx context.Context, data *model.Comment) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockCommentModelMockRecorder) Update(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockCommentModel)(nil).Update), ctx, data) +} + +// UpdateOptional mocks base method. +func (m *MockCommentModel) UpdateOptional(ctx context.Context, data *model.Comment) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateOptional", ctx, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateOptional indicates an expected call of UpdateOptional. +func (mr *MockCommentModelMockRecorder) UpdateOptional(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOptional", reflect.TypeOf((*MockCommentModel)(nil).UpdateOptional), ctx, data) +} diff --git a/internal/mock/model/comment_model_gen.go b/internal/mock/model/comment_model_gen.go new file mode 100644 index 0000000..5de5dd7 --- /dev/null +++ b/internal/mock/model/comment_model_gen.go @@ -0,0 +1,101 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/model/mongo/comment_model_gen.go +// +// Generated by this command: +// +// mockgen -source=./internal/model/mongo/comment_model_gen.go -destination=./internal/mock/model/comment_model_gen.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + model "app-cloudep-tweeting-service/internal/model/mongo" + context "context" + reflect "reflect" + + mongo "go.mongodb.org/mongo-driver/mongo" + gomock "go.uber.org/mock/gomock" +) + +// MockcommentModel is a mock of commentModel interface. +type MockcommentModel struct { + ctrl *gomock.Controller + recorder *MockcommentModelMockRecorder +} + +// MockcommentModelMockRecorder is the mock recorder for MockcommentModel. +type MockcommentModelMockRecorder struct { + mock *MockcommentModel +} + +// NewMockcommentModel creates a new mock instance. +func NewMockcommentModel(ctrl *gomock.Controller) *MockcommentModel { + mock := &MockcommentModel{ctrl: ctrl} + mock.recorder = &MockcommentModelMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockcommentModel) EXPECT() *MockcommentModelMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockcommentModel) Delete(ctx context.Context, id string) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockcommentModelMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockcommentModel)(nil).Delete), ctx, id) +} + +// FindOne mocks base method. +func (m *MockcommentModel) FindOne(ctx context.Context, id string) (*model.Comment, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOne", ctx, id) + ret0, _ := ret[0].(*model.Comment) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindOne indicates an expected call of FindOne. +func (mr *MockcommentModelMockRecorder) FindOne(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockcommentModel)(nil).FindOne), ctx, id) +} + +// Insert mocks base method. +func (m *MockcommentModel) Insert(ctx context.Context, data *model.Comment) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockcommentModelMockRecorder) Insert(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockcommentModel)(nil).Insert), ctx, data) +} + +// Update mocks base method. +func (m *MockcommentModel) Update(ctx context.Context, data *model.Comment) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockcommentModelMockRecorder) Update(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockcommentModel)(nil).Update), ctx, data) +} diff --git a/internal/mock/model/post_model.go b/internal/mock/model/post_model.go new file mode 100644 index 0000000..bf3fd1b --- /dev/null +++ b/internal/mock/model/post_model.go @@ -0,0 +1,152 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/model/mongo/post_model.go +// +// Generated by this command: +// +// mockgen -source=./internal/model/mongo/post_model.go -destination=./internal/mock/model/post_model.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + model "app-cloudep-tweeting-service/internal/model/mongo" + context "context" + reflect "reflect" + + mongo "go.mongodb.org/mongo-driver/mongo" + gomock "go.uber.org/mock/gomock" +) + +// MockPostModel is a mock of PostModel interface. +type MockPostModel struct { + ctrl *gomock.Controller + recorder *MockPostModelMockRecorder +} + +// MockPostModelMockRecorder is the mock recorder for MockPostModel. +type MockPostModelMockRecorder struct { + mock *MockPostModel +} + +// NewMockPostModel creates a new mock instance. +func NewMockPostModel(ctrl *gomock.Controller) *MockPostModel { + mock := &MockPostModel{ctrl: ctrl} + mock.recorder = &MockPostModelMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockPostModel) EXPECT() *MockPostModelMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockPostModel) Delete(ctx context.Context, id string) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockPostModelMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockPostModel)(nil).Delete), ctx, id) +} + +// DeleteMany mocks base method. +func (m *MockPostModel) DeleteMany(ctx context.Context, id ...string) (int64, error) { + m.ctrl.T.Helper() + varargs := []any{ctx} + for _, a := range id { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteMany", varargs...) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DeleteMany indicates an expected call of DeleteMany. +func (mr *MockPostModelMockRecorder) DeleteMany(ctx any, id ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx}, id...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMany", reflect.TypeOf((*MockPostModel)(nil).DeleteMany), varargs...) +} + +// Find mocks base method. +func (m *MockPostModel) Find(ctx context.Context, param *model.QueryPostModelReq) ([]*model.Post, int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Find", ctx, param) + ret0, _ := ret[0].([]*model.Post) + ret1, _ := ret[1].(int64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// Find indicates an expected call of Find. +func (mr *MockPostModelMockRecorder) Find(ctx, param any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockPostModel)(nil).Find), ctx, param) +} + +// FindOne mocks base method. +func (m *MockPostModel) FindOne(ctx context.Context, id string) (*model.Post, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOne", ctx, id) + ret0, _ := ret[0].(*model.Post) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindOne indicates an expected call of FindOne. +func (mr *MockPostModelMockRecorder) FindOne(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockPostModel)(nil).FindOne), ctx, id) +} + +// Insert mocks base method. +func (m *MockPostModel) Insert(ctx context.Context, data *model.Post) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockPostModelMockRecorder) Insert(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockPostModel)(nil).Insert), ctx, data) +} + +// Update mocks base method. +func (m *MockPostModel) Update(ctx context.Context, data *model.Post) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockPostModelMockRecorder) Update(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockPostModel)(nil).Update), ctx, data) +} + +// UpdateOptional mocks base method. +func (m *MockPostModel) UpdateOptional(ctx context.Context, data *model.Post) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateOptional", ctx, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateOptional indicates an expected call of UpdateOptional. +func (mr *MockPostModelMockRecorder) UpdateOptional(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOptional", reflect.TypeOf((*MockPostModel)(nil).UpdateOptional), ctx, data) +} diff --git a/internal/mock/model/post_model_gen.go b/internal/mock/model/post_model_gen.go new file mode 100644 index 0000000..4b22d6b --- /dev/null +++ b/internal/mock/model/post_model_gen.go @@ -0,0 +1,101 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./internal/model/mongo/post_model_gen.go +// +// Generated by this command: +// +// mockgen -source=./internal/model/mongo/post_model_gen.go -destination=./internal/mock/model/post_model_gen.go -package=mock +// + +// Package mock is a generated GoMock package. +package mock + +import ( + model "app-cloudep-tweeting-service/internal/model/mongo" + context "context" + reflect "reflect" + + mongo "go.mongodb.org/mongo-driver/mongo" + gomock "go.uber.org/mock/gomock" +) + +// MockpostModel is a mock of postModel interface. +type MockpostModel struct { + ctrl *gomock.Controller + recorder *MockpostModelMockRecorder +} + +// MockpostModelMockRecorder is the mock recorder for MockpostModel. +type MockpostModelMockRecorder struct { + mock *MockpostModel +} + +// NewMockpostModel creates a new mock instance. +func NewMockpostModel(ctrl *gomock.Controller) *MockpostModel { + mock := &MockpostModel{ctrl: ctrl} + mock.recorder = &MockpostModelMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockpostModel) EXPECT() *MockpostModelMockRecorder { + return m.recorder +} + +// Delete mocks base method. +func (m *MockpostModel) Delete(ctx context.Context, id string) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", ctx, id) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Delete indicates an expected call of Delete. +func (mr *MockpostModelMockRecorder) Delete(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockpostModel)(nil).Delete), ctx, id) +} + +// FindOne mocks base method. +func (m *MockpostModel) FindOne(ctx context.Context, id string) (*model.Post, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FindOne", ctx, id) + ret0, _ := ret[0].(*model.Post) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// FindOne indicates an expected call of FindOne. +func (mr *MockpostModelMockRecorder) FindOne(ctx, id any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockpostModel)(nil).FindOne), ctx, id) +} + +// Insert mocks base method. +func (m *MockpostModel) Insert(ctx context.Context, data *model.Post) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Insert", ctx, data) + ret0, _ := ret[0].(error) + return ret0 +} + +// Insert indicates an expected call of Insert. +func (mr *MockpostModelMockRecorder) Insert(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockpostModel)(nil).Insert), ctx, data) +} + +// Update mocks base method. +func (m *MockpostModel) Update(ctx context.Context, data *model.Post) (*mongo.UpdateResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Update", ctx, data) + ret0, _ := ret[0].(*mongo.UpdateResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Update indicates an expected call of Update. +func (mr *MockpostModelMockRecorder) Update(ctx, data any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockpostModel)(nil).Update), ctx, data) +} diff --git a/internal/model/mongo/comment_likes_model.go b/internal/model/mongo/comment_likes_model.go deleted file mode 100644 index 720b166..0000000 --- a/internal/model/mongo/comment_likes_model.go +++ /dev/null @@ -1,25 +0,0 @@ -package model - -import "github.com/zeromicro/go-zero/core/stores/mon" - -var _ Comment_likesModel = (*customComment_likesModel)(nil) - -type ( - // Comment_likesModel is an interface to be customized, add more methods here, - // and implement the added methods in customComment_likesModel. - Comment_likesModel interface { - comment_likesModel - } - - customComment_likesModel struct { - *defaultComment_likesModel - } -) - -// NewComment_likesModel returns a model for the mongo. -func NewComment_likesModel(url, db, collection string) Comment_likesModel { - conn := mon.MustNewModel(url, db, collection) - return &customComment_likesModel{ - defaultComment_likesModel: newDefaultComment_likesModel(conn), - } -} diff --git a/internal/model/mongo/comment_likes_model_gen.go b/internal/model/mongo/comment_likes_model_gen.go deleted file mode 100644 index cf5a194..0000000 --- a/internal/model/mongo/comment_likes_model_gen.go +++ /dev/null @@ -1,74 +0,0 @@ -// Code generated by goctl. DO NOT EDIT. -package model - -import ( - "context" - "time" - - "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" -) - -type comment_likesModel interface { - Insert(ctx context.Context, data *Comment_likes) error - FindOne(ctx context.Context, id string) (*Comment_likes, error) - Update(ctx context.Context, data *Comment_likes) (*mongo.UpdateResult, error) - Delete(ctx context.Context, id string) (int64, error) -} - -type defaultComment_likesModel struct { - conn *mon.Model -} - -func newDefaultComment_likesModel(conn *mon.Model) *defaultComment_likesModel { - return &defaultComment_likesModel{conn: conn} -} - -func (m *defaultComment_likesModel) Insert(ctx context.Context, data *Comment_likes) error { - if data.ID.IsZero() { - data.ID = primitive.NewObjectID() - data.CreateAt = time.Now() - data.UpdateAt = time.Now() - } - - _, err := m.conn.InsertOne(ctx, data) - return err -} - -func (m *defaultComment_likesModel) FindOne(ctx context.Context, id string) (*Comment_likes, error) { - oid, err := primitive.ObjectIDFromHex(id) - if err != nil { - return nil, ErrInvalidObjectId - } - - var data Comment_likes - - err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid}) - switch err { - case nil: - return &data, nil - case mon.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultComment_likesModel) Update(ctx context.Context, data *Comment_likes) (*mongo.UpdateResult, error) { - data.UpdateAt = time.Now() - - res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) - return res, err -} - -func (m *defaultComment_likesModel) Delete(ctx context.Context, id string) (int64, error) { - oid, err := primitive.ObjectIDFromHex(id) - if err != nil { - return 0, ErrInvalidObjectId - } - - res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid}) - return res, err -} diff --git a/internal/model/mongo/comment_likes_types.go b/internal/model/mongo/comment_likes_types.go deleted file mode 100644 index be19d28..0000000 --- a/internal/model/mongo/comment_likes_types.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -import ( - "time" - - "go.mongodb.org/mongo-driver/bson/primitive" -) - -type Comment_likes 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"` -} diff --git a/internal/model/mongo/comment_model.go b/internal/model/mongo/comment_model.go index 00478ac..abd9baa 100644 --- a/internal/model/mongo/comment_model.go +++ b/internal/model/mongo/comment_model.go @@ -1,8 +1,14 @@ package model import ( - "github.com/zeromicro/go-zero/core/stores/cache" - "github.com/zeromicro/go-zero/core/stores/monc" + "context" + "time" + + "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" ) var _ CommentModel = (*customCommentModel)(nil) @@ -12,17 +18,84 @@ 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, c cache.CacheConf) CommentModel { - conn := monc.MustNewModel(url, db, collection, c) +func NewCommentModel(url, db, collection string) CommentModel { + conn := mon.MustNewModel(url, db, collection) return &customCommentModel{ 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 85b05dd..e575dd8 100644 --- a/internal/model/mongo/comment_model_gen.go +++ b/internal/model/mongo/comment_model_gen.go @@ -5,14 +5,12 @@ import ( "context" "time" - "github.com/zeromicro/go-zero/core/stores/monc" + "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" ) -var prefixCommentCacheKey = "cache:comment:" - type commentModel interface { Insert(ctx context.Context, data *Comment) error FindOne(ctx context.Context, id string) (*Comment, error) @@ -21,22 +19,21 @@ type commentModel interface { } type defaultCommentModel struct { - conn *monc.Model + conn *mon.Model } -func newDefaultCommentModel(conn *monc.Model) *defaultCommentModel { +func newDefaultCommentModel(conn *mon.Model) *defaultCommentModel { return &defaultCommentModel{conn: conn} } 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() } - key := prefixCommentCacheKey + data.ID.Hex() - _, err := m.conn.InsertOne(ctx, key, data) + _, err := m.conn.InsertOne(ctx, data) return err } @@ -47,12 +44,12 @@ func (m *defaultCommentModel) FindOne(ctx context.Context, id string) (*Comment, } var data Comment - key := prefixCommentCacheKey + id - err = m.conn.FindOne(ctx, key, &data, bson.M{"_id": oid}) + + err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid}) switch err { case nil: return &data, nil - case monc.ErrNotFound: + case mon.ErrNotFound: return nil, ErrNotFound default: return nil, err @@ -60,9 +57,9 @@ 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() - key := prefixCommentCacheKey + data.ID.Hex() - res, err := m.conn.UpdateOne(ctx, key, bson.M{"_id": data.ID}, bson.M{"$set": data}) + data.UpdateAt = time.Now().UTC().UnixNano() + + res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) return res, err } @@ -71,7 +68,7 @@ func (m *defaultCommentModel) Delete(ctx context.Context, id string) (int64, err if err != nil { return 0, ErrInvalidObjectId } - key := prefixCommentCacheKey + id - res, err := m.conn.DeleteOne(ctx, key, bson.M{"_id": oid}) + + res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid}) 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_likes_model.go b/internal/model/mongo/post_likes_model.go deleted file mode 100644 index 9d1a52a..0000000 --- a/internal/model/mongo/post_likes_model.go +++ /dev/null @@ -1,25 +0,0 @@ -package model - -import "github.com/zeromicro/go-zero/core/stores/mon" - -var _ Post_likesModel = (*customPost_likesModel)(nil) - -type ( - // Post_likesModel is an interface to be customized, add more methods here, - // and implement the added methods in customPost_likesModel. - Post_likesModel interface { - post_likesModel - } - - customPost_likesModel struct { - *defaultPost_likesModel - } -) - -// NewPost_likesModel returns a model for the mongo. -func NewPost_likesModel(url, db, collection string) Post_likesModel { - conn := mon.MustNewModel(url, db, collection) - return &customPost_likesModel{ - defaultPost_likesModel: newDefaultPost_likesModel(conn), - } -} diff --git a/internal/model/mongo/post_likes_model_gen.go b/internal/model/mongo/post_likes_model_gen.go deleted file mode 100644 index e0499b7..0000000 --- a/internal/model/mongo/post_likes_model_gen.go +++ /dev/null @@ -1,74 +0,0 @@ -// Code generated by goctl. DO NOT EDIT. -package model - -import ( - "context" - "time" - - "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" -) - -type post_likesModel interface { - Insert(ctx context.Context, data *Post_likes) error - FindOne(ctx context.Context, id string) (*Post_likes, error) - Update(ctx context.Context, data *Post_likes) (*mongo.UpdateResult, error) - Delete(ctx context.Context, id string) (int64, error) -} - -type defaultPost_likesModel struct { - conn *mon.Model -} - -func newDefaultPost_likesModel(conn *mon.Model) *defaultPost_likesModel { - return &defaultPost_likesModel{conn: conn} -} - -func (m *defaultPost_likesModel) Insert(ctx context.Context, data *Post_likes) error { - if data.ID.IsZero() { - data.ID = primitive.NewObjectID() - data.CreateAt = time.Now() - data.UpdateAt = time.Now() - } - - _, err := m.conn.InsertOne(ctx, data) - return err -} - -func (m *defaultPost_likesModel) FindOne(ctx context.Context, id string) (*Post_likes, error) { - oid, err := primitive.ObjectIDFromHex(id) - if err != nil { - return nil, ErrInvalidObjectId - } - - var data Post_likes - - err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid}) - switch err { - case nil: - return &data, nil - case mon.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultPost_likesModel) Update(ctx context.Context, data *Post_likes) (*mongo.UpdateResult, error) { - data.UpdateAt = time.Now() - - res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) - return res, err -} - -func (m *defaultPost_likesModel) Delete(ctx context.Context, id string) (int64, error) { - oid, err := primitive.ObjectIDFromHex(id) - if err != nil { - return 0, ErrInvalidObjectId - } - - res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid}) - return res, err -} diff --git a/internal/model/mongo/post_likes_types.go b/internal/model/mongo/post_likes_types.go deleted file mode 100644 index 5b8f595..0000000 --- a/internal/model/mongo/post_likes_types.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -import ( - "time" - - "go.mongodb.org/mongo-driver/bson/primitive" -) - -type Post_likes 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"` -} diff --git a/internal/model/mongo/post_model.go b/internal/model/mongo/post_model.go index 096dda8..d97b772 100644 --- a/internal/model/mongo/post_model.go +++ b/internal/model/mongo/post_model.go @@ -1,8 +1,18 @@ package model import ( - "github.com/zeromicro/go-zero/core/stores/cache" + "context" + "errors" + "time" + "github.com/zeromicro/go-zero/core/stores/monc" + "go.mongodb.org/mongo-driver/mongo/options" + + "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" ) var _ PostModel = (*customPostModel)(nil) @@ -12,17 +22,164 @@ type ( // and implement the added methods in customPostModel. PostModel interface { postModel + DeleteMany(ctx context.Context, id ...string) (int64, error) + UpdateOptional(ctx context.Context, data *Post) (*mongo.UpdateResult, error) + Find(ctx context.Context, param *QueryPostModelReq) ([]*Post, int64, error) } customPostModel struct { *defaultPostModel } + + QueryPostModelReq struct { + UID []string + Id []string + OnlyAds *bool + PageSize int64 + PageIndex int64 + } ) // NewPostModel returns a model for the mongo. -func NewPostModel(url, db, collection string, c cache.CacheConf) PostModel { - conn := monc.MustNewModel(url, db, collection, c) +func NewPostModel(url, db, collection string) PostModel { + conn := mon.MustNewModel(url, db, collection) return &customPostModel{ defaultPostModel: newDefaultPostModel(conn), } } + +func (m *defaultPostModel) 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 *defaultPostModel) UpdateOptional(ctx context.Context, data *Post) (*mongo.UpdateResult, error) { + update := bson.M{"$set": bson.M{}} + + if data.UID != "" { + update["$set"].(bson.M)["uid"] = data.UID + } + + if data.Content != "" { + update["$set"].(bson.M)["content"] = data.Content + } + + if data.Status != 0 { + update["$set"].(bson.M)["status"] = data.Status + } + + if data.IsAd { + update["$set"].(bson.M)["is_ad"] = data.IsAd + } + + if len(data.Tags) > 0 { + update["$set"].(bson.M)["tags"] = data.Tags + } + + if len(data.MediaURL) > 0 { + update["$set"].(bson.M)["media_url"] = data.MediaURL + } + + if data.Like != -1 { + update["$set"].(bson.M)["like"] = data.Like + } + + if data.DisLike != -1 { + update["$set"].(bson.M)["dislike"] = data.DisLike + } + + // 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 +} + +// Find 貼文列表 +func (m *defaultPostModel) Find(ctx context.Context, param *QueryPostModelReq) ([]*Post, int64, error) { + filter := bson.M{} + + // 添加 UID 過濾條件 + if len(param.UID) > 0 { + filter["uid"] = bson.M{"$in": param.UID} + } + + // 添加 ID 過濾條件 + if len(param.Id) > 0 { + var ids []primitive.ObjectID + for _, item := range param.Id { + oid, err := primitive.ObjectIDFromHex(item) + if err != nil { + // log + continue + } + ids = append(ids, oid) + } + + filter["_id"] = bson.M{"$in": ids} + } + + // 添加 OnlyAds 過濾條件 + if param.OnlyAds != nil { + // true 是廣告 false 不是廣告 , 沒寫就是不過率 + filter["is_ad"] = *param.OnlyAds + } + + // 分頁處理 + opts := options.Find() + opts.SetSort(bson.D{{"create_time", -1}}) + if param.PageSize > 0 { + opts.SetLimit(param.PageSize) + } + if param.PageIndex > 0 { + opts.SetSkip((param.PageIndex - 1) * param.PageSize) + } + + // 計算總數(不考慮分頁) + totalCount, err := m.conn.CountDocuments(ctx, filter) + if err != nil { + return nil, 0, err + } + + result := make([]*Post, 0, param.PageSize) + // 執行查詢 + err = m.conn.Find(ctx, &result, filter, opts) + if err != nil { + return nil, 0, err + } + + switch { + case err == nil: + return result, totalCount, nil + case errors.Is(err, monc.ErrNotFound): + return nil, 0, ErrNotFound + default: + return nil, 0, err + } +} diff --git a/internal/model/mongo/post_model_gen.go b/internal/model/mongo/post_model_gen.go index c7aa965..8948be5 100644 --- a/internal/model/mongo/post_model_gen.go +++ b/internal/model/mongo/post_model_gen.go @@ -5,14 +5,12 @@ import ( "context" "time" - "github.com/zeromicro/go-zero/core/stores/monc" + "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" ) -var prefixPostCacheKey = "cache:post:" - type postModel interface { Insert(ctx context.Context, data *Post) error FindOne(ctx context.Context, id string) (*Post, error) @@ -21,22 +19,21 @@ type postModel interface { } type defaultPostModel struct { - conn *monc.Model + conn *mon.Model } -func newDefaultPostModel(conn *monc.Model) *defaultPostModel { +func newDefaultPostModel(conn *mon.Model) *defaultPostModel { return &defaultPostModel{conn: conn} } func (m *defaultPostModel) Insert(ctx context.Context, data *Post) 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() } - key := prefixPostCacheKey + data.ID.Hex() - _, err := m.conn.InsertOne(ctx, key, data) + _, err := m.conn.InsertOne(ctx, data) return err } @@ -47,12 +44,12 @@ func (m *defaultPostModel) FindOne(ctx context.Context, id string) (*Post, error } var data Post - key := prefixPostCacheKey + id - err = m.conn.FindOne(ctx, key, &data, bson.M{"_id": oid}) + + err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid}) switch err { case nil: return &data, nil - case monc.ErrNotFound: + case mon.ErrNotFound: return nil, ErrNotFound default: return nil, err @@ -60,9 +57,9 @@ func (m *defaultPostModel) FindOne(ctx context.Context, id string) (*Post, error } func (m *defaultPostModel) Update(ctx context.Context, data *Post) (*mongo.UpdateResult, error) { - data.UpdateAt = time.Now() - key := prefixPostCacheKey + data.ID.Hex() - res, err := m.conn.UpdateOne(ctx, key, bson.M{"_id": data.ID}, bson.M{"$set": data}) + data.UpdateAt = time.Now().UTC().UnixNano() + + res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) return res, err } @@ -71,7 +68,7 @@ func (m *defaultPostModel) Delete(ctx context.Context, id string) (int64, error) if err != nil { return 0, ErrInvalidObjectId } - key := prefixPostCacheKey + id - res, err := m.conn.DeleteOne(ctx, key, bson.M{"_id": oid}) + + res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid}) return res, err } diff --git a/internal/model/mongo/post_types.go b/internal/model/mongo/post_types.go index 1a5802f..7b50259 100644 --- a/internal/model/mongo/post_types.go +++ b/internal/model/mongo/post_types.go @@ -1,14 +1,32 @@ package model import ( - "time" - "go.mongodb.org/mongo-driver/bson/primitive" ) type Post 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"` + UID string `bson:"uid" json:"uid"` // 搜尋條件 + Content string `bson:"content" json:"content"` // 內容 + Status int8 `bson:"status" json:"status"` // 1. 等待審核中 , 2 審核通過,預設為 1 ->過濾條件 + IsAd bool `bson:"is_ad" json:"is_ad"` // 此則貼文是否為廣告貼文 -> 過濾條件 + Tags []string `bson:"tags" json:"tags"` // 本則貼文的標籤,不提供搜尋,僅提供顯示(存名字,ID 建立之後就不提供修改與刪除) + MediaURL []Media `bson:"media_url" json:"media_url"` // 網址 + 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"` // -> 排序條件 } + +type Media struct { + Type string // media type jpeg, m3u8 之類的 + Links string // 連結的網址 +} + +func (p *Post) CollectionName() string { + return "post" +} + +// 照邏輯,應該需要建立的索引有 +// 單列索引:UID、Status、IsAd、CreateAt +// 複合索引:(UID + Status + IsAd)、(UID + CreateAt) diff --git a/internal/model/mongo/tags_model.go b/internal/model/mongo/tags_model.go deleted file mode 100644 index 6863721..0000000 --- a/internal/model/mongo/tags_model.go +++ /dev/null @@ -1,25 +0,0 @@ -package model - -import "github.com/zeromicro/go-zero/core/stores/mon" - -var _ TagsModel = (*customTagsModel)(nil) - -type ( - // TagsModel is an interface to be customized, add more methods here, - // and implement the added methods in customTagsModel. - TagsModel interface { - tagsModel - } - - customTagsModel struct { - *defaultTagsModel - } -) - -// NewTagsModel returns a model for the mongo. -func NewTagsModel(url, db, collection string) TagsModel { - conn := mon.MustNewModel(url, db, collection) - return &customTagsModel{ - defaultTagsModel: newDefaultTagsModel(conn), - } -} diff --git a/internal/model/mongo/tags_model_gen.go b/internal/model/mongo/tags_model_gen.go deleted file mode 100644 index db82d2b..0000000 --- a/internal/model/mongo/tags_model_gen.go +++ /dev/null @@ -1,74 +0,0 @@ -// Code generated by goctl. DO NOT EDIT. -package model - -import ( - "context" - "time" - - "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" -) - -type tagsModel interface { - Insert(ctx context.Context, data *Tags) error - FindOne(ctx context.Context, id string) (*Tags, error) - Update(ctx context.Context, data *Tags) (*mongo.UpdateResult, error) - Delete(ctx context.Context, id string) (int64, error) -} - -type defaultTagsModel struct { - conn *mon.Model -} - -func newDefaultTagsModel(conn *mon.Model) *defaultTagsModel { - return &defaultTagsModel{conn: conn} -} - -func (m *defaultTagsModel) Insert(ctx context.Context, data *Tags) error { - if data.ID.IsZero() { - data.ID = primitive.NewObjectID() - data.CreateAt = time.Now() - data.UpdateAt = time.Now() - } - - _, err := m.conn.InsertOne(ctx, data) - return err -} - -func (m *defaultTagsModel) FindOne(ctx context.Context, id string) (*Tags, error) { - oid, err := primitive.ObjectIDFromHex(id) - if err != nil { - return nil, ErrInvalidObjectId - } - - var data Tags - - err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid}) - switch err { - case nil: - return &data, nil - case mon.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *defaultTagsModel) Update(ctx context.Context, data *Tags) (*mongo.UpdateResult, error) { - data.UpdateAt = time.Now() - - res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) - return res, err -} - -func (m *defaultTagsModel) Delete(ctx context.Context, id string) (int64, error) { - oid, err := primitive.ObjectIDFromHex(id) - if err != nil { - return 0, ErrInvalidObjectId - } - - res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid}) - return res, err -} diff --git a/internal/model/mongo/tags_types.go b/internal/model/mongo/tags_types.go deleted file mode 100644 index 6972376..0000000 --- a/internal/model/mongo/tags_types.go +++ /dev/null @@ -1,14 +0,0 @@ -package model - -import ( - "time" - - "go.mongodb.org/mongo-driver/bson/primitive" -) - -type Tags 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"` -} diff --git a/internal/server/commentservice/comment_service_server.go b/internal/server/commentservice/comment_service_server.go index 8e4eab8..9fed63f 100644 --- a/internal/server/commentservice/comment_service_server.go +++ b/internal/server/commentservice/comment_service_server.go @@ -7,7 +7,7 @@ import ( "context" "app-cloudep-tweeting-service/gen_result/pb/tweeting" - "app-cloudep-tweeting-service/internal/logic/commentservice" + commentservicelogic "app-cloudep-tweeting-service/internal/logic/commentservice" "app-cloudep-tweeting-service/internal/svc" ) @@ -23,7 +23,7 @@ func NewCommentServiceServer(svcCtx *svc.ServiceContext) *CommentServiceServer { } // NewComment 發表評論 -func (s *CommentServiceServer) NewComment(ctx context.Context, in *tweeting.CommentPostReq) (*tweeting.OKResp, error) { +func (s *CommentServiceServer) NewComment(ctx context.Context, in *tweeting.CommentPostReq) (*tweeting.CommentPostResp, error) { l := commentservicelogic.NewNewCommentLogic(ctx, s.svcCtx) return l.NewComment(in) } @@ -45,27 +45,3 @@ func (s *CommentServiceServer) UpdateComment(ctx context.Context, in *tweeting.U l := commentservicelogic.NewUpdateCommentLogic(ctx, s.svcCtx) return l.UpdateComment(in) } - -// LikeComment 點讚/取消讚 評論 -func (s *CommentServiceServer) LikeComment(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) { - l := commentservicelogic.NewLikeCommentLogic(ctx, s.svcCtx) - return l.LikeComment(in) -} - -// GetLikeStatus 取得讚/不讚評論狀態 -func (s *CommentServiceServer) GetLikeStatus(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) { - l := commentservicelogic.NewGetLikeStatusLogic(ctx, s.svcCtx) - return l.GetLikeStatus(in) -} - -// LikeList 取得讚/不讚評論列表 -func (s *CommentServiceServer) LikeList(ctx context.Context, in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) { - l := commentservicelogic.NewLikeListLogic(ctx, s.svcCtx) - return l.LikeList(in) -} - -// CountLike 取得讚/不讚評論數量 -func (s *CommentServiceServer) CountLike(ctx context.Context, in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) { - l := commentservicelogic.NewCountLikeLogic(ctx, s.svcCtx) - return l.CountLike(in) -} diff --git a/internal/server/postservice/post_service_server.go b/internal/server/postservice/post_service_server.go index b5fdaad..639c1f4 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" - "app-cloudep-tweeting-service/internal/logic/postservice" + postservicelogic "app-cloudep-tweeting-service/internal/logic/postservice" "app-cloudep-tweeting-service/internal/svc" ) @@ -22,10 +22,10 @@ func NewPostServiceServer(svcCtx *svc.ServiceContext) *PostServiceServer { } } -// NewPost 新增貼文 -func (s *PostServiceServer) NewPost(ctx context.Context, in *tweeting.NewPostReq) (*tweeting.PostResp, error) { - l := postservicelogic.NewNewPostLogic(ctx, s.svcCtx) - return l.NewPost(in) +// CreatePost 新增貼文 +func (s *PostServiceServer) CreatePost(ctx context.Context, in *tweeting.NewPostReq) (*tweeting.PostResp, error) { + l := postservicelogic.NewCreatePostLogic(ctx, s.svcCtx) + return l.CreatePost(in) } // DeletePost 刪除貼文 @@ -45,27 +45,3 @@ func (s *PostServiceServer) ListPosts(ctx context.Context, in *tweeting.QueryPos l := postservicelogic.NewListPostsLogic(ctx, s.svcCtx) return l.ListPosts(in) } - -// Like 點讚/取消讚 貼文 -func (s *PostServiceServer) Like(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) { - l := postservicelogic.NewLikeLogic(ctx, s.svcCtx) - return l.Like(in) -} - -// GetLikeStatus 取得讚/不讚狀態 -func (s *PostServiceServer) GetLikeStatus(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) { - l := postservicelogic.NewGetLikeStatusLogic(ctx, s.svcCtx) - return l.GetLikeStatus(in) -} - -// LikeList 取得讚/不讚列表 -func (s *PostServiceServer) LikeList(ctx context.Context, in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) { - l := postservicelogic.NewLikeListLogic(ctx, s.svcCtx) - return l.LikeList(in) -} - -// CountLike 取得讚/不讚數量 -func (s *PostServiceServer) CountLike(ctx context.Context, in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) { - l := postservicelogic.NewCountLikeLogic(ctx, s.svcCtx) - return l.CountLike(in) -} diff --git a/internal/svc/init_mongo.go b/internal/svc/init_mongo.go new file mode 100644 index 0000000..d815d98 --- /dev/null +++ b/internal/svc/init_mongo.go @@ -0,0 +1,27 @@ +package svc + +import ( + "app-cloudep-tweeting-service/internal/config" + model "app-cloudep-tweeting-service/internal/model/mongo" + "fmt" +) + +func mustMongoConnectUrl(c config.Config) string { + return fmt.Sprintf("%s://%s:%s", + c.Mongo.Schema, + c.Mongo.Host, + c.Mongo.Port, + ) +} + +// 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 37ac177..6dff22c 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -1,13 +1,25 @@ package svc -import "app-cloudep-tweeting-service/internal/config" +import ( + "app-cloudep-tweeting-service/internal/config" + model "app-cloudep-tweeting-service/internal/model/mongo" + + vi "code.30cm.net/digimon/library-go/validator" +) type ServiceContext struct { - Config config.Config + Config config.Config + Validate vi.Validate + + PostModel model.PostModel + CommentModel model.CommentModel } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ - Config: c, + Config: c, + Validate: vi.MustValidator(), + PostModel: MustPostModel(c), + CommentModel: MustCommentModel(c), } } diff --git a/tweeting.go b/tweeting.go index 64e8540..c708b77 100644 --- a/tweeting.go +++ b/tweeting.go @@ -6,7 +6,6 @@ import ( "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/config" - commentserviceServer "app-cloudep-tweeting-service/internal/server/commentservice" postserviceServer "app-cloudep-tweeting-service/internal/server/postservice" "app-cloudep-tweeting-service/internal/svc" @@ -28,7 +27,6 @@ func main() { s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { tweeting.RegisterPostServiceServer(grpcServer, postserviceServer.NewPostServiceServer(ctx)) - tweeting.RegisterCommentServiceServer(grpcServer, commentserviceServer.NewCommentServiceServer(ctx)) if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer)