feature/post_v2 #2

Merged
daniel.w merged 5 commits from feature/post_v2 into main 2024-08-30 07:08:46 +00:00
59 changed files with 2599 additions and 894 deletions

View File

@ -49,9 +49,21 @@ build-docker:
gen-mongo-model: # 建立 rpc 資料庫
# 只產生 Model 剩下的要自己撰寫,連欄位名稱也是
goctl model mongo -c no -t post --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
goctl model mongo -c no -t comment --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
goctl model mongo -t tags --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
goctl model mongo -t post_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
goctl model mongo -t comment_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
@echo "Generate mongo model files successfully"
goctl model mongo -t post --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
goctl model mongo -t comment --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
# goctl model mongo -t tags --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
# goctl model mongo -t post_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
# goctl model mongo -t comment_likes --dir ./internal/model/mongo --style $(GO_ZERO_STYLE)
@echo "Generate mongo model files successfully"
.PHONY: mock-gen
mock-gen: # 建立 mock 資料
mockgen -source=./internal/model/mongo/post_model_gen.go -destination=./internal/mock/model/post_model_gen.go -package=mock
mockgen -source=./internal/model/mongo/post_model.go -destination=./internal/mock/model/post_model.go -package=mock
mockgen -source=./internal/model/mongo/comment_model_gen.go -destination=./internal/mock/model/comment_model_gen.go -package=mock
mockgen -source=./internal/model/mongo/comment_model.go -destination=./internal/mock/model/comment_model.go -package=mock
@echo "Generate mock files successfully"
.PHONY: migrate-database
migrate-database:
migrate -source file://generate/database/migrations/mongodb -database 'mongodb://127.0.0.1:27017/digimon_tweeting' up

View File

@ -4,3 +4,11 @@ Etcd:
Hosts:
- 127.0.0.1:2379
Key: tweeting.rpc
Mongo:
Schema: mongodb
Host: 127.0.0.1
User: ""
Password: ""
Port: "27017"
Database: digimon_tweeting

View File

@ -0,0 +1,9 @@
use digimon_tweeting;
db.post.createIndex({ "uid": 1});
db.post.createIndex({ "status": 1});
db.post.createIndex({ "is_ad": 1});
db.post.createIndex({ "createAt": 1 });
db.post.createIndex({ "uid": 1,"status": 1, "createAt": 1 });
db.post.createIndex({ "uid": 1, "createAt": 1 });
// TODO 看是否有要刪除過多的索引,要在測試一下

View File

