refactoring of the sendreports method and adding the necessary clients

This commit is contained in:
pasha1coil 2023-11-26 16:02:01 +03:00
parent 7614255851
commit 3d0db1f54b
10 changed files with 272 additions and 100 deletions

@ -72,7 +72,9 @@ func Run(config *models.Config, logger *zap.Logger) (appErr error) {
HubadminURL: &config.Service.HubadminMicroservice.URL, HubadminURL: &config.Service.HubadminMicroservice.URL,
CurrencyURL: &config.Service.CurrencyMicroservice.URL, CurrencyURL: &config.Service.CurrencyMicroservice.URL,
DiscountServiceConfiguration: &config.Service.DiscountMicroservice, DiscountServiceConfiguration: &config.Service.DiscountMicroservice,
VerificationURL: &config.Service.VerificationMicroservice.URL,
PaymentServiceConfiguration: &config.Service.PaymentMicroservice, PaymentServiceConfiguration: &config.Service.PaymentMicroservice,
TemplategenURL: &config.Service.TemplategenMicroserviceURL.URL,
}) })
repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ repositories := initialize.NewRepositories(initialize.RepositoriesDeps{

@ -13,14 +13,18 @@ type ClientsDeps struct {
CurrencyURL *models.CurrencyMicroserviceURL CurrencyURL *models.CurrencyMicroserviceURL
DiscountServiceConfiguration *models.DiscountMicroserviceConfiguration DiscountServiceConfiguration *models.DiscountMicroserviceConfiguration
PaymentServiceConfiguration *models.PaymentMicroserviceConfiguration PaymentServiceConfiguration *models.PaymentMicroserviceConfiguration
VerificationURL *models.VerificationMicroserviceURL
TemplategenURL *models.TemplategenMicroserviceURL
} }
type Clients struct { type Clients struct {
AuthClient *client.AuthClient AuthClient *client.AuthClient
HubadminClient *client.HubadminClient HubadminClient *client.HubadminClient
CurrencyClient *client.CurrencyClient CurrencyClient *client.CurrencyClient
DiscountClient *client.DiscountClient DiscountClient *client.DiscountClient
PaymentClient *client.PaymentClient PaymentClient *client.PaymentClient
VerificationClient *client.VerificationClient
TemplateClient *client.TemplateClient
} }
func NewClients(deps ClientsDeps) *Clients { func NewClients(deps ClientsDeps) *Clients {
@ -45,5 +49,13 @@ func NewClients(deps ClientsDeps) *Clients {
Logger: deps.Logger, Logger: deps.Logger,
PaymentServiceHost: deps.PaymentServiceConfiguration.HostGRPC, PaymentServiceHost: deps.PaymentServiceConfiguration.HostGRPC,
}), }),
VerificationClient: client.NewVerificationClient(client.VerificationClientDeps{
Logger: deps.Logger,
URLs: deps.VerificationURL,
}),
TemplateClient: client.NewTemplateClient(client.TemplateClientDeps{
Logger: deps.Logger,
URLs: deps.TemplategenURL,
}),
} }
} }

@ -0,0 +1,62 @@
package client
import (
"context"
"fmt"
"go.uber.org/zap"
"log"
"net/url"
"path"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client"
)
type TemplateClientDeps struct {
Logger *zap.Logger
URLs *models.TemplategenMicroserviceURL
}
type TemplateClient struct {
logger *zap.Logger
urls *models.TemplategenMicroserviceURL
}
func NewTemplateClient(deps TemplateClientDeps) *TemplateClient {
if deps.Logger == nil {
log.Panicln("logger is nil on <NewTemplateClient>")
}
if deps.URLs == nil {
log.Panicln("urls is nil on <NewTemplateClient>")
}
return &TemplateClient{
logger: deps.Logger,
urls: deps.URLs,
}
}
func (receiver *TemplateClient) PostToTemplategen(ctx context.Context, data any) errors.Error {
tmplURL, err := url.Parse(receiver.urls.Templategen)
if err != nil {
return errors.New(
fmt.Errorf("failed to parse URL on <PostToTemplategen>: %w", err),
errors.ErrInternalError,
)
}
tmplURL.Path = path.Join(tmplURL.Path)
_, err = client.Post[interface{}, models.FastifyError](ctx, &client.RequestSettings{
URL: tmplURL.String(),
Headers: map[string]string{"Content-Type": "multipart/form-data"},
Body: data,
})
if err != nil {
return errors.New(err, errors.ErrInternalError)
}
return nil
}

