package billing import ( "errors" "strconv" "juhe-factory/api/internal/model" "github.com/google/uuid" "gorm.io/gorm" ) // PrechargeGenerationTask creates a task and reserves its full quoted cost in // the same transaction. A zero price is allowed only when the model has an // explicit zero-price record; callers must reject a missing price record. func PrechargeGenerationTask(tx *gorm.DB, task *model.GenerationTask, unitPrice string, quantity int, remark string) error { price, err := strconv.ParseFloat(unitPrice, 64) if err != nil || price < 0 || quantity <= 0 { return errors.New("生成任务价格无效") } var amount string if err := tx.Raw("SELECT round(?::numeric * ?::numeric,2)::text", unitPrice, quantity).Scan(&amount).Error; err != nil { return err } if amount == "" { return errors.New("生成任务价格无效") } task.EstimatedPoints = amount task.PrepaidPoints = amount if task.ID == uuid.Nil { task.ID = uuid.New() } if amountValue, _ := strconv.ParseFloat(amount, 64); amountValue > 0 { if _, err := DebitPoints(tx, task.UserID, amount, "generation_hold", task.ID.String(), remark, "generation:hold:"+task.ID.String()); err != nil { return err } } if err := tx.Create(task).Error; err != nil { return err } return nil } // RefundGenerationTask refunds a prepaid task exactly once. The caller must // hold a row lock on task inside the same transaction. func RefundGenerationTask(tx *gorm.DB, task *model.GenerationTask, remark string) (bool, error) { amount, _ := strconv.ParseFloat(task.PrepaidPoints, 64) if task.CostRefunded || amount <= 0 { return false, nil } refunded, err := RefundDebit(tx, task.UserID, "generation:hold:"+task.ID.String(), "generation:refund:"+task.ID.String(), "generation_refund", task.ID.String(), remark) if err != nil { return false, err } task.CostRefunded = true return refunded, nil }