@ -1,136 +1,147 @@
syntax = "proto3";
package tweeting;
option go_package="./tweeting";
option go_package = "./tweeting";
//
message OKResp {}
// ========== ===========
message OKResp {}
//
message NoneReq {}
message NoneReq {}
//
message Pager {
int64 total =1; //
int64 size=2; //
int64 index=3; //
message Pager
{
int64 total = 1; //
int64 size = 2; //
int64 index = 3; //
}
//
message NewPostReq {
int64 user_id = 1; // ID
string content = 2; //
repeated string tags = 3; //
repeated string media_url = 4; // Media URL
bool is_ad = 5; //
// ========== ===========
// ------ NewPost --------
message NewPostReq
{
string uid = 1; // ID
string content = 2; //
repeated string tags = 3; //
repeated Media media = 4; // Media URL
bool is_ad = 5; //
}
message Media
{
string type = 1;
string url = 2;
}
//
message PostResp {
string post_id = 1; // ID
message PostResp
{
string post_id = 1; // ID
}
// ------ DeletePost ------
//
message DeletePostsReq {
repeated string post_id = 1; // ID
message DeletePostsReq
{
repeated string post_id = 1; // ID
}
// ------ UpdatePost ------
//
message UpdatePostReq {
string post_id = 1; // ID
repeated string tags = 2; //
repeated string media_url = 3; // Media URL
optional string content = 4; //
optional int64 like_count = 5; //
optional int64 dislike_count = 6; //
message UpdatePostReq
{
string post_id = 1; // ID
repeated string tags = 2; //
repeated Media media = 3; // Media URL
optional string content = 4; //
optional int64 like_count = 5; //
optional int64 dislike_count = 6; //
}
// ------ListPosts ------
//
message QueryPostsReq {
repeated int64 user_id = 1; // ID篩選貼文
repeated int64 id = 2; // ID篩選貼文
repeated string tags = 3; //
optional bool only_ads = 4; //
int32 page_index = 5; //
int32 page_size = 6; //
message QueryPostsReq
{
repeated string uid = 1; // ID篩選貼文
repeated string post_id = 2; // ID篩選貼
optional int32 only_ads = 3; // 0 1 2
int32 page_index = 4; //
int32 page_size = 5; //
}
//
message PostDetailItem {
string post_id = 1; // ID
int64 user_id = 2; // ID
string content = 3; //
repeated string tags = 4; //
repeated string media_url = 5; // URL
bool is_ad = 6; //
int64 created_at = 7; //
int64 update_at = 8; //
int64 like_count = 9; //
int64 dislike_count = 10; //
message PostDetailItem
{
string post_id = 1; // ID
string uid = 2; // ID
string content = 3; //
repeated string tags = 4; //
repeated Media media = 5; // URL
bool is_ad = 6; //
int64 created_at = 7; //
int64 update_at = 8; //
int64 like_count = 9; //
int64 dislike_count = 10; //
}
//
message ListPostsResp {
repeated PostDetailItem posts = 1; //
Pager page =2;
}
// /
message LikeReq {
string target_id = 1; // IDID或評論ID
int64 user_id = 2; // ID
int64 like_type = 3; //
}
// /
message LikeItem {
string target_id = 1; // IDID或評論ID
int64 user_id = 2; // ID
int64 like_type = 3; //
}
// /
message LikeListReq {
string target_id = 1; // IDID或評論ID
int64 like_type = 2; //
int32 page_index = 3; //
int32 page_size = 4; //
}
// /
message LikeListResp {
repeated LikeItem list = 1; // /
message ListPostsResp
{
repeated PostDetailItem posts = 1; //
Pager page = 2;
}
// /
message LikeCountReq {
string target_id = 1; // IDID或評論ID
int64 like_type = 2; //
message ModifyLikeDislikeCountReq
{
string post_id = 1; // ID
int64 reaction_type = 2; //
bool is_increment = 3; // true false
int64 count = 4; //
}
// /
message LikeCountResp {
string count = 1; //
// ========== () ==========
service PostService
{
// CreatePost
rpc CreatePost(NewPostReq) returns (PostResp);
// DeletePost
rpc DeletePost(DeletePostsReq) returns (OKResp);
// UpdatePost
rpc UpdatePost(UpdatePostReq) returns (OKResp);
// ListPosts
rpc ListPosts(QueryPostsReq) returns (ListPostsResp);
}
//
message CommentPostReq {
string post_id = 1; // ID
int64 user_id = 2; // ID
string content = 3; //
// =================================================================================================
// ------------ ------------
message CommentPostReq
{
string post_id = 1; // ID
string uid = 2; // ID
string content = 3; //
}
//
message GetCommentsReq {
string post_id = 1; // ID
int32 page_index = 2; //
int32 page_size = 3; //
message CommentPostResp
{
string comment_id = 1; // ID
}
// ------------ ------------
message GetCommentsReq
{
string post_id = 1; // ID
int32 page_index = 2; //
int32 page_size = 3; //
}
//
message CommentDetail {
message CommentDetail
{
string comment_id = 1; // ID
int64 user_id = 2; // ID
string uid = 2; // ID
string content = 3; //
int64 created_at = 4; //
int64 like_count = 5; //
@ -138,60 +149,36 @@ message CommentDetail {
}
//
message GetCommentsResp {
message GetCommentsResp
{
repeated CommentDetail comments = 1; //
Pager page = 2;
}
//
message DeleteCommentReq {
string comment_id = 1; // ID
// ------------ ------------
message DeleteCommentReq
{
repeated string comment_id = 1; // ID
}
//
message UpdateCommentReq {
string comment_id = 1; // ID
string content = 2; //
}
//
service PostService {
// NewPost
rpc NewPost(NewPostReq) returns(PostResp);
// DeletePost
rpc DeletePost(DeletePostsReq) returns (OKResp);
// UpdatePost
rpc UpdatePost(UpdatePostReq) returns (OKResp);
// ListPosts
rpc ListPosts(QueryPostsReq) returns (ListPostsResp);
// Like /
rpc Like(LikeReq) returns (OKResp);
// GetLikeStatus /
rpc GetLikeStatus(LikeReq) returns (OKResp);
// LikeList /
rpc LikeList(LikeListReq) returns (LikeListResp);
// CountLike /
rpc CountLike(LikeCountReq) returns (LikeCountResp);
message UpdateCommentReq
{
string comment_id = 1; // ID
string content = 2; //
optional int64 like_count = 3; //
optional int64 dislike_count = 4; //
}
//
service CommentService {
service CommentService
{
// NewComment
rpc NewComment(CommentPostReq) returns (OKResp);
rpc NewComment(CommentPostReq) returns (CommentPostResp);
// GetComments
rpc GetComments(GetCommentsReq) returns (GetCommentsResp);
// DeleteComment
rpc DeleteComment(DeleteCommentReq) returns (OKResp);
// UpdateComment
rpc UpdateComment(UpdateCommentReq) returns (OKResp);
// LikeComment /
rpc LikeComment(LikeReq) returns (OKResp);
// GetLikeStatus /
rpc GetLikeStatus(LikeReq) returns (OKResp);
// LikeList /
rpc LikeList(LikeListReq) returns (LikeListResp);
// CountLike /
rpc CountLike(LikeCountReq) returns (LikeCountResp);
}

20
go.mod
View File

@ -3,7 +3,12 @@ module app-cloudep-tweeting-service
go 1.22.3
require (
code.30cm.net/digimon/library-go/errs v1.2.4
code.30cm.net/digimon/library-go/validator v1.0.0
github.com/stretchr/testify v1.9.0
github.com/zeromicro/go-zero v1.7.0
go.mongodb.org/mongo-driver v1.16.0
go.uber.org/mock v0.4.0
google.golang.org/grpc v1.66.0
google.golang.org/protobuf v1.34.2
)
@ -18,14 +23,19 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.22.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
@ -33,20 +43,28 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/redis/go-redis/v9 v9.6.1 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
go.etcd.io/etcd/api/v3 v3.5.15 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.15 // indirect
go.etcd.io/etcd/client/v3 v3.5.15 // indirect
@ -65,8 +83,10 @@ require (
go.uber.org/automaxprocs v1.5.3 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect

View File

@ -4,4 +4,12 @@ import "github.com/zeromicro/go-zero/zrpc"
type Config struct {
zrpc.RpcServerConf
Mongo struct {
Schema string
User string
Password string
Host string
Port string
Database string
}
}

13
internal/domain/const.go Normal file
View File

@ -0,0 +1,13 @@
package domain
type AdType int32
func (a AdType) ToInt32() int32 {
return int32(a)
}
const (
AdTypeAll AdType = iota
AdTypeOnlyAd
AdTypeOnlyNotAd
)

48
internal/domain/errors.go Normal file
View File

@ -0,0 +1,48 @@
package domain
import (
"fmt"
"strings"
ers "code.30cm.net/digimon/library-go/errs"
"code.30cm.net/digimon/library-go/errs/code"
"github.com/zeromicro/go-zero/core/logx"
)
type ErrorCode uint32
func (e ErrorCode) ToUint32() uint32 {
return uint32(e)
}
// Error Code 統一這邊改
const (
_ = iota
PostMongoErrorCode ErrorCode = iota
CreatePostError
DelPostError
UpdatePostError
ListPostError
)
const (
CommentFoundErrorCode ErrorCode = iota + 10
CommentInsertErrorCode
CommentDeleteErrorCode
CommentUpdateErrorCode
CommentListErrorCode
)
func CommentError(ec ErrorCode, s ...string) *ers.LibError {
return ers.NewError(code.CloudEPTweeting, code.DBError,
ec.ToUint32(),
fmt.Sprintf("%s", strings.Join(s, " ")))
}
func CommentErrorL(ec ErrorCode,
l logx.Logger, filed []logx.LogField, s ...string) *ers.LibError {
e := CommentError(ec, s...)
l.WithCallerSkip(1).WithFields(filed...).Error(e.Error())
return e
}

12
internal/domain/status.go Normal file
View File

@ -0,0 +1,12 @@
package domain
type TweetingStatus int8
func (t TweetingStatus) ToInt8() int8 {
return int8(t)
}
const (
TweetingStatusNotReviewedYet TweetingStatus = iota + 1
TweetingStatusPass
)

View File

@ -1,31 +0,0 @@
package commentservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type CountLikeLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCountLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountLikeLogic {
return &CountLikeLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// CountLike 取得讚/不讚評論數量
func (l *CountLikeLogic) CountLike(in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) {
// todo: add your logic here and delete this line
return &tweeting.LikeCountResp{}, nil
}

View File

@ -1,6 +1,7 @@
package commentservicelogic
import (
"app-cloudep-tweeting-service/internal/domain"
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
@ -25,7 +26,19 @@ func NewDeleteCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Del
// DeleteComment 刪除評論
func (l *DeleteCommentLogic) DeleteComment(in *tweeting.DeleteCommentReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
_, err := l.svcCtx.CommentModel.DeleteMany(l.ctx, in.GetCommentId()...)
if err != nil {
e := domain.CommentErrorL(
domain.CommentDeleteErrorCode,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "req", Value: in},
{Key: "func", Value: "CommentModel.DeleteMany"},
{Key: "err", Value: err},
},
"failed to del comment").Wrap(err)
return nil, e
}
return &tweeting.OKResp{}, nil
}

View File

@ -0,0 +1,85 @@
package commentservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
)
func TestDeleteComment(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockCommentModel := mockmodel.NewMockCommentModel(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
CommentModel: mockCommentModel,
}
// 測試數據
commentReq := &tweeting.DeleteCommentReq{
CommentId: []string{"12345", "67890"},
}
// 測試數據集
tests := []struct {
name string
input *tweeting.DeleteCommentReq
prepare func()
expectErr bool
}{
{
name: "成功刪除評論",
input: commentReq,
prepare: func() {
// 模擬 DeleteMany 成功
mockCommentModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(2), nil).Times(1)
},
expectErr: false,
},
{
name: "刪除評論失敗",
input: commentReq,
prepare: func() {
// 模擬 DeleteMany 失敗
mockCommentModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(0), errors.New("delete failed")).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 DeleteCommentLogic
logic := DeleteCommentLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 DeleteComment
resp, err := logic.DeleteComment(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
}
})
}
}

View File

@ -1,8 +1,12 @@
package commentservicelogic
import (
"app-cloudep-tweeting-service/internal/domain"
model "app-cloudep-tweeting-service/internal/model/mongo"
"context"
ers "code.30cm.net/digimon/library-go/errs"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
@ -23,9 +27,77 @@ func NewGetCommentsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCo
}
}
// GetComments 查詢評論
func (l *GetCommentsLogic) GetComments(in *tweeting.GetCommentsReq) (*tweeting.GetCommentsResp, error) {
// todo: add your logic here and delete this line
return &tweeting.GetCommentsResp{}, nil
// 只列出要驗證的資料
type listReq struct {
PostID string `json:"post_id" validate:"required"`
PageSize int64 `json:"page_size" validate:"required"`
PageIndex int64 `json:"page_index" validate:"required"`
}
// 將單個 Post 轉換為 PostDetailItem
func convertToCommentDetailItem(item *model.Comment) *tweeting.CommentDetail {
return &tweeting.CommentDetail{
CommentId: item.ID.Hex(),
Uid: item.UID,
Content: item.Content,
CreatedAt: item.CreateAt,
LikeCount: int64(item.LikeCount),
DislikeCount: int64(item.DisLikeCount),
}
}
// GetComments 查詢評論
// 目前應該是沒有需求是要看 uid 在哪裡留過言,如果未來業務邏輯有再新增
func (l *GetCommentsLogic) GetComments(in *tweeting.GetCommentsReq) (*tweeting.GetCommentsResp, error) {
// 將 PageSize 和 PageIndex 提前轉換為 int64
pageSize := int64(in.GetPageSize())
pageIndex := int64(in.GetPageIndex())
// 驗證資料
if err := l.svcCtx.Validate.ValidateAll(&listReq{
PageSize: pageSize,
PageIndex: pageIndex,
PostID: in.GetPostId(),
}); err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat(err.Error())
}
// 構建查詢條件
query := &model.QueryCommentModelReq{
PostID: in.GetPostId(),
PageSize: pageSize,
PageIndex: pageIndex,
}
// 執行查詢
find, count, err := l.svcCtx.CommentModel.Find(l.ctx, query)
if err != nil {
e := domain.CommentErrorL(
domain.CommentListErrorCode,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "query", Value: query},
{Key: "func", Value: "CommentModel.Find"},
{Key: "err", Value: err},
},
"failed to find comment").Wrap(err)
return nil, e
}
// 將查詢結果轉換為 API 回應格式
result := make([]*tweeting.CommentDetail, 0, count)
for _, item := range find {
result = append(result, convertToCommentDetailItem(item))
}
// 返回結果
return &tweeting.GetCommentsResp{
Comments: result,
Page: &tweeting.Pager{
Total: count,
Index: pageIndex,
Size: pageSize,
},
}, nil
}

View File

@ -0,0 +1,120 @@
package commentservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
model "app-cloudep-tweeting-service/internal/model/mongo"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.uber.org/mock/gomock"
mocklib "app-cloudep-tweeting-service/internal/mock/lib"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
)
func TestGetComments(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockCommentModel := mockmodel.NewMockCommentModel(ctrl)
mockValidate := mocklib.NewMockValidate(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
CommentModel: mockCommentModel,
Validate: mockValidate,
}
// 測試數據
getCommentsReq := &tweeting.GetCommentsReq{
PostId: "12345",
PageSize: 10,
PageIndex: 1,
}
mockComments := []*model.Comment{
{
ID: primitive.NewObjectID(),
PostID: "12345",
UID: "54321",
Content: "This is a comment",
LikeCount: 10,
DisLikeCount: 2,
},
{
ID: primitive.NewObjectID(),
PostID: "12345",
UID: "67890",
Content: "This is another comment",
LikeCount: 5,
DisLikeCount: 1,
},
}
// 測試數據集
tests := []struct {
name string
input *tweeting.GetCommentsReq
prepare func()
expectErr bool
}{
{
name: "成功查詢評論",
input: getCommentsReq,
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
mockCommentModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(mockComments, int64(len(mockComments)), nil).Times(1)
},
expectErr: false,
},
{
name: "查詢評論失敗",
input: getCommentsReq,
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
mockCommentModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("find failed")).Times(1)
},
expectErr: true,
},
{
name: "驗證失敗",
input: getCommentsReq,
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 GetCommentsLogic
logic := GetCommentsLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 GetComments
resp, err := logic.GetComments(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Len(t, resp.Comments, len(mockComments))
}
})
}
}

View File

@ -1,31 +0,0 @@
package commentservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type GetLikeStatusLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetLikeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLikeStatusLogic {
return &GetLikeStatusLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// GetLikeStatus 取得讚/不讚評論狀態
func (l *GetLikeStatusLogic) GetLikeStatus(in *tweeting.LikeReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
return &tweeting.OKResp{}, nil
}

View File

@ -1,31 +0,0 @@
package commentservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type LikeCommentLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewLikeCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeCommentLogic {
return &LikeCommentLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// LikeComment 點讚/取消讚 評論
func (l *LikeCommentLogic) LikeComment(in *tweeting.LikeReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
return &tweeting.OKResp{}, nil
}

View File

@ -1,31 +0,0 @@
package commentservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type LikeListLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewLikeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeListLogic {
return &LikeListLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// LikeList 取得讚/不讚評論列表
func (l *LikeListLogic) LikeList(in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) {
// todo: add your logic here and delete this line
return &tweeting.LikeListResp{}, nil
}

View File

@ -1,10 +1,14 @@
package commentservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/domain"
model "app-cloudep-tweeting-service/internal/model/mongo"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
ers "code.30cm.net/digimon/library-go/errs"
"github.com/zeromicro/go-zero/core/logx"
)
@ -23,9 +27,70 @@ func NewNewCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewCom
}
}
// NewComment 發表評論
func (l *NewCommentLogic) NewComment(in *tweeting.CommentPostReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
return &tweeting.OKResp{}, nil
// 輸入的定義 -> 檢查用
type newCommentReq struct {
UID string `json:"uid" validate:"required"`
Content string `json:"content" validate:"required,lte=500"` // 貼文限制 500 字內
PostID string `json:"post_id" validate:"required"`
}
// NewComment 發表評論
func (l *NewCommentLogic) NewComment(in *tweeting.CommentPostReq) (*tweeting.CommentPostResp, error) {
// 驗證資料
if err := l.svcCtx.Validate.ValidateAll(&newCommentReq{
UID: in.GetUid(),
Content: in.GetContent(),
PostID: in.GetPostId(),
}); err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat(err.Error())
}
// 檢查是否有這個文章
_, err := l.svcCtx.PostModel.FindOne(l.ctx, in.GetPostId())
if err != nil {
if errors.Is(model.ErrNotFound, err) {
// 錯誤代碼 05-031-00
return nil, ers.ResourceNotFound("failed to find post: ", in.GetPostId())
}
// 錯誤代碼 05-021-10
e := domain.CommentErrorL(
domain.CommentFoundErrorCode,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "req", Value: in},
{Key: "func", Value: "PostModel.FindOne"},
{Key: "err", Value: err},
},
"failed to find post:", in.GetPostId()).Wrap(err)
return nil, e
}
data := &model.Comment{
PostID: in.GetPostId(),
UID: in.GetUid(),
Content: in.GetContent(),
LikeCount: 0,
DisLikeCount: 0,
}
err = l.svcCtx.CommentModel.Insert(l.ctx, data)
if err != nil {
// 錯誤代碼 05-021-11
e := domain.CommentErrorL(
domain.CommentInsertErrorCode,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "req", Value: in},
{Key: "func", Value: "CommentModel.Insert"},
{Key: "err", Value: err},
},
"failed to insert comment:", in.GetPostId()).Wrap(err)
return nil, e
}
return &tweeting.CommentPostResp{
CommentId: data.ID.Hex(),
}, nil
}

View File

@ -0,0 +1,124 @@
package commentservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
model "app-cloudep-tweeting-service/internal/model/mongo"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.uber.org/mock/gomock"
mocklib "app-cloudep-tweeting-service/internal/mock/lib"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
)
func TestNewComment(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockPostModel := mockmodel.NewMockPostModel(ctrl)
mockCommentModel := mockmodel.NewMockCommentModel(ctrl)
mockValidate := mocklib.NewMockValidate(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
PostModel: mockPostModel,
CommentModel: mockCommentModel,
Validate: mockValidate,
}
// 測試數據
postID := primitive.NewObjectID().Hex()
commentReq := &tweeting.CommentPostReq{
Uid: "12345",
Content: "This is a comment",
PostId: postID,
}
// 測試數據集
tests := []struct {
name string
input *tweeting.CommentPostReq
prepare func()
expectErr bool
}{
{
name: "成功發表評論",
input: commentReq,
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
// 模擬 FindOne 成功找到文章
mockPostModel.EXPECT().FindOne(gomock.Any(), postID).Return(&model.Post{}, nil).Times(1)
// 模擬 Insert 成功
mockCommentModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1)
},
expectErr: false,
},
{
name: "驗證失敗",
input: commentReq,
prepare: func() {
// 模擬 Validate 失敗
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1)
},
expectErr: true,
},
{
name: "文章不存在",
input: commentReq,
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
// 模擬 FindOne 找不到文章
mockPostModel.EXPECT().FindOne(gomock.Any(), postID).Return(nil, model.ErrNotFound).Times(1)
},
expectErr: true,
},
{
name: "插入評論失敗",
input: commentReq,
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
// 模擬 FindOne 成功找到文章
mockPostModel.EXPECT().FindOne(gomock.Any(), postID).Return(&model.Post{}, nil).Times(1)
// 模擬 Insert 失敗
mockCommentModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(errors.New("insert failed")).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 NewCommentLogic
logic := NewCommentLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 NewComment
resp, err := logic.NewComment(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.NotEmpty(t, resp.CommentId)
}
})
}
}

