refactor
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
aovantsev
2025-10-10 19:56:06 +03:00
parent f081c9d947
commit 73ed514a34
30 changed files with 1728 additions and 1020 deletions

View File

@@ -0,0 +1,60 @@
package entities
import "time"
// Counter represents a counter entity
type Counter struct {
ID int `json:"id" db:"id"`
UserID int `json:"user_id" db:"user_id"`
Name string `json:"name" db:"name"`
Description string `json:"description" db:"description"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// CounterEntry represents a single increment/decrement entry
type CounterEntry struct {
ID int `json:"id" db:"id"`
CounterID int `json:"counter_id" db:"counter_id"`
Value int `json:"value" db:"value"` // +1 for increment, -1 for decrement
Date time.Time `json:"date" db:"date"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// CounterWithStats represents a counter with aggregated statistics
type CounterWithStats struct {
Counter
TotalValue int `json:"total_value"`
TodayValue int `json:"today_value"`
WeekValue int `json:"week_value"`
MonthValue int `json:"month_value"`
EntryCount int `json:"entry_count"`
}
// DailyStat represents daily statistics for a counter
type DailyStat struct {
Date time.Time `json:"date"`
Total int `json:"total"`
}
// Validate validates counter data
func (c *Counter) Validate() error {
if c.Name == "" {
return ErrInvalidCounterName
}
if c.UserID <= 0 {
return ErrInvalidUserID
}
return nil
}
// Validate validates counter entry data
func (ce *CounterEntry) Validate() error {
if ce.CounterID <= 0 {
return ErrInvalidCounterID
}
if ce.Value == 0 {
return ErrInvalidEntryValue
}
return nil
}

View File

@@ -0,0 +1,19 @@
package entities
import "errors"
// Domain errors
var (
ErrInvalidUsername = errors.New("username is required")
ErrInvalidEmail = errors.New("email is required")
ErrInvalidPassword = errors.New("password is required")
ErrInvalidCounterName = errors.New("counter name is required")
ErrInvalidUserID = errors.New("invalid user ID")
ErrInvalidCounterID = errors.New("invalid counter ID")
ErrInvalidEntryValue = errors.New("entry value cannot be zero")
ErrUserNotFound = errors.New("user not found")
ErrCounterNotFound = errors.New("counter not found")
ErrCounterEntryNotFound = errors.New("counter entry not found")
ErrUserAlreadyExists = errors.New("user already exists")
ErrInvalidCredentials = errors.New("invalid credentials")
)

View File

@@ -0,0 +1,32 @@
package entities
import "time"
// User represents a registered user
type User struct {
ID int `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Email string `json:"email" db:"email"`
Password string `json:"-" db:"password"` // Hidden from JSON
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// Validate validates user data
func (u *User) Validate() error {
if u.Username == "" {
return ErrInvalidUsername
}
if u.Email == "" {
return ErrInvalidEmail
}
if u.Password == "" {
return ErrInvalidPassword
}
return nil
}
// ClearPassword removes password from user for safe serialization
func (u *User) ClearPassword() {
u.Password = ""
}

View File

@@ -0,0 +1,20 @@
package repositories
import (
"context"
"counter/internal/domain/entities"
"time"
)
// CounterRepository defines the interface for counter data operations
type CounterRepository interface {
Create(ctx context.Context, counter *entities.Counter) error
FindByID(ctx context.Context, id, userID int) (*entities.CounterWithStats, error)
FindByUserID(ctx context.Context, userID int, search string) ([]*entities.CounterWithStats, error)
Update(ctx context.Context, counter *entities.Counter) error
Delete(ctx context.Context, id, userID int) error
AddEntry(ctx context.Context, entry *entities.CounterEntry) error
GetEntries(ctx context.Context, counterID, userID int, startDate, endDate *time.Time) ([]*entities.CounterEntry, error)
GetDailyStats(ctx context.Context, counterID, userID int, days int) ([]*entities.DailyStat, error)
Exists(ctx context.Context, id, userID int) (bool, error)
}

View File

@@ -0,0 +1,16 @@
package repositories
import (
"context"
"counter/internal/domain/entities"
)
// UserRepository defines the interface for user data operations
type UserRepository interface {
Create(ctx context.Context, user *entities.User) error
FindByID(ctx context.Context, id int) (*entities.User, error)
FindByUsername(ctx context.Context, username string) (*entities.User, error)
FindByEmail(ctx context.Context, email string) (*entities.User, error)
Update(ctx context.Context, user *entities.User) error
Delete(ctx context.Context, id int) error
}