修改每小时收益的字段类型可以为小数

This commit is contained in:
zerosaturation 2026-05-19 12:38:12 +08:00
parent ca832abb2c
commit 9ac75d034b
13 changed files with 74 additions and 53 deletions

View File

@ -914,6 +914,7 @@ func (ctrl *SocialController) GetMyLikedAssets(c *gin.Context) {
resp, err := ctrl.socialService.GetMyLikedAssets(ctx, &pbSocial.GetMyLikedAssetsRequest{
Page: int32(page),
PageSize: int32(pageSize),
OrderBy: c.DefaultQuery("order_by", "like_count"),
})
if err != nil {

View File

@ -82,7 +82,7 @@ type AssetDTO struct {
Info string `json:"info"` // 藏品信息
DisplayStatus int32 `json:"display_status"` // 展示状态0=待展示, 1=已展示
Earnings int64 `json:"earnings"` // 当前展出收益(实时计算,仅展出中时有值)
HourlyEarnings int64 `json:"hourly_earnings"` // 每小时收益(实时计算,仅展出中时有值)
HourlyEarnings float64 `json:"hourly_earnings"` // 每小时收益(实时计算,仅展出中时有值)
ExhibitionExpireAt int64 `json:"exhibition_expire_at"` // 展出过期时间毫秒时间戳仅展出中时有值0=未展出)
}

View File

@ -80,7 +80,7 @@ type ExhibitedAssetItemDTO struct {
ExhibitedAt int64 `json:"exhibited_at"` // 展出开始时间(毫秒时间戳)
ExpireAt int64 `json:"expire_at"` // 展出过期时间(毫秒时间戳)
Earnings int64 `json:"earnings"` // 当前可领取收益
HourlyEarnings int64 `json:"hourly_earnings"` // 每小时收益
HourlyEarnings float64 `json:"hourly_earnings"` // 每小时收益
SlotIndex int32 `json:"slot_index"` // 展位序号
IsLenticular bool `json:"is_lenticular"` // 是否为光栅卡
}

View File

@ -50,7 +50,7 @@ type Asset struct {
Info string `protobuf:"bytes,21,opt,name=info,proto3" json:"info,omitempty"` // 藏品信息
DisplayStatus int32 `protobuf:"varint,22,opt,name=display_status,json=displayStatus,proto3" json:"display_status,omitempty"` // 展示状态0=待展示, 1=已展示
Earnings int64 `protobuf:"varint,23,opt,name=earnings,proto3" json:"earnings,omitempty"` // 当前展出收益(实时计算,仅展出中时有值)
HourlyEarnings int64 `protobuf:"varint,25,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益(实时计算,仅展出中时有值)
HourlyEarnings float64 `protobuf:"fixed64,25,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益(实时计算,仅展出中时有值)
ExhibitionExpireAt int64 `protobuf:"varint,24,opt,name=exhibition_expire_at,json=exhibitionExpireAt,proto3" json:"exhibition_expire_at,omitempty"` // 展出过期时间毫秒时间戳仅展出中时有值0=未展出)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
@ -247,7 +247,7 @@ func (x *Asset) GetEarnings() int64 {
return 0
}
func (x *Asset) GetHourlyEarnings() int64 {
func (x *Asset) GetHourlyEarnings() float64 {
if x != nil {
return x.HourlyEarnings
}
@ -3670,7 +3670,7 @@ const file_asset_proto_rawDesc = "" +
"\x04info\x18\x15 \x01(\tR\x04info\x12%\n" +
"\x0edisplay_status\x18\x16 \x01(\x05R\rdisplayStatus\x12\x1a\n" +
"\bearnings\x18\x17 \x01(\x03R\bearnings\x12'\n" +
"\x0fhourly_earnings\x18\x19 \x01(\x03R\x0ehourlyEarnings\x120\n" +
"\x0fhourly_earnings\x18\x19 \x01(\x01R\x0ehourlyEarnings\x120\n" +
"\x14exhibition_expire_at\x18\x18 \x01(\x03R\x12exhibitionExpireAt\"X\n" +
"\tOwnerInfo\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x1a\n" +

View File

@ -1215,16 +1215,16 @@ func (x *ExhibitedAssetsData) GetHasMore() bool {
// 展出作品项
type ExhibitedAssetItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // 藏品名称
CoverUrl string `protobuf:"bytes,3,opt,name=cover_url,json=coverUrl,proto3" json:"cover_url,omitempty"` // 封面图URL
LikeCount int32 `protobuf:"varint,4,opt,name=like_count,json=likeCount,proto3" json:"like_count,omitempty"` // 实时点赞数
ExhibitedAt int64 `protobuf:"varint,5,opt,name=exhibited_at,json=exhibitedAt,proto3" json:"exhibited_at,omitempty"` // 展出开始时间(毫秒时间戳)
ExpireAt int64 `protobuf:"varint,6,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` // 展出过期时间(毫秒时间戳)
Earnings int64 `protobuf:"varint,7,opt,name=earnings,proto3" json:"earnings,omitempty"` // 当前可领取收益
HourlyEarnings int64 `protobuf:"varint,10,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
SlotIndex int32 `protobuf:"varint,8,opt,name=slot_index,json=slotIndex,proto3" json:"slot_index,omitempty"` // 展位序号
IsLenticular bool `protobuf:"varint,9,opt,name=is_lenticular,json=isLenticular,proto3" json:"is_lenticular,omitempty"` // 是否为光栅卡(根据 tags 判断)
AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // 藏品名称
CoverUrl string `protobuf:"bytes,3,opt,name=cover_url,json=coverUrl,proto3" json:"cover_url,omitempty"` // 封面图URL
LikeCount int32 `protobuf:"varint,4,opt,name=like_count,json=likeCount,proto3" json:"like_count,omitempty"` // 实时点赞数
ExhibitedAt int64 `protobuf:"varint,5,opt,name=exhibited_at,json=exhibitedAt,proto3" json:"exhibited_at,omitempty"` // 展出开始时间(毫秒时间戳)
ExpireAt int64 `protobuf:"varint,6,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"` // 展出过期时间(毫秒时间戳)
Earnings int64 `protobuf:"varint,7,opt,name=earnings,proto3" json:"earnings,omitempty"` // 当前可领取收益
HourlyEarnings float64 `protobuf:"fixed64,10,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
SlotIndex int32 `protobuf:"varint,8,opt,name=slot_index,json=slotIndex,proto3" json:"slot_index,omitempty"` // 展位序号
IsLenticular bool `protobuf:"varint,9,opt,name=is_lenticular,json=isLenticular,proto3" json:"is_lenticular,omitempty"` // 是否为光栅卡(根据 tags 判断)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -1308,7 +1308,7 @@ func (x *ExhibitedAssetItem) GetEarnings() int64 {
return 0
}
func (x *ExhibitedAssetItem) GetHourlyEarnings() int64 {
func (x *ExhibitedAssetItem) GetHourlyEarnings() float64 {
if x != nil {
return x.HourlyEarnings
}
@ -1836,7 +1836,7 @@ const file_gallery_proto_rawDesc = "" +
"\texpire_at\x18\x06 \x01(\x03R\bexpireAt\x12\x1a\n" +
"\bearnings\x18\a \x01(\x03R\bearnings\x12'\n" +
"\x0fhourly_earnings\x18\n" +
" \x01(\x03R\x0ehourlyEarnings\x12\x1d\n" +
" \x01(\x01R\x0ehourlyEarnings\x12\x1d\n" +
"\n" +
"slot_index\x18\b \x01(\x05R\tslotIndex\x12#\n" +
"\ris_lenticular\x18\t \x01(\bR\fisLenticular\"\x9a\x01\n" +

View File

@ -2153,6 +2153,7 @@ type GetMyLikedAssetsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"` // 页码默认1
PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` // 每页数量默认20最大100
OrderBy string `protobuf:"bytes,3,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"` // 排序字段liked_at(默认,按点赞时间), like_count(按点赞数)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -2201,6 +2202,13 @@ func (x *GetMyLikedAssetsRequest) GetPageSize() int32 {
return 0
}
func (x *GetMyLikedAssetsRequest) GetOrderBy() string {
if x != nil {
return x.OrderBy
}
return ""
}
// 获取我点赞的作品列表响应
type GetMyLikedAssetsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
@ -2334,13 +2342,13 @@ func (x *LikedAssetsData) GetHasMore() bool {
// 点赞作品项
type LikedAssetItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // 藏品名称
CoverUrl string `protobuf:"bytes,3,opt,name=cover_url,json=coverUrl,proto3" json:"cover_url,omitempty"` // 封面图URL
LikeCount int32 `protobuf:"varint,4,opt,name=like_count,json=likeCount,proto3" json:"like_count,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"` // 当前可领取收益
HourlyEarnings int64 `protobuf:"varint,7,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // 藏品名称
CoverUrl string `protobuf:"bytes,3,opt,name=cover_url,json=coverUrl,proto3" json:"cover_url,omitempty"` // 封面图URL
LikeCount int32 `protobuf:"varint,4,opt,name=like_count,json=likeCount,proto3" json:"like_count,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"` // 当前可领取收益
HourlyEarnings float64 `protobuf:"fixed64,7,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -2417,7 +2425,7 @@ func (x *LikedAssetItem) GetEarnings() int64 {
return 0
}
func (x *LikedAssetItem) GetHourlyEarnings() int64 {
func (x *LikedAssetItem) GetHourlyEarnings() float64 {
if x != nil {
return x.HourlyEarnings
}
@ -2915,10 +2923,11 @@ const file_social_proto_rawDesc = "" +
"\basset_id\x18\x01 \x01(\x03R\aassetId\"e\n" +
"\x16CheckAssetLikeResponse\x120\n" +
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x19\n" +
"\bis_liked\x18\x02 \x01(\bR\aisLiked\"J\n" +
"\bis_liked\x18\x02 \x01(\bR\aisLiked\"e\n" +
"\x17GetMyLikedAssetsRequest\x12\x12\n" +
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x1b\n" +
"\tpage_size\x18\x02 \x01(\x05R\bpageSize\"\x81\x01\n" +
"\tpage_size\x18\x02 \x01(\x05R\bpageSize\x12\x19\n" +
"\border_by\x18\x03 \x01(\tR\aorderBy\"\x81\x01\n" +
"\x18GetMyLikedAssetsResponse\x120\n" +
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x123\n" +
"\x04data\x18\x02 \x01(\v2\x1f.topfans.social.LikedAssetsDataR\x04data\"\xa9\x01\n" +
@ -2936,7 +2945,7 @@ const file_social_proto_rawDesc = "" +
"like_count\x18\x04 \x01(\x05R\tlikeCount\x12\x19\n" +
"\bliked_at\x18\x05 \x01(\x03R\alikedAt\x12\x1a\n" +
"\bearnings\x18\x06 \x01(\x03R\bearnings\x12'\n" +
"\x0fhourly_earnings\x18\a \x01(\x03R\x0ehourlyEarnings\"O\n" +
"\x0fhourly_earnings\x18\a \x01(\x01R\x0ehourlyEarnings\"O\n" +
"\x1cGetMyTodayLikedAssetsRequest\x12\x12\n" +
"\x04page\x18\x01 \x01(\x05R\x04page\x12\x1b\n" +
"\tpage_size\x18\x02 \x01(\x05R\bpageSize\"\x86\x01\n" +

View File

@ -36,7 +36,7 @@ message Asset {
string info = 21; //
int32 display_status = 22; // 0=, 1=
int64 earnings = 23; //
int64 hourly_earnings = 25; //
double hourly_earnings = 25; //
int64 exhibition_expire_at = 24; // 0=
}

View File

@ -207,7 +207,7 @@ message ExhibitedAssetItem {
int64 exhibited_at = 5; //
int64 expire_at = 6; //
int64 earnings = 7; //
int64 hourly_earnings = 10; //
double hourly_earnings = 10; //
int32 slot_index = 8; //
bool is_lenticular = 9; // tags
}

View File

@ -266,6 +266,7 @@ message CheckAssetLikeResponse {
message GetMyLikedAssetsRequest {
int32 page = 1; // 1
int32 page_size = 2; // 20100
string order_by = 3; // liked_at(), like_count()
}
//
@ -291,7 +292,7 @@ message LikedAssetItem {
int32 like_count = 4; //
int64 liked_at = 5; //
int64 earnings = 6; //
int64 hourly_earnings = 7; //
double hourly_earnings = 7; //
}
//

View File

@ -659,7 +659,7 @@ func ModelToProtoAsset(asset *models.Asset) *pb.AssetListItem {
}
// ModelToProtoAssetDetail 将数据库模型转换为Proto格式Asset详情
func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked bool, displayStatus int32, earnings int64, hourlyEarnings int64, exhibitionExpireAt int64, grade int32) *pb.Asset {
func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked bool, displayStatus int32, earnings int64, hourlyEarnings float64, exhibitionExpireAt int64, grade int32) *pb.Asset {
if asset == nil {
return nil
}
@ -732,8 +732,8 @@ func getStatusString(status int32) string {
// calculateHourlyEarnings 计算每小时收益
// 公式R0 × [100% + Buff(n)]
// R0 = 5 水晶/小时Buff(n) 根据点赞数计算
func calculateHourlyEarnings(likeCount int32) int64 {
R0 := int64(5) // 水晶/小时
func calculateHourlyEarnings(likeCount int32) float64 {
R0 := float64(5) // 水晶/小时
// 计算Buff
var buff int
@ -749,7 +749,7 @@ func calculateHourlyEarnings(likeCount int32) int64 {
}
// 应用Buff加成R1 = R0 × (100% + Buff)
return R0 * (100 + int64(buff)) / 100
return R0 * (100 + float64(buff)) / 100
}
// calculateRealtimeEarnings 实时计算展示收益
@ -762,6 +762,6 @@ func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
T = 1 // 最少1小时
}
// 总收益 = 每小时收益 × 时长
return calculateHourlyEarnings(likeCount) * T
// 总收益 = 每小时收益 × 时长转int64取整
return int64(calculateHourlyEarnings(likeCount) * float64(T))
}

View File

@ -99,7 +99,7 @@ type ExhibitedAssetInfo struct {
ExhibitedAt int64
ExpireAt int64
Earnings int64
HourlyEarnings int64
HourlyEarnings float64
SlotIndex int32
IsLenticular bool
}
@ -625,8 +625,8 @@ func generateHostProfileID(userID, starID int64) int64 {
// calculateHourlyEarnings 计算每小时收益
// 公式R0 × [100% + Buff(n)]
// R0 = 5 水晶/小时Buff(n) 根据点赞数计算
func calculateHourlyEarnings(likeCount int32) int64 {
R0 := int64(5) // 水晶/小时
func calculateHourlyEarnings(likeCount int32) float64 {
R0 := float64(5) // 水晶/小时
// 计算Buff
var buff int
@ -642,7 +642,7 @@ func calculateHourlyEarnings(likeCount int32) int64 {
}
// 应用Buff加成R1 = R0 × (100% + Buff)
return R0 * (100 + int64(buff)) / 100
return R0 * (100 + float64(buff)) / 100
}
// calculateRealtimeEarnings 实时计算展示收益
@ -655,6 +655,6 @@ func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
T = 1 // 最少1小时
}
// 总收益 = 每小时收益 × 时长
return calculateHourlyEarnings(likeCount) * T
// 总收益 = 每小时收益 × 时长转int64取整
return int64(calculateHourlyEarnings(likeCount) * float64(T))
}

View File

@ -109,8 +109,9 @@ type SocialRepository interface {
// starID: 明星ID
// page: 页码从1开始
// pageSize: 每页数量
// orderBy: 排序字段liked_at 或 like_count
// 返回: 作品列表、总数量
GetMyLikedAssets(userID, starID int64, page, pageSize int) ([]*LikedAssetInfo, int64, error)
GetMyLikedAssets(userID, starID int64, page, pageSize int, orderBy string) ([]*LikedAssetInfo, int64, error)
// GetMyTodayLikedAssets 获取我今日点赞的作品列表(只返回展出中且未过期的)
// userID: 用户ID
@ -145,7 +146,7 @@ type LikedAssetInfo struct {
LikeCount int32
LikedAt int64
Earnings int64
HourlyEarnings int64
HourlyEarnings float64
}
// RandomUserInfo 随机用户信息
@ -575,10 +576,19 @@ func (r *socialRepositoryImpl) GetFanProfilesByNickname(starID int64, nickname s
// ========== 我的作品相关实现 ==========
// GetMyLikedAssets 获取我点赞的作品列表(只返回展出中且未过期的,含收益)
func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, pageSize int) ([]*LikedAssetInfo, int64, error) {
func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, pageSize int, orderBy string) ([]*LikedAssetInfo, int64, error) {
var items []*LikedAssetInfo
var total int64
// 根据 orderBy 确定排序字段
var orderClause string
switch orderBy {
case "like_count":
orderClause = "a.like_count DESC, asset_likes.created_at DESC"
default: // 默认按点赞时间
orderClause = "asset_likes.created_at DESC"
}
// 计数查询(使用 DISTINCT 因为一个资产可能在多个展位展出)
// 只要资产未删除且未下架就显示,包含已过期的(用户可继续查看点赞记录)
countQuery := r.db.Model(&models.AssetLike{}).
@ -603,7 +613,7 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
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").
Order("asset_likes.created_at DESC").
Order(orderClause).
Limit(pageSize).
Offset(offset).
Scan(&items).Error
@ -630,8 +640,8 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
// calculateHourlyEarnings 计算每小时收益
// 公式R0 × [100% + Buff(n)]
// R0 = 5 水晶/小时Buff(n) 根据点赞数计算
func calculateHourlyEarnings(likeCount int32) int64 {
R0 := int64(5) // 水晶/小时
func calculateHourlyEarnings(likeCount int32) float64 {
R0 := float64(5) // 水晶/小时
// 计算Buff
var buff int
@ -647,7 +657,7 @@ func calculateHourlyEarnings(likeCount int32) int64 {
}
// 应用Buff加成R1 = R0 × (100% + Buff)
return R0 * (100 + int64(buff)) / 100
return R0 * (100 + float64(buff)) / 100
}
// calculateRealtimeEarnings 实时计算展示收益
@ -660,8 +670,8 @@ func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
T = 1 // 最少1小时
}
// 总收益 = 每小时收益 × 时长
return calculateHourlyEarnings(likeCount) * T
// 总收益 = 每小时收益 × 时长转int64取整
return int64(calculateHourlyEarnings(likeCount) * float64(T))
}
// GetMyTodayLikedAssets 获取我今日点赞的作品列表(只返回展出中且未过期的)

View File

@ -251,7 +251,7 @@ func (s *AssetLikeService) GetMyLikedAssets(ctx context.Context, req *pb.GetMyLi
pageSize = 100
}
items, total, err := s.socialRepo.GetMyLikedAssets(userID, starID, int(page), int(pageSize))
items, total, err := s.socialRepo.GetMyLikedAssets(userID, starID, int(page), int(pageSize), req.OrderBy)
if err != nil {
logger.Logger.Error("Failed to get my liked assets",
zap.Error(err),