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 @@