@ -0,0 +1,58 @@
package client
import (
"context"
"fmt"
"go.uber.org/zap"
"log"
"net/url"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/client"
)
type VerificationClientDeps struct {
Logger *zap.Logger
URLs *models.VerificationMicroserviceURL
}
type VerificationClient struct {
logger *zap.Logger
urls *models.VerificationMicroserviceURL
}
func NewVerificationClient(deps VerificationClientDeps) *VerificationClient {
if deps.Logger == nil {
log.Panicln("logger is nil on <NewVerificationClient>")
}
if deps.URLs == nil {
log.Panicln("urls is nil on <NewVerificationClient>")
}
return &VerificationClient{
logger: deps.Logger,
urls: deps.URLs,
}
}
func (receiver *VerificationClient) GetVerification(ctx context.Context, userID string) (*models.Verification, errors.Error) {
verifURL, err := url.JoinPath(receiver.urls.Verification, userID)
if err != nil {
return nil, errors.New(
fmt.Errorf("failed to join path on <GetVerification> of <VerificationClient>: %w", err),
errors.ErrInternalError,
)
}
response, err := client.Get[models.Verification, models.FastifyError](ctx, &client.RequestSettings{
URL: verifURL,
Headers: map[string]string{"Content-Type": "application/json"},
})
if err != nil {
return nil, errors.New(err, errors.ErrInternalError)
}
return response.Body, nil
}

