diff --git a/backend/services/socialService/repository/social_repository.go b/backend/services/socialService/repository/social_repository.go index b0a91b4..9367475 100644 --- a/backend/services/socialService/repository/social_repository.go +++ b/backend/services/socialService/repository/social_repository.go @@ -591,11 +591,11 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page } // 计数查询(使用 DISTINCT 因为一个资产可能在多个展位展出) - // 显示:展品存在且未领取(is_processed=false),无论是否过期 - // 不显示:已领取的(is_processed=true)或从未展出过的 + // 显示:展品未下架(deleted_at IS NULL),无论是否过期 + // 不显示:已领取下架的(deleted_at IS NOT NULL)或从未展出过的 countQuery := r.db.Model(&models.AssetLike{}). Joins("JOIN assets a ON a.id = asset_likes.asset_id"). - Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL AND e.is_processed = false"). + Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL"). Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID). Where("a.deleted_at IS NULL AND a.is_active = ?", true) @@ -603,14 +603,14 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page return nil, 0, err } - // 数据查询:只过滤已下架的,不过滤过期(已过期但未领取的仍显示) + // 数据查询:只过滤已下架的,不过滤过期(用户领取收益后才下架) offset := (page - 1) * pageSize err := r.db.Model(&models.AssetLike{}). Select(`asset_likes.asset_id, a.name, a.cover_url, a.like_count, asset_likes.created_at as liked_at, (a.tags @> '["craft:lenticular"]') as is_lenticular`). Joins("JOIN assets a ON a.id = asset_likes.asset_id"). - Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL AND e.is_processed = false"). + Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL"). Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID). Where("a.deleted_at IS NULL AND a.is_active = ?", true). Group("asset_likes.asset_id, a.name, a.cover_url, a.like_count, asset_likes.created_at, is_lenticular"). diff --git a/backend/services/taskService/service/revenue_service.go b/backend/services/taskService/service/revenue_service.go index 4b9dba3..6b8dfda 100644 --- a/backend/services/taskService/service/revenue_service.go +++ b/backend/services/taskService/service/revenue_service.go @@ -163,9 +163,6 @@ func (s *revenueService) ClaimExhibitionRevenue(ctx context.Context, userID, sta // 增加用户累计上架时长(触发升级检查) // 计算上架时长(毫秒转小时) exhibitionHours := (record.CycleEndTime - record.CycleStartTime) / 3600000 - if exhibitionHours < 1 { - exhibitionHours = 1 // 最少1小时 - } if s.userRPCClient != nil { newLevel, levelDelta, crystalReward, err := s.userRPCClient.AddExhibitionHours(ctx, userID, starID, exhibitionHours, fmt.Sprintf("%d", record.ID)) @@ -341,9 +338,6 @@ func (s *revenueService) OnExhibitionCompleted(ctx context.Context, req *pb.OnEx startTime := req.StartTime expireAt := req.ExpireAt actualHours := (expireAt - startTime) / 3600000 - if actualHours < 1 { - actualHours = 1 - } // sourceID 用于去重,避免重复累计 sourceID := fmt.Sprintf("exhibition_%d", req.ExhibitionId) diff --git a/frontend/pages/profile/myWorks.vue b/frontend/pages/profile/myWorks.vue index a433086..a109fa7 100644 --- a/frontend/pages/profile/myWorks.vue +++ b/frontend/pages/profile/myWorks.vue @@ -133,7 +133,17 @@ - + @@ -518,6 +528,10 @@ const lenticularLayersByAsset = ref({}); const activeLenticularId = ref(null); const gyroSourceLabel = ref('device'); +// 点赞作品光栅卡数据 +const likedLenticularLayersByAsset = ref({}); +const likedLenticularTransformsMap = ref({}); + // 创建单一引擎供模拟倾斜使用 const lenticularPhysics = ref(null); const lenticularEngine = ref(null); @@ -534,6 +548,86 @@ function getLenticularTransforms(assetId) { return lenticularTransformsMap.value[assetId] || {}; } +// 点赞作品光栅卡相关函数 +function getLikedLenticularLayers(assetId) { + return likedLenticularLayersByAsset.value[assetId] || []; +} + +function getLikedLenticularTransforms(assetId) { + return likedLenticularTransformsMap.value[assetId] || {}; +} + +function onLikedLenticularSimulate(assetId, x, y) { + simulateLikedLenticularTilt(assetId, x, y); +} + +function simulateLikedLenticularTilt(assetId, x, y) { + if (!lenticularEngine.value) return; + const layers = likedLenticularLayersByAsset.value[assetId]; + if (!layers || layers.length === 0) return; + + lenticularEngine.value.setLayers(layers); + const renderState = lenticularEngine.value.feedSimulatedTilt(x, y ? y : 0); + + const transforms = {}; + for (const l of layers) { + const rs = renderState.layerOffsets.get(l.id); + const op = renderState.layerOpacities.get(l.id); + transforms[l.id] = { + x: rs ? rs.x : 0, + y: rs ? rs.y : 0, + opacity: op != null ? op : l.opacity, + }; + } + likedLenticularTransformsMap.value = { ...likedLenticularTransformsMap.value, [assetId]: transforms }; +} + +async function loadLikedLenticularLayersForAsset(assetId) { + const item = likedWorks.value.find(w => w.id === assetId); + if (!item) return; + + try { + const materialsRes = await getAssetMaterialsApi(assetId); + if (materialsRes.code === 200 && materialsRes.data) { + const materialsList = Array.isArray(materialsRes.data) ? materialsRes.data : materialsRes.data.materials || []; + let subjectUrl = item.cover_url; + let bgUrl = ''; + for (const mat of materialsList) { + if (mat.material_type === 'main' && mat.material_url_signed) { + subjectUrl = mat.material_url_signed; + } + if (mat.material_type === 'bg' && mat.material_url_signed) { + bgUrl = mat.material_url_signed; + } + } + if (bgUrl) { + const { buildLenticularLayersTwo } = await import('@/utils/castloveMintForm.js'); + likedLenticularLayersByAsset.value[assetId] = buildLenticularLayersTwo(bgUrl, subjectUrl); + } else { + likedLenticularLayersByAsset.value[assetId] = buildLenticularLayers(subjectUrl); + } + initLikedTransformsForAsset(assetId); + } else { + likedLenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.cover_url); + initLikedTransformsForAsset(assetId); + } + } catch (e) { + console.error('[myWorks] 获取点赞作品素材列表失败:', e); + likedLenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.cover_url); + initLikedTransformsForAsset(assetId); + } +} + +function initLikedTransformsForAsset(assetId) { + const layers = likedLenticularLayersByAsset.value[assetId]; + if (!layers || layers.length === 0) return; + const transforms = {}; + for (const l of layers) { + transforms[l.id] = { x: 0, y: 0, opacity: l.opacity || 1 }; + } + likedLenticularTransformsMap.value = { ...likedLenticularTransformsMap.value, [assetId]: transforms }; +} + async function loadLenticularLayersForAsset(assetId) { // 需要获取 bg + subject 素材构建 layers // 目前使用 buildLenticularLayers(coverUrl) 作为占位 @@ -650,6 +744,24 @@ function startLenticularRenderLoop() { } lenticularTransformsMap.value = { ...lenticularTransformsMap.value, [assetId]: transforms }; } + // 遍历点赞作品的光栅卡 + for (const assetId of Object.keys(likedLenticularLayersByAsset.value)) { + const layers = likedLenticularLayersByAsset.value[assetId]; + if (!layers || layers.length === 0) continue; + lenticularEngine.value.setLayers(layers); + const renderState = lenticularEngine.value.feedSimulatedTilt(0, 0); + const transforms = {}; + for (const l of layers) { + const rs = renderState.layerOffsets.get(l.id); + const op = renderState.layerOpacities.get(l.id); + transforms[l.id] = { + x: rs ? rs.x : 0, + y: rs ? rs.y : 0, + opacity: op != null ? op : l.opacity, + }; + } + likedLenticularTransformsMap.value = { ...likedLenticularTransformsMap.value, [assetId]: transforms }; + } lenticularRafId = requestAnimationFrame(tick); }; lenticularRafId = requestAnimationFrame(tick); @@ -667,6 +779,9 @@ const switchLikedTab = async (tab) => { if (likedTab.value === tab) return; likedTab.value = tab; likedWorks.value = []; + // 清理点赞作品光栅卡数据 + likedLenticularLayersByAsset.value = {}; + likedLenticularTransformsMap.value = {}; await loadLikedAssets(); }; @@ -726,11 +841,19 @@ const loadLikedAssets = async () => { earnings: item.earnings, liked_at: item.liked_at, name: item.name, + is_lenticular: item.is_lenticular ?? false, // 暂时用排名模拟状态文字 status_text: index < 3 ? '排名进榜' : '潜力待挖', score: item.like_count, reward: Math.floor(item.earnings || 0), })); + + // 为每个光栅卡加载层级数据 + for (const item of likedWorks.value) { + if (item.is_lenticular) { + loadLikedLenticularLayersForAsset(item.id); + } + } } } catch (err) { console.error('加载点赞作品失败:', err); @@ -1318,6 +1441,17 @@ onShow(() => { padding: 0.25rem; } +.liked-lenticular { + width: 90%; + height: 90%; + border-radius: 24rpx; + transform: rotate(-22deg); + transform-origin: center center; + position: relative; + z-index: 3; + overflow: hidden; +} + .liked-cover-frame { position: absolute; top: 0;