feat: 点赞列表修改,增加光栅卡字段
This commit is contained in:
parent
9ac75d034b
commit
94b9271184
@ -894,6 +894,7 @@ func (ctrl *SocialController) UnlikeAsset(c *gin.Context) {
|
|||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Param page query int false "页码,默认1"
|
// @Param page query int false "页码,默认1"
|
||||||
// @Param page_size query int false "每页数量,默认20,最大100"
|
// @Param page_size query int false "每页数量,默认20,最大100"
|
||||||
|
// @Param order_by query string false "排序字段:liked_at(默认), like_count(按点赞数)"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Router /api/v1/me/liked-assets [get]
|
// @Router /api/v1/me/liked-assets [get]
|
||||||
func (ctrl *SocialController) GetMyLikedAssets(c *gin.Context) {
|
func (ctrl *SocialController) GetMyLikedAssets(c *gin.Context) {
|
||||||
@ -938,6 +939,7 @@ func (ctrl *SocialController) GetMyLikedAssets(c *gin.Context) {
|
|||||||
"liked_at": item.LikedAt,
|
"liked_at": item.LikedAt,
|
||||||
"earnings": item.Earnings,
|
"earnings": item.Earnings,
|
||||||
"hourly_earnings": item.HourlyEarnings,
|
"hourly_earnings": item.HourlyEarnings,
|
||||||
|
"is_lenticular": item.IsLenticular,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ type Exhibition struct {
|
|||||||
CreatedAt int64 `gorm:"column:created_at;not null"`
|
CreatedAt int64 `gorm:"column:created_at;not null"`
|
||||||
UpdatedAt int64 `gorm:"column:updated_at;not null"`
|
UpdatedAt int64 `gorm:"column:updated_at;not null"`
|
||||||
DeletedAt *int64 `gorm:"column:deleted_at"`
|
DeletedAt *int64 `gorm:"column:deleted_at"`
|
||||||
|
IsProcessed bool `gorm:"column:is_processed;default:false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableName 指定表名
|
// TableName 指定表名
|
||||||
|
|||||||
@ -2349,6 +2349,7 @@ type LikedAssetItem struct {
|
|||||||
LikedAt int64 `protobuf:"varint,5,opt,name=liked_at,json=likedAt,proto3" json:"liked_at,omitempty"` // 点赞时间(毫秒时间戳)
|
LikedAt int64 `protobuf:"varint,5,opt,name=liked_at,json=likedAt,proto3" json:"liked_at,omitempty"` // 点赞时间(毫秒时间戳)
|
||||||
Earnings int64 `protobuf:"varint,6,opt,name=earnings,proto3" json:"earnings,omitempty"` // 当前可领取收益
|
Earnings int64 `protobuf:"varint,6,opt,name=earnings,proto3" json:"earnings,omitempty"` // 当前可领取收益
|
||||||
HourlyEarnings float64 `protobuf:"fixed64,7,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
|
HourlyEarnings float64 `protobuf:"fixed64,7,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
|
||||||
|
IsLenticular bool `protobuf:"varint,8,opt,name=is_lenticular,json=isLenticular,proto3" json:"is_lenticular,omitempty"` // 是否为光栅卡
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -2432,6 +2433,13 @@ func (x *LikedAssetItem) GetHourlyEarnings() float64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LikedAssetItem) GetIsLenticular() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IsLenticular
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// 获取我今日点赞的作品列表请求(暂不实现)
|
// 获取我今日点赞的作品列表请求(暂不实现)
|
||||||
type GetMyTodayLikedAssetsRequest struct {
|
type GetMyTodayLikedAssetsRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@ -2936,7 +2944,7 @@ const file_social_proto_rawDesc = "" +
|
|||||||
"\x04page\x18\x02 \x01(\x05R\x04page\x12\x1b\n" +
|
"\x04page\x18\x02 \x01(\x05R\x04page\x12\x1b\n" +
|
||||||
"\tpage_size\x18\x03 \x01(\x05R\bpageSize\x12\x14\n" +
|
"\tpage_size\x18\x03 \x01(\x05R\bpageSize\x12\x14\n" +
|
||||||
"\x05total\x18\x04 \x01(\x03R\x05total\x12\x19\n" +
|
"\x05total\x18\x04 \x01(\x03R\x05total\x12\x19\n" +
|
||||||
"\bhas_more\x18\x05 \x01(\bR\ahasMore\"\xdb\x01\n" +
|
"\bhas_more\x18\x05 \x01(\bR\ahasMore\"\x80\x02\n" +
|
||||||
"\x0eLikedAssetItem\x12\x19\n" +
|
"\x0eLikedAssetItem\x12\x19\n" +
|
||||||
"\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x12\n" +
|
"\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x12\n" +
|
||||||
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" +
|
"\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n" +
|
||||||
@ -2945,7 +2953,8 @@ const file_social_proto_rawDesc = "" +
|
|||||||
"like_count\x18\x04 \x01(\x05R\tlikeCount\x12\x19\n" +
|
"like_count\x18\x04 \x01(\x05R\tlikeCount\x12\x19\n" +
|
||||||
"\bliked_at\x18\x05 \x01(\x03R\alikedAt\x12\x1a\n" +
|
"\bliked_at\x18\x05 \x01(\x03R\alikedAt\x12\x1a\n" +
|
||||||
"\bearnings\x18\x06 \x01(\x03R\bearnings\x12'\n" +
|
"\bearnings\x18\x06 \x01(\x03R\bearnings\x12'\n" +
|
||||||
"\x0fhourly_earnings\x18\a \x01(\x01R\x0ehourlyEarnings\"O\n" +
|
"\x0fhourly_earnings\x18\a \x01(\x01R\x0ehourlyEarnings\x12#\n" +
|
||||||
|
"\ris_lenticular\x18\b \x01(\bR\fisLenticular\"O\n" +
|
||||||
"\x1cGetMyTodayLikedAssetsRequest\x12\x12\n" +
|
"\x1cGetMyTodayLikedAssetsRequest\x12\x12\n" +
|
||||||
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x1b\n" +
|
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x1b\n" +
|
||||||
"\tpage_size\x18\x02 \x01(\x05R\bpageSize\"\x86\x01\n" +
|
"\tpage_size\x18\x02 \x01(\x05R\bpageSize\"\x86\x01\n" +
|
||||||
|
|||||||
@ -293,6 +293,7 @@ message LikedAssetItem {
|
|||||||
int64 liked_at = 5; // 点赞时间(毫秒时间戳)
|
int64 liked_at = 5; // 点赞时间(毫秒时间戳)
|
||||||
int64 earnings = 6; // 当前可领取收益
|
int64 earnings = 6; // 当前可领取收益
|
||||||
double hourly_earnings = 7; // 每小时收益
|
double hourly_earnings = 7; // 每小时收益
|
||||||
|
bool is_lenticular = 8; // 是否为光栅卡
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取我今日点赞的作品列表请求(暂不实现)
|
// 获取我今日点赞的作品列表请求(暂不实现)
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
-- Migration: Add is_processed field to exhibitions table
|
||||||
|
-- Description: Mark exhibitions as processed to prevent duplicate revenue record generation
|
||||||
|
-- Date: 2026-05-19
|
||||||
|
|
||||||
|
ALTER TABLE exhibitions ADD COLUMN IF NOT EXISTS is_processed BOOLEAN DEFAULT false;
|
||||||
|
|
||||||
|
-- Create index for faster queries on unprocessed expired exhibitions
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_exhibitions_unprocessed ON exhibitions(expire_at) WHERE is_processed = false AND deleted_at IS NULL;
|
||||||
@ -40,6 +40,8 @@ type GalleryRepository interface {
|
|||||||
RemoveExhibitionTx(exhibitionID int64, assetID int64) error
|
RemoveExhibitionTx(exhibitionID int64, assetID int64) error
|
||||||
// 更新展览过期时间(用于清理后防止重复处理)
|
// 更新展览过期时间(用于清理后防止重复处理)
|
||||||
UpdateExhibitionExpireAt(exhibitionID int64, expireAt int64) error
|
UpdateExhibitionExpireAt(exhibitionID int64, expireAt int64) error
|
||||||
|
// 设置展品已处理(用于清理后标记已处理,防止重复生成收益记录)
|
||||||
|
SetExhibitionProcessed(exhibitionID int64, processed bool) error
|
||||||
|
|
||||||
// ========== 我的作品相关 ==========
|
// ========== 我的作品相关 ==========
|
||||||
|
|
||||||
@ -299,10 +301,10 @@ func (r *galleryRepository) DeleteExhibitionByAsset(assetID int64) error {
|
|||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExpiredExhibitions 获取过期的展品展示记录(不含已删除)
|
// GetExpiredExhibitions 获取过期的展品展示记录(不含已删除且未处理)
|
||||||
func (r *galleryRepository) GetExpiredExhibitions(beforeTime int64) ([]*models.Exhibition, error) {
|
func (r *galleryRepository) GetExpiredExhibitions(beforeTime int64) ([]*models.Exhibition, error) {
|
||||||
var exhibitions []*models.Exhibition
|
var exhibitions []*models.Exhibition
|
||||||
err := r.db.Where("expire_at <= ? AND deleted_at IS NULL", beforeTime).Find(&exhibitions).Error
|
err := r.db.Where("expire_at <= ? AND deleted_at IS NULL AND is_processed = false", beforeTime).Find(&exhibitions).Error
|
||||||
return exhibitions, err
|
return exhibitions, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,6 +401,13 @@ func (r *galleryRepository) UpdateExhibitionExpireAt(exhibitionID int64, expireA
|
|||||||
Update("expire_at", expireAt).Error
|
Update("expire_at", expireAt).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetExhibitionProcessed 设置展品已处理标记(用于清理后标记已处理,防止重复生成收益记录)
|
||||||
|
func (r *galleryRepository) SetExhibitionProcessed(exhibitionID int64, processed bool) error {
|
||||||
|
return r.db.Model(&models.Exhibition{}).
|
||||||
|
Where("id = ?", exhibitionID).
|
||||||
|
Update("is_processed", processed).Error
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 我的作品相关实现 ==========
|
// ========== 我的作品相关实现 ==========
|
||||||
|
|
||||||
// GetMyExhibitedAssets 获取我展出的作品列表(返回展出中且未下架的,含收益)
|
// GetMyExhibitedAssets 获取我展出的作品列表(返回展出中且未下架的,含收益)
|
||||||
|
|||||||
@ -146,9 +146,9 @@ func (w *CleanupWorker) cleanupExpiredExhibitions(now int64) {
|
|||||||
|
|
||||||
successCount++
|
successCount++
|
||||||
|
|
||||||
// 4.5 更新展览过期时间为历史值,防止重复处理(领取收益时才会真正下架)
|
// 4.5 标记展品已处理,防止重复生成收益记录
|
||||||
if err := w.repo.UpdateExhibitionExpireAt(e.ID, now-3600000); err != nil {
|
if err := w.repo.SetExhibitionProcessed(e.ID, true); err != nil {
|
||||||
logger.Logger.Error("更新展览过期时间失败",
|
logger.Logger.Error("标记展品已处理失败",
|
||||||
zap.Int64("exhibition_id", e.ID),
|
zap.Int64("exhibition_id", e.ID),
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -147,6 +147,7 @@ type LikedAssetInfo struct {
|
|||||||
LikedAt int64
|
LikedAt int64
|
||||||
Earnings int64
|
Earnings int64
|
||||||
HourlyEarnings float64
|
HourlyEarnings float64
|
||||||
|
IsLenticular bool // 是否为光栅卡
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomUserInfo 随机用户信息
|
// RandomUserInfo 随机用户信息
|
||||||
@ -590,29 +591,29 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 计数查询(使用 DISTINCT 因为一个资产可能在多个展位展出)
|
// 计数查询(使用 DISTINCT 因为一个资产可能在多个展位展出)
|
||||||
// 只要资产未删除且未下架就显示,包含已过期的(用户可继续查看点赞记录)
|
// 显示:展品存在且未领取(is_processed=false),无论是否过期
|
||||||
|
// 不显示:已领取的(is_processed=true)或从未展出过的
|
||||||
countQuery := r.db.Model(&models.AssetLike{}).
|
countQuery := r.db.Model(&models.AssetLike{}).
|
||||||
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
Joins("JOIN assets a ON a.id = asset_likes.asset_id").
|
||||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL AND e.is_processed = false").
|
||||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
Where("a.deleted_at IS NULL AND a.is_active = ?", true)
|
||||||
Where("e.deleted_at IS NULL")
|
|
||||||
|
|
||||||
if err := countQuery.Distinct("asset_likes.asset_id").Count(&total).Error; err != nil {
|
if err := countQuery.Distinct("asset_likes.asset_id").Count(&total).Error; err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据查询:只过滤已下架的,不过滤过期
|
// 数据查询:只过滤已下架的,不过滤过期(已过期但未领取的仍显示)
|
||||||
offset := (page - 1) * pageSize
|
offset := (page - 1) * pageSize
|
||||||
err := r.db.Model(&models.AssetLike{}).
|
err := r.db.Model(&models.AssetLike{}).
|
||||||
Select(`asset_likes.asset_id, a.name, a.cover_url, a.like_count,
|
Select(`asset_likes.asset_id, a.name, a.cover_url, a.like_count,
|
||||||
asset_likes.created_at as liked_at`).
|
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 assets a ON a.id = asset_likes.asset_id").
|
||||||
Joins("JOIN exhibitions e ON e.asset_id = a.id").
|
Joins("JOIN exhibitions e ON e.asset_id = a.id AND e.deleted_at IS NULL AND e.is_processed = false").
|
||||||
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
Where("asset_likes.user_id = ? AND asset_likes.star_id = ?", userID, starID).
|
||||||
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
Where("a.deleted_at IS NULL AND a.is_active = ?", true).
|
||||||
Where("e.deleted_at IS NULL").
|
Group("asset_likes.asset_id, a.name, a.cover_url, a.like_count, asset_likes.created_at, is_lenticular").
|
||||||
Group("asset_likes.asset_id, a.name, a.cover_url, a.like_count, asset_likes.created_at").
|
|
||||||
Order(orderClause).
|
Order(orderClause).
|
||||||
Limit(pageSize).
|
Limit(pageSize).
|
||||||
Offset(offset).
|
Offset(offset).
|
||||||
|
|||||||
@ -278,6 +278,7 @@ func (s *AssetLikeService) GetMyLikedAssets(ctx context.Context, req *pb.GetMyLi
|
|||||||
LikedAt: item.LikedAt,
|
LikedAt: item.LikedAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
HourlyEarnings: item.HourlyEarnings,
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
|
IsLenticular: item.IsLenticular,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -217,7 +217,7 @@
|
|||||||
.level-badge {
|
.level-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
|
background: linear-gradient(165deg, #F0E4B1 0%, #F08399 50%, #B94E73 90%, #834B9E 100%);
|
||||||
width: 88rpx;
|
width: 112rpx;
|
||||||
height: 24rpx;
|
height: 24rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@ -393,7 +393,7 @@ const handleExhibitionCardTap = (item, index) => {
|
|||||||
// exhibitionWorks.value[index].earnings = data.earnings;
|
// exhibitionWorks.value[index].earnings = data.earnings;
|
||||||
// } else {
|
// } else {
|
||||||
// 如果没有返回收益数据,刷新列表获取最新收益
|
// 如果没有返回收益数据,刷新列表获取最新收益
|
||||||
await loadExhibitedAssets();
|
await loadLikedAssets();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
uni.showToast({ title: '点赞成功', icon: 'success' });
|
uni.showToast({ title: '点赞成功', icon: 'success' });
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user