View File

@ -1,10 +1,14 @@
package commentservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/domain"
model "app-cloudep-tweeting-service/internal/model/mongo"
"app-cloudep-tweeting-service/internal/svc"
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
ers "code.30cm.net/digimon/library-go/errs"
"go.mongodb.org/mongo-driver/bson/primitive"
"github.com/zeromicro/go-zero/core/logx"
)
@ -23,9 +27,56 @@ func NewUpdateCommentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Upd
}
}
type checkCommentId struct {
CommentId string `validate:"required"`
Content string `json:"content,omitempty" validate:"lte=500"`
}
// UpdateComment 更新評論
func (l *UpdateCommentLogic) UpdateComment(in *tweeting.UpdateCommentReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
// 驗證資料
if err := l.svcCtx.Validate.ValidateAll(&checkCommentId{
CommentId: in.GetCommentId(),
Content: in.GetContent(),
}); err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat(err.Error())
}
// 沒有就沒有,有就走全覆蓋
update := model.Comment{}
oid, err := primitive.ObjectIDFromHex(in.GetCommentId())
if err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat("failed to get correct comment id")
}
update.ID = oid
update.Content = in.GetContent()
// 因為 0 也有意義,所以如果是真的沒帶進來,用 -1 帶進去表示不作動
if in.LikeCount == nil {
update.LikeCount = -1
} else {
update.LikeCount = in.GetLikeCount()
}
if in.DislikeCount == nil {
update.DisLikeCount = -1
} else {
update.DisLikeCount = in.GetDislikeCount()
}
_, err = l.svcCtx.CommentModel.UpdateOptional(l.ctx, &update)
if err != nil {
// 錯誤代碼 05-021-13
e := domain.CommentErrorL(
domain.CommentUpdateErrorCode,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "req", Value: in},
{Key: "func", Value: "CommentModel.UpdateOptional"},
{Key: "err", Value: err},
},
"failed to update comment:", in.CommentId).Wrap(err)
return nil, e
}
return &tweeting.OKResp{}, nil
}

View File

@ -0,0 +1,124 @@
package commentservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
mocklib "app-cloudep-tweeting-service/internal/mock/lib"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/mock/gomock"
"google.golang.org/protobuf/proto"
)
func TestUpdateComment(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockCommentModel := mockmodel.NewMockCommentModel(ctrl)
mockValidate := mocklib.NewMockValidate(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
CommentModel: mockCommentModel,
Validate: mockValidate,
}
// 測試數據
commentID := primitive.NewObjectID().Hex()
updateCommentReq := &tweeting.UpdateCommentReq{
CommentId: commentID,
Content: "Updated content",
LikeCount: proto.Int64(5),
DislikeCount: proto.Int64(2),
}
// 測試數據集
tests := []struct {
name string
input *tweeting.UpdateCommentReq
prepare func()
expectErr bool
}{
{
name: "成功更新評論",
input: updateCommentReq,
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
// 模擬 UpdateOptional 成功
mockCommentModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return(&mongo.UpdateResult{
ModifiedCount: 1,
}, nil).Times(1)
},
expectErr: false,
},
{
name: "驗證失敗",
input: updateCommentReq,
prepare: func() {
// 模擬 Validate 失敗
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1)
},
expectErr: true,
},
{
name: "更新評論失敗",
input: updateCommentReq,
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
// 模擬 UpdateOptional 失敗
mockCommentModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return(
&mongo.UpdateResult{
ModifiedCount: 0,
}, errors.New("update failed")).Times(1)
},
expectErr: true,
},
{
name: "無效的評論ID",
input: &tweeting.UpdateCommentReq{
CommentId: "invalid_id",
Content: "Updated content",
},
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 UpdateCommentLogic
logic := UpdateCommentLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 UpdateComment
resp, err := logic.UpdateComment(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
assert.Nil(t, resp)
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
}
})
}
}

View File

@ -1,31 +0,0 @@
package postservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type CountLikeLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCountLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CountLikeLogic {
return &CountLikeLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// CountLike 取得讚/不讚數量
func (l *CountLikeLogic) CountLike(in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) {
// todo: add your logic here and delete this line
return &tweeting.LikeCountResp{}, nil
}

View File

@ -0,0 +1,94 @@
package postservicelogic
import (
"app-cloudep-tweeting-service/internal/domain"
model "app-cloudep-tweeting-service/internal/model/mongo"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"context"
ers "code.30cm.net/digimon/library-go/errs"
"github.com/zeromicro/go-zero/core/logx"
)
type CreatePostLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewCreatePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreatePostLogic {
return &CreatePostLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// TODO 要調查一下內容如果存 html 是否有需要Encode
// 輸入的定義 -> 檢查用
type newTweetingReq struct {
UID string `json:"uid" validate:"required"`
Content string `json:"content" validate:"required,lte=500"` // 貼文限制 500 字內
Tags []string `json:"tags"`
MediaUrl []string `json:"media_url"`
IsAd bool `json:"is_ad"` // default false
}
// CreatePost 新增貼文
func (l *CreatePostLogic) CreatePost(in *tweeting.NewPostReq) (*tweeting.PostResp, error) {
// 驗證資料
if err := l.svcCtx.Validate.ValidateAll(&newTweetingReq{
UID: in.GetUid(),
Content: in.GetContent(),
}); err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat(err.Error())
}
// ============ prepare ============
// 新增資料
tweet := &model.Post{
UID: in.GetUid(),
Content: in.GetContent(),
Status: domain.TweetingStatusNotReviewedYet.ToInt8(),
IsAd: in.IsAd,
}
if len(in.GetTags()) > 0 {
// 存在貼文內的不提供搜尋純顯示用只不過在原始的tag 發生變動的時候,並不會一起改變
// 搜尋會貼文與Tag 的表會再另外一邊做關聯
// 暫時業務邏輯上tag 只提供新增,不提供修改以及刪除,故目前版本可行
tweet.Tags = in.GetTags()
}
if len(in.Media) > 0 {
// 將 Media 存入
var media []model.Media
for _, item := range in.GetMedia() {
media = append(media, model.Media{
Links: item.Url,
Type: item.Type,
})
}
tweet.MediaURL = media
}
// ============ insert ============
err := l.svcCtx.PostModel.Insert(l.ctx, tweet)
if err != nil {
// 錯誤代碼 05-021-02
e := domain.CommentErrorL(
domain.CreatePostError,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "req", Value: in},
{Key: "func", Value: "PostModel.Insert"},
{Key: "err", Value: err},
},
"failed to add new post").Wrap(err)
return nil, e
}
return &tweeting.PostResp{
PostId: tweet.ID.Hex(),
}, nil
}

View File

@ -0,0 +1,107 @@
package postservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"testing"
mocklib "app-cloudep-tweeting-service/internal/mock/lib"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
)
func TestCreatePost(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockPostModel := mockmodel.NewMockPostModel(ctrl)
mockValidate := mocklib.NewMockValidate(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
PostModel: mockPostModel,
Validate: mockValidate,
}
// 測試數據集
tests := []struct {
name string
input *tweeting.NewPostReq
prepare func()
expectErr bool
}{
{
name: "成功創建貼文",
input: &tweeting.NewPostReq{
Uid: "12345",
Content: "Test content",
IsAd: false,
Tags: []string{"tag1", "tag2"},
Media: []*tweeting.Media{
{
Url: "http://example.com/image.png",
Type: "image",
},
},
},
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
mockPostModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(nil).Times(1)
},
expectErr: false,
},
{
name: "驗證失敗",
input: &tweeting.NewPostReq{
Uid: "",
Content: "",
},
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1)
},
expectErr: true,
},
{
name: "插入貼文失敗",
input: &tweeting.NewPostReq{
Uid: "12345",
Content: "Test content",
IsAd: false,
},
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
mockPostModel.EXPECT().Insert(gomock.Any(), gomock.Any()).Return(errors.New("insert failed")).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 CreatePostLogic
logic := CreatePostLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 CreatePost
_, err := logic.CreatePost(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@ -1,10 +1,10 @@
package postservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/domain"
"app-cloudep-tweeting-service/internal/svc"
"context"
"github.com/zeromicro/go-zero/core/logx"
)
@ -25,7 +25,20 @@ func NewDeletePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Delete
// DeletePost 刪除貼文
func (l *DeletePostLogic) DeletePost(in *tweeting.DeletePostsReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
_, err := l.svcCtx.PostModel.DeleteMany(l.ctx, in.GetPostId()...)
if err != nil {
// 錯誤代碼 05-021-03
e := domain.CommentErrorL(
domain.DelPostError,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "req", Value: in},
{Key: "func", Value: "PostModel.DeleteMany"},
{Key: "err", Value: err},
},
"failed to del post").Wrap(err)
return nil, e
}
return &tweeting.OKResp{}, nil
}

View File

