add service
All checks were successful
Deploy Smart Search Backend Test / deploy (push) Successful in 2m25s
All checks were successful
Deploy Smart Search Backend Test / deploy (push) Successful in 2m25s
This commit is contained in:
@@ -44,8 +44,13 @@ message GetBalanceStatisticsRequest {
|
|||||||
int64 user_id = 1;
|
int64 user_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetBalanceStatisticsResponse {
|
message WriteOffHistoryItem {
|
||||||
double balance = 1;
|
string operation_id = 1;
|
||||||
int32 total_requests = 2;
|
string data = 2;
|
||||||
double total_spent = 3;
|
double amount = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetBalanceStatisticsResponse {
|
||||||
|
double average_cost = 1;
|
||||||
|
repeated WriteOffHistoryItem write_off_history = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func NewHandlers(pool *pgxpool.Pool, jwtSecret, cryptoSecret, openAIKey, perplex
|
|||||||
perplexityClient := ai.NewPerplexityClient(perplexityKey)
|
perplexityClient := ai.NewPerplexityClient(perplexityKey)
|
||||||
|
|
||||||
authService := service.NewAuthService(userRepo, sessionRepo, inviteRepo, txManager, jwtSecret, cryptoSecret)
|
authService := service.NewAuthService(userRepo, sessionRepo, inviteRepo, txManager, jwtSecret, cryptoSecret)
|
||||||
userService := service.NewUserService(userRepo, requestRepo, cryptoSecret)
|
userService := service.NewUserService(userRepo, requestRepo, tokenUsageRepo, cryptoSecret)
|
||||||
inviteService := service.NewInviteService(inviteRepo, userRepo, txManager)
|
inviteService := service.NewInviteService(inviteRepo, userRepo, txManager)
|
||||||
requestService := service.NewRequestService(requestRepo, supplierRepo, tokenUsageRepo, userRepo, openAIClient, perplexityClient, txManager)
|
requestService := service.NewRequestService(requestRepo, supplierRepo, tokenUsageRepo, userRepo, openAIClient, perplexityClient, txManager)
|
||||||
supplierService := service.NewSupplierService(supplierRepo, requestRepo)
|
supplierService := service.NewSupplierService(supplierRepo, requestRepo)
|
||||||
|
|||||||
@@ -48,19 +48,22 @@ func (h *UserHandler) GetStatistics(ctx context.Context, req *pb.GetStatisticsRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *UserHandler) GetBalanceStatistics(ctx context.Context, req *pb.GetBalanceStatisticsRequest) (*pb.GetBalanceStatisticsResponse, error) {
|
func (h *UserHandler) GetBalanceStatistics(ctx context.Context, req *pb.GetBalanceStatisticsRequest) (*pb.GetBalanceStatisticsResponse, error) {
|
||||||
balance, err := h.userService.GetBalance(ctx, int(req.UserId))
|
stats, err := h.userService.GetBalanceStatistics(ctx, int(req.UserId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetBalanceStatistics")
|
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetBalanceStatistics")
|
||||||
}
|
}
|
||||||
|
|
||||||
stats, err := h.userService.GetStatistics(ctx, int(req.UserId))
|
history := make([]*pb.WriteOffHistoryItem, 0, len(stats.WriteOffHistory))
|
||||||
if err != nil {
|
for _, item := range stats.WriteOffHistory {
|
||||||
return nil, errors.ToGRPCError(err, h.logger, "UserService.GetBalanceStatistics")
|
history = append(history, &pb.WriteOffHistoryItem{
|
||||||
|
OperationId: item.OperationID,
|
||||||
|
Data: item.Data,
|
||||||
|
Amount: item.Amount,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return &pb.GetBalanceStatisticsResponse{
|
return &pb.GetBalanceStatisticsResponse{
|
||||||
Balance: balance,
|
AverageCost: stats.AverageCost,
|
||||||
TotalRequests: int32(stats.RequestsCount),
|
WriteOffHistory: history,
|
||||||
TotalSpent: 0,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,3 +26,9 @@ type TokenUsage struct {
|
|||||||
Type string
|
Type string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WriteOffHistory struct {
|
||||||
|
OperationID string
|
||||||
|
Data string
|
||||||
|
Amount float64
|
||||||
|
}
|
||||||
|
|||||||
@@ -64,4 +64,5 @@ type SupplierRepository interface {
|
|||||||
type TokenUsageRepository interface {
|
type TokenUsageRepository interface {
|
||||||
Create(ctx context.Context, usage *model.TokenUsage) error
|
Create(ctx context.Context, usage *model.TokenUsage) error
|
||||||
CreateTx(ctx context.Context, tx pgx.Tx, usage *model.TokenUsage) error
|
CreateTx(ctx context.Context, tx pgx.Tx, usage *model.TokenUsage) error
|
||||||
|
GetBalanceStatistics(ctx context.Context, userID int) (averageCost float64, history []*model.WriteOffHistory, err error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"git.techease.ru/Smart-search/smart-search-back/internal/model"
|
"git.techease.ru/Smart-search/smart-search-back/internal/model"
|
||||||
errs "git.techease.ru/Smart-search/smart-search-back/pkg/errors"
|
errs "git.techease.ru/Smart-search/smart-search-back/pkg/errors"
|
||||||
@@ -49,3 +50,59 @@ func (r *tokenUsageRepository) createWithExecutor(ctx context.Context, exec DBTX
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *tokenUsageRepository) GetBalanceStatistics(ctx context.Context, userID int) (float64, []*model.WriteOffHistory, error) {
|
||||||
|
avgQuery := r.qb.Select("COALESCE(AVG(COALESCE(rtu.token_cost, 0)), 0)").
|
||||||
|
From("request_token_usage rtu").
|
||||||
|
Join("requests_for_suppliers rfs ON rtu.request_id = rfs.id").
|
||||||
|
Where(sq.Eq{"rfs.user_id": userID})
|
||||||
|
|
||||||
|
avgSQL, avgArgs, err := avgQuery.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, errs.NewInternalError(errs.DatabaseError, "failed to build average query", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var averageCost float64
|
||||||
|
if err := r.pool.QueryRow(ctx, avgSQL, avgArgs...).Scan(&averageCost); err != nil {
|
||||||
|
return 0, nil, errs.NewInternalError(errs.DatabaseError, "failed to get average cost", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
historyQuery := r.qb.Select(
|
||||||
|
"rtu.id",
|
||||||
|
"TO_CHAR(rtu.created_at, 'DD-MM-YYYY')",
|
||||||
|
"COALESCE(rtu.token_cost, 0)",
|
||||||
|
).
|
||||||
|
From("request_token_usage rtu").
|
||||||
|
Join("requests_for_suppliers rfs ON rtu.request_id = rfs.id").
|
||||||
|
Where(sq.Eq{"rfs.user_id": userID}).
|
||||||
|
OrderBy("rtu.created_at DESC").
|
||||||
|
Limit(8)
|
||||||
|
|
||||||
|
historySQL, historyArgs, err := historyQuery.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, errs.NewInternalError(errs.DatabaseError, "failed to build history query", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := r.pool.Query(ctx, historySQL, historyArgs...)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, errs.NewInternalError(errs.DatabaseError, "failed to get write-off history", err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var history []*model.WriteOffHistory
|
||||||
|
for rows.Next() {
|
||||||
|
var operationID int
|
||||||
|
var item model.WriteOffHistory
|
||||||
|
if err := rows.Scan(&operationID, &item.Data, &item.Amount); err != nil {
|
||||||
|
return 0, nil, errs.NewInternalError(errs.DatabaseError, "failed to scan write-off history", err)
|
||||||
|
}
|
||||||
|
item.OperationID = strconv.Itoa(operationID)
|
||||||
|
history = append(history, &item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return 0, nil, errs.NewInternalError(errs.DatabaseError, "failed to iterate write-off history", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return averageCost, history, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type UserService interface {
|
|||||||
GetInfo(ctx context.Context, userID int) (*UserInfo, error)
|
GetInfo(ctx context.Context, userID int) (*UserInfo, error)
|
||||||
GetBalance(ctx context.Context, userID int) (float64, error)
|
GetBalance(ctx context.Context, userID int) (float64, error)
|
||||||
GetStatistics(ctx context.Context, userID int) (*Statistics, error)
|
GetStatistics(ctx context.Context, userID int) (*Statistics, error)
|
||||||
|
GetBalanceStatistics(ctx context.Context, userID int) (*BalanceStatistics, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type InviteService interface {
|
type InviteService interface {
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"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/internal/repository"
|
||||||
"git.techease.ru/Smart-search/smart-search-back/pkg/crypto"
|
"git.techease.ru/Smart-search/smart-search-back/pkg/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userService struct {
|
type userService struct {
|
||||||
userRepo repository.UserRepository
|
userRepo repository.UserRepository
|
||||||
requestRepo repository.RequestRepository
|
requestRepo repository.RequestRepository
|
||||||
cryptoHelper *crypto.Crypto
|
tokenUsageRepo repository.TokenUsageRepository
|
||||||
|
cryptoHelper *crypto.Crypto
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
@@ -27,11 +29,17 @@ type Statistics struct {
|
|||||||
CreatedTZ int
|
CreatedTZ int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserService(userRepo repository.UserRepository, requestRepo repository.RequestRepository, cryptoSecret string) UserService {
|
type BalanceStatistics struct {
|
||||||
|
AverageCost float64
|
||||||
|
WriteOffHistory []*model.WriteOffHistory
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserService(userRepo repository.UserRepository, requestRepo repository.RequestRepository, tokenUsageRepo repository.TokenUsageRepository, cryptoSecret string) UserService {
|
||||||
return &userService{
|
return &userService{
|
||||||
userRepo: userRepo,
|
userRepo: userRepo,
|
||||||
requestRepo: requestRepo,
|
requestRepo: requestRepo,
|
||||||
cryptoHelper: crypto.NewCrypto(cryptoSecret),
|
tokenUsageRepo: tokenUsageRepo,
|
||||||
|
cryptoHelper: crypto.NewCrypto(cryptoSecret),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,3 +89,15 @@ func (s *userService) GetStatistics(ctx context.Context, userID int) (*Statistic
|
|||||||
CreatedTZ: createdTZ,
|
CreatedTZ: createdTZ,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *userService) GetBalanceStatistics(ctx context.Context, userID int) (*BalanceStatistics, error) {
|
||||||
|
averageCost, history, err := s.tokenUsageRepo.GetBalanceStatistics(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BalanceStatistics{
|
||||||
|
AverageCost: averageCost,
|
||||||
|
WriteOffHistory: history,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -385,18 +385,77 @@ func (x *GetBalanceStatisticsRequest) GetUserId() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetBalanceStatisticsResponse struct {
|
type WriteOffHistoryItem struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Balance float64 `protobuf:"fixed64,1,opt,name=balance,proto3" json:"balance,omitempty"`
|
OperationId string `protobuf:"bytes,1,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"`
|
||||||
TotalRequests int32 `protobuf:"varint,2,opt,name=total_requests,json=totalRequests,proto3" json:"total_requests,omitempty"`
|
Data string `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||||
TotalSpent float64 `protobuf:"fixed64,3,opt,name=total_spent,json=totalSpent,proto3" json:"total_spent,omitempty"`
|
Amount float64 `protobuf:"fixed64,3,opt,name=amount,proto3" json:"amount,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *WriteOffHistoryItem) Reset() {
|
||||||
|
*x = WriteOffHistoryItem{}
|
||||||
|
mi := &file_user_user_proto_msgTypes[7]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WriteOffHistoryItem) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*WriteOffHistoryItem) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *WriteOffHistoryItem) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_user_user_proto_msgTypes[7]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use WriteOffHistoryItem.ProtoReflect.Descriptor instead.
|
||||||
|
func (*WriteOffHistoryItem) Descriptor() ([]byte, []int) {
|
||||||
|
return file_user_user_proto_rawDescGZIP(), []int{7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WriteOffHistoryItem) GetOperationId() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.OperationId
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WriteOffHistoryItem) GetData() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Data
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *WriteOffHistoryItem) GetAmount() float64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Amount
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetBalanceStatisticsResponse struct {
|
||||||
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
|
AverageCost float64 `protobuf:"fixed64,1,opt,name=average_cost,json=averageCost,proto3" json:"average_cost,omitempty"`
|
||||||
|
WriteOffHistory []*WriteOffHistoryItem `protobuf:"bytes,2,rep,name=write_off_history,json=writeOffHistory,proto3" json:"write_off_history,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
}
|
||||||
|
|
||||||
func (x *GetBalanceStatisticsResponse) Reset() {
|
func (x *GetBalanceStatisticsResponse) Reset() {
|
||||||
*x = GetBalanceStatisticsResponse{}
|
*x = GetBalanceStatisticsResponse{}
|
||||||
mi := &file_user_user_proto_msgTypes[7]
|
mi := &file_user_user_proto_msgTypes[8]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -408,7 +467,7 @@ func (x *GetBalanceStatisticsResponse) String() string {
|
|||||||
func (*GetBalanceStatisticsResponse) ProtoMessage() {}
|
func (*GetBalanceStatisticsResponse) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *GetBalanceStatisticsResponse) ProtoReflect() protoreflect.Message {
|
func (x *GetBalanceStatisticsResponse) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_user_user_proto_msgTypes[7]
|
mi := &file_user_user_proto_msgTypes[8]
|
||||||
if x != nil {
|
if x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -421,28 +480,21 @@ func (x *GetBalanceStatisticsResponse) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use GetBalanceStatisticsResponse.ProtoReflect.Descriptor instead.
|
// Deprecated: Use GetBalanceStatisticsResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*GetBalanceStatisticsResponse) Descriptor() ([]byte, []int) {
|
func (*GetBalanceStatisticsResponse) Descriptor() ([]byte, []int) {
|
||||||
return file_user_user_proto_rawDescGZIP(), []int{7}
|
return file_user_user_proto_rawDescGZIP(), []int{8}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetBalanceStatisticsResponse) GetBalance() float64 {
|
func (x *GetBalanceStatisticsResponse) GetAverageCost() float64 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Balance
|
return x.AverageCost
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetBalanceStatisticsResponse) GetTotalRequests() int32 {
|
func (x *GetBalanceStatisticsResponse) GetWriteOffHistory() []*WriteOffHistoryItem {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.TotalRequests
|
return x.WriteOffHistory
|
||||||
}
|
}
|
||||||
return 0
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
func (x *GetBalanceStatisticsResponse) GetTotalSpent() float64 {
|
|
||||||
if x != nil {
|
|
||||||
return x.TotalSpent
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var File_user_user_proto protoreflect.FileDescriptor
|
var File_user_user_proto protoreflect.FileDescriptor
|
||||||
@@ -471,12 +523,14 @@ const file_user_user_proto_rawDesc = "" +
|
|||||||
"\vtotal_spent\x18\x04 \x01(\x01R\n" +
|
"\vtotal_spent\x18\x04 \x01(\x01R\n" +
|
||||||
"totalSpent\"6\n" +
|
"totalSpent\"6\n" +
|
||||||
"\x1bGetBalanceStatisticsRequest\x12\x17\n" +
|
"\x1bGetBalanceStatisticsRequest\x12\x17\n" +
|
||||||
"\auser_id\x18\x01 \x01(\x03R\x06userId\"\x80\x01\n" +
|
"\auser_id\x18\x01 \x01(\x03R\x06userId\"d\n" +
|
||||||
"\x1cGetBalanceStatisticsResponse\x12\x18\n" +
|
"\x13WriteOffHistoryItem\x12!\n" +
|
||||||
"\abalance\x18\x01 \x01(\x01R\abalance\x12%\n" +
|
"\foperation_id\x18\x01 \x01(\tR\voperationId\x12\x12\n" +
|
||||||
"\x0etotal_requests\x18\x02 \x01(\x05R\rtotalRequests\x12\x1f\n" +
|
"\x04data\x18\x02 \x01(\tR\x04data\x12\x16\n" +
|
||||||
"\vtotal_spent\x18\x03 \x01(\x01R\n" +
|
"\x06amount\x18\x03 \x01(\x01R\x06amount\"\x88\x01\n" +
|
||||||
"totalSpent2\xaf\x02\n" +
|
"\x1cGetBalanceStatisticsResponse\x12!\n" +
|
||||||
|
"\faverage_cost\x18\x01 \x01(\x01R\vaverageCost\x12E\n" +
|
||||||
|
"\x11write_off_history\x18\x02 \x03(\v2\x19.user.WriteOffHistoryItemR\x0fwriteOffHistory2\xaf\x02\n" +
|
||||||
"\vUserService\x126\n" +
|
"\vUserService\x126\n" +
|
||||||
"\aGetInfo\x12\x14.user.GetInfoRequest\x1a\x15.user.GetInfoResponse\x12?\n" +
|
"\aGetInfo\x12\x14.user.GetInfoRequest\x1a\x15.user.GetInfoResponse\x12?\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
@@ -496,7 +550,7 @@ func file_user_user_proto_rawDescGZIP() []byte {
|
|||||||
return file_user_user_proto_rawDescData
|
return file_user_user_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_user_user_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
|
var file_user_user_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||||
var file_user_user_proto_goTypes = []any{
|
var file_user_user_proto_goTypes = []any{
|
||||||
(*GetInfoRequest)(nil), // 0: user.GetInfoRequest
|
(*GetInfoRequest)(nil), // 0: user.GetInfoRequest
|
||||||
(*GetInfoResponse)(nil), // 1: user.GetInfoResponse
|
(*GetInfoResponse)(nil), // 1: user.GetInfoResponse
|
||||||
@@ -505,22 +559,24 @@ var file_user_user_proto_goTypes = []any{
|
|||||||
(*GetStatisticsRequest)(nil), // 4: user.GetStatisticsRequest
|
(*GetStatisticsRequest)(nil), // 4: user.GetStatisticsRequest
|
||||||
(*GetStatisticsResponse)(nil), // 5: user.GetStatisticsResponse
|
(*GetStatisticsResponse)(nil), // 5: user.GetStatisticsResponse
|
||||||
(*GetBalanceStatisticsRequest)(nil), // 6: user.GetBalanceStatisticsRequest
|
(*GetBalanceStatisticsRequest)(nil), // 6: user.GetBalanceStatisticsRequest
|
||||||
(*GetBalanceStatisticsResponse)(nil), // 7: user.GetBalanceStatisticsResponse
|
(*WriteOffHistoryItem)(nil), // 7: user.WriteOffHistoryItem
|
||||||
|
(*GetBalanceStatisticsResponse)(nil), // 8: user.GetBalanceStatisticsResponse
|
||||||
}
|
}
|
||||||
var file_user_user_proto_depIdxs = []int32{
|
var file_user_user_proto_depIdxs = []int32{
|
||||||
0, // 0: user.UserService.GetInfo:input_type -> user.GetInfoRequest
|
7, // 0: user.GetBalanceStatisticsResponse.write_off_history:type_name -> user.WriteOffHistoryItem
|
||||||
2, // 1: user.UserService.GetBalance:input_type -> user.GetBalanceRequest
|
0, // 1: user.UserService.GetInfo:input_type -> user.GetInfoRequest
|
||||||
4, // 2: user.UserService.GetStatistics:input_type -> user.GetStatisticsRequest
|
2, // 2: user.UserService.GetBalance:input_type -> user.GetBalanceRequest
|
||||||
6, // 3: user.UserService.GetBalanceStatistics:input_type -> user.GetBalanceStatisticsRequest
|
4, // 3: user.UserService.GetStatistics:input_type -> user.GetStatisticsRequest
|
||||||
1, // 4: user.UserService.GetInfo:output_type -> user.GetInfoResponse
|
6, // 4: user.UserService.GetBalanceStatistics:input_type -> user.GetBalanceStatisticsRequest
|
||||||
3, // 5: user.UserService.GetBalance:output_type -> user.GetBalanceResponse
|
1, // 5: user.UserService.GetInfo:output_type -> user.GetInfoResponse
|
||||||
5, // 6: user.UserService.GetStatistics:output_type -> user.GetStatisticsResponse
|
3, // 6: user.UserService.GetBalance:output_type -> user.GetBalanceResponse
|
||||||
7, // 7: user.UserService.GetBalanceStatistics:output_type -> user.GetBalanceStatisticsResponse
|
5, // 7: user.UserService.GetStatistics:output_type -> user.GetStatisticsResponse
|
||||||
4, // [4:8] is the sub-list for method output_type
|
8, // 8: user.UserService.GetBalanceStatistics:output_type -> user.GetBalanceStatisticsResponse
|
||||||
0, // [0:4] is the sub-list for method input_type
|
5, // [5:9] is the sub-list for method output_type
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
1, // [1:5] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
0, // [0:0] is the sub-list for field type_name
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_user_user_proto_init() }
|
func init() { file_user_user_proto_init() }
|
||||||
@@ -534,7 +590,7 @@ func file_user_user_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_user_proto_rawDesc), len(file_user_user_proto_rawDesc)),
|
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_user_proto_rawDesc), len(file_user_user_proto_rawDesc)),
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 8,
|
NumMessages: 9,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -233,6 +233,64 @@ func (s *IntegrationSuite) TestRepository_TokenUsageCreate() {
|
|||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *IntegrationSuite) TestRepository_TokenUsageGetBalanceStatistics() {
|
||||||
|
tokenRepo := repository.NewTokenUsageRepository(s.pool)
|
||||||
|
requestRepo := repository.NewRequestRepository(s.pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
var userID int
|
||||||
|
err := s.pool.QueryRow(ctx, "SELECT id FROM users LIMIT 1").Scan(&userID)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
req := &model.Request{
|
||||||
|
UserID: userID,
|
||||||
|
RequestTxt: "Test request for balance statistics",
|
||||||
|
}
|
||||||
|
err = requestRepo.Create(ctx, req)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
usage1 := &model.TokenUsage{
|
||||||
|
RequestID: req.ID,
|
||||||
|
RequestTokenCount: 100,
|
||||||
|
ResponseTokenCount: 50,
|
||||||
|
TokenCost: 10.0,
|
||||||
|
Type: "openai",
|
||||||
|
}
|
||||||
|
err = tokenRepo.Create(ctx, usage1)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
usage2 := &model.TokenUsage{
|
||||||
|
RequestID: req.ID,
|
||||||
|
RequestTokenCount: 200,
|
||||||
|
ResponseTokenCount: 100,
|
||||||
|
TokenCost: 20.0,
|
||||||
|
Type: "perplexity",
|
||||||
|
}
|
||||||
|
err = tokenRepo.Create(ctx, usage2)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
averageCost, history, err := tokenRepo.GetBalanceStatistics(ctx, userID)
|
||||||
|
s.NoError(err)
|
||||||
|
s.Greater(averageCost, 0.0)
|
||||||
|
s.GreaterOrEqual(len(history), 2)
|
||||||
|
|
||||||
|
for _, item := range history {
|
||||||
|
s.NotEmpty(item.OperationID)
|
||||||
|
s.NotEmpty(item.Data)
|
||||||
|
s.GreaterOrEqual(item.Amount, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IntegrationSuite) TestRepository_TokenUsageGetBalanceStatisticsEmpty() {
|
||||||
|
tokenRepo := repository.NewTokenUsageRepository(s.pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
averageCost, history, err := tokenRepo.GetBalanceStatistics(ctx, 999999)
|
||||||
|
s.NoError(err)
|
||||||
|
s.Equal(0.0, averageCost)
|
||||||
|
s.Empty(history)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *IntegrationSuite) TestRepository_UserCreate() {
|
func (s *IntegrationSuite) TestRepository_UserCreate() {
|
||||||
userRepo := repository.NewUserRepository(s.pool, testCryptoSecret)
|
userRepo := repository.NewUserRepository(s.pool, testCryptoSecret)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
@@ -72,8 +72,7 @@ func (s *IntegrationSuite) TestUserHandler_GetBalanceStatistics() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.NotNil(resp)
|
s.NotNil(resp)
|
||||||
s.GreaterOrEqual(resp.Balance, 0.0)
|
s.GreaterOrEqual(resp.AverageCost, 0.0)
|
||||||
s.GreaterOrEqual(resp.TotalRequests, int32(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *IntegrationSuite) TestUserHandler_GetInfoWithValidUser() {
|
func (s *IntegrationSuite) TestUserHandler_GetInfoWithValidUser() {
|
||||||
|
|||||||
Reference in New Issue
Block a user