customer/internal/interface/controller/http/history/controllers.go
2024-05-23 17:34:45 +03:00

385 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package history
import (
"fmt"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"math"
"os"
"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"
"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"
"strings"
"time"
)
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) Get(ctx *fiber.Ctx) error {
var userID string
pageStr := ctx.Query("page")
limitStr := ctx.Query("limit")
tipe := ctx.Query("type")
accountID := ctx.Query("accountID")
if accountID != "" {
userID = accountID
} else {
id, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || id == "" {
return receiver.middleWare.NoAuth(ctx)
}
userID = id
}
limit, err := strconv.ParseInt(limitStr, 10, 64)
if err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid limit format")
}
page, err := strconv.ParseInt(pageStr, 10, 64)
if err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid page format")
}
dto := &history.GetHistories{
UserID: userID,
Type: &tipe,
Pagination: &models.Pagination{
Page: page,
Limit: limit,
},
}
count, err := receiver.historyRepo.CountAll(ctx.Context(), dto)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
if count == 0 {
returnHistories := models.PaginationResponse[models.History]{TotalPages: 0, Records: []models.History{}}
return ctx.Status(fiber.StatusOK).JSON(returnHistories)
}
totalPages := int64(math.Ceil(float64(count) / float64(dto.Pagination.Limit)))
histories, err := receiver.historyRepo.FindMany(ctx.Context(), dto)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
returnHistories := models.PaginationResponse[models.History]{
TotalPages: totalPages,
Records: histories,
}
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 == "" {
return receiver.middleWare.NoAuth(ctx)
}
tariffs, err := receiver.historyRepo.GetRecentTariffs(ctx.Context(), userID)
if err != nil {
receiver.logger.Error("failed to get recent tariffs on <GetRecentTariffs> of <API2>",
zap.String("userId", userID),
zap.Error(err),
)
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(tariffs)
}
func (receiver *HistoryController) SendReport(ctx *fiber.Ctx) error {
hlogger := receiver.middleWare.ExtractLogger(ctx)
var req struct {
Id string `json:"id"`
}
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.Id == "" {
receiver.logger.Error("history id is missing in <GetHistoryById> of <HistoryService>")
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "history id is missing")
}
tariffs, err := receiver.historyRepo.GetHistoryByID(ctx.Context(), req.Id)
if err != nil {
receiver.logger.Error(
"failed to get history by id in <GetHistoryById> of <HistoryService>",
zap.String("historyID", req.Id),
zap.Error(err),
)
return receiver.middleWare.ErrorOld(ctx, err)
}
if tariffs.Key != models.CustomerHistoryKeyPayCart {
receiver.logger.Error(
"invalid history record key",
zap.String("historyID", req.Id),
zap.Error(err),
)
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid history record key")
}
historyMap, err := receiver.historyRepo.GetDocNumber(ctx.Context(), tariffs.UserID)
if err != nil {
receiver.logger.Error(
"failed to get history of sorting by date created in <GetDocNumber> of <HistoryService>",
zap.String("historyID", req.Id),
zap.Error(err),
)
return receiver.middleWare.ErrorOld(ctx, err)
}
token := ctx.Get("Authorization")
fmt.Println("HEADERS", ctx.Request().Header)
verifuser, err := receiver.verifyClient.GetVerification(ctx.Context(), token, 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 receiver.middleWare.ErrorOld(ctx, err)
}
if !verifuser.Accepted {
receiver.logger.Error(
"verification not accepted",
zap.String("userID", tariffs.UserID),
zap.Error(err),
)
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "verification not accepted")
}
account, err := receiver.accountRepo.FindByUserID(ctx.Context(), tariffs.UserID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
authuser, err := receiver.authClient.GetUser(ctx.Context(), 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 receiver.middleWare.ErrorOld(ctx, err)
}
fileContents, readerr := os.ReadFile("./report.docx")
if readerr != nil {
return receiver.middleWare.Error(ctx, fiber.StatusInternalServerError, "failed to read file")
}
if account.Name.Orgname == "" {
account.Name.Orgname = "Безымянное предприятие"
}
for _, tariff := range tariffs.RawDetails.Tariffs {
totalAmount := uint64(0)
privilegeMeasurement := ""
piecePrice := ""
privilegeName := ""
for _, privilege := range tariff.Privileges {
totalAmount += privilege.Amount
privilegeMeasurement = string(privilege.Type)
piecePrice = fmt.Sprintf("%.2f", float64(privilege.Price)/100)
privilegeName = privilege.Name
}
data := models.RespGeneratorService{
DocNumber: fmt.Sprint(historyMap[req.Id] + 1),
Date: time.Now().Format("2006-01-02"),
OrgTaxNum: verifuser.TaxNumber,
OrgName: account.Name.Orgname,
Name: tariff.Name + " " + privilegeName,
Amount: fmt.Sprint(totalAmount),
Unit: piecePrice,
Price: fmt.Sprintf("%.2f", float64(tariffs.RawDetails.Price)/100),
Sum: privilegeMeasurement,
}
err = receiver.templateClient.SendData(ctx.Context(), data, fileContents, authuser.Login)
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 receiver.middleWare.ErrorOld(ctx, err)
}
}
hlogger.Emit(models.InfoReportRequest{
CtxUserID: tariffs.UserID,
CtxAccountID: account.ID,
CtxID: req.Id,
CtxTariff: strings.Join(utils.GetTariffsIDs(tariffs.RawDetails.Tariffs), ","),
CtxOrgName: account.Name.Orgname,
})
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)
}