@ -0,0 +1,81 @@
package postservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
)
func TestDeletePost(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockPostModel := mockmodel.NewMockPostModel(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
PostModel: mockPostModel,
}
// 測試數據集
tests := []struct {
name string
input *tweeting.DeletePostsReq
prepare func()
expectErr bool
}{
{
name: "成功刪除貼文",
input: &tweeting.DeletePostsReq{
PostId: []string{"12345", "67890"},
},
prepare: func() {
// 模擬 DeleteMany 成功
mockPostModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(2), nil).Times(1)
},
expectErr: false,
},
{
name: "刪除貼文失敗",
input: &tweeting.DeletePostsReq{
PostId: []string{"12345", "67890"},
},
prepare: func() {
// 模擬 DeleteMany 失敗
mockPostModel.EXPECT().DeleteMany(gomock.Any(), "12345", "67890").Return(int64(0), errors.New("delete failed")).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 DeletePostLogic
logic := DeletePostLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 DeletePost
_, err := logic.DeletePost(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@ -1,31 +0,0 @@
package postservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type GetLikeStatusLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetLikeStatusLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLikeStatusLogic {
return &GetLikeStatusLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// GetLikeStatus 取得讚/不讚狀態
func (l *GetLikeStatusLogic) GetLikeStatus(in *tweeting.LikeReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
return &tweeting.OKResp{}, nil
}

View File

@ -1,31 +0,0 @@
package postservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type LikeListLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewLikeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeListLogic {
return &LikeListLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// LikeList 取得讚/不讚列表
func (l *LikeListLogic) LikeList(in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) {
// todo: add your logic here and delete this line
return &tweeting.LikeListResp{}, nil
}

View File

@ -1,31 +0,0 @@
package postservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type LikeLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewLikeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LikeLogic {
return &LikeLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// Like 點讚/取消讚 貼文
func (l *LikeLogic) Like(in *tweeting.LikeReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
return &tweeting.OKResp{}, nil
}

View File

@ -1,8 +1,13 @@
package postservicelogic
import (
"app-cloudep-tweeting-service/internal/domain"
model "app-cloudep-tweeting-service/internal/model/mongo"
"context"
ers "code.30cm.net/digimon/library-go/errs"
"google.golang.org/protobuf/proto"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
@ -23,9 +28,96 @@ func NewListPostsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListPos
}
}
// ListPosts 查詢貼文
func (l *ListPostsLogic) ListPosts(in *tweeting.QueryPostsReq) (*tweeting.ListPostsResp, error) {
// todo: add your logic here and delete this line
return &tweeting.ListPostsResp{}, nil
// 只列出要驗證的資料
type listReq struct {
OnlyAdds int32 `json:"only_adds" validate:"oneof=0 1 2 3"`
PageSize int64 `json:"page_size" validate:"required"`
PageIndex int64 `json:"page_index" validate:"required"`
}
// 將單個 Post 轉換為 PostDetailItem
func convertToPostDetailItem(item *model.Post) *tweeting.PostDetailItem {
media := make([]*tweeting.Media, 0, len(item.MediaURL))
for _, subItem := range item.MediaURL {
media = append(media, &tweeting.Media{
Type: subItem.Type,
Url: subItem.Links,
})
}
return &tweeting.PostDetailItem{
PostId: item.ID.Hex(),
Uid: item.UID,
Content: item.Content,
Tags: item.Tags,
Media: media,
IsAd: item.IsAd,
CreatedAt: item.CreateAt,
UpdateAt: item.UpdateAt,
LikeCount: int64(item.Like),
DislikeCount: int64(item.DisLike),
}
}
// ListPosts 查詢貼文 -> 主流程
func (l *ListPostsLogic) ListPosts(in *tweeting.QueryPostsReq) (*tweeting.ListPostsResp, error) {
// 將 PageSize 和 PageIndex 提前轉換為 int64
pageSize := int64(in.GetPageSize())
pageIndex := int64(in.GetPageIndex())
// 驗證資料
if err := l.svcCtx.Validate.ValidateAll(&listReq{
PageSize: pageSize,
PageIndex: pageIndex,
OnlyAdds: in.GetOnlyAds(),
}); err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat(err.Error())
}
// 構建查詢條件
query := &model.QueryPostModelReq{
UID: in.GetUid(),
Id: in.GetPostId(),
PageSize: pageSize,
PageIndex: pageIndex,
}
// 處理 OnlyAds 條件
if in.OnlyAds != nil {
onlyAds := in.GetOnlyAds()
query.OnlyAds = proto.Bool(onlyAds == domain.AdTypeOnlyAd.ToInt32())
}
// 執行查詢
find, count, err := l.svcCtx.PostModel.Find(l.ctx, query)
if err != nil {
// 錯誤代碼 05-021-05
e := domain.CommentErrorL(
domain.ListPostError,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "query", Value: query},
{Key: "func", Value: "PostModel.Find"},
{Key: "err", Value: err},
},
"failed to find posts").Wrap(err)
return nil, e
}
// 將查詢結果轉換為 API 回應格式
result := make([]*tweeting.PostDetailItem, 0, count)
for _, item := range find {
result = append(result, convertToPostDetailItem(item))
}
// 返回結果
return &tweeting.ListPostsResp{
Posts: result,
Page: &tweeting.Pager{
Total: count,
Index: pageIndex,
Size: pageSize,
},
}, nil
}

View File

@ -0,0 +1,164 @@
package postservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
mocklib "app-cloudep-tweeting-service/internal/mock/lib"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
model "app-cloudep-tweeting-service/internal/model/mongo"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"testing"
"time"
"google.golang.org/protobuf/proto"
"github.com/stretchr/testify/assert"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.uber.org/mock/gomock"
)
func TestConvertToPostDetailItem(t *testing.T) {
// 構建測試數據
postID := primitive.NewObjectID()
post := &model.Post{
ID: postID,
UID: "12345",
Content: "Test Content",
Tags: []string{"tag1", "tag2"},
MediaURL: []model.Media{
{Type: "image", Links: "http://example.com/image.png"},
{Type: "video", Links: "http://example.com/video.mp4"},
},
IsAd: true,
CreateAt: time.Now().Unix(),
UpdateAt: time.Now().Unix(),
Like: 10,
DisLike: 2,
}
// 執行轉換
result := convertToPostDetailItem(post)
// 驗證結果
assert.Equal(t, postID.Hex(), result.PostId)
assert.Equal(t, "12345", result.Uid)
assert.Equal(t, "Test Content", result.Content)
assert.Equal(t, []string{"tag1", "tag2"}, result.Tags)
assert.Equal(t, true, result.IsAd)
assert.Equal(t, int64(10), result.LikeCount)
assert.Equal(t, int64(2), result.DislikeCount)
// 驗證 Media 的轉換
assert.Len(t, result.Media, 2)
assert.Equal(t, "image", result.Media[0].Type)
assert.Equal(t, "http://example.com/image.png", result.Media[0].Url)
assert.Equal(t, "video", result.Media[1].Type)
assert.Equal(t, "http://example.com/video.mp4", result.Media[1].Url)
}
func TestListPosts(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockPostModel := mockmodel.NewMockPostModel(ctrl)
mockValidate := mocklib.NewMockValidate(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
PostModel: mockPostModel,
Validate: mockValidate,
}
// 構建測試數據
queryReq := &tweeting.QueryPostsReq{
PageSize: 10,
PageIndex: 1,
OnlyAds: proto.Int32(1),
}
mockPosts := []*model.Post{
{
ID: primitive.NewObjectID(),
UID: "12345",
Content: "Test Content 1",
Tags: []string{"tag1", "tag2"},
MediaURL: []model.Media{
{Type: "image", Links: "http://example.com/image1.png"},
},
IsAd: false,
CreateAt: time.Now().Unix(),
UpdateAt: time.Now().Unix(),
Like: 5,
DisLike: 1,
},
{
ID: primitive.NewObjectID(),
UID: "67890",
Content: "Test Content 2",
Tags: []string{"tag3", "tag4"},
MediaURL: []model.Media{
{Type: "video", Links: "http://example.com/video1.mp4"},
},
IsAd: true,
CreateAt: time.Now().Unix(),
UpdateAt: time.Now().Unix(),
Like: 3,
DisLike: 0,
},
}
// 測試數據集
tests := []struct {
name string
input *tweeting.QueryPostsReq
prepare func()
expectErr bool
}{
{
name: "成功查詢貼文",
input: queryReq,
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
mockPostModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(mockPosts, int64(len(mockPosts)), nil).Times(1)
},
expectErr: false,
},
{
name: "查詢貼文失敗",
input: queryReq,
prepare: func() {
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
mockPostModel.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, int64(0), errors.New("find failed")).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 ListPostsLogic
logic := ListPostsLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 ListPosts
resp, err := logic.ListPosts(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.NotNil(t, resp)
assert.Len(t, resp.Posts, len(mockPosts))
}
})
}
}

View File

@ -1,31 +0,0 @@
package postservicelogic
import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
"github.com/zeromicro/go-zero/core/logx"
)
type NewPostLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewNewPostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *NewPostLogic {
return &NewPostLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
// NewPost 新增貼文
func (l *NewPostLogic) NewPost(in *tweeting.NewPostReq) (*tweeting.PostResp, error) {
// todo: add your logic here and delete this line
return &tweeting.PostResp{}, nil
}

View File

@ -1,8 +1,13 @@
package postservicelogic
import (
"app-cloudep-tweeting-service/internal/domain"
model "app-cloudep-tweeting-service/internal/model/mongo"
"context"
ers "code.30cm.net/digimon/library-go/errs"
"go.mongodb.org/mongo-driver/bson/primitive"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/svc"
@ -23,9 +28,68 @@ func NewUpdatePostLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Update
}
}
type checkPostId struct {
PostID string `validate:"required"`
Content string `json:"content,omitempty" validate:"lte=500"`
}
// UpdatePost 更新貼文
func (l *UpdatePostLogic) UpdatePost(in *tweeting.UpdatePostReq) (*tweeting.OKResp, error) {
// todo: add your logic here and delete this line
// 驗證資料
if err := l.svcCtx.Validate.ValidateAll(&checkPostId{
PostID: in.GetPostId(),
Content: in.GetContent(),
}); err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat(err.Error())
}
// 沒有就沒有,有就走全覆蓋
update := model.Post{}
oid, err := primitive.ObjectIDFromHex(in.GetPostId())
if err != nil {
// 錯誤代碼 05-011-00
return nil, ers.InvalidFormat("failed to get correct post id")
}
update.ID = oid
update.Tags = in.GetTags()
// 將 Media 存入
var media []model.Media
for _, item := range in.GetMedia() {
media = append(media, model.Media{
Links: item.Url,
Type: item.Type,
})
}
update.MediaURL = media
update.Content = in.GetContent()
// 因為 0 也有意義,所以如果是真的沒帶進來,用 -1 帶進去表示不作動
if in.LikeCount == nil {
update.Like = -1
} else {
update.Like = in.GetLikeCount()
}
if in.DislikeCount == nil {
update.DisLike = -1
} else {
update.DisLike = in.GetDislikeCount()
}
_, err = l.svcCtx.PostModel.UpdateOptional(l.ctx, &update)
if err != nil {
// 錯誤代碼 05-021-04
e := domain.CommentErrorL(
domain.UpdatePostError,
logx.WithContext(l.ctx),
[]logx.LogField{
{Key: "req", Value: in},
{Key: "func", Value: "PostModel.UpdateOptional"},
{Key: "err", Value: err},
},
"failed to update post", in.PostId).Wrap(err)
return nil, e
}
return &tweeting.OKResp{}, nil
}

View File

