diff --git a/internal/app/app.go b/internal/app/app.go index ffcbdec..c275006 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -72,7 +72,9 @@ func Run(config *models.Config, logger *zap.Logger) (appErr error) { HubadminURL: &config.Service.HubadminMicroservice.URL, CurrencyURL: &config.Service.CurrencyMicroservice.URL, DiscountServiceConfiguration: &config.Service.DiscountMicroservice, + VerificationURL: &config.Service.VerificationMicroservice.URL, PaymentServiceConfiguration: &config.Service.PaymentMicroservice, + TemplategenURL: &config.Service.TemplategenMicroserviceURL.URL, }) repositories := initialize.NewRepositories(initialize.RepositoriesDeps{ diff --git a/internal/initialize/clients.go b/internal/initialize/clients.go index dbe0f04..e991489 100644 --- a/internal/initialize/clients.go +++ b/internal/initialize/clients.go @@ -13,14 +13,18 @@ type ClientsDeps struct { CurrencyURL *models.CurrencyMicroserviceURL DiscountServiceConfiguration *models.DiscountMicroserviceConfiguration PaymentServiceConfiguration *models.PaymentMicroserviceConfiguration + VerificationURL *models.VerificationMicroserviceURL + TemplategenURL *models.TemplategenMicroserviceURL } type Clients struct { - AuthClient *client.AuthClient - HubadminClient *client.HubadminClient - CurrencyClient *client.CurrencyClient - DiscountClient *client.DiscountClient - PaymentClient *client.PaymentClient + AuthClient *client.AuthClient + HubadminClient *client.HubadminClient + CurrencyClient *client.CurrencyClient + DiscountClient *client.DiscountClient + PaymentClient *client.PaymentClient + VerificationClient *client.VerificationClient + TemplateClient *client.TemplateClient } func NewClients(deps ClientsDeps) *Clients { @@ -45,5 +49,13 @@ func NewClients(deps ClientsDeps) *Clients { Logger: deps.Logger, 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, + }), } } diff --git a/internal/interface/client/templategen.go b/internal/interface/client/templategen.go new file mode 100644 index 0000000..7fe3973 --- /dev/null +++ b/internal/interface/client/templategen.go @@ -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 ") + } + + if deps.URLs == nil { + log.Panicln("urls is nil on ") + } + + 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 : %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 +} diff --git a/internal/interface/client/verification.go b/internal/interface/client/verification.go new file mode 100644 index 0000000..2608f8e --- /dev/null +++ b/internal/interface/client/verification.go @@ -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 ") + } + + if deps.URLs == nil { + log.Panicln("urls is nil on ") + } + + 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 of : %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 +} diff --git a/internal/interface/controller/rest/history/history.go b/internal/interface/controller/rest/history/history.go index 9c36939..a6878f9 100644 --- a/internal/interface/controller/rest/history/history.go +++ b/internal/interface/controller/rest/history/history.go @@ -1,15 +1,12 @@ package history import ( - "bytes" "context" - "encoding/json" "fmt" - "log" - "net/http" - "github.com/labstack/echo/v4" "go.uber.org/zap" + "log" + "net/http" "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/interface/swagger" @@ -18,8 +15,8 @@ import ( type historyService interface { GetHistoryList(context.Context, *dto.GetHistories) (*models.PaginationResponse[models.History], errors.Error) - GetRecentTariffs(context.Context, string) ([]models.TariffID, errors.Error) // new - GetHistoryById(context.Context, string) (*models.ReportHistory, errors.Error) // new + GetRecentTariffs(context.Context, string) ([]models.TariffID, errors.Error) // new + GetHistoryById(context.Context, string) errors.Error // new } type Deps struct { @@ -98,75 +95,14 @@ func (receiver *Controller) GetRecentTariffs(ctx echo.Context) error { } // TODO:tests. -// скорее всего нужно будет передавать токен авторизации но пока не точно, также брать урлы из переменных окружения или конфига -// также сделать рефакторинг, пока выглядит не очень -func (receiver *Controller) GetHistoryById(ctx echo.Context) error { +func (receiver *Controller) SendReport(ctx echo.Context) error { historyID := ctx.Param("id") - // Получаем запись из истории по идентификатору - historyRecord, err := receiver.historyService.GetHistoryById(ctx.Request().Context(), historyID) + err := receiver.historyService.GetHistoryById(ctx.Request().Context(), historyID) if err != nil { - receiver.logger.Error("failed to get history record on of ", zap.Error(err)) + receiver.logger.Error("failed to send report on of ", zap.Error(err)) return errors.HTTP(ctx, err) } - // Проверяем, что запись имеет ключ "payCart" - 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 + return ctx.JSON(http.StatusOK, nil) } diff --git a/internal/interface/swagger/api.go b/internal/interface/swagger/api.go index 206fef4..fe13b01 100644 --- a/internal/interface/swagger/api.go +++ b/internal/interface/swagger/api.go @@ -35,7 +35,7 @@ type walletController interface { type historyController interface { GetHistoryList(ctx echo.Context, params GetHistoryParams) error GetRecentTariffs(ctx echo.Context) error - GetHistoryById(ctx echo.Context) error + SendReport(ctx echo.Context) error } type Deps struct { @@ -153,7 +153,7 @@ func (receiver *API) GetRecentTariffs(ctx echo.Context) error { } func (receiver *API) SendReport(ctx echo.Context) error { - return receiver.historyController.GetHistoryById(ctx) + return receiver.historyController.SendReport(ctx) } // Wallet diff --git a/internal/models/config.go b/internal/models/config.go index ef9a9b5..1a9ba1f 100644 --- a/internal/models/config.go +++ b/internal/models/config.go @@ -26,13 +26,15 @@ type ConfigurationGRPC struct { } type ServiceConfiguration struct { - AuthMicroservice AuthMicroserviceConfiguration - HubadminMicroservice HubadminMicroserviceConfiguration - CurrencyMicroservice CurrencyMicroserviceConfiguration - DiscountMicroservice DiscountMicroserviceConfiguration - PaymentMicroservice PaymentMicroserviceConfiguration - JWT JWTConfiguration - Kafka KafkaConfiguration + AuthMicroservice AuthMicroserviceConfiguration + HubadminMicroservice HubadminMicroserviceConfiguration + CurrencyMicroservice CurrencyMicroserviceConfiguration + DiscountMicroservice DiscountMicroserviceConfiguration + PaymentMicroservice PaymentMicroserviceConfiguration + VerificationMicroservice VerificationMicroserviceConfiguration + TemplategenMicroserviceURL TemplategenMicroserviceConfiguration + JWT JWTConfiguration + Kafka KafkaConfiguration } type KafkaConfiguration struct { @@ -65,6 +67,14 @@ type CurrencyMicroserviceConfiguration struct { URL CurrencyMicroserviceURL } +type VerificationMicroserviceConfiguration struct { + URL VerificationMicroserviceURL +} + +type TemplategenMicroserviceConfiguration struct { + URL TemplategenMicroserviceURL +} + type PaymentMicroserviceConfiguration struct { HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"` } @@ -84,3 +94,11 @@ type HubadminMicroserviceURL struct { type CurrencyMicroserviceURL struct { 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"` +} diff --git a/internal/models/templategen.go b/internal/models/templategen.go index c850f65..9bd5a1a 100644 --- a/internal/models/templategen.go +++ b/internal/models/templategen.go @@ -1,7 +1,14 @@ package models -type GeneratorRequest struct { - Email string `json:"email"` - TaxNumber string `json:"taxNumber"` - RawDetails RawDetails `json:"rawDetails"` +import "io" + +type TemplateGenData struct { + Email string `json:"email"` + Data Data `json:"data"` + File io.Reader `json:"file"` +} + +type Data struct { + TaxNumber string `json:"taxnumber"` + History *ReportHistory `json:"history"` } diff --git a/internal/service/history/history.go b/internal/service/history/history.go index 4fa59c1..8d51931 100644 --- a/internal/service/history/history.go +++ b/internal/service/history/history.go @@ -1,6 +1,7 @@ package history import ( + "bytes" "context" "fmt" "log" @@ -20,14 +21,32 @@ type historyRepository interface { 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 { - Logger *zap.Logger - Repository historyRepository + Logger *zap.Logger + Repository historyRepository + AuthClient authClient + VerificationClient verificationClient + TemlategenClient temlategenClient } type Service struct { - logger *zap.Logger - repository historyRepository + logger *zap.Logger + repository historyRepository + AuthClient authClient + VerificationClient verificationClient + TemlategenClient temlategenClient } func New(deps Deps) *Service { @@ -39,9 +58,14 @@ func New(deps Deps) *Service { log.Panicln("repository is nil on ") } + if deps.AuthClient == nil { + log.Panicln("auth client is nil on ") + } + return &Service{ logger: deps.Logger, repository: deps.Repository, + AuthClient: deps.AuthClient, } } @@ -116,10 +140,10 @@ func (receiver *Service) GetRecentTariffs(ctx context.Context, userID string) ([ 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 == "" { - receiver.logger.Error("user id is missing in of ") - return nil, errors.New( + receiver.logger.Error("history id is missing in of ") + return errors.New( fmt.Errorf("history id is missing: %w", errors.ErrInvalidArgs), errors.ErrInvalidArgs, ) @@ -128,12 +152,65 @@ func (receiver *Service) GetHistoryById(ctx context.Context, historyID string) ( tariffs, err := receiver.repository.GetHistoryById(ctx, historyID) if err != nil { receiver.logger.Error( - "failed to find by id in of ", + "failed to get history by id in of ", zap.String("historyID", historyID), 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 of ", + 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 of ", + 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 of ", + zap.Error(err), + zap.String("userID", tariffs.UserID), + ) + + return err + } + return nil } diff --git a/report.docx b/report.docx new file mode 100644 index 0000000..40b88ce Binary files /dev/null and b/report.docx differ