完善生图5.0的功能
This commit is contained in:
1 parent
88a4268d40
commit
678d213f40
31 files changed
+3281
-107
No files matched your search
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"git.nianxx.cn/wangxuming/NianAIGC/backend/internal/billing"
|
||||
@@ -161,8 +162,15 @@ func (c *CreationCoordinator) charge(ctx context.Context, job jobs.Job, reused b
|
||||
return c.writeCharge(ctx, job, reused, charge.raw)
|
||||
}
|
||||
var use usageContext
|
||||
if json.Unmarshal(job.UsageContext, &use) != nil || use.OrganizationID == "" || charge.AmountFen <= 0 {
|
||||
return c.failCharge(ctx, job, reused, errors.New("charge generation job"), charge.raw)
|
||||
if json.Unmarshal(job.UsageContext, &use) != nil || charge.AmountFen <= 0 {
|
||||
cause := billing.ErrProviderUnavailable
|
||||
logGenerationBillingFailure(job.ID, "validate", cause, cause)
|
||||
return c.failCharge(ctx, job, reused, cause, charge.raw)
|
||||
}
|
||||
if use.OrganizationID == "" {
|
||||
cause := &billing.StatusError{Status: 409, Err: billing.ErrOrganizationUnavailable}
|
||||
logGenerationBillingFailure(job.ID, "validate", cause, cause)
|
||||
return c.failCharge(ctx, job, reused, cause, charge.raw)
|
||||
}
|
||||
request := billing.ChargeRequest{OrganizationID: use.OrganizationID, AccountID: use.AccountID, JobID: job.ID, AmountFen: charge.AmountFen, Description: capabilityLabel(job.Capability) + " · " + job.ReqKey, Metadata: map[string]any{"quote": cloneMap(charge.raw), "accountName": use.DisplayName, "organizationName": use.OrganizationName}}
|
||||
if atomic, ok := c.state.(AtomicCreationCharger); ok {
|
||||
@@ -173,16 +181,22 @@ func (c *CreationCoordinator) charge(ctx context.Context, job jobs.Job, reused b
|
||||
encoded, atomicErr := atomic.ChargeAndActivateCreation(ctx, request, pending)
|
||||
if atomicErr != nil {
|
||||
if errors.Is(atomicErr, billing.ErrCommitOutcomeUnknown) {
|
||||
return jobs.Job{}, reused, errors.New("charge generation job")
|
||||
publicErr := errors.New("charge generation job")
|
||||
logGenerationBillingFailure(job.ID, "commit", atomicErr, publicErr)
|
||||
return jobs.Job{}, reused, publicErr
|
||||
}
|
||||
return c.failCharge(ctx, job, reused, safeBillingError(atomicErr), charge.raw)
|
||||
safeErr := safeBillingError(atomicErr)
|
||||
logGenerationBillingFailure(job.ID, "charge", atomicErr, safeErr)
|
||||
return c.failCharge(ctx, job, reused, safeErr, charge.raw)
|
||||
}
|
||||
job.Billing = encoded
|
||||
return job, reused, nil
|
||||
}
|
||||
posting, err := c.charges.Charge(ctx, request)
|
||||
if err != nil {
|
||||
return c.failCharge(ctx, job, reused, safeBillingError(err), charge.raw)
|
||||
safeErr := safeBillingError(err)
|
||||
logGenerationBillingFailure(job.ID, "charge", err, safeErr)
|
||||
return c.failCharge(ctx, job, reused, safeErr, charge.raw)
|
||||
}
|
||||
charge.raw["status"] = "charged"
|
||||
charge.raw["ledgerEntryId"] = posting.LedgerID
|
||||
@@ -205,7 +219,8 @@ func (c *CreationCoordinator) failCharge(ctx context.Context, job jobs.Job, reus
|
||||
job.Status = jobs.StatusFailed
|
||||
job.Error = &jobs.JobError{Message: "generation charge failed", Retryable: false}
|
||||
if err := c.state.FailCreation(ctx, job); err != nil {
|
||||
return jobs.Job{}, reused, errors.New("persist failed generation charge")
|
||||
logGenerationBillingFailure(job.ID, "persist_failure", err, billing.ErrProviderUnavailable)
|
||||
return jobs.Job{}, reused, billing.ErrProviderUnavailable
|
||||
}
|
||||
return job, reused, cause
|
||||
}
|
||||
@@ -231,10 +246,64 @@ func safeBillingError(err error) error {
|
||||
if strings.Contains(message, "BILLING_IDEMPOTENCY_PAYLOAD_MISMATCH") {
|
||||
return &billing.StatusError{Status: 409, Err: billing.ErrIdempotencyConflict}
|
||||
}
|
||||
return errors.New("charge generation job")
|
||||
return billing.ErrProviderUnavailable
|
||||
}
|
||||
}
|
||||
|
||||
func logGenerationBillingFailure(jobID, phase string, rawErr, publicErr error) {
|
||||
log.Printf(
|
||||
"zhinian-api generation billing failed jobId=%s phase=%s status=%d code=%q errorClass=%s",
|
||||
jobID,
|
||||
phase,
|
||||
billing.HTTPStatus(publicErr),
|
||||
billingSQLState(rawErr),
|
||||
generationBillingErrorClass(rawErr, publicErr),
|
||||
)
|
||||
}
|
||||
|
||||
func generationBillingErrorClass(rawErr, publicErr error) string {
|
||||
switch {
|
||||
case errors.Is(publicErr, billing.ErrInsufficientBalance):
|
||||
return "insufficient_balance"
|
||||
case errors.Is(publicErr, billing.ErrIdempotencyConflict):
|
||||
return "idempotency_conflict"
|
||||
case errors.Is(publicErr, billing.ErrOrganizationUnavailable):
|
||||
return "organization_unavailable"
|
||||
case errors.Is(rawErr, billing.ErrCommitOutcomeUnknown):
|
||||
return "commit_outcome_unknown"
|
||||
case errors.Is(rawErr, context.DeadlineExceeded):
|
||||
return "timeout"
|
||||
case errors.Is(rawErr, context.Canceled):
|
||||
return "canceled"
|
||||
case billingSQLState(rawErr) != "":
|
||||
return "database"
|
||||
case errors.Is(publicErr, billing.ErrProviderUnavailable):
|
||||
return "unavailable"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func billingSQLState(err error) string {
|
||||
type sqlStateCarrier interface{ SQLState() string }
|
||||
var carrier sqlStateCarrier
|
||||
if !errors.As(err, &carrier) {
|
||||
return ""
|
||||
}
|
||||
code := strings.TrimSpace(carrier.SQLState())
|
||||
if len(code) != 5 {
|
||||
return ""
|
||||
}
|
||||
for _, char := range code {
|
||||
isDigit := char >= '0' && char <= '9'
|
||||
isUppercase := char >= 'A' && char <= 'Z'
|
||||
if !isDigit && !isUppercase {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func retryEngine(provider, capability string) string {
|
||||
if capability == "video.generate" {
|
||||
if provider == "seedance" || provider == "bailian" {
|
||||
|
||||
Reference in new issue
Block a user