feature/post #1
|
@ -15,12 +15,17 @@ message Pager {
|
||||||
int64 index=3; // 當前頁碼
|
int64 index=3; // 當前頁碼
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Media{
|
||||||
|
string type =1;
|
||||||
|
string url =2;
|
||||||
|
}
|
||||||
|
|
||||||
// 新增貼文的請求
|
// 新增貼文的請求
|
||||||
message NewPostReq {
|
message NewPostReq {
|
||||||
int64 user_id = 1; // 發佈貼文的用戶ID
|
string uid = 1; // 發佈貼文的用戶ID
|
||||||
string content = 2; // 貼文內容
|
string content = 2; // 貼文內容
|
||||||
repeated string tags = 3; // 貼文相關標籤
|
repeated string tags = 3; // 貼文相關標籤
|
||||||
repeated string media_url = 4; // 這筆文章的所有 Media URL
|
repeated Media media = 4; // 這筆文章的所有 Media URL
|
||||||
bool is_ad = 5; // 是否為廣告
|
bool is_ad = 5; // 是否為廣告
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +43,7 @@ message DeletePostsReq {
|
||||||
message UpdatePostReq {
|
message UpdatePostReq {
|
||||||
string post_id = 1; // 貼文ID
|
string post_id = 1; // 貼文ID
|
||||||
repeated string tags = 2; // 新的標籤列表
|
repeated string tags = 2; // 新的標籤列表
|
||||||
repeated string media_url = 3; // 這筆文章的所有 Media URL
|
repeated Media media = 3; // 這筆文章的所有 Media URL
|
||||||
optional string content = 4; // 新的貼文內容
|
optional string content = 4; // 新的貼文內容
|
||||||
optional int64 like_count = 5; // 喜歡數量
|
optional int64 like_count = 5; // 喜歡數量
|
||||||
optional int64 dislike_count = 6; // 不喜歡數量
|
optional int64 dislike_count = 6; // 不喜歡數量
|
||||||
|
@ -60,7 +65,7 @@ message PostDetailItem {
|
||||||
int64 user_id = 2; // 發佈用戶ID
|
int64 user_id = 2; // 發佈用戶ID
|
||||||
string content = 3; // 貼文內容
|
string content = 3; // 貼文內容
|
||||||
repeated string tags = 4; // 標籤
|
repeated string tags = 4; // 標籤
|
||||||
repeated string media_url = 5; // 圖片URL
|
repeated Media media = 5; // 圖片URL
|
||||||
bool is_ad = 6; // 是否為廣告
|
bool is_ad = 6; // 是否為廣告
|
||||||
int64 created_at = 7; // 發佈時間
|
int64 created_at = 7; // 發佈時間
|
||||||
int64 update_at = 8; // 更新時間
|
int64 update_at = 8; // 更新時間
|
||||||
|
|
17
go.mod
17
go.mod
|
@ -3,7 +3,10 @@ module app-cloudep-tweeting-service
|
||||||
go 1.22.3
|
go 1.22.3
|
||||||
|
|
||||||
require (
|
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
|
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/grpc v1.66.0
|
||||||
google.golang.org/protobuf v1.34.2
|
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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
github.com/fatih/color v1.17.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/logr v1.4.2 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/swag v0.22.4 // 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/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // 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/gnostic-models v0.6.8 // indirect
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
github.com/google/gofuzz v1.2.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/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // 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/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // 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/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/redis/go-redis/v9 v9.6.1 // indirect
|
github.com/redis/go-redis/v9 v9.6.1 // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // 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/api/v3 v3.5.15 // indirect
|
||||||
go.etcd.io/etcd/client/pkg/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
|
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/automaxprocs v1.5.3 // indirect
|
||||||
go.uber.org/multierr v1.9.0 // indirect
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
go.uber.org/zap v1.24.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/net v0.27.0 // indirect
|
||||||
golang.org/x/oauth2 v0.21.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/sys v0.22.0 // indirect
|
||||||
golang.org/x/term v0.22.0 // indirect
|
golang.org/x/term v0.22.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
|
|
|
@ -1,7 +1,22 @@
|
||||||
package config
|
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 {
|
type Config struct {
|
||||||
zrpc.RpcServerConf
|
zrpc.RpcServerConf
|
||||||
|
Mongo struct {
|
||||||
|
Schema string
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
Database string
|
||||||
|
Collection string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 快取
|
||||||
|
Cache cache.CacheConf
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
type TweetingStatus int8
|
||||||
|
|
||||||
|
func (t TweetingStatus) ToInt8() int8 {
|
||||||
|
return int8(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TweetingStatusNotReviewedYet TweetingStatus = iota + 1
|
||||||
|
TweetingStatusPass
|
||||||
|
)
|
|
@ -1,10 +1,12 @@
|
||||||
package postservicelogic
|
package postservicelogic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
|
"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"
|
"app-cloudep-tweeting-service/internal/svc"
|
||||||
|
ers "code.30cm.net/digimon/library-go/errs"
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"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 新增貼文
|
// NewPost 新增貼文
|
||||||
func (l *NewPostLogic) NewPost(in *tweeting.NewPostReq) (*tweeting.PostResp, error) {
|
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{
|
||||||
return &tweeting.PostResp{}, nil
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,24 @@ import (
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO Tag 這裡在效能與正確性之間做取捨
|
||||||
|
// 存在貼文內的不提供搜尋,純顯示用,只不過在原始的tag 發生變動的時候,並不會一起改變
|
||||||
|
// 搜尋會貼文與Tag 的表會再另外一邊做關聯
|
||||||
|
// 暫時業務邏輯上tag 只提供新增,不提供修改以及刪除,故目前版本可行
|
||||||
|
|
||||||
type Post struct {
|
type Post struct {
|
||||||
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
|
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
|
||||||
// TODO: Fill your own fields
|
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"`
|
UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
|
||||||
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
|
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Media struct {
|
||||||
|
Type string // media type jpeg, m3u8 之類的
|
||||||
|
Links string // 連結的網址
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,31 @@
|
||||||
package svc
|
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 {
|
type ServiceContext struct {
|
||||||
Config config.Config
|
Config config.Config
|
||||||
|
Validate vi.Validate
|
||||||
|
|
||||||
|
PostModel model.PostModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
baseMongo := MustMongoConnectUrl(c)
|
||||||
|
|
||||||
return &ServiceContext{
|
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)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue