From 132f1ba9514527774758394ff56e52bd5e7a82b1 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 09:08:15 +0800 Subject: [PATCH 1/7] add new post --- generate/protobuf/tweeting.proto | 13 ++-- go.mod | 17 +++++ internal/config/config.go | 17 ++++- internal/domain/errors.go | 38 ++++++++++++ internal/domain/status.go | 12 ++++ internal/logic/postservice/new_post_logic.go | 65 ++++++++++++++++++-- internal/model/mongo/post_types.go | 23 +++++-- internal/svc/service_context.go | 24 +++++++- 8 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 internal/domain/errors.go create mode 100644 internal/domain/status.go diff --git a/generate/protobuf/tweeting.proto b/generate/protobuf/tweeting.proto index be6b914..6f6bea9 100644 --- a/generate/protobuf/tweeting.proto +++ b/generate/protobuf/tweeting.proto @@ -15,12 +15,17 @@ message Pager { int64 index=3; // 當前頁碼 } +message Media{ + string type =1; + string url =2; +} + // 新增貼文的請求 message NewPostReq { - int64 user_id = 1; // 發佈貼文的用戶ID + string uid = 1; // 發佈貼文的用戶ID string content = 2; // 貼文內容 repeated string tags = 3; // 貼文相關標籤 - repeated string media_url = 4; // 這筆文章的所有 Media URL + repeated Media media = 4; // 這筆文章的所有 Media URL bool is_ad = 5; // 是否為廣告 } @@ -38,7 +43,7 @@ message DeletePostsReq { message UpdatePostReq { string post_id = 1; // 貼文ID repeated string tags = 2; // 新的標籤列表 - repeated string media_url = 3; // 這筆文章的所有 Media URL + repeated Media media = 3; // 這筆文章的所有 Media URL optional string content = 4; // 新的貼文內容 optional int64 like_count = 5; // 喜歡數量 optional int64 dislike_count = 6; // 不喜歡數量 @@ -60,7 +65,7 @@ message PostDetailItem { int64 user_id = 2; // 發佈用戶ID string content = 3; // 貼文內容 repeated string tags = 4; // 標籤 - repeated string media_url = 5; // 圖片URL + repeated Media media = 5; // 圖片URL bool is_ad = 6; // 是否為廣告 int64 created_at = 7; // 發佈時間 int64 update_at = 8; // 更新時間 diff --git a/go.mod b/go.mod index 1be6bfb..44c507f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,10 @@ 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/zeromicro/go-zero v1.7.0 + go.mongodb.org/mongo-driver v1.16.1 google.golang.org/grpc v1.66.0 google.golang.org/protobuf v1.34.2 ) @@ -18,14 +21,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,11 +41,14 @@ 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 @@ -47,6 +58,10 @@ require ( 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 +80,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..7055491 100755 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,7 +1,22 @@ package config -import "github.com/zeromicro/go-zero/zrpc" +import ( + "github.com/zeromicro/go-zero/core/stores/cache" + "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 + Collection string + } + + // 快取 + Cache cache.CacheConf } diff --git a/internal/domain/errors.go b/internal/domain/errors.go new file mode 100644 index 0000000..a41d179 --- /dev/null +++ b/internal/domain/errors.go @@ -0,0 +1,38 @@ +package domain + +import ( + "code.30cm.net/digimon/library-go/errs" + "code.30cm.net/digimon/library-go/errs/code" + "fmt" + "github.com/zeromicro/go-zero/core/logx" + "strings" +) + +type ErrorCode uint32 + +func (e ErrorCode) ToUint32() uint32 { + return uint32(e) +} + +const ( + _ = iota + PostMongoErrorCode ErrorCode = iota +) + +// PostMongoError ... +func PostMongoError(s ...string) *errs.LibError { + return errs.NewError(code.CloudEPTweeting, code.DBError, + PostMongoErrorCode.ToUint32(), + fmt.Sprintf("%s", strings.Join(s, " "))) +} + +// PostMongoErrorL logs error message and returns Err +func PostMongoErrorL(l logx.Logger, filed []logx.LogField, s ...string) *errs.LibError { + e := PostMongoError(s...) + if filed != nil || len(filed) >= 0 { + l.WithCallerSkip(1).WithFields(filed...).Error(e.Error()) + } + l.WithCallerSkip(1).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/postservice/new_post_logic.go b/internal/logic/postservice/new_post_logic.go index a755c05..ef95a4e 100644 --- a/internal/logic/postservice/new_post_logic.go +++ b/internal/logic/postservice/new_post_logic.go @@ -1,10 +1,12 @@ package postservicelogic 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" + ers "code.30cm.net/digimon/library-go/errs" + "context" "github.com/zeromicro/go-zero/core/logx" ) @@ -23,9 +25,64 @@ func NewNewPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewPostLo } } +type newTweetingReq struct { + UID string `json:"uid" validate:"required"` + Content string `json:"content" validate:"required,gte=500"` // 貼文限制 500 字內 + Tags []string `json:"tags"` + MediaUrl []string `json:"media_url"` + IsAd bool `json:"is_ad"` // default false +} + // NewPost 新增貼文 func (l *NewPostLogic) NewPost(in *tweeting.NewPostReq) (*tweeting.PostResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&newTweetingReq{ + UID: in.GetUid(), + Content: in.GetContent(), + }); err != nil { + 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() + } - return &tweeting.PostResp{}, nil + 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.Media = media + } + // ============ insert ============ + err := l.svcCtx.PostModel.Insert(l.ctx, tweet) + if err != nil { + e := domain.PostMongoErrorL( + 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/model/mongo/post_types.go b/internal/model/mongo/post_types.go index 1a5802f..fa07b17 100644 --- a/internal/model/mongo/post_types.go +++ b/internal/model/mongo/post_types.go @@ -6,9 +6,24 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" ) +// TODO Tag 這裡在效能與正確性之間做取捨 +// 存在貼文內的不提供搜尋,純顯示用,只不過在原始的tag 發生變動的時候,並不會一起改變 +// 搜尋會貼文與Tag 的表會再另外一邊做關聯 +// 暫時業務邏輯上tag 只提供新增,不提供修改以及刪除,故目前版本可行 + 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 建立之後就不提供修改與刪除) + Media []Media `bson:"media_url" json:"media_url"` // 網址 + UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"` + CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"` +} + +type Media struct { + Type string // media type jpeg, m3u8 之類的 + Links string // 連結的網址 } diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index 37ac177..cb07911 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -1,13 +1,31 @@ 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" + "fmt" +) type ServiceContext struct { - Config config.Config + Config config.Config + Validate vi.Validate + + PostModel model.PostModel } func NewServiceContext(c config.Config) *ServiceContext { + baseMongo := MustMongoConnectUrl(c) + return &ServiceContext{ - Config: c, + Config: c, + Validate: vi.MustValidator(), + PostModel: model.NewPostModel(baseMongo, c.Mongo.Database, "post", c.Cache), } } + +func MustMongoConnectUrl(c config.Config) string { + return fmt.Sprintf( + "%s://%s:%s", c.Mongo.Schema, + c.Mongo.Host, c.Mongo.Port) +} -- 2.40.1 From b4e70825768c099da6ccb68e40186a581ae810a7 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 10:41:34 +0800 Subject: [PATCH 2/7] add update and delete post --- etc/tweeting.yaml | 22 +++++ internal/config/config.go | 13 ++- .../logic/postservice/delete_post_logic.go | 14 ++- internal/logic/postservice/new_post_logic.go | 2 +- .../logic/postservice/update_post_logic.go | 50 +++++++++- internal/model/mongo/post_model.go | 92 +++++++++++++++++++ internal/model/mongo/post_model_gen.go | 6 +- internal/model/mongo/post_types.go | 8 +- 8 files changed, 190 insertions(+), 17 deletions(-) diff --git a/etc/tweeting.yaml b/etc/tweeting.yaml index c611c48..9d1d04c 100644 --- a/etc/tweeting.yaml +++ b/etc/tweeting.yaml @@ -4,3 +4,25 @@ Etcd: Hosts: - 127.0.0.1:2379 Key: tweeting.rpc + +Cache: + - Host: 127.0.0.1:7001 + type: cluster + - Host: 127.0.0.1:7002 + type: cluster + - Host: 127.0.0.1:7003 + type: cluster + - Host: 127.0.0.1:7004 + type: cluster + - Host: 127.0.0.1:7005 + type: cluster + - Host: 127.0.0.1:7006 + type: cluster + +Mongo: + Schema: mongodb + Host: 127.0.0.1 + User: "" + Password: "" + Port: "27017" + Database: digimon_tweeting \ No newline at end of file diff --git a/internal/config/config.go b/internal/config/config.go index 7055491..eecde82 100755 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -8,13 +8,12 @@ import ( type Config struct { zrpc.RpcServerConf Mongo struct { - Schema string - User string - Password string - Host string - Port string - Database string - Collection string + Schema string + User string + Password string + Host string + Port string + Database string } // 快取 diff --git a/internal/logic/postservice/delete_post_logic.go b/internal/logic/postservice/delete_post_logic.go index 714e1f5..41f7ddc 100644 --- a/internal/logic/postservice/delete_post_logic.go +++ b/internal/logic/postservice/delete_post_logic.go @@ -1,6 +1,7 @@ package postservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" "context" "app-cloudep-tweeting-service/gen_result/pb/tweeting" @@ -25,7 +26,18 @@ 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 { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.DeletePost"}, + {Key: "err", Value: err}, + }, + "failed to add del post").Wrap(err) + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/logic/postservice/new_post_logic.go b/internal/logic/postservice/new_post_logic.go index ef95a4e..f555b84 100644 --- a/internal/logic/postservice/new_post_logic.go +++ b/internal/logic/postservice/new_post_logic.go @@ -27,7 +27,7 @@ func NewNewPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewPostLo type newTweetingReq struct { UID string `json:"uid" validate:"required"` - Content string `json:"content" validate:"required,gte=500"` // 貼文限制 500 字內 + 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 diff --git a/internal/logic/postservice/update_post_logic.go b/internal/logic/postservice/update_post_logic.go index 454844b..23b5ee3 100644 --- a/internal/logic/postservice/update_post_logic.go +++ b/internal/logic/postservice/update_post_logic.go @@ -1,7 +1,11 @@ package postservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + ers "code.30cm.net/digimon/library-go/errs" "context" + "go.mongodb.org/mongo-driver/bson/primitive" "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" @@ -23,9 +27,53 @@ 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 { + return nil, ers.InvalidFormat(err.Error()) + } + // 沒有就沒有,有就走全覆蓋 + update := model.Post{} + oid, err := primitive.ObjectIDFromHex(in.GetPostId()) + if err != nil { + 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.Media = media + update.Content = in.GetContent() + update.Like = uint64(in.GetLikeCount()) + update.DisLike = uint64(in.GetDislikeCount()) + + _, err = l.svcCtx.PostModel.UpdateOptional(l.ctx, &update) + if err != nil { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.UpdateOptional"}, + {Key: "err", Value: err}, + }, + "failed to add new post").Wrap(err) + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/model/mongo/post_model.go b/internal/model/mongo/post_model.go index 096dda8..d57daab 100644 --- a/internal/model/mongo/post_model.go +++ b/internal/model/mongo/post_model.go @@ -1,8 +1,15 @@ package model import ( + "context" + "fmt" + "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/monc" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" + "time" ) var _ PostModel = (*customPostModel)(nil) @@ -12,6 +19,8 @@ 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) } customPostModel struct { @@ -26,3 +35,86 @@ func NewPostModel(url, db, collection string, c cache.CacheConf) PostModel { defaultPostModel: newDefaultPostModel(conn), } } + +func (m *defaultPostModel) DeleteMany(ctx context.Context, id ...string) (int64, error) { + objectIDs := make([]primitive.ObjectID, 0, len(id)) + key := make([]string, 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) + key = append(key, prefixPostCacheKey+item) + } + + // 檢查是否有有效的 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 + } + + err = m.conn.DelCache(ctx, key...) + 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.Media) > 0 { + update["$set"].(bson.M)["media_url"] = data.Media + } + + if data.Like != 0 { + update["$set"].(bson.M)["like"] = data.Like + } + + if data.DisLike != 0 { + update["$set"].(bson.M)["dislike"] = data.DisLike + } + + // UpdateAt 是每次都需要更新的,不用檢查 + update["$set"].(bson.M)["updateAt"] = time.Now().UTC().UnixNano() + fmt.Println("update map", update) + + key := prefixPostCacheKey + data.ID.Hex() + res, err := m.conn.UpdateOne(ctx, key, bson.M{"_id": data.ID}, update) + + return res, err +} diff --git a/internal/model/mongo/post_model_gen.go b/internal/model/mongo/post_model_gen.go index c7aa965..386d9c7 100644 --- a/internal/model/mongo/post_model_gen.go +++ b/internal/model/mongo/post_model_gen.go @@ -31,8 +31,8 @@ func newDefaultPostModel(conn *monc.Model) *defaultPostModel { 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() @@ -60,7 +60,7 @@ 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() + data.UpdateAt = time.Now().UTC().UnixNano() key := prefixPostCacheKey + data.ID.Hex() res, err := m.conn.UpdateOne(ctx, key, bson.M{"_id": data.ID}, bson.M{"$set": data}) return res, err diff --git a/internal/model/mongo/post_types.go b/internal/model/mongo/post_types.go index fa07b17..7677d60 100644 --- a/internal/model/mongo/post_types.go +++ b/internal/model/mongo/post_types.go @@ -1,8 +1,6 @@ package model import ( - "time" - "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -19,8 +17,10 @@ type Post struct { IsAd bool `bson:"is_ad" json:"is_ad"` // 此則貼文是否為廣告貼文 Tags []string `bson:"tags" json:"tags"` // 本則貼文的標籤,不提供搜尋,僅提供顯示(存名字,ID 建立之後就不提供修改與刪除) Media []Media `bson:"media_url" json:"media_url"` // 網址 - UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"` - CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"` + Like uint64 `bson:"like" json:"like"` // 讚 + DisLike uint64 `bson:"dislike" json:"dislike"` // 不讚 + UpdateAt int64 `bson:"updateAt,omitempty" json:"updateAt,omitempty"` + CreateAt int64 `bson:"createAt,omitempty" json:"createAt,omitempty"` } type Media struct { -- 2.40.1 From 518e1673fece8ffc17db52b0e356c00704baabda Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 17:33:57 +0800 Subject: [PATCH 3/7] add list posts --- generate/protobuf/tweeting.proto | 9 +- internal/domain/const.go | 13 +++ .../logic/postservice/list_posts_logic.go | 86 ++++++++++++++++++- internal/model/mongo/post_model.go | 73 ++++++++++++++++ 4 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 internal/domain/const.go diff --git a/generate/protobuf/tweeting.proto b/generate/protobuf/tweeting.proto index 6f6bea9..bbc7229 100644 --- a/generate/protobuf/tweeting.proto +++ b/generate/protobuf/tweeting.proto @@ -51,10 +51,9 @@ message UpdatePostReq { // 查詢貼文的請求 message QueryPostsReq { - repeated int64 user_id = 1; // 可選:根據用戶ID篩選貼文 - repeated int64 id = 2; // 可選:根據貼文ID篩選貼文 - repeated string tags = 3; // 可選:根據標籤篩選貼文 - optional bool only_ads = 4; // 可選:是否只顯示廣告 + repeated string uid = 1; // 可選:根據用戶ID篩選貼文 + repeated string id = 2; // 可選:根據貼文ID篩選貼文 + optional int32 only_ads = 4; // 可選:是否只顯示廣告 0 不篩選 1 只顯示廣告 2 不顯示廣告 int32 page_index = 5; // 分頁的頁碼 int32 page_size = 6; // 每頁顯示的數量 } @@ -62,7 +61,7 @@ message QueryPostsReq { // 貼文詳情 message PostDetailItem { string post_id = 1; // 貼文ID - int64 user_id = 2; // 發佈用戶ID + string uid = 2; // 發佈用戶ID string content = 3; // 貼文內容 repeated string tags = 4; // 標籤 repeated Media media = 5; // 圖片URL 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/logic/postservice/list_posts_logic.go b/internal/logic/postservice/list_posts_logic.go index abba88d..b8bf565 100644 --- a/internal/logic/postservice/list_posts_logic.go +++ b/internal/logic/postservice/list_posts_logic.go @@ -1,10 +1,13 @@ package postservicelogic 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" + ers "code.30cm.net/digimon/library-go/errs" + "context" + "google.golang.org/protobuf/proto" "github.com/zeromicro/go-zero/core/logx" ) @@ -23,9 +26,84 @@ func NewListPostsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListPos } } +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"` +} + // ListPosts 查詢貼文 func (l *ListPostsLogic) ListPosts(in *tweeting.QueryPostsReq) (*tweeting.ListPostsResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&listReq{ + PageSize: int64(in.GetPageSize()), + PageIndex: int64(in.GetPageIndex()), + OnlyAdds: in.GetOnlyAds(), + }); err != nil { + return nil, ers.InvalidFormat(err.Error()) + } - return &tweeting.ListPostsResp{}, nil + query := &model.QueryPostModelReq{ + UID: in.GetUid(), + Id: in.GetId(), + PageSize: int64(in.GetPageSize()), + PageIndex: int64(in.GetPageIndex()), + } + + if in.OnlyAds != nil { + switch in.GetOnlyAds() { + case domain.AdTypeOnlyAd.ToInt32(): + query.OnlyAds = proto.Bool(true) + case domain.AdTypeOnlyNotAd.ToInt32(): + query.OnlyAds = proto.Bool(false) + default: + } + } + + find, count, err := l.svcCtx.PostModel.Find(l.ctx, query) + if err != nil { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "query", Value: query}, + {Key: "func", Value: "PostModel.Find"}, + {Key: "err", Value: err}, + }, + "failed to add new post").Wrap(err) + return nil, e + } + + result := make([]*tweeting.PostDetailItem, 0, count) + + for _, item := range find { + media := make([]*tweeting.Media, 0, len(item.Media)) + for _, subItem := range item.Media { + media = append(media, &tweeting.Media{ + Type: subItem.Type, + Url: subItem.Links, + }) + } + + result = append(result, &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), + }) + } + + return &tweeting.ListPostsResp{ + Posts: result, + Page: &tweeting.Pager{ + Total: count, + Index: int64(in.GetPageIndex()), + Size: int64(in.GetPageSize()), + }, + }, nil } diff --git a/internal/model/mongo/post_model.go b/internal/model/mongo/post_model.go index d57daab..f968892 100644 --- a/internal/model/mongo/post_model.go +++ b/internal/model/mongo/post_model.go @@ -2,6 +2,7 @@ package model import ( "context" + "errors" "fmt" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/cache" @@ -9,6 +10,7 @@ import ( "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "time" ) @@ -21,11 +23,20 @@ type ( 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. @@ -118,3 +129,65 @@ func (m *defaultPostModel) UpdateOptional(ctx context.Context, data *Post) (*mon 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 + } + + // 分頁處理 + options := options.Find() + if param.PageSize > 0 { + options.SetLimit(param.PageSize) + } + if param.PageIndex > 0 { + options.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, options) + 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 + } +} -- 2.40.1 From 98605573f360c0896a64c73f7dd0ed05a120d100 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 21:26:01 +0800 Subject: [PATCH 4/7] add list posts --- .../mongodb/20240829054501_post.up.text | 5 + .../mongodb/20240829055501_likes.up.text | 5 + generate/protobuf/tweeting.proto | 26 ++++- internal/domain/const.go | 11 +++ .../postservice/get_like_status_logic.go | 2 + internal/logic/postservice/like_list_logic.go | 58 ++++++++++- internal/logic/postservice/like_logic.go | 35 ++++++- .../model/mongo/comment_likes_model_gen.go | 14 +-- internal/model/mongo/comment_likes_types.go | 2 +- internal/model/mongo/post_likes_model.go | 96 ++++++++++++++++++- internal/model/mongo/post_likes_model_gen.go | 19 ++-- internal/model/mongo/post_likes_types.go | 17 ++-- internal/model/mongo/post_model.go | 9 +- internal/model/mongo/post_types.go | 4 + internal/svc/service_context.go | 12 ++- 15 files changed, 272 insertions(+), 43 deletions(-) create mode 100644 generate/database/mongodb/20240829054501_post.up.text create mode 100644 generate/database/mongodb/20240829055501_likes.up.text diff --git a/generate/database/mongodb/20240829054501_post.up.text b/generate/database/mongodb/20240829054501_post.up.text new file mode 100644 index 0000000..4a809a7 --- /dev/null +++ b/generate/database/mongodb/20240829054501_post.up.text @@ -0,0 +1,5 @@ +use digimon_tweeting; +db.post.createIndex({ "uid": 1, "create_time": 1 }); +db.post.createIndex({ "is_ad": 1, "create_time": 1 }); +db.post.createIndex({ "create_time": 1 }); + diff --git a/generate/database/mongodb/20240829055501_likes.up.text b/generate/database/mongodb/20240829055501_likes.up.text new file mode 100644 index 0000000..09628ff --- /dev/null +++ b/generate/database/mongodb/20240829055501_likes.up.text @@ -0,0 +1,5 @@ +db.post_likes.createIndex( + { "target_id": 1, "uid": 1, "type": 1 }, + { unique: true } +); +db.post_likes.createIndex({ "create_time": 1 }); \ No newline at end of file diff --git a/generate/protobuf/tweeting.proto b/generate/protobuf/tweeting.proto index bbc7229..8a0b6f3 100644 --- a/generate/protobuf/tweeting.proto +++ b/generate/protobuf/tweeting.proto @@ -81,14 +81,30 @@ message ListPostsResp { // 讚/不讚請求 message LikeReq { string target_id = 1; // 目標ID(可以是貼文ID或評論ID) - int64 user_id = 2; // 點讚的用戶ID + string uid = 2; // 點讚的用戶ID int64 like_type = 3; // 讚或爛的類型 } +message GetLikeStatusReq{ + string uid = 1; // 點讚的用戶ID + repeated string target_id = 2; // 目標ID(可以是貼文ID或評論ID) + int64 like_type = 3; // 讚或爛的類型 +} + +message GetLikeStatusItem{ + string target_id = 1; // 目標ID(可以是貼文ID或評論ID) + bool status = 2; // 是否有按過 +} + +message GetLikeStatusResp{ + repeated GetLikeStatusItem data = 1; // 目標ID(可以是貼文ID或評論ID) +} + + // 讚/不讚項目 message LikeItem { string target_id = 1; // 目標ID(可以是貼文ID或評論ID) - int64 user_id = 2; // 點讚的用戶ID + string uid = 2; // 點讚的用戶ID int64 like_type = 3; // 讚或爛的類型 } @@ -96,8 +112,8 @@ message LikeItem { message LikeListReq { string target_id = 1; // 目標ID(可以是貼文ID或評論ID) int64 like_type = 2; // 讚或爛的類型 - int32 page_index = 3; // 當前頁碼 - int32 page_size = 4; // 每頁顯示數量 + int64 page_index = 3; // 當前頁碼 + int64 page_size = 4; // 每頁顯示數量 } // 讚/不讚列表回應 @@ -172,7 +188,7 @@ service PostService { // Like 點讚/取消讚 貼文 rpc Like(LikeReq) returns (OKResp); // GetLikeStatus 取得讚/不讚狀態 - rpc GetLikeStatus(LikeReq) returns (OKResp); + rpc GetLikeStatus(GetLikeStatusReq) returns (GetLikeStatusResp); // LikeList 取得讚/不讚列表 rpc LikeList(LikeListReq) returns (LikeListResp); // CountLike 取得讚/不讚數量 diff --git a/internal/domain/const.go b/internal/domain/const.go index 0e7211b..6b58d8f 100644 --- a/internal/domain/const.go +++ b/internal/domain/const.go @@ -11,3 +11,14 @@ const ( AdTypeOnlyAd AdTypeOnlyNotAd ) + +type LikeType int8 + +func (l LikeType) ToInt32() int8 { + return int8(l) +} + +const ( + LikeTypeLike LikeType = iota + 1 // 按揍 + LikeTypeDisLike +) diff --git a/internal/logic/postservice/get_like_status_logic.go b/internal/logic/postservice/get_like_status_logic.go index ba92101..edf812c 100644 --- a/internal/logic/postservice/get_like_status_logic.go +++ b/internal/logic/postservice/get_like_status_logic.go @@ -23,6 +23,8 @@ func NewGetLikeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get } } +// 這個人按讚的文章列表(輸入UID 以及文章id,返回這個人有沒有對這些文章按讚) + // GetLikeStatus 取得讚/不讚狀態 func (l *GetLikeStatusLogic) GetLikeStatus(in *tweeting.LikeReq) (*tweeting.OKResp, error) { // todo: add your logic here and delete this line diff --git a/internal/logic/postservice/like_list_logic.go b/internal/logic/postservice/like_list_logic.go index 6f4023b..65135df 100644 --- a/internal/logic/postservice/like_list_logic.go +++ b/internal/logic/postservice/like_list_logic.go @@ -1,6 +1,9 @@ package postservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + ers "code.30cm.net/digimon/library-go/errs" "context" "app-cloudep-tweeting-service/gen_result/pb/tweeting" @@ -23,9 +26,60 @@ func NewLikeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeList } } +// 換句話說就是對這個文章按讚的人的列表 + +type likeListReq struct { + Target string `json:"target" validate:"required"` + LikeType domain.LikeType `json:"like_type" validate:"required,oneof=1 2"` + PageSize int64 `json:"page_size" validate:"required"` + PageIndex int64 `json:"page_index" validate:"required"` +} + // LikeList 取得讚/不讚列表 func (l *LikeListLogic) LikeList(in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&likeListReq{ + Target: in.TargetId, + LikeType: domain.LikeType(in.GetLikeType()), + PageSize: in.GetPageSize(), + PageIndex: in.GetPageIndex(), + }); err != nil { + return nil, ers.InvalidFormat(err.Error()) + } - return &tweeting.LikeListResp{}, nil + result, total, err := l.svcCtx.PostLikeModel.FindLikeUsers(l.ctx, &model.QueryPostLikeReq{ + Target: in.GetTargetId(), + LikeType: domain.LikeType(in.GetLikeType()), + PageSize: in.GetPageSize(), + PageIndex: in.GetPageIndex(), + }) + if err != nil { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.LikeDislike"}, + {Key: "err", Value: err}, + }, + "failed to like or dislike").Wrap(err) + return nil, e + } + + var list = make([]*tweeting.LikeItem, 0, len(result)) + for _, item := range result { + list = append(list, &tweeting.LikeItem{ + LikeType: int64(item.Type), + TargetId: item.TargetID, + Uid: item.UID, + }) + } + + return &tweeting.LikeListResp{ + List: list, + Page: &tweeting.Pager{ + Size: in.GetPageSize(), + Index: in.GetPageIndex(), + Total: total, + }, + }, nil } diff --git a/internal/logic/postservice/like_logic.go b/internal/logic/postservice/like_logic.go index 3965b38..5c26126 100644 --- a/internal/logic/postservice/like_logic.go +++ b/internal/logic/postservice/like_logic.go @@ -1,6 +1,9 @@ package postservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + ers "code.30cm.net/digimon/library-go/errs" "context" "app-cloudep-tweeting-service/gen_result/pb/tweeting" @@ -23,9 +26,39 @@ func NewLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeLogic { } } +type likeReq struct { + Target string `json:"target" validate:"required"` + UID string `json:"uid" validate:"required"` + LikeType domain.LikeType `json:"like_type" validate:"required,oneof=1 2"` +} + // Like 點讚/取消讚 貼文 func (l *LikeLogic) Like(in *tweeting.LikeReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&likeReq{ + Target: in.TargetId, + UID: in.GetUid(), + LikeType: domain.LikeType(in.GetLikeType()), + }); err != nil { + return nil, ers.InvalidFormat(err.Error()) + } + + err := l.svcCtx.PostLikeModel.LikeDislike(l.ctx, &model.PostLikes{ + TargetID: in.GetTargetId(), + UID: in.GetUid(), + Type: int8(in.GetLikeType()), + }) + if err != nil { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.LikeDislike"}, + {Key: "err", Value: err}, + }, + "failed to like or dislike").Wrap(err) + return nil, e + } return &tweeting.OKResp{}, nil } diff --git a/internal/model/mongo/comment_likes_model_gen.go b/internal/model/mongo/comment_likes_model_gen.go index cf5a194..790db69 100644 --- a/internal/model/mongo/comment_likes_model_gen.go +++ b/internal/model/mongo/comment_likes_model_gen.go @@ -12,9 +12,9 @@ import ( ) 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) + Insert(ctx context.Context, data *CommentLikes) error + FindOne(ctx context.Context, id string) (*CommentLikes, error) + Update(ctx context.Context, data *CommentLikes) (*mongo.UpdateResult, error) Delete(ctx context.Context, id string) (int64, error) } @@ -26,7 +26,7 @@ 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 { +func (m *defaultComment_likesModel) Insert(ctx context.Context, data *CommentLikes) error { if data.ID.IsZero() { data.ID = primitive.NewObjectID() data.CreateAt = time.Now() @@ -37,13 +37,13 @@ func (m *defaultComment_likesModel) Insert(ctx context.Context, data *Comment_li return err } -func (m *defaultComment_likesModel) FindOne(ctx context.Context, id string) (*Comment_likes, error) { +func (m *defaultComment_likesModel) FindOne(ctx context.Context, id string) (*CommentLikes, error) { oid, err := primitive.ObjectIDFromHex(id) if err != nil { return nil, ErrInvalidObjectId } - var data Comment_likes + var data CommentLikes err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid}) switch err { @@ -56,7 +56,7 @@ func (m *defaultComment_likesModel) FindOne(ctx context.Context, id string) (*Co } } -func (m *defaultComment_likesModel) Update(ctx context.Context, data *Comment_likes) (*mongo.UpdateResult, error) { +func (m *defaultComment_likesModel) Update(ctx context.Context, data *CommentLikes) (*mongo.UpdateResult, error) { data.UpdateAt = time.Now() res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) diff --git a/internal/model/mongo/comment_likes_types.go b/internal/model/mongo/comment_likes_types.go index be19d28..8165180 100644 --- a/internal/model/mongo/comment_likes_types.go +++ b/internal/model/mongo/comment_likes_types.go @@ -6,7 +6,7 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" ) -type Comment_likes struct { +type CommentLikes struct { ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` // TODO: Fill your own fields UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"` diff --git a/internal/model/mongo/post_likes_model.go b/internal/model/mongo/post_likes_model.go index 9d1a52a..7905c39 100644 --- a/internal/model/mongo/post_likes_model.go +++ b/internal/model/mongo/post_likes_model.go @@ -1,6 +1,16 @@ package model -import "github.com/zeromicro/go-zero/core/stores/mon" +import ( + "app-cloudep-tweeting-service/internal/domain" + "context" + "errors" + "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" + "go.mongodb.org/mongo-driver/mongo/options" + "time" +) var _ Post_likesModel = (*customPost_likesModel)(nil) @@ -9,6 +19,15 @@ type ( // and implement the added methods in customPost_likesModel. Post_likesModel interface { post_likesModel + LikeDislike(ctx context.Context, postLike *PostLikes) error + FindLikeUsers(ctx context.Context, param *QueryPostLikeReq) ([]*PostLikes, int64, error) + } + + QueryPostLikeReq struct { + Target string `bson:"target"` + LikeType domain.LikeType `bson:"like_type"` + PageIndex int64 `bson:"page_index"` + PageSize int64 `bson:"page_size"` } customPost_likesModel struct { @@ -23,3 +42,78 @@ func NewPost_likesModel(url, db, collection string) Post_likesModel { defaultPost_likesModel: newDefaultPost_likesModel(conn), } } + +func (m *defaultPost_likesModel) LikeDislike(ctx context.Context, postLike *PostLikes) error { + // 使用 target_id、uid、type 來查詢資料是否存在 + filter := bson.M{ + "target_id": postLike.TargetID, + "uid": postLike.UID, + "type": postLike.Type, + } + + // 查詢資料是否存在 + var existingPostLike PostLikes + err := m.conn.FindOne(ctx, &existingPostLike, filter) + + if err == nil { + // 資料存在,進行刪除操作 + _, err = m.conn.DeleteOne(ctx, filter) + if err != nil { + return err // 刪除失敗 + } + return nil // 刪除成功 + } else if errors.Is(mongo.ErrNoDocuments, err) { + // 資料不存在,進行插入操作 + postLike.ID = primitive.NewObjectID() // 設置新的 ObjectID + postLike.CreateAt = time.Now().UTC().UnixNano() + _, err = m.conn.InsertOne(ctx, postLike) + if err != nil { + return err // 插入失敗 + } + return nil // 插入成功 + } else { + // 其他錯誤 + return err + } +} + +func (m *defaultPost_likesModel) FindLikeUsers(ctx context.Context, param *QueryPostLikeReq) ([]*PostLikes, int64, error) { + // 建立篩選條件 + filter := bson.M{} + + // 如果指定了 Target 條件,將其添加到篩選條件中 + if param.Target != "" { + filter["target_id"] = param.Target + } + + // 如果指定了 LikeType 條件,將其添加到篩選條件中 + if param.LikeType != 0 { + filter["type"] = param.LikeType + } + + // 計算符合條件的文檔總數 + totalCount, err := m.conn.CountDocuments(ctx, filter) + if err != nil { + return nil, 0, err + } + + // 設置分頁和排序選項 + opts := options.Find() + opts.SetSort(bson.D{{"createAt", -1}}) // 按照創建時間倒序排序 + if param.PageSize > 0 { + opts.SetLimit(param.PageSize) + } + if param.PageIndex > 0 && param.PageSize > 0 { + opts.SetSkip((param.PageIndex - 1) * param.PageSize) + } + + // 查詢符合條件的文檔 + var results []*PostLikes + err = m.conn.Find(ctx, &results, filter, opts) + if err != nil { + return nil, 0, err + } + + // 返回結果集、總數和錯誤信息 + return results, totalCount, nil +} diff --git a/internal/model/mongo/post_likes_model_gen.go b/internal/model/mongo/post_likes_model_gen.go index e0499b7..c7c6920 100644 --- a/internal/model/mongo/post_likes_model_gen.go +++ b/internal/model/mongo/post_likes_model_gen.go @@ -12,9 +12,9 @@ import ( ) 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) + Insert(ctx context.Context, data *PostLikes) error + FindOne(ctx context.Context, id string) (*PostLikes, error) + Update(ctx context.Context, data *PostLikes) (*mongo.UpdateResult, error) Delete(ctx context.Context, id string) (int64, error) } @@ -26,24 +26,23 @@ 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 { +func (m *defaultPost_likesModel) Insert(ctx context.Context, data *PostLikes) error { if data.ID.IsZero() { data.ID = primitive.NewObjectID() - data.CreateAt = time.Now() - data.UpdateAt = time.Now() + data.CreateAt = time.Now().UTC().UnixNano() } _, err := m.conn.InsertOne(ctx, data) return err } -func (m *defaultPost_likesModel) FindOne(ctx context.Context, id string) (*Post_likes, error) { +func (m *defaultPost_likesModel) FindOne(ctx context.Context, id string) (*PostLikes, error) { oid, err := primitive.ObjectIDFromHex(id) if err != nil { return nil, ErrInvalidObjectId } - var data Post_likes + var data PostLikes err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid}) switch err { @@ -56,9 +55,7 @@ func (m *defaultPost_likesModel) FindOne(ctx context.Context, id string) (*Post_ } } -func (m *defaultPost_likesModel) Update(ctx context.Context, data *Post_likes) (*mongo.UpdateResult, error) { - data.UpdateAt = time.Now() - +func (m *defaultPost_likesModel) Update(ctx context.Context, data *PostLikes) (*mongo.UpdateResult, error) { res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data}) return res, err } diff --git a/internal/model/mongo/post_likes_types.go b/internal/model/mongo/post_likes_types.go index 5b8f595..fd1fa1a 100644 --- a/internal/model/mongo/post_likes_types.go +++ b/internal/model/mongo/post_likes_types.go @@ -1,14 +1,17 @@ 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"` +type PostLikes struct { + ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"` + TargetID string `bson:"target_id" json:"target_id"` + UID string `bson:"uid" json:"uid"` + Type int8 `bson:"type" json:"type"` + CreateAt int64 `bson:"createAt,omitempty" json:"createAt,omitempty"` +} + +func (p *PostLikes) CollectionName() string { + return "post_like" } diff --git a/internal/model/mongo/post_model.go b/internal/model/mongo/post_model.go index f968892..998bde5 100644 --- a/internal/model/mongo/post_model.go +++ b/internal/model/mongo/post_model.go @@ -161,12 +161,13 @@ func (m *defaultPostModel) Find(ctx context.Context, param *QueryPostModelReq) ( } // 分頁處理 - options := options.Find() + opts := options.Find() + opts.SetSort(bson.D{{"create_time", -1}}) if param.PageSize > 0 { - options.SetLimit(param.PageSize) + opts.SetLimit(param.PageSize) } if param.PageIndex > 0 { - options.SetSkip((param.PageIndex - 1) * param.PageSize) + opts.SetSkip((param.PageIndex - 1) * param.PageSize) } // 計算總數(不考慮分頁) @@ -177,7 +178,7 @@ func (m *defaultPostModel) Find(ctx context.Context, param *QueryPostModelReq) ( result := make([]*Post, 0, param.PageSize) // 執行查詢 - err = m.conn.Find(ctx, &result, filter, options) + err = m.conn.Find(ctx, &result, filter, opts) if err != nil { return nil, 0, err } diff --git a/internal/model/mongo/post_types.go b/internal/model/mongo/post_types.go index 7677d60..3021f99 100644 --- a/internal/model/mongo/post_types.go +++ b/internal/model/mongo/post_types.go @@ -27,3 +27,7 @@ type Media struct { Type string // media type jpeg, m3u8 之類的 Links string // 連結的網址 } + +func (p *Post) CollectionName() string { + return "post" +} diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index cb07911..ca5e019 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -11,16 +11,20 @@ type ServiceContext struct { Config config.Config Validate vi.Validate - PostModel model.PostModel + PostModel model.PostModel + PostLikeModel model.Post_likesModel } func NewServiceContext(c config.Config) *ServiceContext { baseMongo := MustMongoConnectUrl(c) + postCollection := model.Post{} + postLikeCollection := model.PostLikes{} return &ServiceContext{ - Config: c, - Validate: vi.MustValidator(), - PostModel: model.NewPostModel(baseMongo, c.Mongo.Database, "post", c.Cache), + Config: c, + Validate: vi.MustValidator(), + PostModel: model.NewPostModel(baseMongo, c.Mongo.Database, postCollection.CollectionName(), c.Cache), + PostLikeModel: model.NewPost_likesModel(baseMongo, c.Mongo.Database, postLikeCollection.CollectionName()), } } -- 2.40.1 From 30fb3b372195b56ad3d93cb93221e6197261278d Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 21:49:47 +0800 Subject: [PATCH 5/7] add get like status logic --- .../postservice/get_like_status_logic.go | 48 +++++++++++++++-- internal/model/mongo/post_likes_model.go | 54 +++++++++++++++++++ .../server/postservice/post_service_server.go | 2 +- 3 files changed, 100 insertions(+), 4 deletions(-) diff --git a/internal/logic/postservice/get_like_status_logic.go b/internal/logic/postservice/get_like_status_logic.go index edf812c..116a9ac 100644 --- a/internal/logic/postservice/get_like_status_logic.go +++ b/internal/logic/postservice/get_like_status_logic.go @@ -1,6 +1,9 @@ package postservicelogic import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + ers "code.30cm.net/digimon/library-go/errs" "context" "app-cloudep-tweeting-service/gen_result/pb/tweeting" @@ -24,10 +27,49 @@ func NewGetLikeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get } // 這個人按讚的文章列表(輸入UID 以及文章id,返回這個人有沒有對這些文章按讚) +type getLikeStatusReq struct { + Targets []string `json:"targets" validate:"required"` + LikeType domain.LikeType `json:"like_type" validate:"required,oneof=1 2"` + UID string `json:"uid" validate:"required"` +} // GetLikeStatus 取得讚/不讚狀態 -func (l *GetLikeStatusLogic) GetLikeStatus(in *tweeting.LikeReq) (*tweeting.OKResp, error) { - // todo: add your logic here and delete this line +func (l *GetLikeStatusLogic) GetLikeStatus(in *tweeting.GetLikeStatusReq) (*tweeting.GetLikeStatusResp, error) { + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&getLikeStatusReq{ + Targets: in.GetTargetId(), + LikeType: domain.LikeType(in.GetLikeType()), + UID: in.GetUid(), + }); err != nil { + return nil, ers.InvalidFormat(err.Error()) + } - return &tweeting.OKResp{}, nil + list, err := l.svcCtx.PostLikeModel.FindUIDPostLikeStatus(l.ctx, &model.QueryUIDPostLikeStatusReq{ + Targets: in.GetTargetId(), + LikeType: domain.LikeType(in.GetLikeType()), + UID: in.GetUid(), + }) + if err != nil { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.FindUIDPostLikeStatus"}, + {Key: "err", Value: err}, + }, + "failed to find uid post like status list").Wrap(err) + return nil, e + } + var result = make([]*tweeting.GetLikeStatusItem, 0, len(list)) + + for _, item := range list { + result = append(result, &tweeting.GetLikeStatusItem{ + TargetId: item.TargetID, + Status: item.LikeStatus, + }) + } + + return &tweeting.GetLikeStatusResp{ + Data: result, + }, nil } diff --git a/internal/model/mongo/post_likes_model.go b/internal/model/mongo/post_likes_model.go index 7905c39..87cf5aa 100644 --- a/internal/model/mongo/post_likes_model.go +++ b/internal/model/mongo/post_likes_model.go @@ -21,6 +21,7 @@ type ( post_likesModel LikeDislike(ctx context.Context, postLike *PostLikes) error FindLikeUsers(ctx context.Context, param *QueryPostLikeReq) ([]*PostLikes, int64, error) + FindUIDPostLikeStatus(ctx context.Context, param *QueryUIDPostLikeStatusReq) ([]UIDPostLikeStatusResp, error) } QueryPostLikeReq struct { @@ -30,6 +31,17 @@ type ( PageSize int64 `bson:"page_size"` } + QueryUIDPostLikeStatusReq struct { + Targets []string `bson:"target"` + LikeType domain.LikeType `bson:"like_type"` + UID string + } + + UIDPostLikeStatusResp struct { + TargetID string `json:"target_id"` + LikeStatus bool `json:"like_status"` + } + customPost_likesModel struct { *defaultPost_likesModel } @@ -117,3 +129,45 @@ func (m *defaultPost_likesModel) FindLikeUsers(ctx context.Context, param *Query // 返回結果集、總數和錯誤信息 return results, totalCount, nil } + +func (m *defaultPost_likesModel) FindUIDPostLikeStatus(ctx context.Context, param *QueryUIDPostLikeStatusReq) ([]UIDPostLikeStatusResp, error) { + // 初始化返回結果的切片 + var results []UIDPostLikeStatusResp + + // 建立篩選條件 + filter := bson.M{ + "uid": param.UID, // 篩選指定的 UID + "target_id": bson.M{"$in": param.Targets}, // 篩選多個目標 ID + "type": param.LikeType, // 篩選指定的 LikeType + } + + // 查詢符合條件的點讚記錄 + var postLikes []PostLikes + err := m.conn.Find(ctx, &postLikes, filter) + if err != nil { + return nil, err + } + + // 構建一個 map 來保存查詢到的點讚記錄 + targetLikeMap := make(map[string]bool) + for _, like := range postLikes { + targetLikeMap[like.TargetID] = true + } + + // 構建每個目標的點讚狀態 + for _, targetID := range param.Targets { + likeStatus := UIDPostLikeStatusResp{ + TargetID: targetID, + LikeStatus: targetLikeMap[targetID], // 如果 map 中有該 targetID,返回 true,否則返回 false + } + + // 如果該 targetID 沒有點讚記錄,默認 LikeStatus 為 false + if _, found := targetLikeMap[targetID]; !found { + likeStatus.LikeStatus = false + } + + results = append(results, likeStatus) + } + + return results, nil +} diff --git a/internal/server/postservice/post_service_server.go b/internal/server/postservice/post_service_server.go index b5fdaad..16e5576 100644 --- a/internal/server/postservice/post_service_server.go +++ b/internal/server/postservice/post_service_server.go @@ -53,7 +53,7 @@ func (s *PostServiceServer) Like(ctx context.Context, in *tweeting.LikeReq) (*tw } // GetLikeStatus 取得讚/不讚狀態 -func (s *PostServiceServer) GetLikeStatus(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) { +func (s *PostServiceServer) GetLikeStatus(ctx context.Context, in *tweeting.GetLikeStatusReq) (*tweeting.GetLikeStatusResp, error) { l := postservicelogic.NewGetLikeStatusLogic(ctx, s.svcCtx) return l.GetLikeStatus(in) } -- 2.40.1 From a3796aabdc711d8e99563bf0c0f50c03785816da Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 22:49:42 +0800 Subject: [PATCH 6/7] add get inc or dec count --- generate/protobuf/tweeting.proto | 18 +++++- internal/domain/const.go | 2 +- .../logic/postservice/count_like_logic.go | 35 ++++++++-- .../inc_dec_like_dislike_count_logic.go | 64 +++++++++++++++++++ internal/logic/postservice/like_logic.go | 22 ++++--- internal/model/mongo/post_likes_model.go | 35 ++++++++-- internal/model/mongo/post_model.go | 38 +++++++++++ .../server/postservice/post_service_server.go | 8 ++- 8 files changed, 198 insertions(+), 24 deletions(-) create mode 100644 internal/logic/postservice/inc_dec_like_dislike_count_logic.go diff --git a/generate/protobuf/tweeting.proto b/generate/protobuf/tweeting.proto index 8a0b6f3..e760ba9 100644 --- a/generate/protobuf/tweeting.proto +++ b/generate/protobuf/tweeting.proto @@ -130,7 +130,7 @@ message LikeCountReq { // 讚/不讚數量回應 message LikeCountResp { - string count = 1; // 總共按讚數量 + int64 count = 1; // 總共按讚數量 } // 評論貼文的請求 @@ -174,6 +174,18 @@ message UpdateCommentReq { string content = 2; // 更新後的評論內容 } +message PostReactionActionResp { + string PostID =1; // 貼文的 ID + int64 reaction_type = 2; // 用戶的反應類型,可能是讚或不讚 + bool is_increment = 3; // 表示是否增加(true 表示增加,false 表示減少) +} + +message IncDecLikeDislikeCountReq { + string PostID =1; // 貼文的 ID + int64 reaction_type = 2; // 用戶的反應類型,可能是讚或不讚 + bool is_increment = 3; // 表示是否增加(true 表示增加,false 表示減少) +} + // 定義貼文服務 service PostService { // NewPost 新增貼文 @@ -184,9 +196,11 @@ service PostService { rpc UpdatePost(UpdatePostReq) returns (OKResp); // ListPosts 查詢貼文 rpc ListPosts(QueryPostsReq) returns (ListPostsResp); + // IncDecLikeDislikeCount 增減數量 + rpc IncDecLikeDislikeCount(IncDecLikeDislikeCountReq) returns (OKResp); // Like 點讚/取消讚 貼文 - rpc Like(LikeReq) returns (OKResp); + rpc Like(LikeReq) returns (PostReactionActionResp); // GetLikeStatus 取得讚/不讚狀態 rpc GetLikeStatus(GetLikeStatusReq) returns (GetLikeStatusResp); // LikeList 取得讚/不讚列表 diff --git a/internal/domain/const.go b/internal/domain/const.go index 6b58d8f..7329e83 100644 --- a/internal/domain/const.go +++ b/internal/domain/const.go @@ -14,7 +14,7 @@ const ( type LikeType int8 -func (l LikeType) ToInt32() int8 { +func (l LikeType) ToInt8() int8 { return int8(l) } diff --git a/internal/logic/postservice/count_like_logic.go b/internal/logic/postservice/count_like_logic.go index 4fb4e67..0000038 100644 --- a/internal/logic/postservice/count_like_logic.go +++ b/internal/logic/postservice/count_like_logic.go @@ -1,10 +1,11 @@ package postservicelogic import ( - "context" - "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/domain" "app-cloudep-tweeting-service/internal/svc" + ers "code.30cm.net/digimon/library-go/errs" + "context" "github.com/zeromicro/go-zero/core/logx" ) @@ -23,9 +24,35 @@ func NewCountLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountLi } } +type countLikeReq struct { + PostID string `json:"post_id" validate:"required"` // 貼文的 ID + ReactionType domain.LikeType `json:"reaction_type" validate:"required,oneof=1 2"` // 用戶的反應類型,可能是讚或不讚 +} + // CountLike 取得讚/不讚數量 func (l *CountLikeLogic) CountLike(in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) { - // todo: add your logic here and delete this line + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&countLikeReq{ + PostID: in.GetTargetId(), + ReactionType: domain.LikeType(in.GetLikeType()), + }); err != nil { + return nil, ers.InvalidFormat(err.Error()) + } - return &tweeting.LikeCountResp{}, nil + count, err := l.svcCtx.PostLikeModel.Count(l.ctx, in.GetTargetId(), domain.LikeType(in.GetLikeType())) + if err != nil { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostLikeModel.Count"}, + {Key: "err", Value: err}, + }, + "failed to count like or dislike").Wrap(err) + return nil, e + } + + return &tweeting.LikeCountResp{ + Count: count, + }, nil } diff --git a/internal/logic/postservice/inc_dec_like_dislike_count_logic.go b/internal/logic/postservice/inc_dec_like_dislike_count_logic.go new file mode 100644 index 0000000..3d08230 --- /dev/null +++ b/internal/logic/postservice/inc_dec_like_dislike_count_logic.go @@ -0,0 +1,64 @@ +package postservicelogic + +import ( + "app-cloudep-tweeting-service/internal/domain" + model "app-cloudep-tweeting-service/internal/model/mongo" + ers "code.30cm.net/digimon/library-go/errs" + "context" + + "app-cloudep-tweeting-service/gen_result/pb/tweeting" + "app-cloudep-tweeting-service/internal/svc" + + "github.com/zeromicro/go-zero/core/logx" +) + +type IncDecLikeDislikeCountLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewIncDecLikeDislikeCountLogic(ctx context.Context, svcCtx *svc.ServiceContext) *IncDecLikeDislikeCountLogic { + return &IncDecLikeDislikeCountLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +type postReactionAction struct { + PostID string `json:"post_id" validate:"required"` // 貼文的 ID + ReactionType domain.LikeType `json:"reaction_type" validate:"required,oneof=1 2"` // 用戶的反應類型,可能是讚或不讚 + IsIncrement bool `json:"is_increment" validate:"required"` // 表示是否增加(true 表示增加,false 表示減少) +} + +// IncDecLikeDislikeCount 增減數量 +func (l *IncDecLikeDislikeCountLogic) IncDecLikeDislikeCount(in *tweeting.IncDecLikeDislikeCountReq) (*tweeting.OKResp, error) { + // 驗證資料 + if err := l.svcCtx.Validate.ValidateAll(&postReactionAction{ + PostID: in.GetPostID(), + ReactionType: domain.LikeType(in.GetReactionType()), + IsIncrement: in.GetIsIncrement(), + }); err != nil { + return nil, ers.InvalidFormat(err.Error()) + } + + err := l.svcCtx.PostModel.IncDecLikeDislikeCountLogic(l.ctx, &model.PostReactionAction{ + PostID: in.GetPostID(), + ReactionType: domain.LikeType(in.GetReactionType()), + IsIncrement: in.GetIsIncrement(), + }) + if err != nil { + e := domain.PostMongoErrorL( + logx.WithContext(l.ctx), + []logx.LogField{ + {Key: "req", Value: in}, + {Key: "func", Value: "PostModel.IncDecLikeDislikeCountLogic"}, + {Key: "err", Value: err}, + }, + "failed to inc like or dislike").Wrap(err) + return nil, e + } + + return &tweeting.OKResp{}, nil +} diff --git a/internal/logic/postservice/like_logic.go b/internal/logic/postservice/like_logic.go index 5c26126..6d211d8 100644 --- a/internal/logic/postservice/like_logic.go +++ b/internal/logic/postservice/like_logic.go @@ -27,23 +27,21 @@ func NewLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeLogic { } type likeReq struct { - Target string `json:"target" validate:"required"` - UID string `json:"uid" validate:"required"` - LikeType domain.LikeType `json:"like_type" validate:"required,oneof=1 2"` + Target string `json:"target" validate:"required"` + UID string `json:"uid" validate:"required"` } // Like 點讚/取消讚 貼文 -func (l *LikeLogic) Like(in *tweeting.LikeReq) (*tweeting.OKResp, error) { +func (l *LikeLogic) Like(in *tweeting.LikeReq) (*tweeting.PostReactionActionResp, error) { // 驗證資料 if err := l.svcCtx.Validate.ValidateAll(&likeReq{ - Target: in.TargetId, - UID: in.GetUid(), - LikeType: domain.LikeType(in.GetLikeType()), + Target: in.TargetId, + UID: in.GetUid(), }); err != nil { return nil, ers.InvalidFormat(err.Error()) } - err := l.svcCtx.PostLikeModel.LikeDislike(l.ctx, &model.PostLikes{ + likeResp, err := l.svcCtx.PostLikeModel.LikeDislike(l.ctx, &model.PostLikes{ TargetID: in.GetTargetId(), UID: in.GetUid(), Type: int8(in.GetLikeType()), @@ -60,5 +58,11 @@ func (l *LikeLogic) Like(in *tweeting.LikeReq) (*tweeting.OKResp, error) { return nil, e } - return &tweeting.OKResp{}, nil + // 將文章的數量增加或減少的功能,是業務邏輯,從外面再決定要不要丟MQ 後算,或是怎麼算 + + return &tweeting.PostReactionActionResp{ + PostID: likeResp.PostID, + ReactionType: int64(likeResp.ReactionType), + IsIncrement: likeResp.IsIncrement, + }, nil } diff --git a/internal/model/mongo/post_likes_model.go b/internal/model/mongo/post_likes_model.go index 87cf5aa..6a73f88 100644 --- a/internal/model/mongo/post_likes_model.go +++ b/internal/model/mongo/post_likes_model.go @@ -19,9 +19,16 @@ type ( // and implement the added methods in customPost_likesModel. Post_likesModel interface { post_likesModel - LikeDislike(ctx context.Context, postLike *PostLikes) error + LikeDislike(ctx context.Context, postLike *PostLikes) (*PostReactionAction, error) FindLikeUsers(ctx context.Context, param *QueryPostLikeReq) ([]*PostLikes, int64, error) FindUIDPostLikeStatus(ctx context.Context, param *QueryUIDPostLikeStatusReq) ([]UIDPostLikeStatusResp, error) + Count(ctx context.Context, target string, likeType domain.LikeType) (int64, error) + } + + PostReactionAction struct { + PostID string // 貼文的 ID + ReactionType domain.LikeType // 用戶的反應類型,可能是讚或不讚 + IsIncrement bool // 表示是否增加(true 表示增加,false 表示減少) } QueryPostLikeReq struct { @@ -55,7 +62,12 @@ func NewPost_likesModel(url, db, collection string) Post_likesModel { } } -func (m *defaultPost_likesModel) LikeDislike(ctx context.Context, postLike *PostLikes) error { +func (m *defaultPost_likesModel) LikeDislike(ctx context.Context, postLike *PostLikes) (*PostReactionAction, error) { + result := &PostReactionAction{ + PostID: postLike.TargetID, + ReactionType: domain.LikeType(postLike.Type), + } + // 使用 target_id、uid、type 來查詢資料是否存在 filter := bson.M{ "target_id": postLike.TargetID, @@ -69,23 +81,27 @@ func (m *defaultPost_likesModel) LikeDislike(ctx context.Context, postLike *Post if err == nil { // 資料存在,進行刪除操作 + result.IsIncrement = false _, err = m.conn.DeleteOne(ctx, filter) if err != nil { - return err // 刪除失敗 + return nil, err // 刪除失敗 } - return nil // 刪除成功 + + return result, nil // 刪除成功 } else if errors.Is(mongo.ErrNoDocuments, err) { // 資料不存在,進行插入操作 + result.IsIncrement = true postLike.ID = primitive.NewObjectID() // 設置新的 ObjectID postLike.CreateAt = time.Now().UTC().UnixNano() _, err = m.conn.InsertOne(ctx, postLike) if err != nil { - return err // 插入失敗 + return nil, err // 插入失敗 } - return nil // 插入成功 + + return result, nil // 插入成功 } else { // 其他錯誤 - return err + return nil, err } } @@ -171,3 +187,8 @@ func (m *defaultPost_likesModel) FindUIDPostLikeStatus(ctx context.Context, para return results, nil } + +func (c customPost_likesModel) Count(ctx context.Context, target string, likeType domain.LikeType) (int64, error) { + // TODO implement me + panic("implement me") +} diff --git a/internal/model/mongo/post_model.go b/internal/model/mongo/post_model.go index 998bde5..9024de8 100644 --- a/internal/model/mongo/post_model.go +++ b/internal/model/mongo/post_model.go @@ -1,6 +1,7 @@ package model import ( + "app-cloudep-tweeting-service/internal/domain" "context" "errors" "fmt" @@ -24,6 +25,7 @@ type ( 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) + IncDecLikeDislikeCountLogic(ctx context.Context, param *PostReactionAction) error } customPostModel struct { @@ -192,3 +194,39 @@ func (m *defaultPostModel) Find(ctx context.Context, param *QueryPostModelReq) ( return nil, 0, err } } + +func (c *customPostModel) IncDecLikeDislikeCountLogic(ctx context.Context, param *PostReactionAction) error { + // 建立篩選條件,找到要更新的貼文 + filter := bson.M{"_id": param.PostID} + + // 初始化更新操作 + update := bson.M{} + + // 根據 ReactionType 和 IsIncrement 決定更新邏輯 + if param.ReactionType == domain.LikeTypeLike { + if param.IsIncrement { + // 增加 like 計數 + update = bson.M{"$inc": bson.M{"like": 1}} + } else { + // 減少 like 計數 + update = bson.M{"$inc": bson.M{"like": -1}} + } + } else if param.ReactionType == domain.LikeTypeDisLike { + if param.IsIncrement { + // 增加 dislike 計數 + update = bson.M{"$inc": bson.M{"dislike": 1}} + } else { + // 減少 dislike 計數 + update = bson.M{"$inc": bson.M{"dislike": -1}} + } + } + + // 執行更新操作 + key := prefixPostCacheKey + param.PostID + _, err := c.conn.UpdateOne(ctx, key, filter, update) + if err != nil { + return err // 返回錯誤信息 + } + + return nil // 成功返回 nil +} diff --git a/internal/server/postservice/post_service_server.go b/internal/server/postservice/post_service_server.go index 16e5576..43968db 100644 --- a/internal/server/postservice/post_service_server.go +++ b/internal/server/postservice/post_service_server.go @@ -46,8 +46,14 @@ func (s *PostServiceServer) ListPosts(ctx context.Context, in *tweeting.QueryPos return l.ListPosts(in) } +// IncDecLikeDislikeCount 增減數量 +func (s *PostServiceServer) IncDecLikeDislikeCount(ctx context.Context, in *tweeting.IncDecLikeDislikeCountReq) (*tweeting.OKResp, error) { + l := postservicelogic.NewIncDecLikeDislikeCountLogic(ctx, s.svcCtx) + return l.IncDecLikeDislikeCount(in) +} + // Like 點讚/取消讚 貼文 -func (s *PostServiceServer) Like(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) { +func (s *PostServiceServer) Like(ctx context.Context, in *tweeting.LikeReq) (*tweeting.PostReactionActionResp, error) { l := postservicelogic.NewLikeLogic(ctx, s.svcCtx) return l.Like(in) } -- 2.40.1 From a76840fe97a1d1de85a41e6835e5a9e8c6aa405a Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 22:50:09 +0800 Subject: [PATCH 7/7] add get inc or dec count --- internal/domain/errors.go | 5 +++-- internal/logic/postservice/count_like_logic.go | 3 ++- internal/logic/postservice/get_like_status_logic.go | 3 ++- .../logic/postservice/inc_dec_like_dislike_count_logic.go | 3 ++- internal/logic/postservice/like_list_logic.go | 3 ++- internal/logic/postservice/like_logic.go | 3 ++- internal/logic/postservice/list_posts_logic.go | 3 ++- internal/logic/postservice/new_post_logic.go | 3 ++- internal/logic/postservice/update_post_logic.go | 3 ++- internal/model/mongo/post_likes_model.go | 3 ++- internal/model/mongo/post_model.go | 3 ++- internal/server/commentservice/comment_service_server.go | 2 +- internal/server/postservice/post_service_server.go | 2 +- internal/svc/service_context.go | 3 ++- 14 files changed, 27 insertions(+), 15 deletions(-) diff --git a/internal/domain/errors.go b/internal/domain/errors.go index a41d179..52ca91d 100644 --- a/internal/domain/errors.go +++ b/internal/domain/errors.go @@ -1,11 +1,12 @@ package domain import ( + "fmt" + "strings" + "code.30cm.net/digimon/library-go/errs" "code.30cm.net/digimon/library-go/errs/code" - "fmt" "github.com/zeromicro/go-zero/core/logx" - "strings" ) type ErrorCode uint32 diff --git a/internal/logic/postservice/count_like_logic.go b/internal/logic/postservice/count_like_logic.go index 0000038..2294a9c 100644 --- a/internal/logic/postservice/count_like_logic.go +++ b/internal/logic/postservice/count_like_logic.go @@ -4,9 +4,10 @@ import ( "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/domain" "app-cloudep-tweeting-service/internal/svc" - ers "code.30cm.net/digimon/library-go/errs" "context" + ers "code.30cm.net/digimon/library-go/errs" + "github.com/zeromicro/go-zero/core/logx" ) diff --git a/internal/logic/postservice/get_like_status_logic.go b/internal/logic/postservice/get_like_status_logic.go index 116a9ac..1cdea66 100644 --- a/internal/logic/postservice/get_like_status_logic.go +++ b/internal/logic/postservice/get_like_status_logic.go @@ -3,9 +3,10 @@ package postservicelogic import ( "app-cloudep-tweeting-service/internal/domain" model "app-cloudep-tweeting-service/internal/model/mongo" - ers "code.30cm.net/digimon/library-go/errs" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" diff --git a/internal/logic/postservice/inc_dec_like_dislike_count_logic.go b/internal/logic/postservice/inc_dec_like_dislike_count_logic.go index 3d08230..0190ece 100644 --- a/internal/logic/postservice/inc_dec_like_dislike_count_logic.go +++ b/internal/logic/postservice/inc_dec_like_dislike_count_logic.go @@ -3,9 +3,10 @@ package postservicelogic import ( "app-cloudep-tweeting-service/internal/domain" model "app-cloudep-tweeting-service/internal/model/mongo" - ers "code.30cm.net/digimon/library-go/errs" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" diff --git a/internal/logic/postservice/like_list_logic.go b/internal/logic/postservice/like_list_logic.go index 65135df..db89d6e 100644 --- a/internal/logic/postservice/like_list_logic.go +++ b/internal/logic/postservice/like_list_logic.go @@ -3,9 +3,10 @@ package postservicelogic import ( "app-cloudep-tweeting-service/internal/domain" model "app-cloudep-tweeting-service/internal/model/mongo" - ers "code.30cm.net/digimon/library-go/errs" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" diff --git a/internal/logic/postservice/like_logic.go b/internal/logic/postservice/like_logic.go index 6d211d8..d918171 100644 --- a/internal/logic/postservice/like_logic.go +++ b/internal/logic/postservice/like_logic.go @@ -3,9 +3,10 @@ package postservicelogic import ( "app-cloudep-tweeting-service/internal/domain" model "app-cloudep-tweeting-service/internal/model/mongo" - ers "code.30cm.net/digimon/library-go/errs" "context" + ers "code.30cm.net/digimon/library-go/errs" + "app-cloudep-tweeting-service/gen_result/pb/tweeting" "app-cloudep-tweeting-service/internal/svc" diff --git a/internal/logic/postservice/list_posts_logic.go b/internal/logic/postservice/list_posts_logic.go index b8bf565..90a7d84 100644 --- a/internal/logic/postservice/list_posts_logic.go +++ b/internal/logic/postservice/list_posts_logic.go @@ -5,8 +5,9 @@ import ( "app-cloudep-tweeting-service/internal/domain" model "app-cloudep-tweeting-service/internal/model/mongo" "app-cloudep-tweeting-service/internal/svc" - ers "code.30cm.net/digimon/library-go/errs" "context" + + ers "code.30cm.net/digimon/library-go/errs" "google.golang.org/protobuf/proto" "github.com/zeromicro/go-zero/core/logx" diff --git a/internal/logic/postservice/new_post_logic.go b/internal/logic/postservice/new_post_logic.go index f555b84..9efb3fb 100644 --- a/internal/logic/postservice/new_post_logic.go +++ b/internal/logic/postservice/new_post_logic.go @@ -5,9 +5,10 @@ import ( "app-cloudep-tweeting-service/internal/domain" model "app-cloudep-tweeting-service/internal/model/mongo" "app-cloudep-tweeting-service/internal/svc" - ers "code.30cm.net/digimon/library-go/errs" "context" + ers "code.30cm.net/digimon/library-go/errs" + "github.com/zeromicro/go-zero/core/logx" ) diff --git a/internal/logic/postservice/update_post_logic.go b/internal/logic/postservice/update_post_logic.go index 23b5ee3..8e7b894 100644 --- a/internal/logic/postservice/update_post_logic.go +++ b/internal/logic/postservice/update_post_logic.go @@ -3,8 +3,9 @@ package postservicelogic import ( "app-cloudep-tweeting-service/internal/domain" model "app-cloudep-tweeting-service/internal/model/mongo" - ers "code.30cm.net/digimon/library-go/errs" "context" + + ers "code.30cm.net/digimon/library-go/errs" "go.mongodb.org/mongo-driver/bson/primitive" "app-cloudep-tweeting-service/gen_result/pb/tweeting" diff --git a/internal/model/mongo/post_likes_model.go b/internal/model/mongo/post_likes_model.go index 6a73f88..f818972 100644 --- a/internal/model/mongo/post_likes_model.go +++ b/internal/model/mongo/post_likes_model.go @@ -4,12 +4,13 @@ import ( "app-cloudep-tweeting-service/internal/domain" "context" "errors" + "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" "go.mongodb.org/mongo-driver/mongo/options" - "time" ) var _ Post_likesModel = (*customPost_likesModel)(nil) diff --git a/internal/model/mongo/post_model.go b/internal/model/mongo/post_model.go index 9024de8..30dd425 100644 --- a/internal/model/mongo/post_model.go +++ b/internal/model/mongo/post_model.go @@ -5,6 +5,8 @@ import ( "context" "errors" "fmt" + "time" + "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/cache" "github.com/zeromicro/go-zero/core/stores/monc" @@ -12,7 +14,6 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - "time" ) var _ PostModel = (*customPostModel)(nil) diff --git a/internal/server/commentservice/comment_service_server.go b/internal/server/commentservice/comment_service_server.go index 8e4eab8..bb41396 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" ) diff --git a/internal/server/postservice/post_service_server.go b/internal/server/postservice/post_service_server.go index 43968db..61341db 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" ) diff --git a/internal/svc/service_context.go b/internal/svc/service_context.go index ca5e019..c2c7471 100644 --- a/internal/svc/service_context.go +++ b/internal/svc/service_context.go @@ -3,8 +3,9 @@ package svc import ( "app-cloudep-tweeting-service/internal/config" model "app-cloudep-tweeting-service/internal/model/mongo" - vi "code.30cm.net/digimon/library-go/validator" "fmt" + + vi "code.30cm.net/digimon/library-go/validator" ) type ServiceContext struct { -- 2.40.1