From 18aa681b01b29c782bb06b752dfe1a7cf2e4dfda Mon Sep 17 00:00:00 2001 From: liangxu Date: Fri, 19 Jun 2026 17:20:35 +0800 Subject: [PATCH] fix: enhance publish record deletion logic and add soft delete functionality --- server/internal/tenant/app/media_service.go | 48 ++++++++++++------- .../tenant/app/publish_record_support.go | 10 ++-- ...00_add_publish_record_soft_delete.down.sql | 7 --- ...0000_add_publish_record_soft_delete.up.sql | 10 ---- ...blish_record_visible_status_index.down.sql | 1 + ...publish_record_visible_status_index.up.sql | 3 ++ ...blish_records_active_lookup_index.down.sql | 3 ++ ...publish_records_active_lookup_index.up.sql | 1 + ...blish_records_active_lookup_index.down.sql | 1 + ...publish_records_active_lookup_index.up.sql | 4 ++ 10 files changed, 49 insertions(+), 39 deletions(-) create mode 100644 server/migrations/20260618190001_add_publish_record_visible_status_index.down.sql create mode 100644 server/migrations/20260618190001_add_publish_record_visible_status_index.up.sql create mode 100644 server/migrations/20260618190002_drop_publish_records_active_lookup_index.down.sql create mode 100644 server/migrations/20260618190002_drop_publish_records_active_lookup_index.up.sql create mode 100644 server/migrations/20260618190003_recreate_publish_records_active_lookup_index.down.sql create mode 100644 server/migrations/20260618190003_recreate_publish_records_active_lookup_index.up.sql diff --git a/server/internal/tenant/app/media_service.go b/server/internal/tenant/app/media_service.go index de9d213..161cfc9 100644 --- a/server/internal/tenant/app/media_service.go +++ b/server/internal/tenant/app/media_service.go @@ -425,16 +425,17 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) var publishBatchID int64 var articleID int64 - var cancelErrorJSON []byte - cancelErrorJSON, err = marshalOptionalJSON(map[string]any{ + var publishStatus string + cancelErrorJSONBytes, err := marshalOptionalJSON(map[string]any{ "reason": "publish_record_deleted", "source": "tenant_web", }) if err != nil { return nil, response.ErrInternal(50044, "publish_record_delete_payload_failed", "failed to delete publish record") } + cancelErrorJSON := string(cancelErrorJSONBytes) err = tx.QueryRow(ctx, ` - SELECT pr.publish_batch_id, pr.article_id + SELECT pr.publish_batch_id, pr.article_id, pr.status FROM publish_records pr JOIN articles a ON a.id = pr.article_id @@ -447,7 +448,7 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) AND pb.initiator_user_id = $3 AND pr.deleted_at IS NULL FOR UPDATE OF pr - `, recordID, actor.TenantID, actor.UserID).Scan(&publishBatchID, &articleID) + `, recordID, actor.TenantID, actor.UserID).Scan(&publishBatchID, &articleID, &publishStatus) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, response.ErrNotFound(40435, "publish_record_not_found", "publish record not found") @@ -468,20 +469,22 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) return nil, response.ErrNotFound(40435, "publish_record_not_found", "publish record not found") } - if _, err := tx.Exec(ctx, ` - UPDATE desktop_tasks - SET status = 'aborted', - error = $3, - active_attempt_id = NULL, - lease_token_hash = NULL, - lease_expires_at = NULL, - updated_at = NOW() - WHERE tenant_id = $1 - AND kind = 'publish' - AND status = 'queued' - AND payload->>'publish_record_id' = $2 - `, actor.TenantID, recordID, cancelErrorJSON); err != nil { - return nil, response.ErrInternal(50044, "publish_record_delete_cancel_task_failed", "failed to cancel queued publish task before deleting record") + if publishRecordMayHaveQueuedTask(publishStatus) { + if _, err := tx.Exec(ctx, ` + UPDATE desktop_tasks + SET status = 'aborted', + error = $3::jsonb, + active_attempt_id = NULL, + lease_token_hash = NULL, + lease_expires_at = NULL, + updated_at = NOW() + WHERE tenant_id = $1 + AND kind = 'publish' + AND status = 'queued' + AND payload->>'publish_record_id' = $2::text + `, actor.TenantID, recordID, cancelErrorJSON); err != nil { + return nil, response.ErrInternal(50044, "publish_record_delete_cancel_task_failed", "failed to cancel queued publish task before deleting record") + } } batchStatus, err := recalculatePublishBatchStatus(ctx, tx, publishBatchID) @@ -532,6 +535,15 @@ func publishPlatformRequiresCover(platformID string) bool { } } +func publishRecordMayHaveQueuedTask(status string) bool { + switch strings.TrimSpace(status) { + case "queued", "publishing", "pending", "running": + return true + default: + return false + } +} + func pointerStringValue(value *string) string { if value == nil { return "" diff --git a/server/internal/tenant/app/publish_record_support.go b/server/internal/tenant/app/publish_record_support.go index c84db2c..04b85aa 100644 --- a/server/internal/tenant/app/publish_record_support.go +++ b/server/internal/tenant/app/publish_record_support.go @@ -115,7 +115,7 @@ func cancelQueuedPublishTasksForUnavailableArticle(ctx context.Context, tx pgx.T if tx == nil || tenantID <= 0 || articleID <= 0 { return nil } - errorJSON, err := marshalOptionalJSON(map[string]any{ + errorJSONBytes, err := marshalOptionalJSON(map[string]any{ "code": "article_unavailable", "message": "article is unavailable before publishing", "source": "article_unavailable_cleanup", @@ -123,6 +123,7 @@ func cancelQueuedPublishTasksForUnavailableArticle(ctx context.Context, tx pgx.T if err != nil { return response.ErrInternal(50120, "publish_task_cancel_payload_failed", "failed to encode publish task cancellation payload") } + errorJSON := string(errorJSONBytes) if _, err := tx.Exec(ctx, ` WITH affected_records AS ( @@ -154,7 +155,7 @@ func cancelQueuedPublishTasksForUnavailableArticle(ctx context.Context, tx pgx.T updated_tasks AS ( UPDATE desktop_tasks dt SET status = 'aborted', - error = $3, + error = $3::jsonb, active_attempt_id = NULL, lease_token_hash = NULL, lease_expires_at = NULL, @@ -280,7 +281,7 @@ func cancelQueuedPublishTasksForDesktopAccountUnbind( if tx == nil || tenantID <= 0 || workspaceID <= 0 || accountID == uuid.Nil { return 0, nil } - errorJSON, err := marshalOptionalJSON(map[string]any{ + errorJSONBytes, err := marshalOptionalJSON(map[string]any{ "code": "desktop_account_unbound", "message": "desktop account was unbound before publishing", "source": "desktop_account_unbind", @@ -288,6 +289,7 @@ func cancelQueuedPublishTasksForDesktopAccountUnbind( if err != nil { return 0, response.ErrInternal(50120, "publish_task_cancel_payload_failed", "failed to encode publish task cancellation payload") } + errorJSON := string(errorJSONBytes) rows, err := tx.Query(ctx, ` SELECT DISTINCT pr.id, pr.publish_batch_id, pr.article_id @@ -354,7 +356,7 @@ func cancelQueuedPublishTasksForDesktopAccountUnbind( tag, err := tx.Exec(ctx, ` UPDATE desktop_tasks dt SET status = 'aborted', - error = $4, + error = $4::jsonb, active_attempt_id = NULL, lease_token_hash = NULL, lease_expires_at = NULL, diff --git a/server/migrations/20260618190000_add_publish_record_soft_delete.down.sql b/server/migrations/20260618190000_add_publish_record_soft_delete.down.sql index 59ec2f2..c1068ca 100644 --- a/server/migrations/20260618190000_add_publish_record_soft_delete.down.sql +++ b/server/migrations/20260618190000_add_publish_record_soft_delete.down.sql @@ -1,9 +1,2 @@ -DROP INDEX CONCURRENTLY IF EXISTS idx_publish_records_visible_status; - -DROP INDEX CONCURRENTLY IF EXISTS idx_publish_records_article_account_active; -CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_publish_records_article_account_active - ON publish_records (tenant_id, article_id, platform_account_id, updated_at DESC, id DESC) - WHERE status IN ('queued', 'publishing', 'success'); - ALTER TABLE publish_records DROP COLUMN IF EXISTS deleted_at; diff --git a/server/migrations/20260618190000_add_publish_record_soft_delete.up.sql b/server/migrations/20260618190000_add_publish_record_soft_delete.up.sql index 25ba896..d3c505f 100644 --- a/server/migrations/20260618190000_add_publish_record_soft_delete.up.sql +++ b/server/migrations/20260618190000_add_publish_record_soft_delete.up.sql @@ -1,12 +1,2 @@ ALTER TABLE publish_records ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; - -CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_publish_records_visible_status - ON publish_records (tenant_id, status, created_at DESC, id DESC) - WHERE deleted_at IS NULL; - -DROP INDEX CONCURRENTLY IF EXISTS idx_publish_records_article_account_active; -CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_publish_records_article_account_active - ON publish_records (tenant_id, article_id, platform_account_id, updated_at DESC, id DESC) - WHERE status IN ('queued', 'publishing', 'success') - AND deleted_at IS NULL; diff --git a/server/migrations/20260618190001_add_publish_record_visible_status_index.down.sql b/server/migrations/20260618190001_add_publish_record_visible_status_index.down.sql new file mode 100644 index 0000000..b5b72f1 --- /dev/null +++ b/server/migrations/20260618190001_add_publish_record_visible_status_index.down.sql @@ -0,0 +1 @@ +DROP INDEX CONCURRENTLY IF EXISTS idx_publish_records_visible_status; diff --git a/server/migrations/20260618190001_add_publish_record_visible_status_index.up.sql b/server/migrations/20260618190001_add_publish_record_visible_status_index.up.sql new file mode 100644 index 0000000..33de1be --- /dev/null +++ b/server/migrations/20260618190001_add_publish_record_visible_status_index.up.sql @@ -0,0 +1,3 @@ +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_publish_records_visible_status + ON publish_records (tenant_id, status, created_at DESC, id DESC) + WHERE deleted_at IS NULL; diff --git a/server/migrations/20260618190002_drop_publish_records_active_lookup_index.down.sql b/server/migrations/20260618190002_drop_publish_records_active_lookup_index.down.sql new file mode 100644 index 0000000..d83c19f --- /dev/null +++ b/server/migrations/20260618190002_drop_publish_records_active_lookup_index.down.sql @@ -0,0 +1,3 @@ +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_publish_records_article_account_active + ON publish_records (tenant_id, article_id, platform_account_id, updated_at DESC, id DESC) + WHERE status IN ('queued', 'publishing', 'success'); diff --git a/server/migrations/20260618190002_drop_publish_records_active_lookup_index.up.sql b/server/migrations/20260618190002_drop_publish_records_active_lookup_index.up.sql new file mode 100644 index 0000000..28ab4f9 --- /dev/null +++ b/server/migrations/20260618190002_drop_publish_records_active_lookup_index.up.sql @@ -0,0 +1 @@ +DROP INDEX CONCURRENTLY IF EXISTS idx_publish_records_article_account_active; diff --git a/server/migrations/20260618190003_recreate_publish_records_active_lookup_index.down.sql b/server/migrations/20260618190003_recreate_publish_records_active_lookup_index.down.sql new file mode 100644 index 0000000..28ab4f9 --- /dev/null +++ b/server/migrations/20260618190003_recreate_publish_records_active_lookup_index.down.sql @@ -0,0 +1 @@ +DROP INDEX CONCURRENTLY IF EXISTS idx_publish_records_article_account_active; diff --git a/server/migrations/20260618190003_recreate_publish_records_active_lookup_index.up.sql b/server/migrations/20260618190003_recreate_publish_records_active_lookup_index.up.sql new file mode 100644 index 0000000..e5f8087 --- /dev/null +++ b/server/migrations/20260618190003_recreate_publish_records_active_lookup_index.up.sql @@ -0,0 +1,4 @@ +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_publish_records_article_account_active + ON publish_records (tenant_id, article_id, platform_account_id, updated_at DESC, id DESC) + WHERE status IN ('queued', 'publishing', 'success') + AND deleted_at IS NULL;