@ -1,15 +1,12 @@
package history package history
import ( import (
"bytes"
"context" "context"
"encoding/json"
"fmt" "fmt"
"log"
"net/http"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"go.uber.org/zap" "go.uber.org/zap"
"log"
"net/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/dto" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/dto"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
@ -18,8 +15,8 @@ import (
type historyService interface { type historyService interface {
GetHistoryList(context.Context, *dto.GetHistories) (*models.PaginationResponse[models.History], errors.Error) GetHistoryList(context.Context, *dto.GetHistories) (*models.PaginationResponse[models.History], errors.Error)
GetRecentTariffs(context.Context, string) ([]models.TariffID, errors.Error) // new GetRecentTariffs(context.Context, string) ([]models.TariffID, errors.Error) // new
GetHistoryById(context.Context, string) (*models.ReportHistory, errors.Error) // new GetHistoryById(context.Context, string) errors.Error // new
} }
type Deps struct { type Deps struct {
@ -98,75 +95,14 @@ func (receiver *Controller) GetRecentTariffs(ctx echo.Context) error {
} }
// TODO:tests. // TODO:tests.
// скорее всего нужно будет передавать токен авторизации но пока не точно, также брать урлы из переменных окружения или конфига func (receiver *Controller) SendReport(ctx echo.Context) error {
// также сделать рефакторинг, пока выглядит не очень
func (receiver *Controller) GetHistoryById(ctx echo.Context) error {
historyID := ctx.Param("id") historyID := ctx.Param("id")
// Получаем запись из истории по идентификатору err := receiver.historyService.GetHistoryById(ctx.Request().Context(), historyID)
historyRecord, err := receiver.historyService.GetHistoryById(ctx.Request().Context(), historyID)
if err != nil { if err != nil {
receiver.logger.Error("failed to get history record on <GetHistoryById> of <HistoryController>", zap.Error(err)) receiver.logger.Error("failed to send report on <SendReport> of <HistoryController>", zap.Error(err))
return errors.HTTP(ctx, err) return errors.HTTP(ctx, err)
} }
// Проверяем, что запись имеет ключ "payCart" return ctx.JSON(http.StatusOK, nil)
if historyRecord.Key != models.CustomerHistoryKeyPayCart {
err := errors.NewWithMessage("invalid history record key", errors.ErrInvalidArgs)
receiver.logger.Error("invalid history record key", zap.Error(err))
return errors.HTTP(ctx, err)
}
// Получаем данные о верификации
client := &http.Client{}
url := fmt.Sprintf("http://example/verification/%s", historyRecord.UserID)
response, _ := client.Get(url)
defer response.Body.Close()
var verificationResponse models.Verification
decoder := json.NewDecoder(response.Body)
if err := decoder.Decode(&verificationResponse); err != nil {
err := errors.NewWithMessage(err.Error(), errors.ErrInternalError)
receiver.logger.Error("failed to decode verification response", zap.Error(err))
return errors.HTTP(ctx, err)
}
if !verificationResponse.Accepted {
err := errors.NewWithMessage("Verification not accepted", errors.ErrNoAccess)
receiver.logger.Error("verification not accepted", zap.Error(err))
return errors.HTTP(ctx, err)
}
// Получаем email
url = fmt.Sprintf("http://example/user/%s", historyRecord.UserID)
response, _ = client.Get(url)
var userResponse models.User
decoder = json.NewDecoder(response.Body)
if err := decoder.Decode(&userResponse); err != nil {
err := errors.NewWithMessage(err.Error(), errors.ErrInternalError)
receiver.logger.Error("failed to decode verification response", zap.Error(err))
return errors.HTTP(ctx, err)
}
// Получаем данные для передачи в templategen, возможно придется менять
generatorRequest := models.GeneratorRequest{
Email: userResponse.Email,
TaxNumber: verificationResponse.TaxNumber,
RawDetails: historyRecord.RawDetails,
}
// Маршалируем данные в JSON
requestBody, _ := json.Marshal(generatorRequest)
if err != nil {
return errors.HTTP(ctx, err)
}
generatorURL := "http://example/generator/service"
response, _ = client.Post(generatorURL, "application/json", bytes.NewBuffer(requestBody))
if err != nil {
return errors.HTTP(ctx, err)
}
return nil
} }

@ -35,7 +35,7 @@ type walletController interface {
type historyController interface { type historyController interface {
GetHistoryList(ctx echo.Context, params GetHistoryParams) error GetHistoryList(ctx echo.Context, params GetHistoryParams) error
GetRecentTariffs(ctx echo.Context) error GetRecentTariffs(ctx echo.Context) error
GetHistoryById(ctx echo.Context) error SendReport(ctx echo.Context) error
} }
type Deps struct { type Deps struct {
@ -153,7 +153,7 @@ func (receiver *API) GetRecentTariffs(ctx echo.Context) error {
} }
func (receiver *API) SendReport(ctx echo.Context) error { func (receiver *API) SendReport(ctx echo.Context) error {
return receiver.historyController.GetHistoryById(ctx) return receiver.historyController.SendReport(ctx)
} }
// Wallet // Wallet

@ -26,13 +26,15 @@ type ConfigurationGRPC struct {
} }
type ServiceConfiguration struct { type ServiceConfiguration struct {
AuthMicroservice AuthMicroserviceConfiguration AuthMicroservice AuthMicroserviceConfiguration
HubadminMicroservice HubadminMicroserviceConfiguration HubadminMicroservice HubadminMicroserviceConfiguration
CurrencyMicroservice CurrencyMicroserviceConfiguration CurrencyMicroservice CurrencyMicroserviceConfiguration
DiscountMicroservice DiscountMicroserviceConfiguration DiscountMicroservice DiscountMicroserviceConfiguration
PaymentMicroservice PaymentMicroserviceConfiguration PaymentMicroservice PaymentMicroserviceConfiguration
JWT JWTConfiguration VerificationMicroservice VerificationMicroserviceConfiguration
Kafka KafkaConfiguration TemplategenMicroserviceURL TemplategenMicroserviceConfiguration
JWT JWTConfiguration
Kafka KafkaConfiguration
} }
type KafkaConfiguration struct { type KafkaConfiguration struct {
@ -65,6 +67,14 @@ type CurrencyMicroserviceConfiguration struct {
URL CurrencyMicroserviceURL URL CurrencyMicroserviceURL
} }
type VerificationMicroserviceConfiguration struct {
URL VerificationMicroserviceURL
}
type TemplategenMicroserviceConfiguration struct {
URL TemplategenMicroserviceURL
}
type PaymentMicroserviceConfiguration struct { type PaymentMicroserviceConfiguration struct {
HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"` HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"`
} }
@ -84,3 +94,11 @@ type HubadminMicroserviceURL struct {
type CurrencyMicroserviceURL struct { type CurrencyMicroserviceURL struct {
Translate string `env:"CURRENCY_MICROSERVICE_TRANSLATE_URL,required"` Translate string `env:"CURRENCY_MICROSERVICE_TRANSLATE_URL,required"`
} }
type VerificationMicroserviceURL struct {
Verification string `env:"VERIFICATION_MICROSERVICE_USER_URL,required"`
}
type TemplategenMicroserviceURL struct {
Templategen string `env:"TEMPLATEGEN_MICROSERVICE_URL,required"`
}

@ -1,7 +1,14 @@
package models package models
type GeneratorRequest struct { import "io"
Email string `json:"email"`
TaxNumber string `json:"taxNumber"` type TemplateGenData struct {
RawDetails RawDetails `json:"rawDetails"` Email string `json:"email"`
Data Data `json:"data"`
File io.Reader `json:"file"`
}
type Data struct {
TaxNumber string `json:"taxnumber"`
History *ReportHistory `json:"history"`
} }

@ -1,6 +1,7 @@
package history package history
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"log" "log"
@ -20,14 +21,32 @@ type historyRepository interface {
GetHistoryById(context.Context, string) (*models.ReportHistory, errors.Error) GetHistoryById(context.Context, string) (*models.ReportHistory, errors.Error)
} }
type authClient interface {
GetUser(ctx context.Context, userID string) (*models.User, errors.Error)
}
type verificationClient interface {
GetUser(ctx context.Context, userID string) (*models.Verification, errors.Error)
}
type temlategenClient interface {
SendData(ctx context.Context, data models.TemplateGenData) errors.Error
}
type Deps struct { type Deps struct {
Logger *zap.Logger Logger *zap.Logger
Repository historyRepository Repository historyRepository
AuthClient authClient
VerificationClient verificationClient
TemlategenClient temlategenClient
} }
type Service struct { type Service struct {
logger *zap.Logger logger *zap.Logger
repository historyRepository repository historyRepository
AuthClient authClient
VerificationClient verificationClient
TemlategenClient temlategenClient
} }
func New(deps Deps) *Service { func New(deps Deps) *Service {
@ -39,9 +58,14 @@ func New(deps Deps) *Service {
log.Panicln("repository is nil on <New (history service)>") log.Panicln("repository is nil on <New (history service)>")
} }
if deps.AuthClient == nil {
log.Panicln("auth client is nil on <New (account service)>")
}
return &Service{ return &Service{
logger: deps.Logger, logger: deps.Logger,
repository: deps.Repository, repository: deps.Repository,
AuthClient: deps.AuthClient,
} }
} }
@ -116,10 +140,10 @@ func (receiver *Service) GetRecentTariffs(ctx context.Context, userID string) ([
return tariffs, nil return tariffs, nil
} }
func (receiver *Service) GetHistoryById(ctx context.Context, historyID string) (*models.ReportHistory, errors.Error) { func (receiver *Service) GetHistoryById(ctx context.Context, historyID string) errors.Error {
if historyID == "" { if historyID == "" {
receiver.logger.Error("user id is missing in <FindByID> of <HistoryService>") receiver.logger.Error("history id is missing in <GetHistoryById> of <HistoryService>")
return nil, errors.New( return errors.New(
fmt.Errorf("history id is missing: %w", errors.ErrInvalidArgs), fmt.Errorf("history id is missing: %w", errors.ErrInvalidArgs),
errors.ErrInvalidArgs, errors.ErrInvalidArgs,
) )
@ -128,12 +152,65 @@ func (receiver *Service) GetHistoryById(ctx context.Context, historyID string) (
tariffs, err := receiver.repository.GetHistoryById(ctx, historyID) tariffs, err := receiver.repository.GetHistoryById(ctx, historyID)
if err != nil { if err != nil {
receiver.logger.Error( receiver.logger.Error(
"failed to find by id in <FindByID> of <HistoryService>", "failed to get history by id in <GetHistoryById> of <HistoryService>",
zap.String("historyID", historyID), zap.String("historyID", historyID),
zap.Error(err), zap.Error(err),
) )
return nil, err return err
} }
return tariffs, nil if tariffs.Key != models.CustomerHistoryKeyPayCart {
receiver.logger.Error(
"invalid history record key",
zap.String("historyID", historyID),
zap.Error(err),
)
return err
}
verifuser, err := receiver.VerificationClient.GetUser(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error("failed to get user verification on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
if !verifuser.Accepted {
receiver.logger.Error(
"verification not accepted",
zap.String("userID", tariffs.UserID),
zap.Error(err),
)
return err
}
authuser, err := receiver.AuthClient.GetUser(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error("failed to get user on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
data := models.TemplateGenData{
Email: authuser.Email,
Data: models.Data{
TaxNumber: verifuser.TaxNumber,
History: tariffs,
},
File: bytes.NewReader([]byte("./report.docx")),
}
err = receiver.TemlategenClient.SendData(ctx, data)
if err != nil {
receiver.logger.Error("failed to send report to user on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
return nil
} }

BIN
report.docx Normal file

Binary file not shown.