Files
team-api/relay/common/task_provider.go
T
chengqian 108078614d fix: 金额 float64 → decimal 全链路迁移,消除浮点累积漂移
将全库资金金额从 float64 迁移到 shopspring/decimal,落定边界架构:
入口 NewFromFloat → 内部多步运算全程 decimal → 出口 InexactFloat64;
DB NUMERIC(20,10) 走 driver.Valuer 精确直传,Redis 钱包用整数 micro-USD,
CNY→USD 换汇仅在 payment.FulfillOrder recharge 分支。

- money.go 重写为 decimal 原生(运算/收敛/比较/边界/micro 工具)
- billing/tenant/admin/payment/open/task 各层金额运算 decimal 化
- entity/do 资金列重生成为 decimal.Decimal(可空列 *decimal.Decimal)
- 000013 迁移:非金额 NUMERIC 列 → double precision(5 表 13 列)
- Res 出口 .InexactFloat64() 收敛,JSONB 快照缝隙显式转 float64
- CLAUDE.md 新增 decimal 强约束;补充迁移基线/完成总结文档
2026-07-23 22:08:54 +08:00

88 lines
3.1 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 common
import (
"context"
"encoding/json"
"time"
"github.com/shopspring/decimal"
)
// TaskDataProvider 异步任务数据持久化接口
type TaskDataProvider interface {
// CreateTask 创建异步任务记录
CreateTask(ctx context.Context, task *AsyncTask) error
// UpdateTask 更新任务记录
UpdateTask(ctx context.Context, task *AsyncTask) error
// UpdateTaskCAS CAS 状态更新(防重复结算)
UpdateTaskCAS(ctx context.Context, task *AsyncTask, oldStatus string) error
// GetTaskByPublicID 根据公开任务 ID 查询任务
GetTaskByPublicID(ctx context.Context, publicTaskID string) (*AsyncTask, error)
// GetTaskByPublicIDAndUser 根据公开任务 ID + 用户 ID 查询(客户端查询)
GetTaskByPublicIDAndUser(ctx context.Context, publicTaskID string, userID int64, tenantID int64) (*AsyncTask, error)
// GetNonTerminalTasks 获取所有非终态任务(轮询用)
GetNonTerminalTasks(ctx context.Context, limit int) ([]*AsyncTask, error)
// GetTimedOutTasks 获取超时未完成任务
GetTimedOutTasks(ctx context.Context, cutoffUnix int64, limit int) ([]*AsyncTask, error)
// GetUnsettledTasks 获取终态但未结算的任务(结算重试)
GetUnsettledTasks(ctx context.Context, limit int) ([]*AsyncTask, error)
// GetChannelByID 获取渠道基本信息
GetChannelByID(ctx context.Context, channelID int64) (*ChannelBasicInfo, error)
}
// AsyncTask 异步任务数据结构
type AsyncTask struct {
ID int64 `json:"id"`
PublicTaskID string `json:"public_task_id"` // task_xxxxx
RequestID string `json:"request_id"` // 原始请求 ID(req_xxxxx)
Platform string `json:"platform"` // sora, kling, suno
Action string `json:"action"` // generate, music, lyrics
Status string `json:"status"`
Progress string `json:"progress"`
FailReason string `json:"fail_reason,omitempty"`
TenantID int64 `json:"tenant_id"`
UserID int64 `json:"user_id"`
ApiKeyID int64 `json:"api_key_id"`
ChannelID int64 `json:"channel_id"`
ModelName string `json:"model_name"`
UpstreamModel string `json:"upstream_model,omitempty"`
PreDeductAmount decimal.Decimal `json:"pre_deduct_amount"`
ActualCost decimal.Decimal `json:"actual_cost"`
BillingSettled bool `json:"billing_settled"`
PromptTokens int `json:"prompt_tokens,omitempty"`
CompletionTokens int `json:"completion_tokens,omitempty"`
TotalTokens int `json:"total_tokens,omitempty"`
ResultURL string `json:"result_url,omitempty"`
Data json.RawMessage `json:"data,omitempty"` // 上游原始响应
PrivateData json.RawMessage `json:"private_data,omitempty"` // 上游任务 ID、API Key 等敏感数据
SubmitTime *time.Time `json:"submit_time,omitempty"`
StartTime *time.Time `json:"start_time,omitempty"`
FinishTime *time.Time `json:"finish_time,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ChannelBasicInfo 渠道基本信息(轮询时查询)
type ChannelBasicInfo struct {
ID int64
Type int // ProviderType
Name string
BaseURL string
ApiKey string
Settings json.RawMessage
}