From 98605573f360c0896a64c73f7dd0ed05a120d100 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 21:26:01 +0800 Subject: [PATCH] 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()), } }