Merge remote-tracking branch 'origin/2servers' into staging

This commit is contained in:
skeris 2024-07-21 21:39:29 +03:00
commit d93ac3785b
27 changed files with 469 additions and 305 deletions

@ -3,8 +3,10 @@ JWT_ISSUER="pena-auth-service"
JWT_AUDIENCE="pena"
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm69\n80fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6B\ndA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y\n+3GyaOY536H47qyXAgMBAAE=\n-----END PUBLIC KEY-----"
HTTP_HOST=0.0.0.0
HTTP_PORT=8082
EXTERNAL_HTTP_HOST=0.0.0.0
EXTERNAL_HTTP_PORT=8082
INTERNAL_HTTP_HOST=0.0.0.0
INTERNAL_HTTP_PORT=8083
GRPC_HOST=0.0.0.0
GRPC_PORT=9001

@ -118,7 +118,7 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/accounts:
/getList:
get:
tags:
- account

@ -169,14 +169,22 @@ func Run(config *models.Config, logger *zap.Logger, build Build) (appErr error)
MiddleWare: middleWare,
})
serverHTTP := server.NewServer(server.ServerConfig{
serverClientHTTP := server.NewServer(server.ServerConfig{
Logger: logger,
Hlog: loggerHlog,
Controllers: []server.Controller{httpControllers.CurrencyController, httpControllers.HistoryController, httpControllers.CartController, httpControllers.WalletController, httpControllers.AccountController},
Controllers: []server.Controller{httpControllers.CurrencyClientController, httpControllers.HistoryClientController, httpControllers.CartClientController, httpControllers.WalletClientController, httpControllers.AccountClientController},
JWTConfig: &config.Service.JWT,
})
serverHTTP.ListRoutes()
serverAdminHTTP := server.NewServer(server.ServerConfig{
Logger: logger,
Hlog: loggerHlog,
Controllers: []server.Controller{httpControllers.AccountAdminController, httpControllers.CurrencyAdminController, httpControllers.HistoryAdminController},
JWTConfig: &config.Service.JWT,
})
serverClientHTTP.ListRoutes()
serverAdminHTTP.ListRoutes()
serverGRPC, grpcErr := server.NewGRPC(server.DepsGRPC{Logger: logger})
if grpcErr != nil {
@ -186,8 +194,15 @@ func Run(config *models.Config, logger *zap.Logger, build Build) (appErr error)
serverGRPC.Register(rpcControllers)
go func() {
if err := serverHTTP.Start(config.HTTP.Host + ":" + config.HTTP.Port); err != nil {
logger.Error("Server startup error", zap.Error(err))
if err := serverClientHTTP.Start(config.HTTP.ClientHost + ":" + config.HTTP.ClientPort); err != nil {
logger.Error("Server external startup error", zap.Error(err))
cancel()
}
}()
go func() {
if err := serverAdminHTTP.Start(config.HTTP.AdminHost + ":" + config.HTTP.AdminPort); err != nil {
logger.Error("Server internal startup error", zap.Error(err))
cancel()
}
}()
@ -195,7 +210,8 @@ func Run(config *models.Config, logger *zap.Logger, build Build) (appErr error)
go serverGRPC.Run(&config.GRPC)
closer.Add(mongoDB.Client().Disconnect)
closer.Add(serverHTTP.Shutdown)
closer.Add(serverClientHTTP.Shutdown)
closer.Add(serverAdminHTTP.Shutdown)
closer.Add(serverGRPC.Stop)
closer.Add(closer.Wrap(kafkaTariffClient.Close))

@ -62,8 +62,10 @@ func setDefaultTestingENV(t *testing.T) *models.Config {
defaultConfiguration := models.Config{
HTTP: models.ConfigurationHTTP{
Host: "localhost",
Port: "8080",
ClientHost: "localhost",
ClientPort: "8080",
AdminHost: "localhost",
AdminPort: "8081",
},
GRPC: models.ConfigurationGRPC{
Host: "localhost",

@ -8,11 +8,14 @@ import (
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/grpc/customer"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/grpc/payment"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/account"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/cart"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/currency"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/history"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/wallet"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/account_admin"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/account_client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/cart_client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/currency_admin"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/currency_client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/history_admin"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/history_client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http/wallet_client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
@ -20,7 +23,7 @@ type RpcControllersDeps struct {
Logger *zap.Logger
Services *Services
Repositories *Repositories
HLogger hlog.Logger
HLogger hlog.Logger
}
type RpcControllers struct {
@ -54,23 +57,32 @@ type HttpControllersDeps struct {
}
type HttpController struct {
AccountController *account.AccountController
CartController *cart.CartController
HistoryController *history.HistoryController
WalletController *wallet.WalletController
CurrencyController *currency.CurrencyController
AccountClientController *account_client.AccountController
AccountAdminController *account_admin.AccountInternalController
CartClientController *cart_client.CartController
CurrencyClientController *currency_client.CurrencyController
CurrencyAdminController *currency_admin.CurrencyController
HistoryAdminController *history_admin.HistoryController
HistoryClientController *history_client.HistoryController
WalletClientController *wallet_client.WalletController
}
func NewHttpControllers(deps HttpControllersDeps) *HttpController {
return &HttpController{
AccountController: account.NewAccountController(account.Deps{
AccountClientController: account_client.NewAccountController(account_client.Deps{
MiddleWare: deps.MiddleWare,
AccountRepo: deps.Repositories.AccountRepository,
Logger: deps.Logger,
Encrypt: deps.Encrypt,
AuthClient: deps.Clients.AuthClient,
}),
CartController: cart.NewCartController(cart.Deps{
AccountAdminController: account_admin.NewAccountInternalController(
account_admin.Deps{
MiddleWare: deps.MiddleWare,
AccountRepo: deps.Repositories.AccountRepository,
Logger: deps.Logger,
}),
CartClientController: cart_client.NewCartController(cart_client.Deps{
MiddleWare: deps.MiddleWare,
Logger: deps.Logger,
AccountRepo: deps.Repositories.AccountRepository,
@ -80,7 +92,17 @@ func NewHttpControllers(deps HttpControllersDeps) *HttpController {
CurrencyClient: deps.Clients.CurrencyClient,
Producer: deps.Producer,
}),
HistoryController: history.NewHistoryController(history.Deps{
CurrencyClientController: currency_client.NewCurrencyController(currency_client.Deps{
CurrencyRepo: deps.Repositories.CurrencyRepository,
MiddleWare: deps.MiddleWare,
Logger: deps.Logger,
}),
CurrencyAdminController: currency_admin.NewCurrencyController(currency_admin.Deps{
CurrencyRepo: deps.Repositories.CurrencyRepository,
MiddleWare: deps.MiddleWare,
Logger: deps.Logger,
}),
HistoryAdminController: history_admin.NewHistoryController(history_admin.Deps{
MiddleWare: deps.MiddleWare,
HistoryRepo: deps.Repositories.HistoryRepository,
AccountRepo: deps.Repositories.AccountRepository,
@ -90,7 +112,17 @@ func NewHttpControllers(deps HttpControllersDeps) *HttpController {
CodewordClient: deps.Clients.CodewordClient,
Logger: deps.Logger,
}),
WalletController: wallet.NewWalletController(wallet.Deps{
HistoryClientController: history_client.NewHistoryController(history_client.Deps{
MiddleWare: deps.MiddleWare,
HistoryRepo: deps.Repositories.HistoryRepository,
AccountRepo: deps.Repositories.AccountRepository,
VerifyClient: deps.Clients.VerificationClient,
AuthClient: deps.Clients.AuthClient,
TemplateClient: deps.Clients.TemplateClient,
CodewordClient: deps.Clients.CodewordClient,
Logger: deps.Logger,
}),
WalletClientController: wallet_client.NewWalletController(wallet_client.Deps{
MiddleWare: deps.MiddleWare,
AuthClient: deps.Clients.AuthClient,
PaymentClient: deps.Clients.PaymentClient,
@ -101,10 +133,5 @@ func NewHttpControllers(deps HttpControllersDeps) *HttpController {
MailClient: deps.Clients.MailClient,
Logger: deps.Logger,
}),
CurrencyController: currency.NewCurrencyController(currency.Deps{
CurrencyRepo: deps.Repositories.CurrencyRepository,
MiddleWare: deps.MiddleWare,
Logger: deps.Logger,
}),
}
}

@ -4,7 +4,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/twmb/franz-go/pkg/kgo"
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/initialize"
@ -34,22 +33,10 @@ func TestNewControllers(t *testing.T) {
MongoDB: t.Client.Database("test"),
})
brokers := initialize.NewBrokers(initialize.BrokersDeps{
controllers := initialize.NewHttpControllers(initialize.HttpControllersDeps{
Logger: logger,
TariffClient: &kgo.Client{},
})
services := initialize.NewServices(initialize.ServicesDeps{
Logger: logger,
Clients: clients,
Repositories: repositories,
Brokers: brokers,
ConfigurationGRPC: &models.ConfigurationGRPC{Domen: "http://test:8080"},
})
controllers := initialize.NewControllers(initialize.ControllersDeps{
Logger: logger,
Services: services,
Repositories: repositories,
Clients: clients,
})
assert.NotNil(t, controllers)

@ -0,0 +1,99 @@
package account_admin
import (
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"math"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"strconv"
)
type Deps struct {
MiddleWare *http.MiddleWare
AccountRepo *repository.AccountRepository
Logger *zap.Logger
}
type AccountInternalController struct {
middleWare *http.MiddleWare
accountRepo *repository.AccountRepository
logger *zap.Logger
}
func NewAccountInternalController(deps Deps) *AccountInternalController {
return &AccountInternalController{
middleWare: deps.MiddleWare,
accountRepo: deps.AccountRepo,
logger: deps.Logger,
}
}
func (receiver *AccountInternalController) DeleteCurrent(ctx *fiber.Ctx) error {
userID := ctx.Params("userId")
if userID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid format for parameter userId")
}
account, err := receiver.accountRepo.Remove(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountInternalController) GetCurrent(ctx *fiber.Ctx) error {
userID := ctx.Params("userId")
if userID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid format for parameter userId")
}
account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountInternalController) Pagination(ctx *fiber.Ctx) error {
pageStr := ctx.Query("page", "1")
limitStr := ctx.Query("limit", "100")
page, err := strconv.ParseInt(pageStr, 10, 64)
if err != nil || page < 1 {
page = 1
}
limit, err := strconv.ParseInt(limitStr, 10, 64)
if err != nil || limit < 1 {
limit = models.DefaultLimit
} else {
limit = int64(math.Min(float64(limit), float64(models.DefaultLimit)))
}
count, err := receiver.accountRepo.CountAll(ctx.Context())
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
if count == 0 {
response := models.PaginationResponse[models.Account]{TotalPages: 0, Records: []models.Account{}}
return ctx.Status(fiber.StatusOK).JSON(response)
}
totalPages := int64(math.Ceil(float64(count) / float64(limit)))
accounts, err := receiver.accountRepo.FindMany(ctx.Context(), page, limit)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
response := models.PaginationResponse[models.Account]{
TotalPages: totalPages,
Records: accounts,
}
return ctx.Status(fiber.StatusOK).JSON(response)
}

@ -0,0 +1,13 @@
package account_admin
import "github.com/gofiber/fiber/v2"
func (receiver *AccountInternalController) Register(router fiber.Router) {
router.Delete("/account/:userId", receiver.DeleteCurrent)
router.Get("/account/:userId", receiver.GetCurrent)
router.Get("/getList", receiver.Pagination)
}
func (receiver *AccountInternalController) Name() string {
return ""
}

@ -1,4 +1,4 @@
package account
package account_client
import (
"bufio"
@ -7,7 +7,6 @@ import (
"fmt"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"math"
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
qutils "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
@ -15,8 +14,6 @@ import (
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"strconv"
"time"
)
type Deps struct {
@ -181,74 +178,6 @@ func (receiver *AccountController) Create(ctx *fiber.Ctx) error {
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) DeleteCurrent(ctx *fiber.Ctx) error {
userID := ctx.Params("userId")
if userID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid format for parameter userId")
}
account, err := receiver.accountRepo.Remove(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) GetCurrent(ctx *fiber.Ctx) error {
userID := ctx.Params("userId")
if userID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid format for parameter userId")
}
account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) Pagination(ctx *fiber.Ctx) error {
pageStr := ctx.Query("page", "1")
limitStr := ctx.Query("limit", "100")
page, err := strconv.ParseInt(pageStr, 10, 64)
if err != nil || page < 1 {
page = 1
}
limit, err := strconv.ParseInt(limitStr, 10, 64)
if err != nil || limit < 1 {
limit = models.DefaultLimit
} else {
limit = int64(math.Min(float64(limit), float64(models.DefaultLimit)))
}
count, err := receiver.accountRepo.CountAll(ctx.Context())
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
if count == 0 {
response := models.PaginationResponse[models.Account]{TotalPages: 0, Records: []models.Account{}}
return ctx.Status(fiber.StatusOK).JSON(response)
}
totalPages := int64(math.Ceil(float64(count) / float64(limit)))
accounts, err := receiver.accountRepo.FindMany(ctx.Context(), page, limit)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
response := models.PaginationResponse[models.Account]{
TotalPages: totalPages,
Records: accounts,
}
return ctx.Status(fiber.StatusOK).JSON(response)
}
func (receiver *AccountController) AccountPipe(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
@ -271,8 +200,6 @@ func (receiver *AccountController) AccountPipe(ctx *fiber.Ctx) error {
}(cancelCtx)
ctx.Status(fiber.StatusOK).Context().SetBodyStreamWriter(func(w *bufio.Writer) {
pingTicker := time.NewTicker(5 * time.Second)
defer pingTicker.Stop()
for {
select {
case account, ok := <-accountCh:
@ -288,14 +215,6 @@ func (receiver *AccountController) AccountPipe(ctx *fiber.Ctx) error {
if err := w.Flush(); err != nil {
receiver.logger.Error("error flushing", zap.Error(err))
cancel()
receiver.logger.Info("Close connection Account Pipe sse")
return
}
case <-pingTicker.C:
fmt.Fprintf(w, "data: %s\n\n", `{"event": "ping"}`)
if err := w.Flush(); err != nil {
receiver.logger.Error("error sending ping Account Pipe sse, close connection", zap.Error(err))
cancel()
return
}
}

@ -1,4 +1,4 @@
package account
package account_client
import "github.com/gofiber/fiber/v2"
@ -8,10 +8,7 @@ func (receiver *AccountController) Register(router fiber.Router) {
router.Patch("/account", receiver.Update)
router.Post("/account", receiver.Create)
router.Get("/account/pipe", receiver.AccountPipe)
router.Delete("/account/:userId", receiver.DeleteCurrent)
router.Get("/account/:userId", receiver.GetCurrent)
router.Patch("/account/:userId", receiver.SetVerificationStatus)
router.Get("/accounts", receiver.Pagination)
}
func (receiver *AccountController) Name() string {

@ -1,4 +1,4 @@
package cart
package cart_client
import (
"github.com/gofiber/fiber/v2"

@ -1,4 +1,4 @@
package cart
package cart_client
import "github.com/gofiber/fiber/v2"

@ -1,4 +1,4 @@
package currency
package currency_admin
import (
"github.com/gofiber/fiber/v2"
@ -29,19 +29,6 @@ func NewCurrencyController(deps Deps) *CurrencyController {
}
}
func (receiver *CurrencyController) Get(ctx *fiber.Ctx) error {
currencyList, err := receiver.currencyRepo.FindCurrenciesList(ctx.Context(), models.DefaultCurrencyListName)
if err != nil && err.Type() != errors.ErrNotFound {
return receiver.middleWare.ErrorOld(ctx, err)
}
if err != nil && err.Type() == errors.ErrNotFound {
return ctx.Status(fiber.StatusOK).JSON([]string{})
}
return ctx.Status(fiber.StatusOK).JSON(currencyList)
}
func (receiver *CurrencyController) Update(ctx *fiber.Ctx) error {
var req struct {
items []string

@ -1,9 +1,8 @@
package currency
package currency_admin
import "github.com/gofiber/fiber/v2"
func (receiver *CurrencyController) Register(router fiber.Router) {
router.Get("/currencies", receiver.Get)
router.Put("/currencies", receiver.Update)
}

@ -0,0 +1,43 @@
package currency_client
import (
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
type Deps struct {
CurrencyRepo *repository.CurrencyRepository
MiddleWare *http.MiddleWare
Logger *zap.Logger
}
type CurrencyController struct {
currencyRepo *repository.CurrencyRepository
middleWare *http.MiddleWare
logger *zap.Logger
}
func NewCurrencyController(deps Deps) *CurrencyController {
return &CurrencyController{
currencyRepo: deps.CurrencyRepo,
middleWare: deps.MiddleWare,
logger: deps.Logger,
}
}
func (receiver *CurrencyController) Get(ctx *fiber.Ctx) error {
currencyList, err := receiver.currencyRepo.FindCurrenciesList(ctx.Context(), models.DefaultCurrencyListName)
if err != nil && err.Type() != errors.ErrNotFound {
return receiver.middleWare.ErrorOld(ctx, err)
}
if err != nil && err.Type() == errors.ErrNotFound {
return ctx.Status(fiber.StatusOK).JSON([]string{})
}
return ctx.Status(fiber.StatusOK).JSON(currencyList)
}

@ -0,0 +1,11 @@
package currency_client
import "github.com/gofiber/fiber/v2"
func (receiver *CurrencyController) Register(router fiber.Router) {
router.Get("/currencies", receiver.Get)
}
func (receiver *CurrencyController) Name() string {
return ""
}

@ -0,0 +1,166 @@
package history_admin
import (
"fmt"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
codeword_rpc "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/codeword"
)
type Deps struct {
MiddleWare *http.MiddleWare
HistoryRepo *repository.HistoryRepository
AccountRepo *repository.AccountRepository
VerifyClient *client.VerificationClient
AuthClient *client.AuthClient
TemplateClient *client.TemplateClient
CodewordClient *client.CodewordClient
Logger *zap.Logger
}
type HistoryController struct {
middleWare *http.MiddleWare
historyRepo *repository.HistoryRepository
accountRepo *repository.AccountRepository
verifyClient *client.VerificationClient
authClient *client.AuthClient
templateClient *client.TemplateClient
codewordClient *client.CodewordClient
logger *zap.Logger
}
func NewHistoryController(deps Deps) *HistoryController {
return &HistoryController{
middleWare: deps.MiddleWare,
historyRepo: deps.HistoryRepo,
authClient: deps.AuthClient,
accountRepo: deps.AccountRepo,
verifyClient: deps.VerifyClient,
templateClient: deps.TemplateClient,
codewordClient: deps.CodewordClient,
logger: deps.Logger,
}
}
func (receiver *HistoryController) CalculateLTV(ctx *fiber.Ctx) error {
var req struct {
From int64 `json:"from"`
To int64 `json:"to"`
}
if err := ctx.BodyParser(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request")
}
if req.From > req.To && req.To != 0 {
receiver.logger.Error("From timestamp must be less than To timestamp unless To is 0")
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "From timestamp must be less than To timestamp unless To is 0")
}
ltv, err := receiver.historyRepo.CalculateCustomerLTV(ctx.Context(), req.From, req.To)
if err != nil {
receiver.logger.Error("failed to calculate LTV", zap.Error(err))
return receiver.middleWare.ErrorOld(ctx, err)
}
response := struct {
LTV int64 `json:"LTV"`
}{
LTV: ltv,
}
return ctx.Status(fiber.StatusOK).JSON(response)
}
func (receiver *HistoryController) QuizLogoStat(ctx *fiber.Ctx) error {
var req struct {
From *int `json:"from,omitempty"`
Limit *int `json:"limit,omitempty"`
Page *int `json:"page,omitempty"`
To *int `json:"to,omitempty"`
}
if err := ctx.BodyParser(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request")
}
result, err := receiver.accountRepo.QuizLogoStat(ctx.Context(), repository.QuizLogoStatDeps{
Page: req.Page,
Limit: req.Limit,
From: req.From,
To: req.To,
})
if err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, fmt.Sprint("failed getting quiz logo stat", err.Error()))
}
return ctx.Status(fiber.StatusOK).JSON(result)
}
func (receiver *HistoryController) PromocodeLTV(ctx *fiber.Ctx) error {
var req struct {
From int `json:"from"`
To int `json:"to"`
}
if err := ctx.BodyParser(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request")
}
// получаем мапу вида [promoID] = []{userid,timeActivate}
// отдаются только первые использованые на аккаунте промокоды, соответсвенно подсчет идет сугубо по ним
// если в запросе время различается с временем активации - если меньше, то учитывается только после применения
// если больше, то учитывается только с начала переданного from
codewordData, err := receiver.codewordClient.GetAllPromoActivations(ctx.Context(), &codeword_rpc.Time{
To: int64(req.To),
From: int64(req.From),
})
if err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, fmt.Sprint("failed getting codeword data", err.Error()))
}
userSumMap, er := receiver.historyRepo.GetPayUsersPromoHistory(ctx.Context(), codewordData, int64(req.From), int64(req.To))
if er != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, fmt.Sprint("failed calculate promo users paid sum", er.Error()))
}
resp := make(map[string]struct {
Regs int
Money int64
})
for promoID, data := range codewordData {
fmt.Println("PROTOMOTO", promoID, data)
for _, value := range data {
paids, ok := userSumMap[value.UserID]
if !ok {
paids = 0
}
fmt.Println("PROTOMOTO1", paids, value)
if value.Time >= int64(req.From) && value.Time <= int64(req.To) {
if _, ok := resp[promoID]; !ok {
resp[promoID] = struct {
Regs int
Money int64
}{Regs: 1, Money: paids}
continue
}
current := resp[promoID]
current.Regs += 1
current.Money += paids
resp[promoID] = current
}
}
}
return ctx.Status(fiber.StatusOK).JSON(resp)
}

@ -1,14 +1,11 @@
package history
package history_admin
import "github.com/gofiber/fiber/v2"
func (receiver *HistoryController) Register(router fiber.Router) {
router.Get("/history", receiver.Get)
router.Post("/history/ltv", receiver.CalculateLTV)
router.Post("/promocode/ltv", receiver.PromocodeLTV)
router.Post("/quizlogo/stat", receiver.QuizLogoStat)
router.Get("/recent", receiver.GetRecentTariffs)
router.Post("/sendReport", receiver.SendReport)
}
func (receiver *HistoryController) Name() string {

@ -1,4 +1,4 @@
package history
package history_client
import (
"fmt"
@ -11,7 +11,6 @@ import (
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
codeword_rpc "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/codeword"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"strconv"
@ -117,37 +116,6 @@ func (receiver *HistoryController) Get(ctx *fiber.Ctx) error {
return ctx.Status(fiber.StatusOK).JSON(returnHistories)
}
func (receiver *HistoryController) CalculateLTV(ctx *fiber.Ctx) error {
var req struct {
From int64 `json:"from"`
To int64 `json:"to"`
}
if err := ctx.BodyParser(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request")
}
if req.From > req.To && req.To != 0 {
receiver.logger.Error("From timestamp must be less than To timestamp unless To is 0")
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "From timestamp must be less than To timestamp unless To is 0")
}
ltv, err := receiver.historyRepo.CalculateCustomerLTV(ctx.Context(), req.From, req.To)
if err != nil {
receiver.logger.Error("failed to calculate LTV", zap.Error(err))
return receiver.middleWare.ErrorOld(ctx, err)
}
response := struct {
LTV int64 `json:"LTV"`
}{
LTV: ltv,
}
return ctx.Status(fiber.StatusOK).JSON(response)
}
func (receiver *HistoryController) GetRecentTariffs(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
@ -294,92 +262,3 @@ func (receiver *HistoryController) SendReport(ctx *fiber.Ctx) error {
return ctx.SendStatus(fiber.StatusOK)
}
func (receiver *HistoryController) QuizLogoStat(ctx *fiber.Ctx) error {
var req struct {
From *int `json:"from,omitempty"`
Limit *int `json:"limit,omitempty"`
Page *int `json:"page,omitempty"`
To *int `json:"to,omitempty"`
}
if err := ctx.BodyParser(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request")
}
result, err := receiver.accountRepo.QuizLogoStat(ctx.Context(), repository.QuizLogoStatDeps{
Page: req.Page,
Limit: req.Limit,
From: req.From,
To: req.To,
})
if err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, fmt.Sprint("failed getting quiz logo stat", err.Error()))
}
return ctx.Status(fiber.StatusOK).JSON(result)
}
func (receiver *HistoryController) PromocodeLTV(ctx *fiber.Ctx) error {
var req struct {
From int `json:"from"`
To int `json:"to"`
}
if err := ctx.BodyParser(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind request")
}
// получаем мапу вида [promoID] = []{userid,timeActivate}
// отдаются только первые использованые на аккаунте промокоды, соответсвенно подсчет идет сугубо по ним
// если в запросе время различается с временем активации - если меньше, то учитывается только после применения
// если больше, то учитывается только с начала переданного from
codewordData, err := receiver.codewordClient.GetAllPromoActivations(ctx.Context(), &codeword_rpc.Time{
To: int64(req.To),
From: int64(req.From),
})
if err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, fmt.Sprint("failed getting codeword data", err.Error()))
}
userSumMap, er := receiver.historyRepo.GetPayUsersPromoHistory(ctx.Context(), codewordData, int64(req.From), int64(req.To))
if er != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, fmt.Sprint("failed calculate promo users paid sum", er.Error()))
}
resp := make(map[string]struct {
Regs int
Money int64
})
for promoID, data := range codewordData {
fmt.Println("PROTOMOTO", promoID, data)
for _, value := range data {
paids, ok := userSumMap[value.UserID]
if !ok {
paids = 0
}
fmt.Println("PROTOMOTO1", paids, value)
if value.Time >= int64(req.From) && value.Time <= int64(req.To) {
if _, ok := resp[promoID]; !ok {
resp[promoID] = struct {
Regs int
Money int64
}{Regs: 1, Money: paids}
continue
}
current := resp[promoID]
current.Regs += 1
current.Money += paids
resp[promoID] = current
}
}
}
return ctx.Status(fiber.StatusOK).JSON(resp)
}

@ -0,0 +1,13 @@
package history_client
import "github.com/gofiber/fiber/v2"
func (receiver *HistoryController) Register(router fiber.Router) {
router.Get("/history", receiver.Get)
router.Get("/recent", receiver.GetRecentTariffs)
router.Post("/sendReport", receiver.SendReport)
}
func (receiver *HistoryController) Name() string {
return ""
}

@ -1,4 +1,4 @@
package wallet
package wallet_client
import (
"context"

@ -1,4 +1,4 @@
package wallet
package wallet_client
import "github.com/gofiber/fiber/v2"

@ -15,8 +15,10 @@ type Config struct {
}
type ConfigurationHTTP struct {
Host string `env:"HTTP_HOST,default=localhost"`
Port string `env:"HTTP_PORT,default=8080"`
ClientHost string `env:"CLIENT_HTTP_HOST,default=localhost"`
ClientPort string `env:"CLIENT_HTTP_PORT,default=8080"`
AdminHost string `env:"ADMIN_HTTP_HOST,default=localhost"`
AdminPort string `env:"ADMIN_HTTP_PORT,default=8081"`
}
type ConfigurationGRPC struct {

@ -94,18 +94,18 @@ func (receiver *PaymentCallbackService) SuccessEvent(ctx context.Context, event
}
//go func() {
if _, err := receiver.historyService.CreateHistory(ctx, &models.History{
UserID: account.UserID,
Comment: event.Message,
Key: event.Key,
RawDetails: models.RawDetails{
Price: event.Amount,
Comment: event.Type + ":" + event.Currency,
},
}); err != nil {
receiver.logger.Error("failed to create history on <SuccessEvent> of <PaymentCallbackService>", zap.Error(err))
return err
}
if _, err := receiver.historyService.CreateHistory(ctx, &models.History{
UserID: account.UserID,
Comment: event.Message,
Key: event.Key,
RawDetails: models.RawDetails{
Price: event.Amount,
Comment: event.Type + ":" + event.Currency,
},
}); err != nil {
receiver.logger.Error("failed to create history on <SuccessEvent> of <PaymentCallbackService>", zap.Error(err))
return err
}
//}()
go func () {
if _, err := receiver.notifier.Send(tb.ChatID(receiver.notifyChannel), fmt.Sprintf(`Внесены деньги %.2f, пользователем %s`,float64(event.Amount/100), receiver.adminURL + "/users/" + account.UserID)); err != nil {
@ -117,10 +117,10 @@ go func () {
func (receiver *PaymentCallbackService) FailureEvent(ctx context.Context, event *models.PaymentEvent) errors.Error {
if _, err := receiver.historyService.CreateHistory(ctx, &models.History{
UserID: event.UserID,
Comment: event.Message,
Key: event.Key,
// RawDetails: fmt.Sprintf("%d%s", event.Amount, event.Currency),
UserID: event.UserID,
Comment: event.Message,
Key: event.Key,
// RawDetails: fmt.Sprintf("%d%s", event.Amount, event.Currency),
}); err != nil {
receiver.logger.Error("failed to create history on <FailureEvent> of <PaymentCallbackService>", zap.Error(err))
return err

@ -116,7 +116,7 @@ func (receiver *Service) ReplenishAccountWallet(ctx context.Context, request *mo
updatedAccount, changeErr := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{
Cash: request.Account.Wallet.Cash + cash,
Money: request.Account.Wallet.Money + request.Cash,
Spent: request.Account.Wallet.Spent,
Spent: request.Account.Wallet.Spent,
PurchasesAmount: request.Account.Wallet.PurchasesAmount,
Currency: request.Account.Wallet.Currency,
LastPaymentID: request.PaymentID,

@ -19,7 +19,7 @@ func TestChangeAccount(t *testing.T) {
defer cancel()
assert.NotPanics(t, func() {
token, tokenErr := jwtUtil.Create("64e5d9830fcca0596d82c0c7")
token, tokenErr := jwtUtil.Create("64ebda4387392e122e5d411f")
if isNoError := assert.NoError(t, tokenErr); !isNoError {
return
}
@ -43,7 +43,7 @@ func TestChangeAccount(t *testing.T) {
return
}
assert.Equal(t, "64e5d9830fcca0596d82c0c7", responseChangeAccount.Body.UserID)
assert.Equal(t, "64ebda4387392e122e5d411f", responseChangeAccount.Body.UserID)
assert.Equal(t, "Aloha", responseChangeAccount.Body.Name.Middlename)
assert.Equal(t, "Holla", responseChangeAccount.Body.Name.FirstName)
assert.Equal(t, "Adios payasos", responseChangeAccount.Body.Name.Orgname)
@ -54,7 +54,7 @@ func TestChangeAccount(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
assert.NotPanics(t, func() {
token, tokenErr := jwtUtil.Create("64e5d9830fcca0596d82c0c7")
token, tokenErr := jwtUtil.Create("64ebda4387392e122e5d411f")
if isNoError := assert.NoError(t, tokenErr); !isNoError {
return
}
@ -64,7 +64,7 @@ func TestChangeAccount(t *testing.T) {
}
responseStatusAccount, errStatusAccount := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/account/64e5d9830fcca0596d82c0c7",
URL: "http://localhost:8083/account/64ebda4387392e122e5d411f",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
Body: statusRequest,
})

@ -47,7 +47,7 @@ func TestGetAccount(t *testing.T) {
}
responseGetAccount, errGetAccount := client.Get[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/account/64e5e1ca87392e122e5d3de7",
URL: "http://localhost:8083/account/64e5e1ca87392e122e5d3de7",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if isNoError := assert.NoError(t, errGetAccount); !isNoError {
@ -61,6 +61,10 @@ func TestGetAccount(t *testing.T) {
})
})
t.Run("Получение аккаунтов с пагинацией", func(t *testing.T) {
token, tokenErr := jwtUtil.Create("64e5e1ca87392e122e5d3de7")
if isNoError := assert.NoError(t, tokenErr); !isNoError {
return
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
assert.NotPanics(t, func() {
@ -77,8 +81,9 @@ func TestGetAccount(t *testing.T) {
}
responseGetAccount, errGetAccount := client.Get[models.PaginationResponse[models.Account], models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8082/accounts",
Body: params,
URL: "http://localhost:8083/accounts",
Body: params,
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if isNoError := assert.NoError(t, errGetAccount); !isNoError {
return