@ -0,0 +1,118 @@
package postservicelogic
import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
mocklib "app-cloudep-tweeting-service/internal/mock/lib"
mockmodel "app-cloudep-tweeting-service/internal/mock/model"
"app-cloudep-tweeting-service/internal/svc"
"context"
"errors"
"go.mongodb.org/mongo-driver/mongo"
"google.golang.org/protobuf/proto"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"testing"
)
func TestUpdatePost(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 初始化 mock 依賴
mockPostModel := mockmodel.NewMockPostModel(ctrl)
mockValidate := mocklib.NewMockValidate(ctrl)
// 初始化服務上下文
svcCtx := &svc.ServiceContext{
PostModel: mockPostModel,
Validate: mockValidate,
}
// 測試數據集
tests := []struct {
name string
input *tweeting.UpdatePostReq
prepare func()
expectErr bool
}{
{
name: "成功更新貼文",
input: &tweeting.UpdatePostReq{
PostId: "66cfdc1d6f8fe7eac1e52523",
Content: proto.String("Updated content"),
Tags: []string{"tag1", "tag2"},
Media: []*tweeting.Media{
{
Url: "http://example.com/image.png",
Type: "image",
},
},
LikeCount: proto.Int64(10),
DislikeCount: proto.Int64(2),
},
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
// 模擬 UpdateOptional 成功
mockPostModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return(&mongo.UpdateResult{
ModifiedCount: 1,
}, nil).Times(1)
},
expectErr: false,
},
{
name: "驗證失敗",
input: &tweeting.UpdatePostReq{
PostId: "",
},
prepare: func() {
// 模擬 Validate 失敗
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(errors.New("validation failed")).Times(1)
},
expectErr: true,
},
{
name: "更新貼文失敗",
input: &tweeting.UpdatePostReq{
PostId: "66cfdc1d6f8fe7eac1e52523",
Content: proto.String("Updated content"),
},
prepare: func() {
// 模擬 Validate 成功
mockValidate.EXPECT().ValidateAll(gomock.Any()).Return(nil).Times(1)
// 模擬 UpdateOptional 失敗
mockPostModel.EXPECT().UpdateOptional(gomock.Any(), gomock.Any()).Return(&mongo.UpdateResult{
ModifiedCount: 0,
}, errors.New("update failed")).Times(1)
},
expectErr: true,
},
}
// 執行測試
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// 設置測試環境
tt.prepare()
// 初始化 UpdatePostLogic
logic := UpdatePostLogic{
svcCtx: svcCtx,
ctx: context.TODO(),
}
// 執行 UpdatePost
_, err := logic.UpdatePost(tt.input)
// 驗證結果
if tt.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@ -0,0 +1,73 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./validate.go
//
// Generated by this command:
//
// mockgen -source=./validate.go -destination=../../mock/lib/validate.go -package=lib
//
// Package lib is a generated GoMock package.
package lib
import (
reflect "reflect"
required "code.30cm.net/digimon/library-go/validator"
gomock "go.uber.org/mock/gomock"
)
// MockValidate is a mock of Validate interface.
type MockValidate struct {
ctrl *gomock.Controller
recorder *MockValidateMockRecorder
}
// MockValidateMockRecorder is the mock recorder for MockValidate.
type MockValidateMockRecorder struct {
mock *MockValidate
}
// NewMockValidate creates a new mock instance.
func NewMockValidate(ctrl *gomock.Controller) *MockValidate {
mock := &MockValidate{ctrl: ctrl}
mock.recorder = &MockValidateMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockValidate) EXPECT() *MockValidateMockRecorder {
return m.recorder
}
// BindToValidator mocks base method.
func (m *MockValidate) BindToValidator(opts ...required.Option) error {
m.ctrl.T.Helper()
varargs := []any{}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "BindToValidator", varargs...)
ret0, _ := ret[0].(error)
return ret0
}
// BindToValidator indicates an expected call of BindToValidator.
func (mr *MockValidateMockRecorder) BindToValidator(opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BindToValidator", reflect.TypeOf((*MockValidate)(nil).BindToValidator), opts...)
}
// ValidateAll mocks base method.
func (m *MockValidate) ValidateAll(obj any) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateAll", obj)
ret0, _ := ret[0].(error)
return ret0
}
// ValidateAll indicates an expected call of ValidateAll.
func (mr *MockValidateMockRecorder) ValidateAll(obj any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAll", reflect.TypeOf((*MockValidate)(nil).ValidateAll), obj)
}

View File

@ -0,0 +1,152 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./internal/model/mongo/comment_model.go
//
// Generated by this command:
//
// mockgen -source=./internal/model/mongo/comment_model.go -destination=./internal/mock/model/comment_model.go -package=mock
//
// Package mock is a generated GoMock package.
package mock
import (
model "app-cloudep-tweeting-service/internal/model/mongo"
context "context"
reflect "reflect"
mongo "go.mongodb.org/mongo-driver/mongo"
gomock "go.uber.org/mock/gomock"
)
// MockCommentModel is a mock of CommentModel interface.
type MockCommentModel struct {
ctrl *gomock.Controller
recorder *MockCommentModelMockRecorder
}
// MockCommentModelMockRecorder is the mock recorder for MockCommentModel.
type MockCommentModelMockRecorder struct {
mock *MockCommentModel
}
// NewMockCommentModel creates a new mock instance.
func NewMockCommentModel(ctrl *gomock.Controller) *MockCommentModel {
mock := &MockCommentModel{ctrl: ctrl}
mock.recorder = &MockCommentModelMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockCommentModel) EXPECT() *MockCommentModelMockRecorder {
return m.recorder
}
// Delete mocks base method.
func (m *MockCommentModel) Delete(ctx context.Context, id string) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", ctx, id)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Delete indicates an expected call of Delete.
func (mr *MockCommentModelMockRecorder) Delete(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCommentModel)(nil).Delete), ctx, id)
}
// DeleteMany mocks base method.
func (m *MockCommentModel) DeleteMany(ctx context.Context, id ...string) (int64, error) {
m.ctrl.T.Helper()
varargs := []any{ctx}
for _, a := range id {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "DeleteMany", varargs...)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DeleteMany indicates an expected call of DeleteMany.
func (mr *MockCommentModelMockRecorder) DeleteMany(ctx any, id ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]any{ctx}, id...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMany", reflect.TypeOf((*MockCommentModel)(nil).DeleteMany), varargs...)
}
// Find mocks base method.
func (m *MockCommentModel) Find(ctx context.Context, param *model.QueryCommentModelReq) ([]*model.Comment, int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Find", ctx, param)
ret0, _ := ret[0].([]*model.Comment)
ret1, _ := ret[1].(int64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// Find indicates an expected call of Find.
func (mr *MockCommentModelMockRecorder) Find(ctx, param any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockCommentModel)(nil).Find), ctx, param)
}
// FindOne mocks base method.
func (m *MockCommentModel) FindOne(ctx context.Context, id string) (*model.Comment, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindOne", ctx, id)
ret0, _ := ret[0].(*model.Comment)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FindOne indicates an expected call of FindOne.
func (mr *MockCommentModelMockRecorder) FindOne(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockCommentModel)(nil).FindOne), ctx, id)
}
// Insert mocks base method.
func (m *MockCommentModel) Insert(ctx context.Context, data *model.Comment) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Insert", ctx, data)
ret0, _ := ret[0].(error)
return ret0
}
// Insert indicates an expected call of Insert.
func (mr *MockCommentModelMockRecorder) Insert(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockCommentModel)(nil).Insert), ctx, data)
}
// Update mocks base method.
func (m *MockCommentModel) Update(ctx context.Context, data *model.Comment) (*mongo.UpdateResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Update", ctx, data)
ret0, _ := ret[0].(*mongo.UpdateResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Update indicates an expected call of Update.
func (mr *MockCommentModelMockRecorder) Update(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockCommentModel)(nil).Update), ctx, data)
}
// UpdateOptional mocks base method.
func (m *MockCommentModel) UpdateOptional(ctx context.Context, data *model.Comment) (*mongo.UpdateResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateOptional", ctx, data)
ret0, _ := ret[0].(*mongo.UpdateResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateOptional indicates an expected call of UpdateOptional.
func (mr *MockCommentModelMockRecorder) UpdateOptional(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOptional", reflect.TypeOf((*MockCommentModel)(nil).UpdateOptional), ctx, data)
}

View File

@ -0,0 +1,101 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./internal/model/mongo/comment_model_gen.go
//
// Generated by this command:
//
// mockgen -source=./internal/model/mongo/comment_model_gen.go -destination=./internal/mock/model/comment_model_gen.go -package=mock
//
// Package mock is a generated GoMock package.
package mock
import (
model "app-cloudep-tweeting-service/internal/model/mongo"
context "context"
reflect "reflect"
mongo "go.mongodb.org/mongo-driver/mongo"
gomock "go.uber.org/mock/gomock"
)
// MockcommentModel is a mock of commentModel interface.
type MockcommentModel struct {
ctrl *gomock.Controller
recorder *MockcommentModelMockRecorder
}
// MockcommentModelMockRecorder is the mock recorder for MockcommentModel.
type MockcommentModelMockRecorder struct {
mock *MockcommentModel
}
// NewMockcommentModel creates a new mock instance.
func NewMockcommentModel(ctrl *gomock.Controller) *MockcommentModel {
mock := &MockcommentModel{ctrl: ctrl}
mock.recorder = &MockcommentModelMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockcommentModel) EXPECT() *MockcommentModelMockRecorder {
return m.recorder
}
// Delete mocks base method.
func (m *MockcommentModel) Delete(ctx context.Context, id string) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", ctx, id)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Delete indicates an expected call of Delete.
func (mr *MockcommentModelMockRecorder) Delete(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockcommentModel)(nil).Delete), ctx, id)
}
// FindOne mocks base method.
func (m *MockcommentModel) FindOne(ctx context.Context, id string) (*model.Comment, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindOne", ctx, id)
ret0, _ := ret[0].(*model.Comment)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FindOne indicates an expected call of FindOne.
func (mr *MockcommentModelMockRecorder) FindOne(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockcommentModel)(nil).FindOne), ctx, id)
}
// Insert mocks base method.
func (m *MockcommentModel) Insert(ctx context.Context, data *model.Comment) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Insert", ctx, data)
ret0, _ := ret[0].(error)
return ret0
}
// Insert indicates an expected call of Insert.
func (mr *MockcommentModelMockRecorder) Insert(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockcommentModel)(nil).Insert), ctx, data)
}
// Update mocks base method.
func (m *MockcommentModel) Update(ctx context.Context, data *model.Comment) (*mongo.UpdateResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Update", ctx, data)
ret0, _ := ret[0].(*mongo.UpdateResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Update indicates an expected call of Update.
func (mr *MockcommentModelMockRecorder) Update(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockcommentModel)(nil).Update), ctx, data)
}

View File

