Handlers, DB, Cache

This commit is contained in:
2026-03-10 19:17:43 +03:00
parent e56b1f1305
commit 968c030939
23 changed files with 566 additions and 34 deletions

39
internal/service/cache/cache_service.go vendored Normal file
View File

@@ -0,0 +1,39 @@
package cache
import (
"time"
"github.com/jellydator/ttlcache/v3"
"payouts/internal/service/database/orm"
)
type cacheService struct {
cache *ttlcache.Cache[string, orm.User]
}
func NewSessionCache(ttl time.Duration) (Service, error) {
return &cacheService{
cache: ttlcache.New(ttlcache.WithTTL[string, orm.User](ttl)),
}, nil
}
// PutSession implements [Service].
func (c *cacheService) PutSession(sessionID string, user orm.User) {
c.cache.Set(sessionID, user, ttlcache.DefaultTTL)
}
// GetUserFromSession implements [Service].
func (c *cacheService) GetSession(sessionId string) (orm.User, error) {
if !c.cache.Has(sessionId) {
return orm.User{}, NoSessionFound
}
cachedItem := c.cache.Get(sessionId)
return cachedItem.Value(), nil
}
// StartBackground implements [Service].
func (c *cacheService) StartBackground() {
c.cache.Start()
}

View File

@@ -0,0 +1,7 @@
package config
import "time"
type Cache struct {
TTL time.Duration
}

50
internal/service/cache/module.go vendored Normal file
View File

@@ -0,0 +1,50 @@
package cache
import (
"context"
"errors"
"go.uber.org/fx"
"payouts/internal/config"
"payouts/internal/service/database/orm"
)
var Module = fx.Options(
fx.Provide(New),
fx.Invoke(StartCache),
)
var NoSessionFound = errors.New("no session found")
type Service interface {
PutSession(string, orm.User)
GetSession(string) (orm.User, error)
StartBackground()
}
type Params struct {
fx.In
AppConfig *config.App
}
func New(p Params) (Service, error) {
return NewSessionCache(p.AppConfig.Cache.TTL)
}
// RegisterRoutes registers the api routes and starts the http server
func StartCache(s Service, lc fx.Lifecycle) {
lc.Append(fx.Hook{
OnStart: func(c context.Context) error {
go func() {
s.StartBackground()
}()
return nil
},
OnStop: func(ctx context.Context) error {
return nil
},
})
}

View File

@@ -1,7 +1,8 @@
package config
type Database struct {
Type string
Connection string
LogLevel string
Type string
Connection string
LogLevel string
TraceRequests bool
}

View File

@@ -1,12 +1,17 @@
package database
import (
"context"
"errors"
"log/slog"
slogGorm "github.com/orandin/slog-gorm"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"payouts/internal/service/database/config"
"payouts/internal/service/database/orm"
)
type dbService struct {
@@ -14,31 +19,67 @@ type dbService struct {
db *gorm.DB
}
func NewDatabaseService(dbType, connection, logLevel string) (DatabaseService, error) {
func NewDatabaseService(conf config.Database) (Service, error) {
var dialector gorm.Dialector
switch dbType {
switch conf.Type {
case "sqlite":
dialector = sqlite.Open(connection)
dialector = sqlite.Open(conf.Connection)
case "postgres":
dialector = postgres.Open(connection)
dialector = postgres.Open(conf.Connection)
default:
return nil, errors.New("unknown dbType")
}
level := slog.LevelInfo
level.UnmarshalText([]byte(conf.LogLevel))
slogGormOpts := []slogGorm.Option{
slogGorm.SetLogLevel(slogGorm.DefaultLogType, level),
}
if conf.TraceRequests {
slogGormOpts = append(slogGormOpts, slogGorm.WithTraceAll())
}
slogGorm := slogGorm.New(slogGormOpts...)
db, err := gorm.Open(dialector, &gorm.Config{
Logger: slogGorm.New(),
Logger: slogGorm,
})
if err == nil {
db.DB()
db.AutoMigrate()
// db.LogMode(true)
db.AutoMigrate(&orm.User{})
}
result := &dbService{}
result.dbType = dbType
result.dbType = conf.Type
result.db = db
return result, err
}
func getParams(options ...Optional) *params {
params := &params{
ctx: context.Background(),
}
for _, opt := range options {
opt(params)
}
return params
}
// AddUser implements [Service].
func (d *dbService) CreateUser(userModel orm.User, opts ...Optional) error {
p := getParams(opts...)
return gorm.G[orm.User](d.db).Create(p.ctx, &userModel)
}
// GetUser implements [Service].
func (d *dbService) GetUser(userModel orm.User, opts ...Optional) (orm.User, error) {
p := getParams(opts...)
userResp, err := gorm.G[orm.User](d.db).Where(&userModel).First(p.ctx)
return userResp, err
}

View File

@@ -1,16 +1,32 @@
package database
import (
"payouts/internal/config"
"context"
"go.uber.org/fx"
"payouts/internal/config"
"payouts/internal/service/database/orm"
)
var Module = fx.Options(
fx.Provide(New),
)
type DatabaseService interface {
type params struct {
ctx context.Context
}
type Optional func(*params)
func WithContext(ctx context.Context) Optional {
return func(p *params) {
p.ctx = ctx
}
}
type Service interface {
CreateUser(user orm.User, opts ...Optional) error
GetUser(user orm.User, opts ...Optional) (orm.User, error)
}
// Params represents the module input params
@@ -21,6 +37,6 @@ type Params struct {
}
// NewPersistence instantiates the persistence module
func New(p Params) (DatabaseService, error) {
return NewDatabaseService(p.AppConfig.Database.Type, p.AppConfig.Database.Connection, p.AppConfig.Database.LogLevel)
func New(p Params) (Service, error) {
return NewDatabaseService(p.AppConfig.Database)
}

View File

@@ -0,0 +1,10 @@
package orm
import "gorm.io/gorm"
type User struct {
gorm.Model
TIN string
Phone string `gorm:"uniqueIndex:idx_phone"`
PasswdHash string
}

View File

@@ -0,0 +1,10 @@
package config
type YooKassa struct {
BaseUrl string
ApiBaseKey string
ApiBaseSecret string
ApiPaymentKey string
ApiPaymentSecret string
}

View File

@@ -0,0 +1,37 @@
package yookassa
import (
"context"
"go.uber.org/fx"
"payouts/internal/config"
)
var Module = fx.Options(
fx.Provide(New),
)
type params struct {
ctx context.Context
}
type Optional func(*params)
func WithContext(ctx context.Context) Optional {
return func(p *params) {
p.ctx = ctx
}
}
type Service interface {
}
type Param struct {
fx.In
AppConfig *config.App
}
func New(p Param) (Service, error) {
return NewYookassaService(p.AppConfig.YooKassa)
}

View File

@@ -0,0 +1,13 @@
package yookassa
import "payouts/internal/service/yookassa/config"
type yookassaService struct {
conf config.YooKassa
}
func NewYookassaService(conf config.YooKassa) (Service, error) {
return &yookassaService{
conf: conf,
}, nil
}