package entities import ( "crypto/rand" "encoding/hex" "errors" "fmt" "sync" "time" "github.com/google/uuid" "github.com/shopspring/decimal" "gorm.io/gorm" ) // ApiCallStatus API调用状态 const ( ApiCallStatusPending = "pending" ApiCallStatusSuccess = "success" ApiCallStatusFailed = "failed" ) // ApiCall错误类型常量定义,供各领域服务和应用层统一引用。 // 使用时可通过 entities.ApiCallErrorInvalidAccess 方式获得,编辑器可自动补全和提示。 // 错误类型与业务含义: // // ApiCallErrorInvalidAccess = "invalid_access" // 无效AccessId // ApiCallErrorFrozenAccount = "frozen_account" // 账户冻结 // ApiCallErrorInvalidIP = "invalid_ip" // IP无效 // ApiCallErrorArrears = "arrears" // 账户欠费 // ApiCallErrorNotSubscribed = "not_subscribed" // 未订阅产品 // ApiCallErrorProductNotFound = "product_not_found" // 产品不存在 // ApiCallErrorProductDisabled = "product_disabled" // 产品已停用 // ApiCallErrorSystem = "system_error" // 系统错误 // ApiCallErrorDatasource = "datasource_error" // 数据源异常 // ApiCallErrorInvalidParam = "invalid_param" // 参数不正确 // ApiCallErrorDecryptFail = "decrypt_fail" // 解密失败 const ( ApiCallErrorInvalidAccess = "invalid_access" // 无效AccessId ApiCallErrorFrozenAccount = "frozen_account" // 账户冻结 ApiCallErrorInvalidIP = "invalid_ip" // IP无效 ApiCallErrorArrears = "arrears" // 账户欠费 ApiCallErrorNotSubscribed = "not_subscribed" // 未订阅产品 ApiCallErrorProductNotFound = "product_not_found" // 产品不存在 ApiCallErrorProductDisabled = "product_disabled" // 产品已停用 ApiCallErrorSystem = "system_error" // 系统错误 ApiCallErrorDatasource = "datasource_error" // 数据源异常 ApiCallErrorInvalidParam = "invalid_param" // 参数不正确 ApiCallErrorDecryptFail = "decrypt_fail" // 解密失败 ApiCallErrorQueryEmpty = "query_empty" // 查询为空 ) // ApiCall API调用(聚合根) type ApiCall struct { ID string `gorm:"type:varchar(64);primaryKey" json:"id"` AccessId string `gorm:"type:varchar(64);not null;index" json:"access_id"` UserId *string `gorm:"type:varchar(36);index" json:"user_id,omitempty"` ProductId *string `gorm:"type:varchar(64);index" json:"product_id,omitempty"` TransactionId string `gorm:"type:varchar(64);not null;uniqueIndex" json:"transaction_id"` ClientIp string `gorm:"type:varchar(64);not null;index" json:"client_ip"` RequestParams string `gorm:"type:text" json:"request_params"` ResponseData *string `gorm:"type:text" json:"response_data,omitempty"` Status string `gorm:"type:varchar(20);not null;default:'pending'" json:"status"` StartAt time.Time `gorm:"not null;index" json:"start_at"` EndAt *time.Time `gorm:"index" json:"end_at,omitempty"` Cost *decimal.Decimal `gorm:"default:0" json:"cost,omitempty"` ErrorType *string `gorm:"type:varchar(32)" json:"error_type,omitempty"` ErrorMsg *string `gorm:"type:varchar(256)" json:"error_msg,omitempty"` CreatedAt time.Time `gorm:"autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"autoUpdateTime" json:"updated_at"` } // NewApiCall 工厂方法 func NewApiCall(accessId, requestParams, clientIp string) (*ApiCall, error) { if accessId == "" { return nil, errors.New("AccessId不能为空") } if requestParams == "" { return nil, errors.New("请求参数不能为空") } if clientIp == "" { return nil, errors.New("ClientIp不能为空") } return &ApiCall{ ID: uuid.New().String(), AccessId: accessId, TransactionId: GenerateTransactionID(), ClientIp: clientIp, RequestParams: requestParams, Status: ApiCallStatusPending, StartAt: time.Now(), }, nil } // MarkSuccess 标记为成功 func (a *ApiCall) MarkSuccess(responseData string, cost decimal.Decimal) error { // 校验除ErrorMsg和ErrorType外所有字段不能为空 if a.ID == "" || a.AccessId == "" || a.TransactionId == "" || a.RequestParams == "" || a.Status == "" || a.StartAt.IsZero() { return errors.New("ApiCall字段不能为空(除ErrorMsg和ErrorType)") } // 可选字段也要有值 if a.UserId == nil || a.ProductId == nil || responseData == "" { return errors.New("ApiCall标记成功时UserId、ProductId、ResponseData不能为空") } a.Status = ApiCallStatusSuccess a.ResponseData = &responseData endAt := time.Now() a.EndAt = &endAt a.Cost = &cost a.ErrorType = nil a.ErrorMsg = nil return nil } // MarkFailed 标记为失败 func (a *ApiCall) MarkFailed(errorType, errorMsg string) { a.Status = ApiCallStatusFailed a.ErrorType = &errorType shortMsg := errorMsg if len(shortMsg) > 120 { shortMsg = shortMsg[:120] } a.ErrorMsg = &shortMsg endAt := time.Now() a.EndAt = &endAt } // Validate 校验ApiCall聚合根的业务规则 func (a *ApiCall) Validate() error { if a.ID == "" { return errors.New("ID不能为空") } if a.AccessId == "" { return errors.New("AccessId不能为空") } if a.TransactionId == "" { return errors.New("TransactionId不能为空") } if a.RequestParams == "" { return errors.New("请求参数不能为空") } if a.Status != ApiCallStatusPending && a.Status != ApiCallStatusSuccess && a.Status != ApiCallStatusFailed { return errors.New("无效的调用状态") } return nil } // 全局计数器,用于确保TransactionID的唯一性 var ( transactionCounter int64 counterMutex sync.Mutex ) // GenerateTransactionID 生成16位数的交易单号 func GenerateTransactionID() string { // 使用互斥锁确保计数器的线程安全 counterMutex.Lock() transactionCounter++ currentCounter := transactionCounter counterMutex.Unlock() // 获取当前时间戳(微秒精度) timestamp := time.Now().UnixMicro() // 组合时间戳和计数器,确保唯一性 combined := fmt.Sprintf("%d%06d", timestamp, currentCounter%1000000) // 如果长度超出16位,截断;如果不够,填充随机字符 if len(combined) >= 16 { return combined[:16] } // 如果长度不够,使用随机字节填充 if len(combined) < 16 { randomBytes := make([]byte, 8) rand.Read(randomBytes) randomHex := hex.EncodeToString(randomBytes) combined += randomHex[:16-len(combined)] } return combined } // TableName 指定数据库表名 func (ApiCall) TableName() string { return "api_calls" } // BeforeCreate GORM钩子:创建前自动生成UUID func (c *ApiCall) BeforeCreate(tx *gorm.DB) error { if c.ID == "" { c.ID = uuid.New().String() } return nil }