@ -0,0 +1,152 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./internal/model/mongo/post_model.go
//
// Generated by this command:
//
// mockgen -source=./internal/model/mongo/post_model.go -destination=./internal/mock/model/post_model.go -package=mock
//
// Package mock is a generated GoMock package.
package mock
import (
model "app-cloudep-tweeting-service/internal/model/mongo"
context "context"
reflect "reflect"
mongo "go.mongodb.org/mongo-driver/mongo"
gomock "go.uber.org/mock/gomock"
)
// MockPostModel is a mock of PostModel interface.
type MockPostModel struct {
ctrl *gomock.Controller
recorder *MockPostModelMockRecorder
}
// MockPostModelMockRecorder is the mock recorder for MockPostModel.
type MockPostModelMockRecorder struct {
mock *MockPostModel
}
// NewMockPostModel creates a new mock instance.
func NewMockPostModel(ctrl *gomock.Controller) *MockPostModel {
mock := &MockPostModel{ctrl: ctrl}
mock.recorder = &MockPostModelMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockPostModel) EXPECT() *MockPostModelMockRecorder {
return m.recorder
}
// Delete mocks base method.
func (m *MockPostModel) Delete(ctx context.Context, id string) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", ctx, id)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Delete indicates an expected call of Delete.
func (mr *MockPostModelMockRecorder) Delete(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockPostModel)(nil).Delete), ctx, id)
}
// DeleteMany mocks base method.
func (m *MockPostModel) DeleteMany(ctx context.Context, id ...string) (int64, error) {
m.ctrl.T.Helper()
varargs := []any{ctx}
for _, a := range id {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "DeleteMany", varargs...)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DeleteMany indicates an expected call of DeleteMany.
func (mr *MockPostModelMockRecorder) DeleteMany(ctx any, id ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]any{ctx}, id...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteMany", reflect.TypeOf((*MockPostModel)(nil).DeleteMany), varargs...)
}
// Find mocks base method.
func (m *MockPostModel) Find(ctx context.Context, param *model.QueryPostModelReq) ([]*model.Post, int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Find", ctx, param)
ret0, _ := ret[0].([]*model.Post)
ret1, _ := ret[1].(int64)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// Find indicates an expected call of Find.
func (mr *MockPostModelMockRecorder) Find(ctx, param any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockPostModel)(nil).Find), ctx, param)
}
// FindOne mocks base method.
func (m *MockPostModel) FindOne(ctx context.Context, id string) (*model.Post, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindOne", ctx, id)
ret0, _ := ret[0].(*model.Post)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FindOne indicates an expected call of FindOne.
func (mr *MockPostModelMockRecorder) FindOne(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockPostModel)(nil).FindOne), ctx, id)
}
// Insert mocks base method.
func (m *MockPostModel) Insert(ctx context.Context, data *model.Post) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Insert", ctx, data)
ret0, _ := ret[0].(error)
return ret0
}
// Insert indicates an expected call of Insert.
func (mr *MockPostModelMockRecorder) Insert(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockPostModel)(nil).Insert), ctx, data)
}
// Update mocks base method.
func (m *MockPostModel) Update(ctx context.Context, data *model.Post) (*mongo.UpdateResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Update", ctx, data)
ret0, _ := ret[0].(*mongo.UpdateResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Update indicates an expected call of Update.
func (mr *MockPostModelMockRecorder) Update(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockPostModel)(nil).Update), ctx, data)
}
// UpdateOptional mocks base method.
func (m *MockPostModel) UpdateOptional(ctx context.Context, data *model.Post) (*mongo.UpdateResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateOptional", ctx, data)
ret0, _ := ret[0].(*mongo.UpdateResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// UpdateOptional indicates an expected call of UpdateOptional.
func (mr *MockPostModelMockRecorder) UpdateOptional(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOptional", reflect.TypeOf((*MockPostModel)(nil).UpdateOptional), ctx, data)
}

View File

@ -0,0 +1,101 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./internal/model/mongo/post_model_gen.go
//
// Generated by this command:
//
// mockgen -source=./internal/model/mongo/post_model_gen.go -destination=./internal/mock/model/post_model_gen.go -package=mock
//
// Package mock is a generated GoMock package.
package mock
import (
model "app-cloudep-tweeting-service/internal/model/mongo"
context "context"
reflect "reflect"
mongo "go.mongodb.org/mongo-driver/mongo"
gomock "go.uber.org/mock/gomock"
)
// MockpostModel is a mock of postModel interface.
type MockpostModel struct {
ctrl *gomock.Controller
recorder *MockpostModelMockRecorder
}
// MockpostModelMockRecorder is the mock recorder for MockpostModel.
type MockpostModelMockRecorder struct {
mock *MockpostModel
}
// NewMockpostModel creates a new mock instance.
func NewMockpostModel(ctrl *gomock.Controller) *MockpostModel {
mock := &MockpostModel{ctrl: ctrl}
mock.recorder = &MockpostModelMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockpostModel) EXPECT() *MockpostModelMockRecorder {
return m.recorder
}
// Delete mocks base method.
func (m *MockpostModel) Delete(ctx context.Context, id string) (int64, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", ctx, id)
ret0, _ := ret[0].(int64)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Delete indicates an expected call of Delete.
func (mr *MockpostModelMockRecorder) Delete(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockpostModel)(nil).Delete), ctx, id)
}
// FindOne mocks base method.
func (m *MockpostModel) FindOne(ctx context.Context, id string) (*model.Post, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FindOne", ctx, id)
ret0, _ := ret[0].(*model.Post)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FindOne indicates an expected call of FindOne.
func (mr *MockpostModelMockRecorder) FindOne(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindOne", reflect.TypeOf((*MockpostModel)(nil).FindOne), ctx, id)
}
// Insert mocks base method.
func (m *MockpostModel) Insert(ctx context.Context, data *model.Post) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Insert", ctx, data)
ret0, _ := ret[0].(error)
return ret0
}
// Insert indicates an expected call of Insert.
func (mr *MockpostModelMockRecorder) Insert(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockpostModel)(nil).Insert), ctx, data)
}
// Update mocks base method.
func (m *MockpostModel) Update(ctx context.Context, data *model.Post) (*mongo.UpdateResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Update", ctx, data)
ret0, _ := ret[0].(*mongo.UpdateResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Update indicates an expected call of Update.
func (mr *MockpostModelMockRecorder) Update(ctx, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockpostModel)(nil).Update), ctx, data)
}

View File

@ -1,25 +0,0 @@
package model
import "github.com/zeromicro/go-zero/core/stores/mon"
var _ Comment_likesModel = (*customComment_likesModel)(nil)
type (
// Comment_likesModel is an interface to be customized, add more methods here,
// and implement the added methods in customComment_likesModel.
Comment_likesModel interface {
comment_likesModel
}
customComment_likesModel struct {
*defaultComment_likesModel
}
)
// NewComment_likesModel returns a model for the mongo.
func NewComment_likesModel(url, db, collection string) Comment_likesModel {
conn := mon.MustNewModel(url, db, collection)
return &customComment_likesModel{
defaultComment_likesModel: newDefaultComment_likesModel(conn),
}
}

View File

@ -1,74 +0,0 @@
// Code generated by goctl. DO NOT EDIT.
package model
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
type comment_likesModel interface {
Insert(ctx context.Context, data *Comment_likes) error
FindOne(ctx context.Context, id string) (*Comment_likes, error)
Update(ctx context.Context, data *Comment_likes) (*mongo.UpdateResult, error)
Delete(ctx context.Context, id string) (int64, error)
}
type defaultComment_likesModel struct {
conn *mon.Model
}
func newDefaultComment_likesModel(conn *mon.Model) *defaultComment_likesModel {
return &defaultComment_likesModel{conn: conn}
}
func (m *defaultComment_likesModel) Insert(ctx context.Context, data *Comment_likes) error {
if data.ID.IsZero() {
data.ID = primitive.NewObjectID()
data.CreateAt = time.Now()
data.UpdateAt = time.Now()
}
_, err := m.conn.InsertOne(ctx, data)
return err
}
func (m *defaultComment_likesModel) FindOne(ctx context.Context, id string) (*Comment_likes, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, ErrInvalidObjectId
}
var data Comment_likes
err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
switch err {
case nil:
return &data, nil
case mon.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultComment_likesModel) Update(ctx context.Context, data *Comment_likes) (*mongo.UpdateResult, error) {
data.UpdateAt = time.Now()
res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
return res, err
}
func (m *defaultComment_likesModel) Delete(ctx context.Context, id string) (int64, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return 0, ErrInvalidObjectId
}
res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
return res, err
}

View File

@ -1,14 +0,0 @@
package model
import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Comment_likes struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
// TODO: Fill your own fields
UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
}

View File

@ -1,8 +1,14 @@
package model
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/monc"
"context"
"time"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var _ CommentModel = (*customCommentModel)(nil)
@ -12,17 +18,84 @@ type (
// and implement the added methods in customCommentModel.
CommentModel interface {
commentModel
DeleteMany(ctx context.Context, id ...string) (int64, error)
UpdateOptional(ctx context.Context, data *Comment) (*mongo.UpdateResult, error)
Find(ctx context.Context, param *QueryCommentModelReq) ([]*Comment, int64, error)
}
customCommentModel struct {
*defaultCommentModel
}
QueryCommentModelReq struct {
PostID string
PageSize int64
PageIndex int64
}
)
func (xw customCommentModel) Find(ctx context.Context, param *QueryCommentModelReq) ([]*Comment, int64, error) {
// TODO implement me
panic("implement me")
}
// NewCommentModel returns a model for the mongo.
func NewCommentModel(url, db, collection string, c cache.CacheConf) CommentModel {
conn := monc.MustNewModel(url, db, collection, c)
func NewCommentModel(url, db, collection string) CommentModel {
conn := mon.MustNewModel(url, db, collection)
return &customCommentModel{
defaultCommentModel: newDefaultCommentModel(conn),
}
}
func (m *defaultCommentModel) DeleteMany(ctx context.Context, id ...string) (int64, error) {
objectIDs := make([]primitive.ObjectID, 0, len(id))
// prepare
for _, item := range id {
oid, err := primitive.ObjectIDFromHex(item)
if err != nil {
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "defaultPostModel.DeleteMany"),
logx.Field("id", item),
).Error(err.Error())
continue
}
objectIDs = append(objectIDs, oid)
}
// 檢查是否有有效的 ObjectIDs
if len(objectIDs) == 0 {
return 0, ErrNotFound
}
// 刪除文檔
res, err := m.conn.DeleteMany(ctx, bson.M{"_id": bson.M{"$in": objectIDs}})
if err != nil {
return 0, err
}
return res, err
}
func (m *defaultCommentModel) UpdateOptional(ctx context.Context, data *Comment) (*mongo.UpdateResult, error) {
update := bson.M{"$set": bson.M{}}
if data.Content != "" {
update["$set"].(bson.M)["content"] = data.Content
}
if data.LikeCount != -1 {
update["$set"].(bson.M)["like_count"] = data.LikeCount
}
if data.DisLikeCount != -1 {
update["$set"].(bson.M)["dis_like_count"] = data.DisLikeCount
}
// UpdateAt 是每次都需要更新的,不用檢查
update["$set"].(bson.M)["updateAt"] = time.Now().UTC().UnixNano()
res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, update)
return res, err
}

View File

