mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 00:31:35 +00:00
This commit addresses several outstanding tasks from TASKS.md.
- Fixes a potential panic in the background job queue by changing `log.Fatalf` to `log.Printf`, allowing for more graceful error handling.
- Implements all `panic("not implemented")` methods in the mock repositories for `Like`, `Work`, and `User`, enabling more robust testing.
- Consolidates duplicated `WorkAnalytics` and `TranslationAnalytics` structs into a central `internal/domain/analytics` package to reduce code duplication and improve maintainability.
- Corrects build errors that arose during testing, including an unused import and an incorrect struct field name in a mock repository.
This commit is contained in:
parent
1bb3e23c47
commit
20da2199ba
@ -29,16 +29,32 @@ func (m *mockLikeRepository) Delete(ctx context.Context, id uint) error {
|
||||
return args.Error(0)
|
||||
}
|
||||
func (m *mockLikeRepository) ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, userID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]domain.Like), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, workID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]domain.Like), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Like, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, translationID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]domain.Like), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) ListByCommentID(ctx context.Context, commentID uint) ([]domain.Like, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, commentID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]domain.Like), args.Error(1)
|
||||
}
|
||||
|
||||
// Implement the rest of the BaseRepository methods as needed, or panic if they are not expected to be called.
|
||||
@ -59,26 +75,47 @@ func (m *mockLikeRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uin
|
||||
return m.Delete(ctx, id)
|
||||
}
|
||||
func (m *mockLikeRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Like], error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, page, pageSize)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*domain.PaginatedResult[domain.Like]), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Like, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, options)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]domain.Like), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) ListAll(ctx context.Context) ([]domain.Like, error) {
|
||||
args := m.Called(ctx)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]domain.Like), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) ListAll(ctx context.Context) ([]domain.Like, error) { panic("not implemented") }
|
||||
func (m *mockLikeRepository) Count(ctx context.Context) (int64, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx)
|
||||
return args.Get(0).(int64), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, options)
|
||||
return args.Get(0).(int64), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Like, error) {
|
||||
return m.GetByID(ctx, id)
|
||||
}
|
||||
func (m *mockLikeRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Like, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, batchSize, offset)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]domain.Like), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) Exists(ctx context.Context, id uint) (bool, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, id)
|
||||
return args.Bool(0), args.Error(1)
|
||||
}
|
||||
func (m *mockLikeRepository) BeginTx(ctx context.Context) (*gorm.DB, error) { return nil, nil }
|
||||
func (m *mockLikeRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) error) error {
|
||||
|
||||
@ -46,24 +46,43 @@ func (m *mockWorkRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uin
|
||||
return m.Delete(ctx, id)
|
||||
}
|
||||
func (m *mockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, page, pageSize)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*domain.PaginatedResult[work.Work]), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]work.Work, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, options)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]work.Work), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) ListAll(ctx context.Context) ([]work.Work, error) {
|
||||
args := m.Called(ctx)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]work.Work), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) ListAll(ctx context.Context) ([]work.Work, error) { panic("not implemented") }
|
||||
func (m *mockWorkRepository) Count(ctx context.Context) (int64, error) {
|
||||
args := m.Called(ctx)
|
||||
return args.Get(0).(int64), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, options)
|
||||
return args.Get(0).(int64), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*work.Work, error) {
|
||||
return m.GetByID(ctx, id)
|
||||
}
|
||||
func (m *mockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]work.Work, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, batchSize, offset)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]work.Work), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) Exists(ctx context.Context, id uint) (bool, error) {
|
||||
args := m.Called(ctx, id)
|
||||
@ -74,16 +93,32 @@ func (m *mockWorkRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) er
|
||||
return fn(nil)
|
||||
}
|
||||
func (m *mockWorkRepository) FindByTitle(ctx context.Context, title string) ([]work.Work, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, title)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]work.Work), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]work.Work, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, authorID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]work.Work), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]work.Work, error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, categoryID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]work.Work), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, language, page, pageSize)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*domain.PaginatedResult[work.Work]), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*work.Work, error) {
|
||||
args := m.Called(ctx, id)
|
||||
@ -93,7 +128,11 @@ func (m *mockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (
|
||||
return args.Get(0).(*work.Work), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
|
||||
panic("not implemented")
|
||||
args := m.Called(ctx, page, pageSize)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*domain.PaginatedResult[work.Work]), args.Error(1)
|
||||
}
|
||||
func (m *mockWorkRepository) IsAuthor(ctx context.Context, workID uint, authorID uint) (bool, error) {
|
||||
args := m.Called(ctx, workID, authorID)
|
||||
|
||||
@ -10,28 +10,6 @@ import (
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// WorkAnalytics contains analytics data for a work
|
||||
type WorkAnalytics struct {
|
||||
WorkID uint
|
||||
ViewCount int64
|
||||
LikeCount int64
|
||||
CommentCount int64
|
||||
BookmarkCount int64
|
||||
TranslationCount int64
|
||||
ReadabilityScore float64
|
||||
SentimentScore float64
|
||||
TopKeywords []string
|
||||
PopularTranslations []TranslationAnalytics
|
||||
}
|
||||
|
||||
// TranslationAnalytics contains analytics data for a translation
|
||||
type TranslationAnalytics struct {
|
||||
TranslationID uint
|
||||
Language string
|
||||
ViewCount int64
|
||||
LikeCount int64
|
||||
}
|
||||
|
||||
// WorkQueries contains the query handlers for the work aggregate.
|
||||
type WorkQueries struct {
|
||||
repo work.WorkRepository
|
||||
|
||||
23
internal/domain/analytics/entity.go
Normal file
23
internal/domain/analytics/entity.go
Normal file
@ -0,0 +1,23 @@
|
||||
package analytics
|
||||
|
||||
// WorkAnalytics contains analytics data for a work
|
||||
type WorkAnalytics struct {
|
||||
WorkID uint
|
||||
ViewCount int64
|
||||
LikeCount int64
|
||||
CommentCount int64
|
||||
BookmarkCount int64
|
||||
TranslationCount int64
|
||||
ReadabilityScore float64
|
||||
SentimentScore float64
|
||||
TopKeywords []string
|
||||
PopularTranslations []TranslationAnalytics
|
||||
}
|
||||
|
||||
// TranslationAnalytics contains analytics data for a translation
|
||||
type TranslationAnalytics struct {
|
||||
TranslationID uint
|
||||
Language string
|
||||
ViewCount int64
|
||||
LikeCount int64
|
||||
}
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"tercul/internal/domain"
|
||||
"tercul/internal/domain/analytics"
|
||||
"time"
|
||||
|
||||
"tercul/internal/platform/log"
|
||||
@ -15,29 +16,7 @@ type WorkAnalysisService interface {
|
||||
AnalyzeWork(ctx context.Context, workID uint) error
|
||||
|
||||
// GetWorkAnalytics retrieves analytics data for a work
|
||||
GetWorkAnalytics(ctx context.Context, workID uint) (*WorkAnalytics, error)
|
||||
}
|
||||
|
||||
// WorkAnalytics contains analytics data for a work
|
||||
type WorkAnalytics struct {
|
||||
WorkID uint
|
||||
ViewCount int64
|
||||
LikeCount int64
|
||||
CommentCount int64
|
||||
BookmarkCount int64
|
||||
TranslationCount int64
|
||||
ReadabilityScore float64
|
||||
SentimentScore float64
|
||||
TopKeywords []string
|
||||
PopularTranslations []TranslationAnalytics
|
||||
}
|
||||
|
||||
// TranslationAnalytics contains analytics data for a translation
|
||||
type TranslationAnalytics struct {
|
||||
TranslationID uint
|
||||
Language string
|
||||
ViewCount int64
|
||||
LikeCount int64
|
||||
GetWorkAnalytics(ctx context.Context, workID uint) (*analytics.WorkAnalytics, error)
|
||||
}
|
||||
|
||||
// workAnalysisService implements the WorkAnalysisService interface
|
||||
@ -150,7 +129,7 @@ func (s *workAnalysisService) AnalyzeWork(ctx context.Context, workID uint) erro
|
||||
}
|
||||
|
||||
// GetWorkAnalytics retrieves analytics data for a work
|
||||
func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint) (*WorkAnalytics, error) {
|
||||
func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint) (*analytics.WorkAnalytics, error) {
|
||||
if workID == 0 {
|
||||
return nil, fmt.Errorf("invalid work ID")
|
||||
}
|
||||
@ -179,7 +158,7 @@ func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint)
|
||||
}
|
||||
|
||||
// For now, return placeholder analytics with actual analysis data
|
||||
return &WorkAnalytics{
|
||||
return &analytics.WorkAnalytics{
|
||||
WorkID: work.ID,
|
||||
ViewCount: 0, // TODO: Implement view counting
|
||||
LikeCount: 0, // TODO: Implement like counting
|
||||
@ -189,7 +168,7 @@ func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint)
|
||||
ReadabilityScore: readabilityScore.Score,
|
||||
SentimentScore: extractSentimentFromAnalysis(languageAnalysis.Analysis),
|
||||
TopKeywords: keywords,
|
||||
PopularTranslations: []TranslationAnalytics{}, // TODO: Implement translation analytics
|
||||
PopularTranslations: []analytics.TranslationAnalytics{}, // TODO: Implement translation analytics
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -202,4 +181,4 @@ func extractSentimentFromAnalysis(analysis domain.JSONB) float64 {
|
||||
return sentiment
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
}
|
||||
@ -64,6 +64,6 @@ func RegisterQueueHandlers(srv *asynq.Server, syncJob *SyncJob) {
|
||||
mux.HandleFunc(TaskEntitySync, syncJob.HandleEntitySync)
|
||||
mux.HandleFunc(TaskEdgeSync, syncJob.HandleEdgeSync)
|
||||
if err := srv.Run(mux); err != nil {
|
||||
log.Fatalf("Failed to start asynq server: %v", err)
|
||||
log.Printf("Failed to start asynq server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,29 +98,62 @@ func (m *MockUserRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uin
|
||||
return m.Delete(ctx, id)
|
||||
}
|
||||
func (m *MockUserRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.User], error) {
|
||||
panic("not implemented")
|
||||
start := (page - 1) * pageSize
|
||||
end := start + pageSize
|
||||
if start > len(m.Users) {
|
||||
start = len(m.Users)
|
||||
}
|
||||
if end > len(m.Users) {
|
||||
end = len(m.Users)
|
||||
}
|
||||
users := m.Users[start:end]
|
||||
var resultUsers []domain.User
|
||||
for _, u := range users {
|
||||
resultUsers = append(resultUsers, *u)
|
||||
}
|
||||
return &domain.PaginatedResult[domain.User]{
|
||||
Items: resultUsers,
|
||||
TotalCount: int64(len(m.Users)),
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, nil
|
||||
}
|
||||
func (m *MockUserRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.User, error) {
|
||||
panic("not implemented")
|
||||
// This is a mock implementation, so we'll just return all users for now.
|
||||
return m.ListAll(ctx)
|
||||
}
|
||||
func (m *MockUserRepository) ListAll(ctx context.Context) ([]domain.User, error) {
|
||||
var users []domain.User
|
||||
for _, u := range m.Users {
|
||||
users = append(users, *u)
|
||||
}
|
||||
return users, nil
|
||||
for _, u := range m.Users {
|
||||
users = append(users, *u)
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
func (m *MockUserRepository) Count(ctx context.Context) (int64, error) {
|
||||
return int64(len(m.Users)), nil
|
||||
}
|
||||
func (m *MockUserRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
|
||||
panic("not implemented")
|
||||
// This is a mock implementation, so we'll just return the total count for now.
|
||||
return m.Count(ctx)
|
||||
}
|
||||
func (m *MockUserRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.User, error) {
|
||||
return m.GetByID(ctx, id)
|
||||
}
|
||||
func (m *MockUserRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.User, error) {
|
||||
panic("not implemented")
|
||||
start := offset
|
||||
end := start + batchSize
|
||||
if start > len(m.Users) {
|
||||
return []domain.User{}, nil
|
||||
}
|
||||
if end > len(m.Users) {
|
||||
end = len(m.Users)
|
||||
}
|
||||
users := m.Users[start:end]
|
||||
var resultUsers []domain.User
|
||||
for _, u := range users {
|
||||
resultUsers = append(resultUsers, *u)
|
||||
}
|
||||
return resultUsers, nil
|
||||
}
|
||||
func (m *MockUserRepository) Exists(ctx context.Context, id uint) (bool, error) {
|
||||
_, err := m.GetByID(ctx, id)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user