From bb797ebf87f0c0df1461aa2e0c0deb2e46f43d23 Mon Sep 17 00:00:00 2001 From: zerosaturation Date: Thu, 21 May 2026 10:45:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E5=8F=8C=E5=87=BB?= =?UTF-8?q?=E7=82=B9=E8=B5=9E=E7=9A=84=E9=80=BB=E8=BE=91=E5=92=8C=E5=A4=84?= =?UTF-8?q?=E7=90=86=E6=94=B6=E7=9B=8A=E8=AE=B0=E5=BD=95=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...eset_incorrectly_processed_exhibitions.sql | 19 +++ ...ncorrectly_processed_exhibitions_check.sql | 29 ++++ .../repository/gallery_repository.go | 12 +- .../galleryService/service/cleanup_worker.go | 5 +- frontend/pages/asset-detail/asset-detail.vue | 8 +- frontend/pages/components/Header.vue | 33 ++-- frontend/pages/profile/myWorks.vue | 3 +- .../pages/square/components/WaterfallGrid.vue | 3 +- frontend/utils/likeHelper.js | 145 +++++++++++------- 9 files changed, 177 insertions(+), 80 deletions(-) create mode 100644 backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions.sql create mode 100644 backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions_check.sql diff --git a/backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions.sql b/backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions.sql new file mode 100644 index 0000000..a71319c --- /dev/null +++ b/backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions.sql @@ -0,0 +1,19 @@ +-- Migration: Reset incorrectly processed exhibitions +-- Description: Reset is_processed=true for exhibitions that have no revenue record +-- (due to bug where OnExhibitionCompleted failure still marked as processed) +-- Date: 2026-05-20 + +-- Reset exhibitions that are marked as processed but have no revenue record +-- These exhibitions need to be reprocessed to generate revenue records +-- Note: expire_at is stored in milliseconds, so we multiply by 1000 +UPDATE exhibitions e +SET is_processed = false +WHERE e.is_processed = true + AND e.deleted_at IS NULL + AND e.expire_at <= EXTRACT(EPOCH FROM NOW()) * 1000 -- expired exhibitions (milliseconds) + AND NOT EXISTS ( + SELECT 1 FROM exhibition_revenue_records r + WHERE r.exhibition_id = e.id + ); + +-- Note: Run this manually via psql or apply via migrate tool \ No newline at end of file diff --git a/backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions_check.sql b/backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions_check.sql new file mode 100644 index 0000000..22b5322 --- /dev/null +++ b/backend/scripts/migrations/migrate_reset_incorrectly_processed_exhibitions_check.sql @@ -0,0 +1,29 @@ +-- Check how many exhibitions were incorrectly marked as processed +-- These are exhibitions that: +-- 1. is_processed = true +-- 2. not deleted (deleted_at IS NULL) +-- 3. expired (expire_at <= now in milliseconds) +-- 4. have no revenue record + +SELECT + COUNT(*) as incorrectly_processed_count +FROM exhibitions e +WHERE e.is_processed = true + AND e.deleted_at IS NULL + AND e.expire_at <= EXTRACT(EPOCH FROM NOW()) * 1000 + AND NOT EXISTS ( + SELECT 1 FROM exhibition_revenue_records r + WHERE r.exhibition_id = e.id + ); + +-- List the affected exhibitions (optional, for debugging) +-- SELECT e.id, e.asset_id, e.slot_id, e.expire_at, e.is_processed +-- FROM exhibitions e +-- WHERE e.is_processed = true +-- AND e.deleted_at IS NULL +-- AND e.expire_at <= EXTRACT(EPOCH FROM NOW()) * 1000 +-- AND NOT EXISTS ( +-- SELECT 1 FROM exhibition_revenue_records r +-- WHERE r.exhibition_id = e.id +-- ) +-- LIMIT 100; \ No newline at end of file diff --git a/backend/services/galleryService/repository/gallery_repository.go b/backend/services/galleryService/repository/gallery_repository.go index 9c0b87c..d473481 100644 --- a/backend/services/galleryService/repository/gallery_repository.go +++ b/backend/services/galleryService/repository/gallery_repository.go @@ -82,6 +82,7 @@ type GalleryRepository interface { // InspirationFlowItem 灵感瀑布展品项 type InspirationFlowItem struct { + ExhibitionID int64 AssetID int64 Name string CoverURL string @@ -89,12 +90,13 @@ type InspirationFlowItem struct { OwnerNickname string Span int32 // 卡片大小: 0-30→1, 31-100→2, 101-200→3, 200+→4 MaterialType string // 素材类型: hot(人气王者), potential(潜力之星), new(新鲜上架) - CreatedAt int64 // 创建时间(用于判断是否为潜力之星) + CreatedAt int64 // 创建时间(用于判断是否为潜力之星) } // ExhibitedAssetInfo 我展出的作品信息 type ExhibitedAssetInfo struct { AssetID int64 + ExhibitionID int64 Name string CoverURL string LikeCount int32 @@ -429,7 +431,7 @@ func (r *galleryRepository) GetMyExhibitedAssets(userID, starID int64, page, pag offset := (page - 1) * pageSize err = r.db.Model(&models.Exhibition{}). Raw(` - SELECT exhibitions.asset_id, a.name, a.cover_url, a.like_count, + SELECT exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, exhibitions.start_time as exhibited_at, exhibitions.expire_at, bs.slot_index, (a.tags @> '["craft:lenticular"]') as is_lenticular FROM exhibitions @@ -475,7 +477,7 @@ func (r *galleryRepository) GetUserExhibitedAssets(userID, starID int64, page, p offset := (page - 1) * pageSize err = r.db.Model(&models.Exhibition{}). Raw(` - SELECT exhibitions.asset_id, a.name, a.cover_url, a.like_count, + SELECT exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, exhibitions.start_time as exhibited_at, exhibitions.expire_at, bs.slot_index, (a.tags @> '["craft:lenticular"]') as is_lenticular FROM exhibitions @@ -542,7 +544,7 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri var err error if materialType == "" || materialType == "all" || materialType == "random" { err = baseQuery. - Select(`exhibitions.asset_id, a.name, a.cover_url, a.like_count, fp.nickname as owner_nickname, a.material_type, a.created_at`). + Select(`exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, fp.nickname as owner_nickname, a.material_type, a.created_at`). Joins("JOIN assets a ON a.id = exhibitions.asset_id"). Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id"). Where("a.status = 1 AND a.is_active = true"). @@ -553,7 +555,7 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri } else { // baseQuery 已经包含了 assets JOIN,不需要重复添加 err = baseQuery. - Select(`exhibitions.asset_id, a.name, a.cover_url, a.like_count, fp.nickname as owner_nickname, a.material_type, a.created_at`). + Select(`exhibitions.id as exhibition_id, exhibitions.asset_id, a.name, a.cover_url, a.like_count, fp.nickname as owner_nickname, a.material_type, a.created_at`). Joins("JOIN fan_profiles fp ON exhibitions.occupier_uid = fp.user_id AND exhibitions.occupier_star_id = fp.star_id"). Where("a.status = 1 AND a.is_active = true"). Order("RANDOM()"). diff --git a/backend/services/galleryService/service/cleanup_worker.go b/backend/services/galleryService/service/cleanup_worker.go index e90287e..e778a93 100644 --- a/backend/services/galleryService/service/cleanup_worker.go +++ b/backend/services/galleryService/service/cleanup_worker.go @@ -134,10 +134,11 @@ func (w *CleanupWorker) cleanupExpiredExhibitions(now int64) { CrystalAmount: revenue, }) if err != nil { - logger.Logger.Error("调用TaskService记录收益失败", + logger.Logger.Error("调用TaskService记录收益失败,跳过标记已处理以便重试", zap.Int64("exhibition_id", e.ID), zap.Error(err)) - // 不阻断主流程 + failedCount++ + continue // 不标记为已处理,让下次重试 } } diff --git a/frontend/pages/asset-detail/asset-detail.vue b/frontend/pages/asset-detail/asset-detail.vue index 5820234..38f2615 100644 --- a/frontend/pages/asset-detail/asset-detail.vue +++ b/frontend/pages/asset-detail/asset-detail.vue @@ -109,9 +109,9 @@ - + - - + + diff --git a/frontend/pages/components/Header.vue b/frontend/pages/components/Header.vue index 9093c86..25f590e 100644 --- a/frontend/pages/components/Header.vue +++ b/frontend/pages/components/Header.vue @@ -37,15 +37,12 @@ - + - + @@ -65,7 +62,8 @@ - + + @@ -97,6 +95,7 @@