feat(volces): 接入任务兼容查询与上游取消

This commit is contained in:
2026-07-22 00:25:22 +08:00
parent ddd68cfebd
commit d7951cfdd2
11 changed files with 885 additions and 55 deletions
+68 -1
View File
@@ -530,7 +530,8 @@ WHERE id = $1::uuid
UPDATE gateway_task_attempts
SET remote_task_id = NULLIF($2::text, ''),
response_snapshot = COALESCE(response_snapshot, '{}'::jsonb) || jsonb_build_object('remote_task_payload', $3::jsonb)
WHERE id = $1::uuid`,
WHERE id = $1::uuid
AND status = 'running'`,
attemptID,
remoteTaskID,
string(payloadJSON),
@@ -585,6 +586,72 @@ WHERE id = $1::uuid
return task, true, nil
}
// CancelSubmittedTask records a confirmed upstream cancellation. Callers must
// first complete the provider-side DELETE so local status never claims a remote
// task was cancelled when the upstream request was not accepted.
func (s *Store) CancelSubmittedTask(ctx context.Context, taskID string, executionToken string, message string) (GatewayTask, bool, error) {
message = strings.TrimSpace(message)
if message == "" {
message = "任务已由上游取消"
}
var task GatewayTask
changed := false
err := pgx.BeginFunc(ctx, s.pool, func(tx pgx.Tx) error {
var err error
task, err = scanGatewayTask(tx.QueryRow(ctx, `
UPDATE gateway_tasks
SET status = 'cancelled',
error = NULLIF($2, ''),
error_code = 'task_cancelled',
error_message = NULLIF($2, ''),
billing_status = CASE
WHEN run_mode <> 'production' OR gateway_user_id IS NULL THEN 'not_required'
WHEN reservation_amount > 0 THEN 'pending'
ELSE 'released'
END,
billing_updated_at = now(),
locked_by = NULL,
locked_at = NULL,
heartbeat_at = NULL,
execution_token = NULL,
execution_lease_expires_at = NULL,
finished_at = now(),
updated_at = now()
WHERE id = $1::uuid
AND NULLIF(remote_task_id, '') IS NOT NULL
AND (
(status = 'running' AND execution_token = NULLIF($3, '')::uuid)
OR status = 'queued'
)
RETURNING `+gatewayTaskColumns, taskID, message, strings.TrimSpace(executionToken)))
if IsNotFound(err) {
return nil
}
if err != nil {
return err
}
changed = true
payloadJSON, _ := json.Marshal(map[string]any{"taskId": taskID, "reason": "upstream_cancelled"})
_, err = tx.Exec(ctx, `
INSERT INTO settlement_outbox (
task_id, event_type, action, amount, currency, pricing_snapshot, payload, status, next_attempt_at
)
SELECT id, 'task.billing.release', 'release', reservation_amount, billing_currency,
pricing_snapshot, $2::jsonb, 'pending', now()
FROM gateway_tasks
WHERE id = $1::uuid
AND run_mode = 'production'
AND gateway_user_id IS NOT NULL
AND reservation_amount > 0
ON CONFLICT (task_id, event_type) DO NOTHING`, taskID, string(payloadJSON))
return err
})
if err != nil {
return GatewayTask{}, false, err
}
return task, changed, nil
}
func (s *Store) ListRecoverableAsyncTasks(ctx context.Context, limit int) ([]AsyncTaskQueueItem, error) {
if limit <= 0 {
limit = 500