feat: 修改双击点赞的逻辑和处理收益记录的bug
This commit is contained in:
parent
afda66cdb9
commit
bb797ebf87
@ -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
|
||||||
@ -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;
|
||||||
@ -82,6 +82,7 @@ type GalleryRepository interface {
|
|||||||
|
|
||||||
// InspirationFlowItem 灵感瀑布展品项
|
// InspirationFlowItem 灵感瀑布展品项
|
||||||
type InspirationFlowItem struct {
|
type InspirationFlowItem struct {
|
||||||
|
ExhibitionID int64
|
||||||
AssetID int64
|
AssetID int64
|
||||||
Name string
|
Name string
|
||||||
CoverURL string
|
CoverURL string
|
||||||
@ -95,6 +96,7 @@ type InspirationFlowItem struct {
|
|||||||
// ExhibitedAssetInfo 我展出的作品信息
|
// ExhibitedAssetInfo 我展出的作品信息
|
||||||
type ExhibitedAssetInfo struct {
|
type ExhibitedAssetInfo struct {
|
||||||
AssetID int64
|
AssetID int64
|
||||||
|
ExhibitionID int64
|
||||||
Name string
|
Name string
|
||||||
CoverURL string
|
CoverURL string
|
||||||
LikeCount int32
|
LikeCount int32
|
||||||
@ -429,7 +431,7 @@ func (r *galleryRepository) GetMyExhibitedAssets(userID, starID int64, page, pag
|
|||||||
offset := (page - 1) * pageSize
|
offset := (page - 1) * pageSize
|
||||||
err = r.db.Model(&models.Exhibition{}).
|
err = r.db.Model(&models.Exhibition{}).
|
||||||
Raw(`
|
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,
|
exhibitions.start_time as exhibited_at, exhibitions.expire_at, bs.slot_index,
|
||||||
(a.tags @> '["craft:lenticular"]') as is_lenticular
|
(a.tags @> '["craft:lenticular"]') as is_lenticular
|
||||||
FROM exhibitions
|
FROM exhibitions
|
||||||
@ -475,7 +477,7 @@ func (r *galleryRepository) GetUserExhibitedAssets(userID, starID int64, page, p
|
|||||||
offset := (page - 1) * pageSize
|
offset := (page - 1) * pageSize
|
||||||
err = r.db.Model(&models.Exhibition{}).
|
err = r.db.Model(&models.Exhibition{}).
|
||||||
Raw(`
|
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,
|
exhibitions.start_time as exhibited_at, exhibitions.expire_at, bs.slot_index,
|
||||||
(a.tags @> '["craft:lenticular"]') as is_lenticular
|
(a.tags @> '["craft:lenticular"]') as is_lenticular
|
||||||
FROM exhibitions
|
FROM exhibitions
|
||||||
@ -542,7 +544,7 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri
|
|||||||
var err error
|
var err error
|
||||||
if materialType == "" || materialType == "all" || materialType == "random" {
|
if materialType == "" || materialType == "all" || materialType == "random" {
|
||||||
err = baseQuery.
|
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 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").
|
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").
|
Where("a.status = 1 AND a.is_active = true").
|
||||||
@ -553,7 +555,7 @@ func (r *galleryRepository) GetRandomExhibitions(starID int64, materialType stri
|
|||||||
} else {
|
} else {
|
||||||
// baseQuery 已经包含了 assets JOIN,不需要重复添加
|
// baseQuery 已经包含了 assets JOIN,不需要重复添加
|
||||||
err = baseQuery.
|
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").
|
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").
|
Where("a.status = 1 AND a.is_active = true").
|
||||||
Order("RANDOM()").
|
Order("RANDOM()").
|
||||||
|
|||||||
@ -134,10 +134,11 @@ func (w *CleanupWorker) cleanupExpiredExhibitions(now int64) {
|
|||||||
CrystalAmount: revenue,
|
CrystalAmount: revenue,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Logger.Error("调用TaskService记录收益失败",
|
logger.Logger.Error("调用TaskService记录收益失败,跳过标记已处理以便重试",
|
||||||
zap.Int64("exhibition_id", e.ID),
|
zap.Int64("exhibition_id", e.ID),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
// 不阻断主流程
|
failedCount++
|
||||||
|
continue // 不标记为已处理,让下次重试
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -109,9 +109,9 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 创作者信息模块 -->
|
<!-- 创作者信息模块 -->
|
||||||
<!-- <view v-if="likeCount > 0" class="creator-section" @tap="showLikeUsersModal = true"> -->
|
<view v-if="likeCount > 0" class="creator-section" @tap="showLikeUsersModal = true">
|
||||||
<!-- 点赞用户头像区域 -->
|
<!-- 点赞用户头像区域 -->
|
||||||
<!-- <view class="liked-users-area">
|
<view class="liked-users-area">
|
||||||
<view v-for="(user, index) in likedUsers" :key="index" class="liked-user-avatar" :style="{
|
<view v-for="(user, index) in likedUsers" :key="index" class="liked-user-avatar" :style="{
|
||||||
transform: `translate(${user.ellipseX || 0}rpx, ${user.ellipseY || 0}rpx) scale(${user.size || 1})`,
|
transform: `translate(${user.ellipseX || 0}rpx, ${user.ellipseY || 0}rpx) scale(${user.size || 1})`,
|
||||||
zIndex: index < 2 ? index + 1 : (index < 4 ? index + 3 : index + 1)
|
zIndex: index < 2 ? index + 1 : (index < 4 ? index + 3 : index + 1)
|
||||||
@ -126,8 +126,8 @@
|
|||||||
<image class="creator-preview" src="/static/rank/activity-support-icon/tubiao.png"
|
<image class="creator-preview" src="/static/rank/activity-support-icon/tubiao.png"
|
||||||
mode="aspectFill"></image>
|
mode="aspectFill"></image>
|
||||||
</view>
|
</view>
|
||||||
</view> -->
|
</view>
|
||||||
<!-- </view> -->
|
</view>
|
||||||
|
|
||||||
<!-- 完整链上哈希显示遮罩层 -->
|
<!-- 完整链上哈希显示遮罩层 -->
|
||||||
<view v-if="showTxHash" class="txhash-mask" @tap="showTxHash = false">
|
<view v-if="showTxHash" class="txhash-mask" @tap="showTxHash = false">
|
||||||
|
|||||||
@ -37,15 +37,12 @@
|
|||||||
|
|
||||||
<!-- 星援活动列表 -->
|
<!-- 星援活动列表 -->
|
||||||
<view v-if="showStarActivityIcon" class="star-activity-list">
|
<view v-if="showStarActivityIcon" class="star-activity-list">
|
||||||
<view
|
<view v-for="activity in starActivities" :key="activity.id" class="daily-task-group"
|
||||||
v-for="activity in starActivities"
|
@click="handleActivityClick(activity)">
|
||||||
:key="activity.id"
|
|
||||||
class="daily-task-group"
|
|
||||||
@click="handleActivityClick(activity)"
|
|
||||||
>
|
|
||||||
<!-- 上层:活动图标 -->
|
<!-- 上层:活动图标 -->
|
||||||
<view class="task-icon-box">
|
<view class="task-icon-box">
|
||||||
<image class="task-icon-img" :src="activity.icon || '/static/icon/bus-icon.png'" mode="aspectFit"></image>
|
<image class="task-icon-img" :src="activity.icon || '/static/icon/bus-icon.png'"
|
||||||
|
mode="aspectFit"></image>
|
||||||
<image class="task-red-dot" src="/static/square/tishi.png" mode="aspectFit"></image>
|
<image class="task-red-dot" src="/static/square/tishi.png" mode="aspectFit"></image>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@ -65,7 +62,8 @@
|
|||||||
|
|
||||||
<!-- 2. 右侧容器:用于包裹背景和文字 -->
|
<!-- 2. 右侧容器:用于包裹背景和文字 -->
|
||||||
<view class="crystal-info-container">
|
<view class="crystal-info-container">
|
||||||
<image class="crystal-bg-img" src="/static/square/shuijingzhanshikuang.png" mode="aspectFill"></image>
|
<image class="crystal-bg-img" src="/static/square/shuijingzhanshikuang.png" mode="aspectFill">
|
||||||
|
</image>
|
||||||
|
|
||||||
<!-- 上层:文字内容 -->
|
<!-- 上层:文字内容 -->
|
||||||
<view class="crystal-text-layer">
|
<view class="crystal-text-layer">
|
||||||
@ -97,6 +95,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted, onUnmounted } from 'vue';
|
import { computed, ref, onMounted, onUnmounted } from 'vue';
|
||||||
|
import { onShow } from '@dcloudio/uni-app';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import Avatar from './Avatar.vue';
|
import Avatar from './Avatar.vue';
|
||||||
import DailyTasks from '@/pages/tasks/daily-tasks.vue';
|
import DailyTasks from '@/pages/tasks/daily-tasks.vue';
|
||||||
@ -238,7 +237,7 @@ function checkAndReportDailyLogin() {
|
|||||||
try {
|
try {
|
||||||
const user = JSON.parse(userStr)
|
const user = JSON.parse(userStr)
|
||||||
userId = user?.uid || null
|
userId = user?.uid || null
|
||||||
} catch (e) {}
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
if (!userId) return
|
if (!userId) return
|
||||||
|
|
||||||
@ -263,10 +262,10 @@ const starActivities = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 组件挂载时加载用户信息和星援活动数据
|
// 组件挂载时加载用户信息和星援活动数据
|
||||||
onMounted(async() => {
|
onMounted(async () => {
|
||||||
await loadUserInfo();
|
await loadUserInfo();
|
||||||
await loadBannerActivities();
|
await loadBannerActivities();
|
||||||
await loadEarningsSummary();
|
// await loadEarningsSummary();
|
||||||
uni.$on('avatarUpdated', handleAvatarUpdate);
|
uni.$on('avatarUpdated', handleAvatarUpdate);
|
||||||
uni.$on('userInfoUpdated', handleUserInfoUpdate);
|
uni.$on('userInfoUpdated', handleUserInfoUpdate);
|
||||||
uni.$on('balanceUpdated', handleBalanceUpdate);
|
uni.$on('balanceUpdated', handleBalanceUpdate);
|
||||||
@ -275,6 +274,11 @@ onMounted(async() => {
|
|||||||
checkAndReportDailyLogin();
|
checkAndReportDailyLogin();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
// 每次页面显示时刷新收益数据
|
||||||
|
loadEarningsSummary();
|
||||||
|
});
|
||||||
|
|
||||||
// 组件卸载时移除事件监听
|
// 组件卸载时移除事件监听
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
uni.$off('avatarUpdated', handleAvatarUpdate);
|
uni.$off('avatarUpdated', handleAvatarUpdate);
|
||||||
@ -602,8 +606,7 @@ defineExpose({
|
|||||||
background: linear-gradient(to bottom right,
|
background: linear-gradient(to bottom right,
|
||||||
#F0E4B1 0%,
|
#F0E4B1 0%,
|
||||||
#F08399 50%,
|
#F08399 50%,
|
||||||
#B94E7399 100%
|
#B94E7399 100%);
|
||||||
);
|
|
||||||
|
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
|
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
|
||||||
|
|||||||
@ -415,7 +415,7 @@ const handleExhibitionCardTap = (item, index) => {
|
|||||||
// 第二次点击,双击点赞
|
// 第二次点击,双击点赞
|
||||||
clearTimeout(cardTapTimers[item.id]);
|
clearTimeout(cardTapTimers[item.id]);
|
||||||
delete cardTapTimers[item.id];
|
delete cardTapTimers[item.id];
|
||||||
doubleTapLike(item.id, async (success, data) => {
|
doubleTapLike(item.id, item.exhibition_id, async (success, data) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
// 更新在展作品的点赞数
|
// 更新在展作品的点赞数
|
||||||
// exhibitionWorks.value[index].like_count = (exhibitionWorks.value[index].like_count || 0) + 1;
|
// exhibitionWorks.value[index].like_count = (exhibitionWorks.value[index].like_count || 0) + 1;
|
||||||
@ -817,6 +817,7 @@ const loadExhibitedAssets = async () => {
|
|||||||
exhibitionWorks.value = res.data.items
|
exhibitionWorks.value = res.data.items
|
||||||
.map(item => ({
|
.map(item => ({
|
||||||
id: item.asset_id,
|
id: item.asset_id,
|
||||||
|
exhibition_id: item.exhibition_id,
|
||||||
cover_url: item.cover_url,
|
cover_url: item.cover_url,
|
||||||
like_count: item.like_count,
|
like_count: item.like_count,
|
||||||
earnings: item.earnings,
|
earnings: item.earnings,
|
||||||
|
|||||||
@ -633,6 +633,7 @@ const loadUsers = async () => {
|
|||||||
const withData = items.map((item) => {
|
const withData = items.map((item) => {
|
||||||
return {
|
return {
|
||||||
id: item.asset_id,
|
id: item.asset_id,
|
||||||
|
exhibition_id: item.exhibition_id,
|
||||||
userId: item.asset_id,
|
userId: item.asset_id,
|
||||||
nickname: item.owner_nickname || item.name,
|
nickname: item.owner_nickname || item.name,
|
||||||
coverUrl: item.cover_url || MOCK_IMAGES[idCounter % MOCK_IMAGES.length],
|
coverUrl: item.cover_url || MOCK_IMAGES[idCounter % MOCK_IMAGES.length],
|
||||||
@ -759,7 +760,7 @@ const handleCardClick = (card) => {
|
|||||||
likingMap.value = { ...likingMap.value, [card.id]: false };
|
likingMap.value = { ...likingMap.value, [card.id]: false };
|
||||||
}, 600);
|
}, 600);
|
||||||
|
|
||||||
doubleTapLike(card.id, (success) => {
|
doubleTapLike(card.id, card.exhibition_id || 0, (success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
// 找到 cards 中的卡片并更新 likes,触发响应式更新
|
// 找到 cards 中的卡片并更新 likes,触发响应式更新
|
||||||
const idx = cards.value.findIndex(c => c.id === card.id)
|
const idx = cards.value.findIndex(c => c.id === card.id)
|
||||||
|
|||||||
@ -2,86 +2,127 @@
|
|||||||
|
|
||||||
import { likeAssetApi } from './api.js';
|
import { likeAssetApi } from './api.js';
|
||||||
|
|
||||||
// 存储已点赞的作品,key: assetId, value: 点赞日期 (YYYY-MM-DD)
|
// 存储已点赞的作品
|
||||||
const LIKE_STORAGE_KEY = 'liked_assets_daily';
|
// - 带 exhibitionId 时:key = "${assetId}_${exhibitionId}",支持每次展示点赞一次
|
||||||
|
// - 不带 exhibitionId 时:key = "asset_${assetId}",兼容旧行为(每天每个作品只能点赞一次)
|
||||||
|
const LIKE_STORAGE_KEY = 'liked_assets_exhibition';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取今天日期字符串
|
* 获取存储键
|
||||||
|
* @param {string|number} assetId
|
||||||
|
* @param {string|number} exhibitionId
|
||||||
*/
|
*/
|
||||||
function getTodayStr() {
|
function getStorageKey(assetId, exhibitionId) {
|
||||||
const now = new Date();
|
if (exhibitionId) {
|
||||||
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
return `${assetId}_${exhibitionId}`;
|
||||||
|
}
|
||||||
|
return `asset_${assetId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查作品今天是否已点赞
|
* 双击点赞处理
|
||||||
|
* - 有 exhibitionId 时:每个展品每次展示只能点赞一次
|
||||||
|
* - 无 exhibitionId 时:每个藏品每天只能点赞一次(兼容旧逻辑)
|
||||||
|
* @param {string|number} assetId - 藏品ID
|
||||||
|
* @param {string|number} [exhibitionId] - 展品展示ID(每次展示唯一,可选)
|
||||||
|
* @param {Function} [callback] - 回调函数,参数为是否成功
|
||||||
*/
|
*/
|
||||||
function hasLikedToday(assetId) {
|
export function doubleTapLike(assetId, exhibitionId, callback) {
|
||||||
|
if (!assetId) {
|
||||||
|
console.error('doubleTapLike: assetId 是必需的');
|
||||||
|
if (callback) callback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果第二个参数是函数,说明调用者传的是 callback,exhibitionId 未提供
|
||||||
|
let actualCallback = callback;
|
||||||
|
let actualExhibitionId = exhibitionId;
|
||||||
|
if (typeof exhibitionId === 'function') {
|
||||||
|
actualCallback = exhibitionId;
|
||||||
|
actualExhibitionId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = getStorageKey(assetId, actualExhibitionId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const storage = uni.getStorageSync(LIKE_STORAGE_KEY) || {};
|
const storage = uni.getStorageSync(LIKE_STORAGE_KEY) || {};
|
||||||
const today = getTodayStr();
|
if (storage[key]) {
|
||||||
return storage[assetId] === today;
|
const msg = actualExhibitionId ? '本次展示已点赞' : '今日已点赞';
|
||||||
|
uni.showToast({ title: msg, icon: 'none' });
|
||||||
|
if (actualCallback) actualCallback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('读取点赞记录失败:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
likeAssetApi(assetId).then(res => {
|
||||||
|
console.log('点赞成功', res);
|
||||||
|
// 记录点赞
|
||||||
|
try {
|
||||||
|
const storage = uni.getStorageSync(LIKE_STORAGE_KEY) || {};
|
||||||
|
storage[key] = Date.now();
|
||||||
|
uni.setStorageSync(LIKE_STORAGE_KEY, storage);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('存储点赞记录失败:', e);
|
||||||
|
}
|
||||||
|
// 触发全局点赞成功事件
|
||||||
|
uni.$emit('assetLiked', {
|
||||||
|
asset_id: assetId,
|
||||||
|
exhibition_id: actualExhibitionId,
|
||||||
|
data: res.data
|
||||||
|
});
|
||||||
|
if (actualCallback) actualCallback(true, res.data);
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('点赞失败:', err);
|
||||||
|
uni.showToast({ title: '点赞失败', icon: 'none' });
|
||||||
|
if (actualCallback) actualCallback(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查藏品在当前展示中是否已点赞
|
||||||
|
* @param {string|number} assetId - 藏品ID
|
||||||
|
* @param {string|number} [exhibitionId] - 展品展示ID
|
||||||
|
*/
|
||||||
|
export function hasLikedExhibition(assetId, exhibitionId) {
|
||||||
|
if (!assetId) return false;
|
||||||
|
const key = getStorageKey(assetId, exhibitionId);
|
||||||
|
try {
|
||||||
|
const storage = uni.getStorageSync(LIKE_STORAGE_KEY) || {};
|
||||||
|
return !!storage[key];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 记录作品今天已点赞
|
* 清除指定展览的点赞记录(用于展品下架时)
|
||||||
|
* @param {string|number} exhibitionId - 展品展示ID
|
||||||
*/
|
*/
|
||||||
function markLikedToday(assetId) {
|
export function clearLikeByExhibition(exhibitionId) {
|
||||||
|
if (!exhibitionId) return;
|
||||||
try {
|
try {
|
||||||
const storage = uni.getStorageSync(LIKE_STORAGE_KEY) || {};
|
const storage = uni.getStorageSync(LIKE_STORAGE_KEY) || {};
|
||||||
storage[assetId] = getTodayStr();
|
|
||||||
uni.setStorageSync(LIKE_STORAGE_KEY, storage);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('存储点赞记录失败:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 清除过期的点赞记录(昨天及之前)
|
|
||||||
*/
|
|
||||||
function cleanExpiredLikes() {
|
|
||||||
try {
|
|
||||||
const storage = uni.getStorageSync(LIKE_STORAGE_KEY) || {};
|
|
||||||
const today = getTodayStr();
|
|
||||||
const keys = Object.keys(storage);
|
const keys = Object.keys(storage);
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
if (storage[key] !== today) {
|
if (key.endsWith(`_${exhibitionId}`)) {
|
||||||
delete storage[key];
|
delete storage[key];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
uni.setStorageSync(LIKE_STORAGE_KEY, storage);
|
uni.setStorageSync(LIKE_STORAGE_KEY, storage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('清除过期点赞记录失败:', e);
|
console.error('清除点赞记录失败:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 双击点赞处理(每天每个作品只能点赞一次)
|
* 清除所有点赞记录
|
||||||
* @param {string|number} assetId - 藏品ID
|
|
||||||
* @param {Function} callback - 回调函数,参数为是否成功
|
|
||||||
*/
|
*/
|
||||||
export function doubleTapLike(assetId, callback) {
|
export function clearAllLikes() {
|
||||||
// 清理过期记录
|
try {
|
||||||
cleanExpiredLikes();
|
uni.removeStorageSync(LIKE_STORAGE_KEY);
|
||||||
|
} catch (e) {
|
||||||
// 检查今天是否已点赞
|
console.error('清除所有点赞记录失败:', e);
|
||||||
if (hasLikedToday(assetId)) {
|
|
||||||
uni.showToast({ title: '今日已点赞', icon: 'none' });
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
likeAssetApi(assetId).then(res => {
|
|
||||||
console.log('点赞成功', res);
|
|
||||||
markLikedToday(assetId);
|
|
||||||
// 触发全局点赞成功事件
|
|
||||||
uni.$emit('assetLiked', { asset_id: assetId, data: res.data });
|
|
||||||
if (callback) callback(true, res.data);
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('点赞失败:', err);
|
|
||||||
uni.showToast({ title: '今日已点赞', icon: 'none' });
|
|
||||||
if (callback) callback(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user