217 lines
7.0 KiB
Go
217 lines
7.0 KiB
Go
package service
|
||
|
||
import (
|
||
"bdqr-server/app/main/model"
|
||
"context"
|
||
"database/sql"
|
||
"encoding/json"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/Masterminds/squirrel"
|
||
"github.com/zeromicro/go-zero/core/logx"
|
||
)
|
||
|
||
// TianyuanapiCallLogService 天元API调用记录服务
|
||
type TianyuanapiCallLogService struct {
|
||
tianyuanapiCallLogModel model.TianyuanapiCallLogModel
|
||
featureModel model.FeatureModel
|
||
}
|
||
|
||
// NewTianyuanapiCallLogService 创建天元API调用记录服务
|
||
func NewTianyuanapiCallLogService(
|
||
tianyuanapiCallLogModel model.TianyuanapiCallLogModel,
|
||
featureModel model.FeatureModel,
|
||
) *TianyuanapiCallLogService {
|
||
return &TianyuanapiCallLogService{
|
||
tianyuanapiCallLogModel: tianyuanapiCallLogModel,
|
||
featureModel: featureModel,
|
||
}
|
||
}
|
||
|
||
// CallLogOptions 调用记录选项
|
||
type CallLogOptions struct {
|
||
FeatureID string // 功能ID
|
||
ApiID string // API标识(如:YYSYBE08)
|
||
OrderID string // 订单ID(可选)
|
||
QueryID string // 查询ID(可选)
|
||
CallStatus int64 // 调用状态:0=失败,1=成功
|
||
ResponseTime int64 // 响应耗时(毫秒)
|
||
ErrorCode string // 错误码(失败时)
|
||
ErrorMessage string // 错误信息(失败时)
|
||
RequestParams interface{} // 请求参数(可选)
|
||
ResponseData interface{} // 响应数据(可选)
|
||
TransactionID string // 天元API流水号
|
||
}
|
||
|
||
// RecordCall 记录天元API调用
|
||
func (s *TianyuanapiCallLogService) RecordCall(ctx context.Context, opts CallLogOptions) error {
|
||
// 1. 获取feature的成本价
|
||
costPrice := 0.00
|
||
if opts.CallStatus == 1 { // 只有成功才计算成本
|
||
if opts.FeatureID == "" {
|
||
logx.Infof("记录API调用时feature_id为空,api_id=%s,无法获取成本价", opts.ApiID)
|
||
} else {
|
||
feature, err := s.featureModel.FindOne(ctx, opts.FeatureID)
|
||
if err == nil {
|
||
costPrice = feature.CostPrice
|
||
logx.Infof("记录API调用 - feature_id=%s, api_id=%s, cost_price=%f", opts.FeatureID, opts.ApiID, costPrice)
|
||
} else {
|
||
logx.Errorf("查询feature成本价失败,feature_id=%s, api_id=%s, err=%v", opts.FeatureID, opts.ApiID, err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2. 转换参数和响应为JSON字符串
|
||
var requestParamsStr, responseDataStr *string
|
||
if opts.RequestParams != nil {
|
||
if bytes, err := json.Marshal(opts.RequestParams); err == nil {
|
||
jsonStr := string(bytes)
|
||
requestParamsStr = &jsonStr
|
||
}
|
||
}
|
||
if opts.ResponseData != nil {
|
||
// 只记录响应数据的前1000个字符,避免存储过大
|
||
if bytes, err := json.Marshal(opts.ResponseData); err == nil {
|
||
jsonStr := string(bytes)
|
||
if len(jsonStr) > 1000 {
|
||
jsonStr = jsonStr[:1000] + "...[truncated]"
|
||
}
|
||
responseDataStr = &jsonStr
|
||
}
|
||
}
|
||
|
||
// 3. 构建调用记录
|
||
callTime := time.Now()
|
||
deleteTime := sql.NullTime{}
|
||
callLog := &model.TianyuanapiCallLog{
|
||
FeatureId: opts.FeatureID,
|
||
ApiId: opts.ApiID,
|
||
OrderId: sql.NullString{},
|
||
QueryId: sql.NullString{},
|
||
CallStatus: opts.CallStatus,
|
||
CallTime: callTime,
|
||
ResponseTime: sql.NullInt64{},
|
||
CostPrice: costPrice,
|
||
ErrorCode: sql.NullString{},
|
||
ErrorMessage: sql.NullString{},
|
||
RequestParams: sql.NullString{},
|
||
ResponseData: sql.NullString{},
|
||
TransactionId: sql.NullString{},
|
||
CreateTime: callTime,
|
||
UpdateTime: callTime,
|
||
DeleteTime: deleteTime,
|
||
DelState: 0,
|
||
Version: 0,
|
||
}
|
||
|
||
// 4. 填充可选字段
|
||
if opts.OrderID != "" {
|
||
callLog.OrderId = sql.NullString{String: opts.OrderID, Valid: true}
|
||
}
|
||
if opts.QueryID != "" {
|
||
callLog.QueryId = sql.NullString{String: opts.QueryID, Valid: true}
|
||
}
|
||
if opts.ResponseTime > 0 {
|
||
callLog.ResponseTime = sql.NullInt64{Int64: opts.ResponseTime, Valid: true}
|
||
}
|
||
if opts.ErrorCode != "" {
|
||
callLog.ErrorCode = sql.NullString{String: opts.ErrorCode, Valid: true}
|
||
}
|
||
if opts.ErrorMessage != "" {
|
||
callLog.ErrorMessage = sql.NullString{String: opts.ErrorMessage, Valid: true}
|
||
}
|
||
if requestParamsStr != nil {
|
||
callLog.RequestParams = sql.NullString{String: *requestParamsStr, Valid: true}
|
||
}
|
||
if responseDataStr != nil {
|
||
callLog.ResponseData = sql.NullString{String: *responseDataStr, Valid: true}
|
||
}
|
||
if opts.TransactionID != "" {
|
||
callLog.TransactionId = sql.NullString{String: opts.TransactionID, Valid: true}
|
||
}
|
||
|
||
// 5. 插入记录
|
||
_, err := s.tianyuanapiCallLogModel.Insert(ctx, nil, callLog)
|
||
if err != nil {
|
||
logx.Errorf("插入天元API调用记录失败,feature_id=%s, api_id=%s, err=%v", opts.FeatureID, opts.ApiID, err)
|
||
return fmt.Errorf("插入调用记录失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GetStatistics 获取统计信息
|
||
type StatisticsFilter struct {
|
||
FeatureID string // 功能ID
|
||
ApiID string // API标识
|
||
StartDate time.Time // 开始日期
|
||
EndDate time.Time // 结束日期
|
||
}
|
||
|
||
// Statistics 统计信息
|
||
type Statistics struct {
|
||
TotalCalls int64 // 总调用次数
|
||
SuccessCalls int64 // 成功次数
|
||
FailedCalls int64 // 失败次数
|
||
TotalCost float64 // 总成本(成功调用的成本之和)
|
||
}
|
||
|
||
// GetStatistics 获取统计信息
|
||
func (s *TianyuanapiCallLogService) GetStatistics(ctx context.Context, filter StatisticsFilter) (*Statistics, error) {
|
||
builder := s.tianyuanapiCallLogModel.SelectBuilder()
|
||
|
||
// 添加过滤条件
|
||
if filter.FeatureID != "" {
|
||
builder = builder.Where(squirrel.Eq{"feature_id": filter.FeatureID})
|
||
}
|
||
if filter.ApiID != "" {
|
||
builder = builder.Where(squirrel.Eq{"api_id": filter.ApiID})
|
||
}
|
||
if !filter.StartDate.IsZero() {
|
||
builder = builder.Where(squirrel.GtOrEq{"call_time": filter.StartDate})
|
||
logx.Infof("API成本统计 - 开始时间: %v", filter.StartDate)
|
||
}
|
||
if !filter.EndDate.IsZero() {
|
||
builder = builder.Where(squirrel.Lt{"call_time": filter.EndDate})
|
||
logx.Infof("API成本统计 - 结束时间: %v", filter.EndDate)
|
||
}
|
||
|
||
// 统计总调用次数
|
||
totalCalls, err := s.tianyuanapiCallLogModel.FindCount(ctx, builder, "id")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("统计总调用次数失败: %w", err)
|
||
}
|
||
logx.Infof("API成本统计 - 总调用次数: %d", totalCalls)
|
||
|
||
// 统计成功次数
|
||
successBuilder := builder.Where(squirrel.Eq{"call_status": 1})
|
||
successCalls, err := s.tianyuanapiCallLogModel.FindCount(ctx, successBuilder, "id")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("统计成功次数失败: %w", err)
|
||
}
|
||
logx.Infof("API成本统计 - 成功调用次数: %d", successCalls)
|
||
|
||
// 统计失败次数
|
||
failedCalls := totalCalls - successCalls
|
||
|
||
// 统计总成本(仅成功调用)
|
||
// 先打印SQL以便调试(复制builder避免影响后续查询)
|
||
debugBuilder := successBuilder
|
||
query, values, _ := debugBuilder.Columns("IFNULL(SUM(cost_price),0)").Where("del_state = ?", 0).ToSql()
|
||
logx.Infof("API成本统计 - SQL: %s, 参数: %v", query, values)
|
||
|
||
totalCost, err := s.tianyuanapiCallLogModel.FindSum(ctx, successBuilder, "cost_price")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("统计总成本失败: %w", err)
|
||
}
|
||
logx.Infof("API成本统计 - 总成本: %f", totalCost)
|
||
|
||
return &Statistics{
|
||
TotalCalls: totalCalls,
|
||
SuccessCalls: successCalls,
|
||
FailedCalls: failedCalls,
|
||
TotalCost: totalCost,
|
||
}, nil
|
||
}
|