@ -5,14 +5,12 @@ import (
"context"
"time"
"github.com/zeromicro/go-zero/core/stores/monc"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var prefixCommentCacheKey = "cache:comment:"
type commentModel interface {
Insert(ctx context.Context, data *Comment) error
FindOne(ctx context.Context, id string) (*Comment, error)
@ -21,22 +19,21 @@ type commentModel interface {
}
type defaultCommentModel struct {
conn *monc.Model
conn *mon.Model
}
func newDefaultCommentModel(conn *monc.Model) *defaultCommentModel {
func newDefaultCommentModel(conn *mon.Model) *defaultCommentModel {
return &defaultCommentModel{conn: conn}
}
func (m *defaultCommentModel) Insert(ctx context.Context, data *Comment) error {
if data.ID.IsZero() {
data.ID = primitive.NewObjectID()
data.CreateAt = time.Now()
data.UpdateAt = time.Now()
data.CreateAt = time.Now().UTC().UnixNano()
data.UpdateAt = time.Now().UTC().UnixNano()
}
key := prefixCommentCacheKey + data.ID.Hex()
_, err := m.conn.InsertOne(ctx, key, data)
_, err := m.conn.InsertOne(ctx, data)
return err
}
@ -47,12 +44,12 @@ func (m *defaultCommentModel) FindOne(ctx context.Context, id string) (*Comment,
}
var data Comment
key := prefixCommentCacheKey + id
err = m.conn.FindOne(ctx, key, &data, bson.M{"_id": oid})
err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
switch err {
case nil:
return &data, nil
case monc.ErrNotFound:
case mon.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
@ -60,9 +57,9 @@ func (m *defaultCommentModel) FindOne(ctx context.Context, id string) (*Comment,
}
func (m *defaultCommentModel) Update(ctx context.Context, data *Comment) (*mongo.UpdateResult, error) {
data.UpdateAt = time.Now()
key := prefixCommentCacheKey + data.ID.Hex()
res, err := m.conn.UpdateOne(ctx, key, bson.M{"_id": data.ID}, bson.M{"$set": data})
data.UpdateAt = time.Now().UTC().UnixNano()
res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
return res, err
}
@ -71,7 +68,7 @@ func (m *defaultCommentModel) Delete(ctx context.Context, id string) (int64, err
if err != nil {
return 0, ErrInvalidObjectId
}
key := prefixCommentCacheKey + id
res, err := m.conn.DeleteOne(ctx, key, bson.M{"_id": oid})
res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
return res, err
}

View File

@ -1,14 +1,23 @@
package model
import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Comment struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
// TODO: Fill your own fields
UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
PostID string `bson:"post_id" json:"post_id"`
UID string `bson:"uid" json:"uid"` // 留下留言的人
Content string `bson:"content" json:"content"` // 留言內容
LikeCount int64 `bson:"like_count" json:"like_count"` // 喜歡這則留言的人
DisLikeCount int64 `bson:"dis_like_count" json:"dis_like_count"` // 不喜歡這則留言的人
UpdateAt int64 `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt int64 `bson:"createAt,omitempty" json:"createAt,omitempty"`
}
func (c Comment) CollectionName() string {
return "comment"
}
// 照邏輯,應該需要建立的索引有
// 複合索引:(PostID + CreateAt)

View File

@ -1,25 +0,0 @@
package model
import "github.com/zeromicro/go-zero/core/stores/mon"
var _ Post_likesModel = (*customPost_likesModel)(nil)
type (
// Post_likesModel is an interface to be customized, add more methods here,
// and implement the added methods in customPost_likesModel.
Post_likesModel interface {
post_likesModel
}
customPost_likesModel struct {
*defaultPost_likesModel
}
)
// NewPost_likesModel returns a model for the mongo.
func NewPost_likesModel(url, db, collection string) Post_likesModel {
conn := mon.MustNewModel(url, db, collection)
return &customPost_likesModel{
defaultPost_likesModel: newDefaultPost_likesModel(conn),
}
}

View File

@ -1,74 +0,0 @@
// Code generated by goctl. DO NOT EDIT.
package model
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
type post_likesModel interface {
Insert(ctx context.Context, data *Post_likes) error
FindOne(ctx context.Context, id string) (*Post_likes, error)
Update(ctx context.Context, data *Post_likes) (*mongo.UpdateResult, error)
Delete(ctx context.Context, id string) (int64, error)
}
type defaultPost_likesModel struct {
conn *mon.Model
}
func newDefaultPost_likesModel(conn *mon.Model) *defaultPost_likesModel {
return &defaultPost_likesModel{conn: conn}
}
func (m *defaultPost_likesModel) Insert(ctx context.Context, data *Post_likes) error {
if data.ID.IsZero() {
data.ID = primitive.NewObjectID()
data.CreateAt = time.Now()
data.UpdateAt = time.Now()
}
_, err := m.conn.InsertOne(ctx, data)
return err
}
func (m *defaultPost_likesModel) FindOne(ctx context.Context, id string) (*Post_likes, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, ErrInvalidObjectId
}
var data Post_likes
err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
switch err {
case nil:
return &data, nil
case mon.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultPost_likesModel) Update(ctx context.Context, data *Post_likes) (*mongo.UpdateResult, error) {
data.UpdateAt = time.Now()
res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
return res, err
}
func (m *defaultPost_likesModel) Delete(ctx context.Context, id string) (int64, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return 0, ErrInvalidObjectId
}
res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
return res, err
}

View File

@ -1,14 +0,0 @@
package model
import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Post_likes struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
// TODO: Fill your own fields
UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
}

View File

@ -1,8 +1,18 @@
package model
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"context"
"errors"
"time"
"github.com/zeromicro/go-zero/core/stores/monc"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var _ PostModel = (*customPostModel)(nil)
@ -12,17 +22,164 @@ type (
// and implement the added methods in customPostModel.
PostModel interface {
postModel
DeleteMany(ctx context.Context, id ...string) (int64, error)
UpdateOptional(ctx context.Context, data *Post) (*mongo.UpdateResult, error)
Find(ctx context.Context, param *QueryPostModelReq) ([]*Post, int64, error)
}
customPostModel struct {
*defaultPostModel
}
QueryPostModelReq struct {
UID []string
Id []string
OnlyAds *bool
PageSize int64
PageIndex int64
}
)
// NewPostModel returns a model for the mongo.
func NewPostModel(url, db, collection string, c cache.CacheConf) PostModel {
conn := monc.MustNewModel(url, db, collection, c)
func NewPostModel(url, db, collection string) PostModel {
conn := mon.MustNewModel(url, db, collection)
return &customPostModel{
defaultPostModel: newDefaultPostModel(conn),
}
}
func (m *defaultPostModel) DeleteMany(ctx context.Context, id ...string) (int64, error) {
objectIDs := make([]primitive.ObjectID, 0, len(id))
// prepare
for _, item := range id {
oid, err := primitive.ObjectIDFromHex(item)
if err != nil {
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "defaultPostModel.DeleteMany"),
logx.Field("id", item),
).Error(err.Error())
continue
}
objectIDs = append(objectIDs, oid)
}
// 檢查是否有有效的 ObjectIDs
if len(objectIDs) == 0 {
return 0, ErrNotFound
}
// 刪除文檔
res, err := m.conn.DeleteMany(ctx, bson.M{"_id": bson.M{"$in": objectIDs}})
if err != nil {
return 0, err
}
return res, err
}
func (m *defaultPostModel) UpdateOptional(ctx context.Context, data *Post) (*mongo.UpdateResult, error) {
update := bson.M{"$set": bson.M{}}
if data.UID != "" {
update["$set"].(bson.M)["uid"] = data.UID
}
if data.Content != "" {
update["$set"].(bson.M)["content"] = data.Content
}
if data.Status != 0 {
update["$set"].(bson.M)["status"] = data.Status
}
if data.IsAd {
update["$set"].(bson.M)["is_ad"] = data.IsAd
}
if len(data.Tags) > 0 {
update["$set"].(bson.M)["tags"] = data.Tags
}
if len(data.MediaURL) > 0 {
update["$set"].(bson.M)["media_url"] = data.MediaURL
}
if data.Like != -1 {
update["$set"].(bson.M)["like"] = data.Like
}
if data.DisLike != -1 {
update["$set"].(bson.M)["dislike"] = data.DisLike
}
// UpdateAt 是每次都需要更新的,不用檢查
update["$set"].(bson.M)["updateAt"] = time.Now().UTC().UnixNano()
res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, update)
return res, err
}
// Find 貼文列表
func (m *defaultPostModel) Find(ctx context.Context, param *QueryPostModelReq) ([]*Post, int64, error) {
filter := bson.M{}
// 添加 UID 過濾條件
if len(param.UID) > 0 {
filter["uid"] = bson.M{"$in": param.UID}
}
// 添加 ID 過濾條件
if len(param.Id) > 0 {
var ids []primitive.ObjectID
for _, item := range param.Id {
oid, err := primitive.ObjectIDFromHex(item)
if err != nil {
// log
continue
}
ids = append(ids, oid)
}
filter["_id"] = bson.M{"$in": ids}
}
// 添加 OnlyAds 過濾條件
if param.OnlyAds != nil {
// true 是廣告 false 不是廣告 , 沒寫就是不過率
filter["is_ad"] = *param.OnlyAds
}
// 分頁處理
opts := options.Find()
opts.SetSort(bson.D{{"create_time", -1}})
if param.PageSize > 0 {
opts.SetLimit(param.PageSize)
}
if param.PageIndex > 0 {
opts.SetSkip((param.PageIndex - 1) * param.PageSize)
}
// 計算總數(不考慮分頁)
totalCount, err := m.conn.CountDocuments(ctx, filter)
if err != nil {
return nil, 0, err
}
result := make([]*Post, 0, param.PageSize)
// 執行查詢
err = m.conn.Find(ctx, &result, filter, opts)
if err != nil {
return nil, 0, err
}
switch {
case err == nil:
return result, totalCount, nil
case errors.Is(err, monc.ErrNotFound):
return nil, 0, ErrNotFound
default:
return nil, 0, err
}
}

View File

@ -5,14 +5,12 @@ import (
"context"
"time"
"github.com/zeromicro/go-zero/core/stores/monc"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var prefixPostCacheKey = "cache:post:"
type postModel interface {
Insert(ctx context.Context, data *Post) error
FindOne(ctx context.Context, id string) (*Post, error)
@ -21,22 +19,21 @@ type postModel interface {
}
type defaultPostModel struct {
conn *monc.Model
conn *mon.Model
}
func newDefaultPostModel(conn *monc.Model) *defaultPostModel {
func newDefaultPostModel(conn *mon.Model) *defaultPostModel {
return &defaultPostModel{conn: conn}
}
func (m *defaultPostModel) Insert(ctx context.Context, data *Post) error {
if data.ID.IsZero() {
data.ID = primitive.NewObjectID()
data.CreateAt = time.Now()
data.UpdateAt = time.Now()
data.CreateAt = time.Now().UTC().UnixNano()
data.UpdateAt = time.Now().UTC().UnixNano()
}
key := prefixPostCacheKey + data.ID.Hex()
_, err := m.conn.InsertOne(ctx, key, data)
_, err := m.conn.InsertOne(ctx, data)
return err
}
@ -47,12 +44,12 @@ func (m *defaultPostModel) FindOne(ctx context.Context, id string) (*Post, error
}
var data Post
key := prefixPostCacheKey + id
err = m.conn.FindOne(ctx, key, &data, bson.M{"_id": oid})
err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
switch err {
case nil:
return &data, nil
case monc.ErrNotFound:
case mon.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
@ -60,9 +57,9 @@ func (m *defaultPostModel) FindOne(ctx context.Context, id string) (*Post, error
}
func (m *defaultPostModel) Update(ctx context.Context, data *Post) (*mongo.UpdateResult, error) {
data.UpdateAt = time.Now()
key := prefixPostCacheKey + data.ID.Hex()
res, err := m.conn.UpdateOne(ctx, key, bson.M{"_id": data.ID}, bson.M{"$set": data})
data.UpdateAt = time.Now().UTC().UnixNano()
res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
return res, err
}
@ -71,7 +68,7 @@ func (m *defaultPostModel) Delete(ctx context.Context, id string) (int64, error)
if err != nil {
return 0, ErrInvalidObjectId
}
key := prefixPostCacheKey + id
res, err := m.conn.DeleteOne(ctx, key, bson.M{"_id": oid})
res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
return res, err
}

View File

@ -1,14 +1,32 @@
package model
import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Post struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
// TODO: Fill your own fields
UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
UID string `bson:"uid" json:"uid"` // 搜尋條件
Content string `bson:"content" json:"content"` // 內容
Status int8 `bson:"status" json:"status"` // 1. 等待審核中 , 2 審核通過,預設為 1 ->過濾條件
IsAd bool `bson:"is_ad" json:"is_ad"` // 此則貼文是否為廣告貼文 -> 過濾條件
Tags []string `bson:"tags" json:"tags"` // 本則貼文的標籤,不提供搜尋,僅提供顯示(存名字ID 建立之後就不提供修改與刪除)
MediaURL []Media `bson:"media_url" json:"media_url"` // 網址
Like int64 `bson:"like" json:"like"` // 讚數量
DisLike int64 `bson:"dislike" json:"dislike"` // 不讚數量
UpdateAt int64 `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt int64 `bson:"createAt,omitempty" json:"createAt,omitempty"` // -> 排序條件
}
type Media struct {
Type string // media type jpeg, m3u8 之類的
Links string // 連結的網址
}
func (p *Post) CollectionName() string {
return "post"
}
// 照邏輯,應該需要建立的索引有
// 單列索引UID、Status、IsAd、CreateAt
// 複合索引:(UID + Status + IsAd)、(UID + CreateAt)

View File

@ -1,25 +0,0 @@
package model
import "github.com/zeromicro/go-zero/core/stores/mon"
var _ TagsModel = (*customTagsModel)(nil)
type (
// TagsModel is an interface to be customized, add more methods here,
// and implement the added methods in customTagsModel.
TagsModel interface {
tagsModel
}
customTagsModel struct {
*defaultTagsModel
}
)
// NewTagsModel returns a model for the mongo.
func NewTagsModel(url, db, collection string) TagsModel {
conn := mon.MustNewModel(url, db, collection)
return &customTagsModel{
defaultTagsModel: newDefaultTagsModel(conn),
}
}

View File

@ -1,74 +0,0 @@
// Code generated by goctl. DO NOT EDIT.
package model
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/stores/mon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
type tagsModel interface {
Insert(ctx context.Context, data *Tags) error
FindOne(ctx context.Context, id string) (*Tags, error)
Update(ctx context.Context, data *Tags) (*mongo.UpdateResult, error)
Delete(ctx context.Context, id string) (int64, error)
}
type defaultTagsModel struct {
conn *mon.Model
}
func newDefaultTagsModel(conn *mon.Model) *defaultTagsModel {
return &defaultTagsModel{conn: conn}
}
func (m *defaultTagsModel) Insert(ctx context.Context, data *Tags) error {
if data.ID.IsZero() {
data.ID = primitive.NewObjectID()
data.CreateAt = time.Now()
data.UpdateAt = time.Now()
}
_, err := m.conn.InsertOne(ctx, data)
return err
}
func (m *defaultTagsModel) FindOne(ctx context.Context, id string) (*Tags, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, ErrInvalidObjectId
}
var data Tags
err = m.conn.FindOne(ctx, &data, bson.M{"_id": oid})
switch err {
case nil:
return &data, nil
case mon.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultTagsModel) Update(ctx context.Context, data *Tags) (*mongo.UpdateResult, error) {
data.UpdateAt = time.Now()
res, err := m.conn.UpdateOne(ctx, bson.M{"_id": data.ID}, bson.M{"$set": data})
return res, err
}
func (m *defaultTagsModel) Delete(ctx context.Context, id string) (int64, error) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
return 0, ErrInvalidObjectId
}
res, err := m.conn.DeleteOne(ctx, bson.M{"_id": oid})
return res, err
}

View File

@ -1,14 +0,0 @@
package model
import (
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Tags struct {
ID primitive.ObjectID `bson:"_id,omitempty" json:"id,omitempty"`
// TODO: Fill your own fields
UpdateAt time.Time `bson:"updateAt,omitempty" json:"updateAt,omitempty"`
CreateAt time.Time `bson:"createAt,omitempty" json:"createAt,omitempty"`
}

View File

@ -7,7 +7,7 @@ import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/logic/commentservice"
commentservicelogic "app-cloudep-tweeting-service/internal/logic/commentservice"
"app-cloudep-tweeting-service/internal/svc"
)
@ -23,7 +23,7 @@ func NewCommentServiceServer(svcCtx *svc.ServiceContext) *CommentServiceServer {
}
// NewComment 發表評論
func (s *CommentServiceServer) NewComment(ctx context.Context, in *tweeting.CommentPostReq) (*tweeting.OKResp, error) {
func (s *CommentServiceServer) NewComment(ctx context.Context, in *tweeting.CommentPostReq) (*tweeting.CommentPostResp, error) {
l := commentservicelogic.NewNewCommentLogic(ctx, s.svcCtx)
return l.NewComment(in)
}
@ -45,27 +45,3 @@ func (s *CommentServiceServer) UpdateComment(ctx context.Context, in *tweeting.U
l := commentservicelogic.NewUpdateCommentLogic(ctx, s.svcCtx)
return l.UpdateComment(in)
}
// LikeComment 點讚/取消讚 評論
func (s *CommentServiceServer) LikeComment(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) {
l := commentservicelogic.NewLikeCommentLogic(ctx, s.svcCtx)
return l.LikeComment(in)
}
// GetLikeStatus 取得讚/不讚評論狀態
func (s *CommentServiceServer) GetLikeStatus(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) {
l := commentservicelogic.NewGetLikeStatusLogic(ctx, s.svcCtx)
return l.GetLikeStatus(in)
}
// LikeList 取得讚/不讚評論列表
func (s *CommentServiceServer) LikeList(ctx context.Context, in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) {
l := commentservicelogic.NewLikeListLogic(ctx, s.svcCtx)
return l.LikeList(in)
}
// CountLike 取得讚/不讚評論數量
func (s *CommentServiceServer) CountLike(ctx context.Context, in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) {
l := commentservicelogic.NewCountLikeLogic(ctx, s.svcCtx)
return l.CountLike(in)
}

View File

@ -7,7 +7,7 @@ import (
"context"
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/logic/postservice"
postservicelogic "app-cloudep-tweeting-service/internal/logic/postservice"
"app-cloudep-tweeting-service/internal/svc"
)
@ -22,10 +22,10 @@ func NewPostServiceServer(svcCtx *svc.ServiceContext) *PostServiceServer {
}
}
// NewPost 新增貼文
func (s *PostServiceServer) NewPost(ctx context.Context, in *tweeting.NewPostReq) (*tweeting.PostResp, error) {
l := postservicelogic.NewNewPostLogic(ctx, s.svcCtx)
return l.NewPost(in)
// CreatePost 新增貼文
func (s *PostServiceServer) CreatePost(ctx context.Context, in *tweeting.NewPostReq) (*tweeting.PostResp, error) {
l := postservicelogic.NewCreatePostLogic(ctx, s.svcCtx)
return l.CreatePost(in)
}
// DeletePost 刪除貼文
@ -45,27 +45,3 @@ func (s *PostServiceServer) ListPosts(ctx context.Context, in *tweeting.QueryPos
l := postservicelogic.NewListPostsLogic(ctx, s.svcCtx)
return l.ListPosts(in)
}
// Like 點讚/取消讚 貼文
func (s *PostServiceServer) Like(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) {
l := postservicelogic.NewLikeLogic(ctx, s.svcCtx)
return l.Like(in)
}
// GetLikeStatus 取得讚/不讚狀態
func (s *PostServiceServer) GetLikeStatus(ctx context.Context, in *tweeting.LikeReq) (*tweeting.OKResp, error) {
l := postservicelogic.NewGetLikeStatusLogic(ctx, s.svcCtx)
return l.GetLikeStatus(in)
}
// LikeList 取得讚/不讚列表
func (s *PostServiceServer) LikeList(ctx context.Context, in *tweeting.LikeListReq) (*tweeting.LikeListResp, error) {
l := postservicelogic.NewLikeListLogic(ctx, s.svcCtx)
return l.LikeList(in)
}
// CountLike 取得讚/不讚數量
func (s *PostServiceServer) CountLike(ctx context.Context, in *tweeting.LikeCountReq) (*tweeting.LikeCountResp, error) {
l := postservicelogic.NewCountLikeLogic(ctx, s.svcCtx)
return l.CountLike(in)
}

View File

@ -0,0 +1,27 @@
package svc
import (
"app-cloudep-tweeting-service/internal/config"
model "app-cloudep-tweeting-service/internal/model/mongo"
"fmt"
)
func mustMongoConnectUrl(c config.Config) string {
return fmt.Sprintf("%s://%s:%s",
c.Mongo.Schema,
c.Mongo.Host,
c.Mongo.Port,
)
}
// TODO 思考快取做在那邊
func MustPostModel(c config.Config) model.PostModel {
postCollection := model.Post{}
return model.NewPostModel(mustMongoConnectUrl(c), c.Mongo.Database, postCollection.CollectionName())
}
func MustCommentModel(c config.Config) model.CommentModel {
m := model.Comment{}
return model.NewCommentModel(mustMongoConnectUrl(c), c.Mongo.Database, m.CollectionName())
}

View File

@ -1,13 +1,25 @@
package svc
import "app-cloudep-tweeting-service/internal/config"
import (
"app-cloudep-tweeting-service/internal/config"
model "app-cloudep-tweeting-service/internal/model/mongo"
vi "code.30cm.net/digimon/library-go/validator"
)
type ServiceContext struct {
Config config.Config
Config config.Config
Validate vi.Validate
PostModel model.PostModel
CommentModel model.CommentModel
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
Config: c,
Validate: vi.MustValidator(),
PostModel: MustPostModel(c),
CommentModel: MustCommentModel(c),
}
}

View File

@ -6,7 +6,6 @@ import (
"app-cloudep-tweeting-service/gen_result/pb/tweeting"
"app-cloudep-tweeting-service/internal/config"
commentserviceServer "app-cloudep-tweeting-service/internal/server/commentservice"
postserviceServer "app-cloudep-tweeting-service/internal/server/postservice"
"app-cloudep-tweeting-service/internal/svc"
@ -28,7 +27,6 @@ func main() {
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
tweeting.RegisterPostServiceServer(grpcServer, postserviceServer.NewPostServiceServer(ctx))
tweeting.RegisterCommentServiceServer(grpcServer, commentserviceServer.NewCommentServiceServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)