Files
smart-search-back/internal/service/request.go
vallyenfail 7e73144486
All checks were successful
Deploy Smart Search Backend Test / deploy (push) Successful in 1m44s
add service
2026-01-20 14:26:27 +03:00

208 lines
5.4 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 service
import (
"context"
"fmt"
"math"
"git.techease.ru/Smart-search/smart-search-back/internal/ai"
"git.techease.ru/Smart-search/smart-search-back/internal/model"
"git.techease.ru/Smart-search/smart-search-back/internal/repository"
"git.techease.ru/Smart-search/smart-search-back/pkg/errors"
"git.techease.ru/Smart-search/smart-search-back/pkg/fileparser"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
)
type requestService struct {
requestRepo repository.RequestRepository
supplierRepo repository.SupplierRepository
tokenUsageRepo repository.TokenUsageRepository
userRepo repository.UserRepository
openAI *ai.OpenAIClient
perplexity *ai.PerplexityClient
txManager *repository.TxManager
}
func NewRequestService(
requestRepo repository.RequestRepository,
supplierRepo repository.SupplierRepository,
tokenUsageRepo repository.TokenUsageRepository,
userRepo repository.UserRepository,
openAI *ai.OpenAIClient,
perplexity *ai.PerplexityClient,
txManager *repository.TxManager,
) RequestService {
return &requestService{
requestRepo: requestRepo,
supplierRepo: supplierRepo,
tokenUsageRepo: tokenUsageRepo,
userRepo: userRepo,
openAI: openAI,
perplexity: perplexity,
txManager: txManager,
}
}
func (s *requestService) CreateTZ(ctx context.Context, userID int, requestTxt string, fileData []byte, fileName string) (uuid.UUID, string, error) {
combinedText := requestTxt
if len(fileData) > 0 && fileName != "" {
fileContent, err := fileparser.ExtractText(fileData, fileName)
if err != nil {
return uuid.Nil, "", err
}
if fileContent != "" {
if combinedText != "" {
combinedText = fmt.Sprintf("%s\n\nСодержимое файла (%s):\n%s", combinedText, fileName, fileContent)
} else {
combinedText = fmt.Sprintf("Содержимое файла (%s):\n%s", fileName, fileContent)
}
}
}
req := &model.Request{
UserID: userID,
RequestTxt: combinedText,
}
if err := s.requestRepo.Create(ctx, req); err != nil {
return uuid.Nil, "", err
}
if combinedText == "" {
return req.ID, "", nil
}
tzText, err := s.openAI.GenerateTZ(combinedText)
if err != nil {
if err := s.requestRepo.UpdateWithTZ(ctx, req.ID, "", false); err != nil {
return req.ID, "", err
}
return req.ID, "", err
}
inputLen := len(requestTxt)
outputLen := len(tzText)
promptTokens := 500
inputTokens := int(math.Ceil(float64(inputLen) / 2.0))
outputTokens := int(math.Ceil(float64(outputLen) / 2.0))
totalTokens := inputTokens + outputTokens + promptTokens
tokenPrice := 25000.0 / 1000000.0
cost := float64(totalTokens) * tokenPrice
tokenUsage := &model.TokenUsage{
RequestID: req.ID,
RequestTokenCount: inputTokens + promptTokens,
ResponseTokenCount: outputTokens,
TokenCost: cost,
Type: "tz",
}
err = s.txManager.WithTx(ctx, func(tx pgx.Tx) error {
if err := s.tokenUsageRepo.CreateTx(ctx, tx, tokenUsage); err != nil {
return err
}
if err := s.userRepo.UpdateBalanceTx(ctx, tx, userID, -cost); err != nil {
return err
}
if err := s.requestRepo.UpdateWithTZTx(ctx, tx, req.ID, tzText, true); err != nil {
return err
}
return nil
})
if err != nil {
return req.ID, "", err
}
return req.ID, tzText, nil
}
func (s *requestService) ApproveTZ(ctx context.Context, requestID uuid.UUID, tzText string, userID int) ([]*model.Supplier, error) {
isOwner, err := s.requestRepo.CheckOwnership(ctx, requestID, userID)
if err != nil {
return nil, err
}
if !isOwner {
return nil, errors.NewBusinessError(errors.PermissionDenied, "access denied to this request")
}
if err = s.requestRepo.UpdateFinalTZ(ctx, requestID, tzText); err != nil {
return nil, err
}
var suppliers []*model.Supplier
var promptTokens, responseTokens int
for attempt := 0; attempt < 3; attempt++ {
suppliers, promptTokens, responseTokens, err = s.perplexity.FindSuppliers(tzText)
if err == nil {
break
}
}
if err != nil {
return nil, err
}
if len(suppliers) == 0 {
return nil, errors.NewInternalError(errors.AIAPIError, "no suppliers found", nil)
}
tokenPrice := 25000.0 / 1000000.0
totalTokens := promptTokens + responseTokens
cost := float64(totalTokens) * tokenPrice
tokenUsage := &model.TokenUsage{
RequestID: requestID,
RequestTokenCount: promptTokens,
ResponseTokenCount: responseTokens,
TokenCost: cost,
Type: "suppliers",
}
err = s.txManager.WithTx(ctx, func(tx pgx.Tx) error {
if err := s.supplierRepo.BulkInsertTx(ctx, tx, requestID, suppliers); err != nil {
return err
}
if err := s.tokenUsageRepo.CreateTx(ctx, tx, tokenUsage); err != nil {
return err
}
if err := s.userRepo.UpdateBalanceTx(ctx, tx, userID, -cost); err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return suppliers, nil
}
func (s *requestService) GetMailingList(ctx context.Context, userID int) ([]*model.Request, error) {
return s.requestRepo.GetByUserID(ctx, userID)
}
func (s *requestService) GetMailingListByID(ctx context.Context, requestID uuid.UUID, userID int) (*model.RequestDetail, error) {
isOwner, err := s.requestRepo.CheckOwnership(ctx, requestID, userID)
if err != nil {
return nil, err
}
if !isOwner {
return nil, errors.NewBusinessError(errors.PermissionDenied, "access denied to this request")
}
return s.requestRepo.GetDetailByID(ctx, requestID)
}