From 132f1ba9514527774758394ff56e52bd5e7a82b1 Mon Sep 17 00:00:00 2001 From: "daniel.w" Date: Thu, 29 Aug 2024 09:08:15 +0800 Subject: [PATCH] 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) +}