feat: 新增每小时的收益字段
This commit is contained in:
parent
ffa53f854c
commit
ca832abb2c
@ -614,7 +614,8 @@ func (ctrl *GalleryController) GetMyExhibitedAssets(c *gin.Context) {
|
|||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
ExhibitedAt: item.ExhibitedAt,
|
ExhibitedAt: item.ExhibitedAt,
|
||||||
ExpireAt: item.ExpireAt,
|
ExpireAt: item.ExpireAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
SlotIndex: item.SlotIndex,
|
SlotIndex: item.SlotIndex,
|
||||||
IsLenticular: item.IsLenticular,
|
IsLenticular: item.IsLenticular,
|
||||||
})
|
})
|
||||||
@ -804,7 +805,8 @@ func (ctrl *GalleryController) GetUserExhibitedAssets(c *gin.Context) {
|
|||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
ExhibitedAt: item.ExhibitedAt,
|
ExhibitedAt: item.ExhibitedAt,
|
||||||
ExpireAt: item.ExpireAt,
|
ExpireAt: item.ExpireAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
SlotIndex: item.SlotIndex,
|
SlotIndex: item.SlotIndex,
|
||||||
IsLenticular: item.IsLenticular,
|
IsLenticular: item.IsLenticular,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -930,12 +930,13 @@ func (ctrl *SocialController) GetMyLikedAssets(c *gin.Context) {
|
|||||||
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
||||||
for _, item := range resp.Data.Items {
|
for _, item := range resp.Data.Items {
|
||||||
items = append(items, map[string]interface{}{
|
items = append(items, map[string]interface{}{
|
||||||
"asset_id": item.AssetId,
|
"asset_id": item.AssetId,
|
||||||
"name": item.Name,
|
"name": item.Name,
|
||||||
"cover_url": item.CoverUrl,
|
"cover_url": item.CoverUrl,
|
||||||
"like_count": item.LikeCount,
|
"like_count": item.LikeCount,
|
||||||
"liked_at": item.LikedAt,
|
"liked_at": item.LikedAt,
|
||||||
"earnings": item.Earnings,
|
"earnings": item.Earnings,
|
||||||
|
"hourly_earnings": item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,12 +994,13 @@ func (ctrl *SocialController) GetMyTodayLikedAssets(c *gin.Context) {
|
|||||||
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
||||||
for _, item := range resp.Data.Items {
|
for _, item := range resp.Data.Items {
|
||||||
items = append(items, map[string]interface{}{
|
items = append(items, map[string]interface{}{
|
||||||
"asset_id": item.AssetId,
|
"asset_id": item.AssetId,
|
||||||
"name": item.Name,
|
"name": item.Name,
|
||||||
"cover_url": item.CoverUrl,
|
"cover_url": item.CoverUrl,
|
||||||
"like_count": item.LikeCount,
|
"like_count": item.LikeCount,
|
||||||
"liked_at": item.LikedAt,
|
"liked_at": item.LikedAt,
|
||||||
"earnings": item.Earnings,
|
"earnings": item.Earnings,
|
||||||
|
"hourly_earnings": item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,12 +1058,13 @@ func (ctrl *SocialController) GetMyWeekLikedAssets(c *gin.Context) {
|
|||||||
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
||||||
for _, item := range resp.Data.Items {
|
for _, item := range resp.Data.Items {
|
||||||
items = append(items, map[string]interface{}{
|
items = append(items, map[string]interface{}{
|
||||||
"asset_id": item.AssetId,
|
"asset_id": item.AssetId,
|
||||||
"name": item.Name,
|
"name": item.Name,
|
||||||
"cover_url": item.CoverUrl,
|
"cover_url": item.CoverUrl,
|
||||||
"like_count": item.LikeCount,
|
"like_count": item.LikeCount,
|
||||||
"liked_at": item.LikedAt,
|
"liked_at": item.LikedAt,
|
||||||
"earnings": item.Earnings,
|
"earnings": item.Earnings,
|
||||||
|
"hourly_earnings": item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1128,12 +1131,13 @@ func (ctrl *SocialController) GetUserLikedAssets(c *gin.Context) {
|
|||||||
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
items := make([]map[string]interface{}, 0, len(resp.Data.Items))
|
||||||
for _, item := range resp.Data.Items {
|
for _, item := range resp.Data.Items {
|
||||||
items = append(items, map[string]interface{}{
|
items = append(items, map[string]interface{}{
|
||||||
"asset_id": item.AssetId,
|
"asset_id": item.AssetId,
|
||||||
"name": item.Name,
|
"name": item.Name,
|
||||||
"cover_url": item.CoverUrl,
|
"cover_url": item.CoverUrl,
|
||||||
"like_count": item.LikeCount,
|
"like_count": item.LikeCount,
|
||||||
"liked_at": item.LikedAt,
|
"liked_at": item.LikedAt,
|
||||||
"earnings": item.Earnings,
|
"earnings": item.Earnings,
|
||||||
|
"hourly_earnings": item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -150,6 +150,7 @@ func ConvertAsset(pbAsset *pbAsset.Asset) AssetDTO {
|
|||||||
Info: pbAsset.Info,
|
Info: pbAsset.Info,
|
||||||
DisplayStatus: pbAsset.DisplayStatus,
|
DisplayStatus: pbAsset.DisplayStatus,
|
||||||
Earnings: pbAsset.Earnings,
|
Earnings: pbAsset.Earnings,
|
||||||
|
HourlyEarnings: pbAsset.HourlyEarnings,
|
||||||
ExhibitionExpireAt: pbAsset.ExhibitionExpireAt,
|
ExhibitionExpireAt: pbAsset.ExhibitionExpireAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -80,9 +80,10 @@ type AssetDTO struct {
|
|||||||
Owner *OwnerInfoDTO `json:"owner,omitempty"` // 持有者信息(可选,保留用于兼容性)
|
Owner *OwnerInfoDTO `json:"owner,omitempty"` // 持有者信息(可选,保留用于兼容性)
|
||||||
IsLiked bool `json:"is_liked"` // 当前用户是否已点赞
|
IsLiked bool `json:"is_liked"` // 当前用户是否已点赞
|
||||||
Info string `json:"info"` // 藏品信息
|
Info string `json:"info"` // 藏品信息
|
||||||
DisplayStatus int32 `json:"display_status"` // 展示状态:0=待展示, 1=已展示
|
DisplayStatus int32 `json:"display_status"` // 展示状态:0=待展示, 1=已展示
|
||||||
Earnings int64 `json:"earnings"` // 当前展出收益(实时计算,仅展出中时有值)
|
Earnings int64 `json:"earnings"` // 当前展出收益(实时计算,仅展出中时有值)
|
||||||
ExhibitionExpireAt int64 `json:"exhibition_expire_at"` // 展出过期时间(毫秒时间戳,仅展出中时有值,0=未展出)
|
HourlyEarnings int64 `json:"hourly_earnings"` // 每小时收益(实时计算,仅展出中时有值)
|
||||||
|
ExhibitionExpireAt int64 `json:"exhibition_expire_at"` // 展出过期时间(毫秒时间戳,仅展出中时有值,0=未展出)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OwnerInfoDTO 持有者信息
|
// OwnerInfoDTO 持有者信息
|
||||||
|
|||||||
@ -80,6 +80,7 @@ type ExhibitedAssetItemDTO struct {
|
|||||||
ExhibitedAt int64 `json:"exhibited_at"` // 展出开始时间(毫秒时间戳)
|
ExhibitedAt int64 `json:"exhibited_at"` // 展出开始时间(毫秒时间戳)
|
||||||
ExpireAt int64 `json:"expire_at"` // 展出过期时间(毫秒时间戳)
|
ExpireAt int64 `json:"expire_at"` // 展出过期时间(毫秒时间戳)
|
||||||
Earnings int64 `json:"earnings"` // 当前可领取收益
|
Earnings int64 `json:"earnings"` // 当前可领取收益
|
||||||
|
HourlyEarnings int64 `json:"hourly_earnings"` // 每小时收益
|
||||||
SlotIndex int32 `json:"slot_index"` // 展位序号
|
SlotIndex int32 `json:"slot_index"` // 展位序号
|
||||||
IsLenticular bool `json:"is_lenticular"` // 是否为光栅卡
|
IsLenticular bool `json:"is_lenticular"` // 是否为光栅卡
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,7 @@ type Asset struct {
|
|||||||
Info string `protobuf:"bytes,21,opt,name=info,proto3" json:"info,omitempty"` // 藏品信息
|
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=已展示
|
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"` // 当前展出收益(实时计算,仅展出中时有值)
|
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"` // 每小时收益(实时计算,仅展出中时有值)
|
||||||
ExhibitionExpireAt int64 `protobuf:"varint,24,opt,name=exhibition_expire_at,json=exhibitionExpireAt,proto3" json:"exhibition_expire_at,omitempty"` // 展出过期时间(毫秒时间戳,仅展出中时有值,0=未展出)
|
ExhibitionExpireAt int64 `protobuf:"varint,24,opt,name=exhibition_expire_at,json=exhibitionExpireAt,proto3" json:"exhibition_expire_at,omitempty"` // 展出过期时间(毫秒时间戳,仅展出中时有值,0=未展出)
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@ -246,6 +247,13 @@ func (x *Asset) GetEarnings() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Asset) GetHourlyEarnings() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.HourlyEarnings
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Asset) GetExhibitionExpireAt() int64 {
|
func (x *Asset) GetExhibitionExpireAt() int64 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.ExhibitionExpireAt
|
return x.ExhibitionExpireAt
|
||||||
@ -3631,7 +3639,7 @@ var File_asset_proto protoreflect.FileDescriptor
|
|||||||
|
|
||||||
const file_asset_proto_rawDesc = "" +
|
const file_asset_proto_rawDesc = "" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"\vasset.proto\x12\rtopfans.asset\x1a\x12proto/common.proto\x1a\x1cgoogle/api/annotations.proto\"\xe1\x05\n" +
|
"\vasset.proto\x12\rtopfans.asset\x1a\x12proto/common.proto\x1a\x1cgoogle/api/annotations.proto\"\x8a\x06\n" +
|
||||||
"\x05Asset\x12\x19\n" +
|
"\x05Asset\x12\x19\n" +
|
||||||
"\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x1b\n" +
|
"\basset_id\x18\x01 \x01(\x03R\aassetId\x12\x1b\n" +
|
||||||
"\towner_uid\x18\x02 \x01(\x03R\bownerUid\x12\x17\n" +
|
"\towner_uid\x18\x02 \x01(\x03R\bownerUid\x12\x17\n" +
|
||||||
@ -3661,7 +3669,8 @@ const file_asset_proto_rawDesc = "" +
|
|||||||
"\bis_liked\x18\x14 \x01(\bR\aisLiked\x12\x12\n" +
|
"\bis_liked\x18\x14 \x01(\bR\aisLiked\x12\x12\n" +
|
||||||
"\x04info\x18\x15 \x01(\tR\x04info\x12%\n" +
|
"\x04info\x18\x15 \x01(\tR\x04info\x12%\n" +
|
||||||
"\x0edisplay_status\x18\x16 \x01(\x05R\rdisplayStatus\x12\x1a\n" +
|
"\x0edisplay_status\x18\x16 \x01(\x05R\rdisplayStatus\x12\x1a\n" +
|
||||||
"\bearnings\x18\x17 \x01(\x03R\bearnings\x120\n" +
|
"\bearnings\x18\x17 \x01(\x03R\bearnings\x12'\n" +
|
||||||
|
"\x0fhourly_earnings\x18\x19 \x01(\x03R\x0ehourlyEarnings\x120\n" +
|
||||||
"\x14exhibition_expire_at\x18\x18 \x01(\x03R\x12exhibitionExpireAt\"X\n" +
|
"\x14exhibition_expire_at\x18\x18 \x01(\x03R\x12exhibitionExpireAt\"X\n" +
|
||||||
"\tOwnerInfo\x12\x17\n" +
|
"\tOwnerInfo\x12\x17\n" +
|
||||||
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x1a\n" +
|
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x1a\n" +
|
||||||
|
|||||||
@ -1214,18 +1214,19 @@ func (x *ExhibitedAssetsData) GetHasMore() bool {
|
|||||||
|
|
||||||
// 展出作品项
|
// 展出作品项
|
||||||
type ExhibitedAssetItem struct {
|
type ExhibitedAssetItem struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID
|
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"` // 藏品名称
|
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
|
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"` // 实时点赞数
|
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"` // 展出开始时间(毫秒时间戳)
|
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"` // 展出过期时间(毫秒时间戳)
|
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"` // 当前可领取收益
|
Earnings int64 `protobuf:"varint,7,opt,name=earnings,proto3" json:"earnings,omitempty"` // 当前可领取收益
|
||||||
SlotIndex int32 `protobuf:"varint,8,opt,name=slot_index,json=slotIndex,proto3" json:"slot_index,omitempty"` // 展位序号
|
HourlyEarnings int64 `protobuf:"varint,10,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
|
||||||
IsLenticular bool `protobuf:"varint,9,opt,name=is_lenticular,json=isLenticular,proto3" json:"is_lenticular,omitempty"` // 是否为光栅卡(根据 tags 判断)
|
SlotIndex int32 `protobuf:"varint,8,opt,name=slot_index,json=slotIndex,proto3" json:"slot_index,omitempty"` // 展位序号
|
||||||
unknownFields protoimpl.UnknownFields
|
IsLenticular bool `protobuf:"varint,9,opt,name=is_lenticular,json=isLenticular,proto3" json:"is_lenticular,omitempty"` // 是否为光栅卡(根据 tags 判断)
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ExhibitedAssetItem) Reset() {
|
func (x *ExhibitedAssetItem) Reset() {
|
||||||
@ -1307,6 +1308,13 @@ func (x *ExhibitedAssetItem) GetEarnings() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ExhibitedAssetItem) GetHourlyEarnings() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.HourlyEarnings
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (x *ExhibitedAssetItem) GetSlotIndex() int32 {
|
func (x *ExhibitedAssetItem) GetSlotIndex() int32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.SlotIndex
|
return x.SlotIndex
|
||||||
@ -1817,7 +1825,7 @@ const file_gallery_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\"\x9f\x02\n" +
|
"\bhas_more\x18\x05 \x01(\bR\ahasMore\"\xc8\x02\n" +
|
||||||
"\x12ExhibitedAssetItem\x12\x19\n" +
|
"\x12ExhibitedAssetItem\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" +
|
||||||
@ -1826,7 +1834,9 @@ const file_gallery_proto_rawDesc = "" +
|
|||||||
"like_count\x18\x04 \x01(\x05R\tlikeCount\x12!\n" +
|
"like_count\x18\x04 \x01(\x05R\tlikeCount\x12!\n" +
|
||||||
"\fexhibited_at\x18\x05 \x01(\x03R\vexhibitedAt\x12\x1b\n" +
|
"\fexhibited_at\x18\x05 \x01(\x03R\vexhibitedAt\x12\x1b\n" +
|
||||||
"\texpire_at\x18\x06 \x01(\x03R\bexpireAt\x12\x1a\n" +
|
"\texpire_at\x18\x06 \x01(\x03R\bexpireAt\x12\x1a\n" +
|
||||||
"\bearnings\x18\a \x01(\x03R\bearnings\x12\x1d\n" +
|
"\bearnings\x18\a \x01(\x03R\bearnings\x12'\n" +
|
||||||
|
"\x0fhourly_earnings\x18\n" +
|
||||||
|
" \x01(\x03R\x0ehourlyEarnings\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"slot_index\x18\b \x01(\x05R\tslotIndex\x12#\n" +
|
"slot_index\x18\b \x01(\x05R\tslotIndex\x12#\n" +
|
||||||
"\ris_lenticular\x18\t \x01(\bR\fisLenticular\"\x9a\x01\n" +
|
"\ris_lenticular\x18\t \x01(\bR\fisLenticular\"\x9a\x01\n" +
|
||||||
|
|||||||
@ -2333,15 +2333,16 @@ func (x *LikedAssetsData) GetHasMore() bool {
|
|||||||
|
|
||||||
// 点赞作品项
|
// 点赞作品项
|
||||||
type LikedAssetItem struct {
|
type LikedAssetItem struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
AssetId int64 `protobuf:"varint,1,opt,name=asset_id,json=assetId,proto3" json:"asset_id,omitempty"` // 资产ID
|
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"` // 藏品名称
|
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
|
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"` // 实时点赞数
|
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"` // 点赞时间(毫秒时间戳)
|
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"` // 当前可领取收益
|
||||||
unknownFields protoimpl.UnknownFields
|
HourlyEarnings int64 `protobuf:"varint,7,opt,name=hourly_earnings,json=hourlyEarnings,proto3" json:"hourly_earnings,omitempty"` // 每小时收益
|
||||||
sizeCache protoimpl.SizeCache
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *LikedAssetItem) Reset() {
|
func (x *LikedAssetItem) Reset() {
|
||||||
@ -2416,6 +2417,13 @@ func (x *LikedAssetItem) GetEarnings() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LikedAssetItem) GetHourlyEarnings() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.HourlyEarnings
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// 获取我今日点赞的作品列表请求(暂不实现)
|
// 获取我今日点赞的作品列表请求(暂不实现)
|
||||||
type GetMyTodayLikedAssetsRequest struct {
|
type GetMyTodayLikedAssetsRequest struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
@ -2919,7 +2927,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\"\xb2\x01\n" +
|
"\bhas_more\x18\x05 \x01(\bR\ahasMore\"\xdb\x01\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" +
|
||||||
@ -2927,7 +2935,8 @@ const file_social_proto_rawDesc = "" +
|
|||||||
"\n" +
|
"\n" +
|
||||||
"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\"O\n" +
|
"\bearnings\x18\x06 \x01(\x03R\bearnings\x12'\n" +
|
||||||
|
"\x0fhourly_earnings\x18\a \x01(\x03R\x0ehourlyEarnings\"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" +
|
||||||
|
|||||||
@ -36,6 +36,7 @@ message Asset {
|
|||||||
string info = 21; // 藏品信息
|
string info = 21; // 藏品信息
|
||||||
int32 display_status = 22; // 展示状态:0=待展示, 1=已展示
|
int32 display_status = 22; // 展示状态:0=待展示, 1=已展示
|
||||||
int64 earnings = 23; // 当前展出收益(实时计算,仅展出中时有值)
|
int64 earnings = 23; // 当前展出收益(实时计算,仅展出中时有值)
|
||||||
|
int64 hourly_earnings = 25; // 每小时收益(实时计算,仅展出中时有值)
|
||||||
int64 exhibition_expire_at = 24; // 展出过期时间(毫秒时间戳,仅展出中时有值,0=未展出)
|
int64 exhibition_expire_at = 24; // 展出过期时间(毫秒时间戳,仅展出中时有值,0=未展出)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -207,6 +207,7 @@ message ExhibitedAssetItem {
|
|||||||
int64 exhibited_at = 5; // 展出开始时间(毫秒时间戳)
|
int64 exhibited_at = 5; // 展出开始时间(毫秒时间戳)
|
||||||
int64 expire_at = 6; // 展出过期时间(毫秒时间戳)
|
int64 expire_at = 6; // 展出过期时间(毫秒时间戳)
|
||||||
int64 earnings = 7; // 当前可领取收益
|
int64 earnings = 7; // 当前可领取收益
|
||||||
|
int64 hourly_earnings = 10; // 每小时收益
|
||||||
int32 slot_index = 8; // 展位序号
|
int32 slot_index = 8; // 展位序号
|
||||||
bool is_lenticular = 9; // 是否为光栅卡(根据 tags 判断)
|
bool is_lenticular = 9; // 是否为光栅卡(根据 tags 判断)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -291,6 +291,7 @@ message LikedAssetItem {
|
|||||||
int32 like_count = 4; // 实时点赞数
|
int32 like_count = 4; // 实时点赞数
|
||||||
int64 liked_at = 5; // 点赞时间(毫秒时间戳)
|
int64 liked_at = 5; // 点赞时间(毫秒时间戳)
|
||||||
int64 earnings = 6; // 当前可领取收益
|
int64 earnings = 6; // 当前可领取收益
|
||||||
|
int64 hourly_earnings = 7; // 每小时收益
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取我今日点赞的作品列表请求(暂不实现)
|
// 获取我今日点赞的作品列表请求(暂不实现)
|
||||||
|
|||||||
@ -475,7 +475,10 @@ func (s *assetService) GetAsset(req *pb.GetAssetRequest, userID, starID int64) (
|
|||||||
displayStatus = 0
|
displayStatus = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 计算当前展出收益和过期时间(仅展出中时有值)
|
// 6. 计算每小时收益(始终计算,基于当前点赞数)
|
||||||
|
hourlyEarnings := calculateHourlyEarnings(asset.LikeCount)
|
||||||
|
|
||||||
|
// 7. 计算当前展出收益和过期时间(仅展出中时有值)
|
||||||
earnings := int64(0)
|
earnings := int64(0)
|
||||||
exhibitionExpireAt := int64(0)
|
exhibitionExpireAt := int64(0)
|
||||||
if displayStatus == 1 {
|
if displayStatus == 1 {
|
||||||
@ -507,7 +510,7 @@ func (s *assetService) GetAsset(req *pb.GetAssetRequest, userID, starID int64) (
|
|||||||
Message: "",
|
Message: "",
|
||||||
Timestamp: time.Now().UnixMilli(),
|
Timestamp: time.Now().UnixMilli(),
|
||||||
},
|
},
|
||||||
Asset: ModelToProtoAssetDetail(asset, ownerNickname, isLiked, displayStatus, earnings, exhibitionExpireAt, grade),
|
Asset: ModelToProtoAssetDetail(asset, ownerNickname, isLiked, displayStatus, earnings, hourlyEarnings, exhibitionExpireAt, grade),
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Logger.Debug("Get asset successful",
|
logger.Logger.Debug("Get asset successful",
|
||||||
@ -656,7 +659,7 @@ func ModelToProtoAsset(asset *models.Asset) *pb.AssetListItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ModelToProtoAssetDetail 将数据库模型转换为Proto格式(Asset详情)
|
// ModelToProtoAssetDetail 将数据库模型转换为Proto格式(Asset详情)
|
||||||
func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked bool, displayStatus int32, earnings int64, exhibitionExpireAt int64, grade int32) *pb.Asset {
|
func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked bool, displayStatus int32, earnings int64, hourlyEarnings int64, exhibitionExpireAt int64, grade int32) *pb.Asset {
|
||||||
if asset == nil {
|
if asset == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -685,6 +688,7 @@ func ModelToProtoAssetDetail(asset *models.Asset, ownerNickname string, isLiked
|
|||||||
Info: asset.Info,
|
Info: asset.Info,
|
||||||
DisplayStatus: displayStatus, // 展示状态:0=待展示, 1=已展示
|
DisplayStatus: displayStatus, // 展示状态:0=待展示, 1=已展示
|
||||||
Earnings: earnings, // 当前展出收益(实时计算)
|
Earnings: earnings, // 当前展出收益(实时计算)
|
||||||
|
HourlyEarnings: hourlyEarnings, // 每小时收益(实时计算)
|
||||||
ExhibitionExpireAt: exhibitionExpireAt, // 展出过期时间
|
ExhibitionExpireAt: exhibitionExpireAt, // 展出过期时间
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -725,18 +729,12 @@ func getStatusString(status int32) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateRealtimeEarnings 实时计算展示收益
|
// calculateHourlyEarnings 计算每小时收益
|
||||||
// 公式:R1 = R0 × T × [100% + Buff(n)]
|
// 公式:R0 × [100% + Buff(n)]
|
||||||
// R0 = 5 水晶/小时,T = 上架时长(小时),Buff(n) 根据点赞数计算
|
// R0 = 5 水晶/小时,Buff(n) 根据点赞数计算
|
||||||
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
func calculateHourlyEarnings(likeCount int32) int64 {
|
||||||
R0 := int64(5) // 水晶/小时
|
R0 := int64(5) // 水晶/小时
|
||||||
|
|
||||||
// 计算上架时长(毫秒转小时)
|
|
||||||
T := (now - startTime) / 3600000
|
|
||||||
if T <= 0 {
|
|
||||||
T = 1 // 最少1小时
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算Buff
|
// 计算Buff
|
||||||
var buff int
|
var buff int
|
||||||
switch {
|
switch {
|
||||||
@ -750,11 +748,20 @@ func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
|||||||
buff = 0
|
buff = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础收益
|
// 应用Buff加成:R1 = R0 × (100% + Buff)
|
||||||
baseRevenue := R0 * T
|
return R0 * (100 + int64(buff)) / 100
|
||||||
|
}
|
||||||
// 应用Buff加成:R1 = R0 × T × (100% + Buff)
|
|
||||||
buffedRevenue := baseRevenue * (100 + int64(buff)) / 100
|
// calculateRealtimeEarnings 实时计算展示收益
|
||||||
|
// 公式:R1 = R0 × T × [100% + Buff(n)]
|
||||||
return buffedRevenue
|
// R0 = 5 水晶/小时,T = 上架时长(小时),Buff(n) 根据点赞数计算
|
||||||
|
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
||||||
|
// 计算上架时长(毫秒转小时)
|
||||||
|
T := (now - startTime) / 3600000
|
||||||
|
if T <= 0 {
|
||||||
|
T = 1 // 最少1小时
|
||||||
|
}
|
||||||
|
|
||||||
|
// 总收益 = 每小时收益 × 时长
|
||||||
|
return calculateHourlyEarnings(likeCount) * T
|
||||||
}
|
}
|
||||||
|
|||||||
@ -464,7 +464,7 @@ func (s *mintService) CreateMintOrder(req *pb.CreateMintOrderRequest, userID, st
|
|||||||
Timestamp: time.Now().UnixMilli(),
|
Timestamp: time.Now().UnixMilli(),
|
||||||
},
|
},
|
||||||
Order: ModelToProtoMintOrder(mintOrder),
|
Order: ModelToProtoMintOrder(mintOrder),
|
||||||
Asset: ModelToProtoAssetDetail(asset, ownerNickname, false, 0, 0, 0, getInt32Value(asset.Grade)), // 新创建的资产,is_liked 为 false,display_status 默认为 0,earnings 和 exhibitionExpireAt 为 0,grade 从 asset.Grade 获取
|
Asset: ModelToProtoAssetDetail(asset, ownerNickname, false, 0, 0, 0, 0, getInt32Value(asset.Grade)), // 新创建的资产,is_liked 为 false,display_status 默认为 0,earnings、hourlyEarnings 和 exhibitionExpireAt 为 0,grade 从 asset.Grade 获取
|
||||||
CostCrystal: capturedCostCrystal,
|
CostCrystal: capturedCostCrystal,
|
||||||
BalanceAfter: newBalance,
|
BalanceAfter: newBalance,
|
||||||
}
|
}
|
||||||
@ -569,7 +569,7 @@ func (s *mintService) GetMintOrder(orderID string, userID, starID int64) (*pb.Ge
|
|||||||
// 由于是查询自己的订单,is_liked 设为 false(简化处理)
|
// 由于是查询自己的订单,is_liked 设为 false(简化处理)
|
||||||
// 获取 display_status
|
// 获取 display_status
|
||||||
displayStatus, _ := s.assetRepo.GetDisplayStatusByAssetID(asset.ID)
|
displayStatus, _ := s.assetRepo.GetDisplayStatusByAssetID(asset.ID)
|
||||||
assetProto = ModelToProtoAssetDetail(asset, ownerNickname, false, displayStatus, 0, 0, getInt32Value(asset.Grade)) // 新创建的资产,earnings 和 exhibitionExpireAt 为 0
|
assetProto = ModelToProtoAssetDetail(asset, ownerNickname, false, displayStatus, 0, 0, 0, getInt32Value(asset.Grade)) // 新创建的资产,earnings、hourlyEarnings 和 exhibitionExpireAt 为 0
|
||||||
|
|
||||||
// 如果 cover_url 存在,生成预签名 URL
|
// 如果 cover_url 存在,生成预签名 URL
|
||||||
if assetProto.CoverUrl != "" {
|
if assetProto.CoverUrl != "" {
|
||||||
|
|||||||
@ -99,6 +99,7 @@ type ExhibitedAssetInfo struct {
|
|||||||
ExhibitedAt int64
|
ExhibitedAt int64
|
||||||
ExpireAt int64
|
ExpireAt int64
|
||||||
Earnings int64
|
Earnings int64
|
||||||
|
HourlyEarnings int64
|
||||||
SlotIndex int32
|
SlotIndex int32
|
||||||
IsLenticular bool
|
IsLenticular bool
|
||||||
}
|
}
|
||||||
@ -438,6 +439,7 @@ func (r *galleryRepository) GetMyExhibitedAssets(userID, starID int64, page, pag
|
|||||||
// R0 = 5 水晶/小时
|
// R0 = 5 水晶/小时
|
||||||
now := time.Now().UnixMilli()
|
now := time.Now().UnixMilli()
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
|
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
|
||||||
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now)
|
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,6 +483,7 @@ func (r *galleryRepository) GetUserExhibitedAssets(userID, starID int64, page, p
|
|||||||
|
|
||||||
// 实时计算每个资产的收益
|
// 实时计算每个资产的收益
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
|
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
|
||||||
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now)
|
item.Earnings = calculateRealtimeEarnings(item.LikeCount, item.ExhibitedAt, now)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,18 +622,12 @@ func generateHostProfileID(userID, starID int64) int64 {
|
|||||||
return userID*1000000 + starID
|
return userID*1000000 + starID
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateRealtimeEarnings 实时计算展示收益
|
// calculateHourlyEarnings 计算每小时收益
|
||||||
// 公式:R1 = R0 × T × [100% + Buff(n)]
|
// 公式:R0 × [100% + Buff(n)]
|
||||||
// R0 = 5 水晶/小时,T = 上架时长(小时),Buff(n) 根据点赞数计算
|
// R0 = 5 水晶/小时,Buff(n) 根据点赞数计算
|
||||||
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
func calculateHourlyEarnings(likeCount int32) int64 {
|
||||||
R0 := int64(5) // 水晶/小时
|
R0 := int64(5) // 水晶/小时
|
||||||
|
|
||||||
// 计算上架时长(毫秒转小时)
|
|
||||||
T := (now - startTime) / 3600000
|
|
||||||
if T <= 0 {
|
|
||||||
T = 1 // 最少1小时
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算Buff
|
// 计算Buff
|
||||||
var buff int
|
var buff int
|
||||||
switch {
|
switch {
|
||||||
@ -644,11 +641,20 @@ func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
|||||||
buff = 0
|
buff = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础收益
|
// 应用Buff加成:R1 = R0 × (100% + Buff)
|
||||||
baseRevenue := R0 * T
|
return R0 * (100 + int64(buff)) / 100
|
||||||
|
}
|
||||||
// 应用Buff加成:R1 = R0 × T × (100% + Buff)
|
|
||||||
buffedRevenue := baseRevenue * (100 + int64(buff)) / 100
|
// calculateRealtimeEarnings 实时计算展示收益
|
||||||
|
// 公式:R1 = R0 × T × [100% + Buff(n)]
|
||||||
return buffedRevenue
|
// R0 = 5 水晶/小时,T = 上架时长(小时),Buff(n) 根据点赞数计算
|
||||||
|
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
||||||
|
// 计算上架时长(毫秒转小时)
|
||||||
|
T := (now - startTime) / 3600000
|
||||||
|
if T <= 0 {
|
||||||
|
T = 1 // 最少1小时
|
||||||
|
}
|
||||||
|
|
||||||
|
// 总收益 = 每小时收益 × 时长
|
||||||
|
return calculateHourlyEarnings(likeCount) * T
|
||||||
}
|
}
|
||||||
|
|||||||
@ -216,6 +216,7 @@ func (s *exhibitionService) GetMyExhibitedAssets(ctx context.Context, userID, st
|
|||||||
ExhibitedAt: item.ExhibitedAt,
|
ExhibitedAt: item.ExhibitedAt,
|
||||||
ExpireAt: item.ExpireAt,
|
ExpireAt: item.ExpireAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
SlotIndex: item.SlotIndex,
|
SlotIndex: item.SlotIndex,
|
||||||
IsLenticular: item.IsLenticular,
|
IsLenticular: item.IsLenticular,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -145,6 +145,7 @@ type LikedAssetInfo struct {
|
|||||||
LikeCount int32
|
LikeCount int32
|
||||||
LikedAt int64
|
LikedAt int64
|
||||||
Earnings int64
|
Earnings int64
|
||||||
|
HourlyEarnings int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomUserInfo 随机用户信息
|
// RandomUserInfo 随机用户信息
|
||||||
@ -618,6 +619,7 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
|
|||||||
var exhibition models.Exhibition
|
var exhibition models.Exhibition
|
||||||
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL", item.AssetID).
|
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL", item.AssetID).
|
||||||
First(&exhibition).Error; err == nil {
|
First(&exhibition).Error; err == nil {
|
||||||
|
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
|
||||||
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now)
|
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -625,18 +627,12 @@ func (r *socialRepositoryImpl) GetMyLikedAssets(userID, starID int64, page, page
|
|||||||
return items, total, nil
|
return items, total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateRealtimeEarnings 实时计算展示收益
|
// calculateHourlyEarnings 计算每小时收益
|
||||||
// 公式:R1 = R0 × T × [100% + Buff(n)]
|
// 公式:R0 × [100% + Buff(n)]
|
||||||
// R0 = 5 水晶/小时,T = 上架时长(小时),Buff(n) 根据点赞数计算
|
// R0 = 5 水晶/小时,Buff(n) 根据点赞数计算
|
||||||
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
func calculateHourlyEarnings(likeCount int32) int64 {
|
||||||
R0 := int64(5) // 水晶/小时
|
R0 := int64(5) // 水晶/小时
|
||||||
|
|
||||||
// 计算上架时长(毫秒转小时)
|
|
||||||
T := (now - startTime) / 3600000
|
|
||||||
if T <= 0 {
|
|
||||||
T = 1 // 最少1小时
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算Buff
|
// 计算Buff
|
||||||
var buff int
|
var buff int
|
||||||
switch {
|
switch {
|
||||||
@ -650,13 +646,22 @@ func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
|||||||
buff = 0
|
buff = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础收益
|
// 应用Buff加成:R1 = R0 × (100% + Buff)
|
||||||
baseRevenue := R0 * T
|
return R0 * (100 + int64(buff)) / 100
|
||||||
|
}
|
||||||
|
|
||||||
// 应用Buff加成:R1 = R0 × T × (100% + Buff)
|
// calculateRealtimeEarnings 实时计算展示收益
|
||||||
buffedRevenue := baseRevenue * (100 + int64(buff)) / 100
|
// 公式:R1 = R0 × T × [100% + Buff(n)]
|
||||||
|
// R0 = 5 水晶/小时,T = 上架时长(小时),Buff(n) 根据点赞数计算
|
||||||
|
func calculateRealtimeEarnings(likeCount int32, startTime, now int64) int64 {
|
||||||
|
// 计算上架时长(毫秒转小时)
|
||||||
|
T := (now - startTime) / 3600000
|
||||||
|
if T <= 0 {
|
||||||
|
T = 1 // 最少1小时
|
||||||
|
}
|
||||||
|
|
||||||
return buffedRevenue
|
// 总收益 = 每小时收益 × 时长
|
||||||
|
return calculateHourlyEarnings(likeCount) * T
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMyTodayLikedAssets 获取我今日点赞的作品列表(只返回展出中且未过期的)
|
// GetMyTodayLikedAssets 获取我今日点赞的作品列表(只返回展出中且未过期的)
|
||||||
@ -704,6 +709,7 @@ func (r *socialRepositoryImpl) GetMyTodayLikedAssets(userID, starID int64, page,
|
|||||||
var exhibition models.Exhibition
|
var exhibition models.Exhibition
|
||||||
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now.UnixMilli()).
|
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now.UnixMilli()).
|
||||||
First(&exhibition).Error; err == nil {
|
First(&exhibition).Error; err == nil {
|
||||||
|
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
|
||||||
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now.UnixMilli())
|
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now.UnixMilli())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -764,6 +770,7 @@ func (r *socialRepositoryImpl) GetMyWeekLikedAssets(userID, starID int64, page,
|
|||||||
var exhibition models.Exhibition
|
var exhibition models.Exhibition
|
||||||
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, nowMillis).
|
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, nowMillis).
|
||||||
First(&exhibition).Error; err == nil {
|
First(&exhibition).Error; err == nil {
|
||||||
|
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
|
||||||
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, nowMillis)
|
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, nowMillis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -813,6 +820,7 @@ func (r *socialRepositoryImpl) GetUserLikedAssets(userID, starID int64, page, pa
|
|||||||
var exhibition models.Exhibition
|
var exhibition models.Exhibition
|
||||||
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now).
|
if err := r.db.Where("asset_id = ? AND deleted_at IS NULL AND expire_at > ?", item.AssetID, now).
|
||||||
First(&exhibition).Error; err == nil {
|
First(&exhibition).Error; err == nil {
|
||||||
|
item.HourlyEarnings = calculateHourlyEarnings(item.LikeCount)
|
||||||
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now)
|
item.Earnings = calculateRealtimeEarnings(item.LikeCount, exhibition.StartTime, now)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -277,6 +277,7 @@ func (s *AssetLikeService) GetMyLikedAssets(ctx context.Context, req *pb.GetMyLi
|
|||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
LikedAt: item.LikedAt,
|
LikedAt: item.LikedAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,6 +338,7 @@ func (s *AssetLikeService) GetMyTodayLikedAssets(ctx context.Context, req *pb.Ge
|
|||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
LikedAt: item.LikedAt,
|
LikedAt: item.LikedAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,6 +399,7 @@ func (s *AssetLikeService) GetMyWeekLikedAssets(ctx context.Context, req *pb.Get
|
|||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
LikedAt: item.LikedAt,
|
LikedAt: item.LikedAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,6 +460,7 @@ func (s *AssetLikeService) GetUserLikedAssets(ctx context.Context, req *pb.GetUs
|
|||||||
LikeCount: item.LikeCount,
|
LikeCount: item.LikeCount,
|
||||||
LikedAt: item.LikedAt,
|
LikedAt: item.LikedAt,
|
||||||
Earnings: item.Earnings,
|
Earnings: item.Earnings,
|
||||||
|
HourlyEarnings: item.HourlyEarnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
163
docs/api-base-url-switch-plan.md
Normal file
163
docs/api-base-url-switch-plan.md
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# 前端 API Base URL 自动切换方案
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
前端 `api.js` 中的 `baseURL` 目前是硬编码,需要根据后端运行环境手动切换。为了实现自动化,制定以下方案。
|
||||||
|
|
||||||
|
> **项目说明**:uniapp 移动端应用,使用 HBuilderX "运行在真机调试"进行开发调试,打包时使用生产环境地址。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方案一:DEBUG_MODE 开关(手动切换)
|
||||||
|
|
||||||
|
在 `api.js` 顶部添加一个开关,真机调试时用开发地址,打包前改成生产地址:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const DEBUG_MODE = true // 真机调试时 true,打包前改成 false
|
||||||
|
|
||||||
|
const DEV_BASE = 'http://192.168.110.60:8080' // 开发环境
|
||||||
|
const PROD_BASE = 'http://101.132.250.62:8080' // 生产环境
|
||||||
|
|
||||||
|
const baseURL = DEBUG_MODE ? DEV_BASE : PROD_BASE
|
||||||
|
```
|
||||||
|
|
||||||
|
### 优点
|
||||||
|
|
||||||
|
| 优点 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **简单直接** | 只需改一个布尔值 |
|
||||||
|
| **零学习成本** | 一眼看出当前用哪个地址 |
|
||||||
|
|
||||||
|
### 缺点
|
||||||
|
|
||||||
|
| 缺点 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **手动切换** | 打包前需记得手动改值 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方案二:自动探测后端可用性(推荐 - 全自动)
|
||||||
|
|
||||||
|
### 实现方式
|
||||||
|
|
||||||
|
启动时自动探测开发环境是否可用,能连通则用开发地址,否则用生产地址:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const DEV_BASE = 'http://192.168.110.60:8080' // 开发环境
|
||||||
|
const PROD_BASE = 'http://101.132.250.62:8080' // 生产环境
|
||||||
|
|
||||||
|
let baseURL = PROD_BASE // 默认生产
|
||||||
|
|
||||||
|
// 启动时探测开发环境是否可用
|
||||||
|
uni.request({
|
||||||
|
url: DEV_BASE + '/api/v1/health',
|
||||||
|
method: 'GET',
|
||||||
|
timeout: 2000,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
baseURL = DEV_BASE // 开发环境可用,用开发
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
// 开发环境不可用,保持生产地址
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**原理**:启动时先尝试连接开发服务器 `192.168.110.60:8080`,能连通就用开发地址,连不通自动用生产地址。
|
||||||
|
|
||||||
|
### 优点
|
||||||
|
|
||||||
|
| 优点 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **全自动** | 无论调试还是打包都自动选择正确地址 |
|
||||||
|
| **零手动操作** | 不需要记得改任何开关 |
|
||||||
|
| **适合移动端** | 开发时手机和电脑同网络,能探测到;生产打包后手机连不上开发地址,自动用生产 |
|
||||||
|
|
||||||
|
### 缺点
|
||||||
|
|
||||||
|
| 缺点 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **启动多一步** | 首次启动会探测一次(2秒超时) |
|
||||||
|
| **依赖后端** | 需要后端有 `/api/v1/health` 接口(或任意能响应的接口) |
|
||||||
|
|
||||||
|
### 工作流程
|
||||||
|
|
||||||
|
| 场景 | 探测结果 | 使用的地址 |
|
||||||
|
|------|---------|-----------|
|
||||||
|
| 真机调试(手机和电脑同局域网) | `192.168.110.60` 能连通 | `http://192.168.110.60:8080`(开发) |
|
||||||
|
| 打包 App(手机连接外网) | `192.168.110.60` 连不通 | `http://101.132.250.62:8080`(生产) |
|
||||||
|
|
||||||
|
### 后端要求
|
||||||
|
|
||||||
|
后端需要有一个能响应 HTTP 200 的健康检查接口。常见选择:
|
||||||
|
|
||||||
|
- **已有接口**:如果后端有 `/health` 或类似接口,直接使用
|
||||||
|
- **新建接口**:在 gateway 添加一个简单的健康检查接口
|
||||||
|
- **复用接口**:用现有的任意接口(如 `/api/v1/auth/me`,但需要 token,可能不适合)
|
||||||
|
|
||||||
|
如果你后端没有现成的健康检查接口,我可以帮你设计一个简单的实现方案。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方案三:uniapp 条件编译(不推荐)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
const baseURL = 'http://101.132.250.62:8080' // App 打包生产环境
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
const baseURL = 'http://192.168.110.60:8080' // H5 开发环境
|
||||||
|
// #endif
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题**:HBuilderX 真机调试默认走 `APP-PLUS` 条件,调试时也会用生产地址,不适合。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 方案四:Vite 环境变量(不推荐)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://192.168.110.60:8080'
|
||||||
|
```
|
||||||
|
|
||||||
|
**问题**:uniapp HBuilderX 不走 Vite 的 dev/build 命令,环境变量不会自动切换。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 总结对比
|
||||||
|
|
||||||
|
| 对比项 | 方案一(开关) | 方案二(自动探测) | 条件编译 | Vite 环境变量 |
|
||||||
|
|--------|:-------------:|:-----------------:|:--------:|:-------------:|
|
||||||
|
| **自动化程度** | ❌ 手动 | ✅ **全自动** | ❌ 手动 | ❌ 手动 |
|
||||||
|
| **uniapp 兼容** | ✅ | ✅ | ❌ 不适合调试 | ❌ 不支持 |
|
||||||
|
| **简单程度** | ✅ 最简单 | 中等 | 中等 | 中等 |
|
||||||
|
| **适合真机调试** | ✅ | ✅ | ❌ 否 | ❌ 否 |
|
||||||
|
| **推荐指数** | ⭐⭐ | ⭐⭐⭐ | ⭐ | ⭐ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 地址确认
|
||||||
|
|
||||||
|
| 环境 | 地址 |
|
||||||
|
|------|------|
|
||||||
|
| **开发环境** | `http://192.168.110.60:8080` |
|
||||||
|
| **生产环境** | `http://101.132.250.62:8080` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 确认事项
|
||||||
|
|
||||||
|
1. **选择方案**:___
|
||||||
|
2. **开发环境地址**:`http://192.168.110.60:8080`
|
||||||
|
3. **生产环境地址**:`http://101.132.250.62:8080`
|
||||||
|
4. **后端是否有健康检查接口**:___(如有请告知路径)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 实施记录
|
||||||
|
|
||||||
|
- [x] ✅ 方案二已实施(自动探测后端可用性)
|
||||||
|
- [x] ✅ 后端 `/health` 接口确认存在
|
||||||
|
- [x] ✅ `api.js` 已修改,使用自动探测逻辑
|
||||||
@ -70,7 +70,7 @@
|
|||||||
<!-- 收益 -->
|
<!-- 收益 -->
|
||||||
<view class="earnings-area">
|
<view class="earnings-area">
|
||||||
<image class="crystal-icon" src="/static/icon/crystal.png" mode="aspectFit"></image>
|
<image class="crystal-icon" src="/static/icon/crystal.png" mode="aspectFit"></image>
|
||||||
<text class="earnings-text">{{ assetData.earnings || 5 }}/时</text>
|
<text class="earnings-text">{{ assetData.hourly_earnings || 5 }}/时</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 倒计时(如有) -->
|
<!-- 倒计时(如有) -->
|
||||||
|
|||||||
@ -68,7 +68,7 @@
|
|||||||
:class="index % 2 === 0 ? 'income-tilt-right' : 'income-tilt-left'">
|
:class="index % 2 === 0 ? 'income-tilt-right' : 'income-tilt-left'">
|
||||||
<image class="topfans-icon" src="/static/icon/crystal.png" mode="aspectFit"></image>
|
<image class="topfans-icon" src="/static/icon/crystal.png" mode="aspectFit"></image>
|
||||||
<view class="card-income-text-wrap">
|
<view class="card-income-text-wrap">
|
||||||
<text class="card-income-text">{{ item.earnings || 0 }}/时</text>
|
<text class="card-income-text">{{ item.hourly_earnings || 0 }}/时</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -387,27 +387,29 @@ const handleExhibitionCardTap = (item, index) => {
|
|||||||
doubleTapLike(item.id, async (success, data) => {
|
doubleTapLike(item.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;
|
||||||
// 如果返回了收益数据,更新显示
|
// // 如果返回了收益数据,更新显示
|
||||||
if (data?.earnings !== undefined) {
|
// if (data?.earnings !== undefined) {
|
||||||
exhibitionWorks.value[index].earnings = data.earnings;
|
// exhibitionWorks.value[index].earnings = data.earnings;
|
||||||
} else {
|
// } else {
|
||||||
// 如果没有返回收益数据,刷新列表获取最新收益
|
// 如果没有返回收益数据,刷新列表获取最新收益
|
||||||
await loadExhibitedAssets();
|
await loadExhibitedAssets();
|
||||||
}
|
// }
|
||||||
// 将作品添加到当前点赞列表
|
|
||||||
const likedItem = {
|
|
||||||
id: item.id,
|
|
||||||
cover_url: item.cover_url,
|
|
||||||
like_count: exhibitionWorks.value[index].like_count,
|
|
||||||
earnings: exhibitionWorks.value[index].earnings,
|
|
||||||
name: item.name,
|
|
||||||
status_text: '潜力待挖',
|
|
||||||
score: exhibitionWorks.value[index].like_count,
|
|
||||||
reward: 0,
|
|
||||||
};
|
|
||||||
likedWorks.value.unshift(likedItem);
|
|
||||||
uni.showToast({ title: '点赞成功', icon: 'success' });
|
uni.showToast({ title: '点赞成功', icon: 'success' });
|
||||||
|
|
||||||
|
// 将作品添加到当前点赞列表
|
||||||
|
// const likedItem = {
|
||||||
|
// id: item.id,
|
||||||
|
// cover_url: item.cover_url,
|
||||||
|
// like_count: exhibitionWorks.value[index].like_count,
|
||||||
|
// earnings: exhibitionWorks.value[index].earnings,
|
||||||
|
// name: item.name,
|
||||||
|
// status_text: '潜力待挖',
|
||||||
|
// score: exhibitionWorks.value[index].like_count,
|
||||||
|
// reward: item.earnings,
|
||||||
|
// };
|
||||||
|
// likedWorks.value.unshift(likedItem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -679,6 +681,7 @@ const loadExhibitedAssets = async () => {
|
|||||||
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,
|
||||||
|
hourly_earnings:item.hourly_earnings,
|
||||||
exhibited_at: item.exhibited_at,
|
exhibited_at: item.exhibited_at,
|
||||||
expire_at: item.expire_at,
|
expire_at: item.expire_at,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
@ -764,7 +767,7 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onShow(() => {
|
onShow(() => {
|
||||||
loadLikedAssets();
|
// loadLikedAssets();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,28 @@
|
|||||||
// API 基础配置
|
// API 基础配置
|
||||||
// 开发阶段用 localhost,打包 App 时自动切换到服务器地址
|
// 自动检测后端环境:探测开发服务器是否可用,能连通则用开发地址,否则用生产地址
|
||||||
// 不需要手动注释!
|
|
||||||
|
|
||||||
// #ifdef H5
|
const DEV_BASE = 'http://192.168.110.60:8080' // 开发环境
|
||||||
// const baseURL = 'http://192.168.110.60:8080' // H5 开发用本机
|
const PROD_BASE = 'http://101.132.250.62:8080' // 生产环境
|
||||||
const baseURL = 'http://101.132.250.62:8080' // H5 开发用本机
|
const HEALTH_URL = DEV_BASE + '/health'
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef APP-PLUS
|
// 默认使用生产地址
|
||||||
// 开发调试:手机和电脑同一WiFi时用这个(改成你电脑IP)
|
let baseURL = PROD_BASE
|
||||||
// 上线后:改成实际服务器地址
|
|
||||||
// const baseURL = 'http://192.168.110.60:8080'
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// 服务器地址(正式上线用)
|
// 启动时探测开发环境是否可用(异步,不阻塞后续逻辑)
|
||||||
// #ifdef APP-PLUS
|
uni.request({
|
||||||
const baseURL = 'http://101.132.250.62:8080'
|
url: HEALTH_URL,
|
||||||
// #endif
|
method: 'GET',
|
||||||
|
timeout: 2000,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
baseURL = DEV_BASE // 开发环境可用,切换到开发地址
|
||||||
|
console.log('[API] 使用开发环境地址:', DEV_BASE)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
console.log('[API] 开发环境不可用,使用生产环境地址:', PROD_BASE)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 是否使用模拟数据(开发调试时设为 true,后端API准备好后改为 false)
|
// 是否使用模拟数据(开发调试时设为 true,后端API准备好后改为 false)
|
||||||
const USE_MOCK_API = false
|
const USE_MOCK_API = false
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user