feat:修改藏品等级硬编码改为根据数据库的配置来定义
This commit is contained in:
parent
8dce6ae11a
commit
7c5d5a7275
@ -119,3 +119,20 @@ var LevelOrderMap = map[string]int{
|
||||
LevelSSR: 4,
|
||||
LevelUR: 5,
|
||||
}
|
||||
|
||||
// LevelToGradeMap 等级字符串到前端Grade的映射(1-5)
|
||||
var LevelToGradeMap = map[string]int{
|
||||
LevelN: 1,
|
||||
LevelR: 2,
|
||||
LevelSR: 3,
|
||||
LevelSSR: 4,
|
||||
LevelUR: 5,
|
||||
}
|
||||
|
||||
// LevelToGrade 将等级字符串转换为前端Grade
|
||||
func LevelToGrade(level string) int {
|
||||
if grade, ok := LevelToGradeMap[level]; ok {
|
||||
return grade
|
||||
}
|
||||
return 1 // 默认返回1
|
||||
}
|
||||
|
||||
@ -1726,6 +1726,7 @@ type OnExhibitionCompletedRequest struct {
|
||||
CrystalAmount int64 `protobuf:"varint,7,opt,name=crystal_amount,json=crystalAmount,proto3" json:"crystal_amount,omitempty"`
|
||||
StartTime int64 `protobuf:"varint,8,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"`
|
||||
ExpireAt int64 `protobuf:"varint,9,opt,name=expire_at,json=expireAt,proto3" json:"expire_at,omitempty"`
|
||||
LikeCount int32 `protobuf:"varint,10,opt,name=like_count,json=likeCount,proto3" json:"like_count,omitempty"` // 点赞数,用于在taskService中根据资产等级重新计算收益
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@ -1823,6 +1824,13 @@ func (x *OnExhibitionCompletedRequest) GetExpireAt() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *OnExhibitionCompletedRequest) GetLikeCount() int32 {
|
||||
if x != nil {
|
||||
return x.LikeCount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type OnExhibitionCompletedResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Base *common.BaseResponse `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
|
||||
@ -2001,7 +2009,7 @@ const file_task_proto_rawDesc = "" +
|
||||
"\astar_id\x18\x02 \x01(\x03R\x06starId\"c\n" +
|
||||
"\x15InitUserTasksResponse\x120\n" +
|
||||
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12\x18\n" +
|
||||
"\asuccess\x18\x02 \x01(\bR\asuccess\"\xcd\x02\n" +
|
||||
"\asuccess\x18\x02 \x01(\bR\asuccess\"\xec\x02\n" +
|
||||
"\x1cOnExhibitionCompletedRequest\x12#\n" +
|
||||
"\rexhibition_id\x18\x01 \x01(\x03R\fexhibitionId\x12\x19\n" +
|
||||
"\basset_id\x18\x02 \x01(\x03R\aassetId\x12\x17\n" +
|
||||
@ -2012,7 +2020,10 @@ const file_task_proto_rawDesc = "" +
|
||||
"\x0ecrystal_amount\x18\a \x01(\x03R\rcrystalAmount\x12\x1d\n" +
|
||||
"\n" +
|
||||
"start_time\x18\b \x01(\x03R\tstartTime\x12\x1b\n" +
|
||||
"\texpire_at\x18\t \x01(\x03R\bexpireAt\"}\n" +
|
||||
"\texpire_at\x18\t \x01(\x03R\bexpireAt\x12\x1d\n" +
|
||||
"\n" +
|
||||
"like_count\x18\n" +
|
||||
" \x01(\x05R\tlikeCount\"}\n" +
|
||||
"\x1dOnExhibitionCompletedResponse\x120\n" +
|
||||
"\x04base\x18\x01 \x01(\v2\x1c.topfans.common.BaseResponseR\x04base\x12*\n" +
|
||||
"\x11revenue_record_id\x18\x02 \x01(\x03R\x0frevenueRecordId2\xc6\f\n" +
|
||||
|
||||
@ -195,6 +195,7 @@ message OnExhibitionCompletedRequest {
|
||||
int64 crystal_amount = 7;
|
||||
int64 start_time = 8;
|
||||
int64 expire_at = 9;
|
||||
int32 like_count = 10; // 点赞数,用于在taskService中根据资产等级重新计算收益
|
||||
}
|
||||
|
||||
message OnExhibitionCompletedResponse {
|
||||
|
||||
@ -55,6 +55,10 @@ func (r *AssetLevelRepository) CreateChangeLog(log *models.AssetLevelChangeLog)
|
||||
return r.db.Create(log).Error
|
||||
}
|
||||
|
||||
func (r *AssetLevelRepository) GetDB() *gorm.DB {
|
||||
return r.db
|
||||
}
|
||||
|
||||
func (r *AssetLevelRepository) GetChangeLogs(assetID int64, limit, offset int) ([]*models.AssetLevelChangeLog, error) {
|
||||
var logs []*models.AssetLevelChangeLog
|
||||
err := r.db.Where("asset_id = ?", assetID).
|
||||
|
||||
@ -48,6 +48,9 @@ type AssetRepository interface {
|
||||
// DecrementLikeCount 减少点赞数
|
||||
DecrementLikeCount(assetID int64) error
|
||||
|
||||
// UpdateGradeByAssetID 根据asset_id更新藏品等级(用于同步AssetLevelRecord.CurrentLevel到AssetRegistry.Grade)
|
||||
UpdateGradeByAssetID(assetID int64, grade int32) error
|
||||
|
||||
// UpdateMaterialTypeByLikes 根据点赞数和创建时间更新素材类型
|
||||
UpdateMaterialTypeByLikes(assetID int64, likes int32, createdAt int64) error
|
||||
|
||||
@ -154,6 +157,17 @@ func (r *assetRepository) GetGradeByAssetID(assetID int64) (int32, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// UpdateGradeByAssetID 根据asset_id更新藏品等级(用于同步AssetLevelRecord.CurrentLevel到AssetRegistry.Grade)
|
||||
func (r *assetRepository) UpdateGradeByAssetID(assetID int64, grade int32) error {
|
||||
if assetID <= 0 {
|
||||
return errors.New("asset_id must be greater than 0")
|
||||
}
|
||||
|
||||
return r.db.Table("public.asset_registry").
|
||||
Where("asset_id = ?", assetID).
|
||||
Update("grade", grade).Error
|
||||
}
|
||||
|
||||
// GetByIDs 批量查询资产
|
||||
func (r *assetRepository) GetByIDs(assetIDs []int64) ([]*models.Asset, error) {
|
||||
if len(assetIDs) == 0 {
|
||||
|
||||
@ -29,6 +29,7 @@ type assetLevelService struct {
|
||||
levelRepo *repository.AssetLevelRepository
|
||||
seasonRepo *repository.SeasonRepository
|
||||
decayConfigRepo *repository.SeasonDecayConfigRepository
|
||||
assetRepo repository.AssetRepository // 用于同步等级到AssetRegistry.Grade(可选)
|
||||
}
|
||||
|
||||
func NewAssetLevelService(
|
||||
@ -173,6 +174,8 @@ func (s *assetLevelService) AddExhibitionHours(assetID int64, hours int) (string
|
||||
s.logLevelChange(record.AssetID, oldLevel, newLevel,
|
||||
"exhibition_complete", record.SeasonExhibitionHours, record.SeasonLikes,
|
||||
fmt.Sprintf("展出完成,时长+%d小时", hours))
|
||||
// 同步等级到AssetRegistry.Grade
|
||||
s.syncGradeToAssetRegistry(record.AssetID, newLevel)
|
||||
}
|
||||
|
||||
return newLevel, upgraded, nil
|
||||
@ -210,6 +213,8 @@ func (s *assetLevelService) AddLikes(assetID int64, count int) (string, bool, er
|
||||
s.logLevelChange(record.AssetID, oldLevel, newLevel,
|
||||
"like_update", record.SeasonExhibitionHours, record.SeasonLikes,
|
||||
fmt.Sprintf("点赞数达到%d触发升级", record.SeasonLikes))
|
||||
// 同步等级到AssetRegistry.Grade
|
||||
s.syncGradeToAssetRegistry(record.AssetID, newLevel)
|
||||
}
|
||||
|
||||
return newLevel, upgraded, nil
|
||||
@ -245,11 +250,53 @@ func (s *assetLevelService) RemoveLikes(assetID int64, count int) (string, bool,
|
||||
s.logLevelChange(record.AssetID, oldLevel, newLevel,
|
||||
"like_remove", record.SeasonExhibitionHours, record.SeasonLikes,
|
||||
fmt.Sprintf("取消点赞,点赞数降至%d触发降级", record.SeasonLikes))
|
||||
// 同步等级到AssetRegistry.Grade
|
||||
s.syncGradeToAssetRegistry(record.AssetID, newLevel)
|
||||
}
|
||||
|
||||
return newLevel, downgraded, nil
|
||||
}
|
||||
|
||||
// syncGradeToAssetRegistry 将等级同步到AssetRegistry.Grade
|
||||
// 直接使用gorm.DB更新,不依赖repository层
|
||||
func (s *assetLevelService) syncGradeToAssetRegistry(assetID int64, level string) {
|
||||
if s.assetRepo == nil {
|
||||
// 直接使用levelRepo的db进行更新
|
||||
grade := models.LevelToGrade(level)
|
||||
db := s.levelRepo.GetDB()
|
||||
if db != nil {
|
||||
if err := db.Table("public.asset_registry").
|
||||
Where("asset_id = ?", assetID).
|
||||
Update("grade", grade).Error; err != nil {
|
||||
logger.Logger.Warn("syncGradeToAssetRegistry failed",
|
||||
zap.Int64("asset_id", assetID),
|
||||
zap.String("level", level),
|
||||
zap.Int("grade", grade),
|
||||
zap.Error(err))
|
||||
} else {
|
||||
logger.Logger.Info("syncGradeToAssetRegistry success",
|
||||
zap.Int64("asset_id", assetID),
|
||||
zap.String("level", level),
|
||||
zap.Int("grade", grade))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
grade := models.LevelToGrade(level)
|
||||
if err := s.assetRepo.UpdateGradeByAssetID(assetID, int32(grade)); err != nil {
|
||||
logger.Logger.Warn("syncGradeToAssetRegistry failed",
|
||||
zap.Int64("asset_id", assetID),
|
||||
zap.String("level", level),
|
||||
zap.Int("grade", grade),
|
||||
zap.Error(err))
|
||||
} else {
|
||||
logger.Logger.Info("syncGradeToAssetRegistry success",
|
||||
zap.Int64("asset_id", assetID),
|
||||
zap.String("level", level),
|
||||
zap.Int("grade", grade))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *assetLevelService) CalculateRevenue(assetID int64, likeCount int, startTime, endTime int64, revenueBoostBps int) (int64, error) {
|
||||
record, err := s.GetRecordByAssetID(assetID)
|
||||
if err != nil || record == nil {
|
||||
@ -372,6 +419,8 @@ func (s *assetLevelService) SeasonReset(seasonID string) error {
|
||||
s.logLevelChange(record.AssetID, oldLevel, newLevel,
|
||||
"season_decay", record.SeasonExhibitionHours, record.SeasonLikes,
|
||||
fmt.Sprintf("赛季%s降序,保留%d%%", seasonID, preservePercent))
|
||||
// 同步等级到AssetRegistry.Grade
|
||||
s.syncGradeToAssetRegistry(record.AssetID, newLevel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ type OnExhibitionCompletedRequest struct {
|
||||
StartTime int64
|
||||
ExpireAt int64
|
||||
CrystalAmount int64
|
||||
LikeCount int32 // 点赞数,用于taskService根据资产等级重新计算收益
|
||||
}
|
||||
|
||||
// OnExhibitionCompletedResponse 展位完成响应
|
||||
@ -72,6 +73,7 @@ func (c *taskRPCClient) OnExhibitionCompleted(ctx context.Context, req *OnExhibi
|
||||
StartTime: req.StartTime,
|
||||
ExpireAt: req.ExpireAt,
|
||||
CrystalAmount: req.CrystalAmount,
|
||||
LikeCount: req.LikeCount,
|
||||
}
|
||||
|
||||
resp, err := c.client.OnExhibitionCompleted(ctx, pbReq)
|
||||
|
||||
@ -132,6 +132,7 @@ func (w *CleanupWorker) cleanupExpiredExhibitions(now int64) {
|
||||
StartTime: e.StartTime,
|
||||
ExpireAt: now,
|
||||
CrystalAmount: revenue,
|
||||
LikeCount: int32(likeCount),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger.Error("调用TaskService记录收益失败,跳过标记已处理以便重试",
|
||||
|
||||
@ -28,6 +28,7 @@ type RevenueService interface {
|
||||
type AssetLevelService interface {
|
||||
GetOrCreateRecord(assetID int64) (*models.AssetLevelRecord, error)
|
||||
AddExhibitionHours(assetID int64, hours int) (string, bool, error)
|
||||
CalculateRevenue(assetID int64, likeCount int, startTime, endTime int64, revenueBoostBps int) (int64, error)
|
||||
}
|
||||
|
||||
// revenueService 展示收益Service实现
|
||||
@ -322,7 +323,33 @@ func (s *revenueService) OnExhibitionCompleted(ctx context.Context, req *pb.OnEx
|
||||
zap.Int64("slot_id", req.SlotId),
|
||||
zap.Int64("occupier_uid", req.OccupierUid),
|
||||
zap.Int64("slot_owner_uid", req.SlotOwnerUid),
|
||||
zap.Int64("crystal_amount", req.CrystalAmount))
|
||||
zap.Int64("crystal_amount", req.CrystalAmount),
|
||||
zap.Int32("like_count", req.LikeCount))
|
||||
|
||||
// 计算实际上架时长(毫秒转小时)
|
||||
startTime := req.StartTime
|
||||
expireAt := req.ExpireAt
|
||||
actualHours := (expireAt - startTime) / 3600000
|
||||
|
||||
// 重新计算收益(使用资产等级对应的R0值,而非galleryService传来的硬编码R0=5)
|
||||
var finalRevenue int64
|
||||
if s.assetLevelService != nil && req.AssetId > 0 {
|
||||
if calculatedRevenue, err := s.assetLevelService.CalculateRevenue(req.AssetId, int(req.LikeCount), startTime, expireAt, 0); err == nil {
|
||||
finalRevenue = calculatedRevenue
|
||||
logger.Logger.Info("OnExhibitionCompleted: recalculated revenue using asset level",
|
||||
zap.Int64("asset_id", req.AssetId),
|
||||
zap.Int64("original_revenue", req.CrystalAmount),
|
||||
zap.Int64("recalculated_revenue", finalRevenue))
|
||||
} else {
|
||||
// 计算失败,使用传来的值
|
||||
finalRevenue = req.CrystalAmount
|
||||
logger.Logger.Warn("OnExhibitionCompleted: failed to calculate revenue with asset level, using original",
|
||||
zap.Int64("asset_id", req.AssetId),
|
||||
zap.Error(err))
|
||||
}
|
||||
} else {
|
||||
finalRevenue = req.CrystalAmount
|
||||
}
|
||||
|
||||
// 收益归属资产主人(铸爱用户),无论展位是否为自己
|
||||
now := time.Now().UnixMilli()
|
||||
@ -334,7 +361,7 @@ func (s *revenueService) OnExhibitionCompleted(ctx context.Context, req *pb.OnEx
|
||||
SlotID: req.SlotId,
|
||||
SlotOwnerUID: req.SlotOwnerUid, // 记录展位所有者信息(仅供参考)
|
||||
SlotType: "exhibition", // 上架展示收益
|
||||
CrystalAmount: req.CrystalAmount,
|
||||
CrystalAmount: finalRevenue, // 使用重新计算的收益
|
||||
CycleStartTime: req.StartTime,
|
||||
CycleEndTime: req.ExpireAt,
|
||||
Status: "claimable",
|
||||
@ -349,12 +376,6 @@ func (s *revenueService) OnExhibitionCompleted(ctx context.Context, req *pb.OnEx
|
||||
return &pb.OnExhibitionCompletedResponse{Base: &pbCommon.BaseResponse{Code: pbCommon.StatusCode_STATUS_INTERNAL_ERROR}}, err
|
||||
}
|
||||
|
||||
// 增加用户累计上架时长(展位主人获得上架时长累计)
|
||||
// 计算实际上架时长(毫秒转小时)
|
||||
startTime := req.StartTime
|
||||
expireAt := req.ExpireAt
|
||||
actualHours := (expireAt - startTime) / 3600000
|
||||
|
||||
// sourceID 用于去重,避免重复累计
|
||||
sourceID := fmt.Sprintf("exhibition_%d", req.ExhibitionId)
|
||||
|
||||
@ -423,15 +444,17 @@ func CalculateBuff(likeCount int) int {
|
||||
}
|
||||
}
|
||||
|
||||
// CalculateExhibitionRevenue 计算单次上架收益
|
||||
// CalculateExhibitionRevenue 计算单次上架收益(参考实现,未被调用)
|
||||
// 注意:实际收益计算在 OnExhibitionCompleted 中通过 AssetLevelService.CalculateRevenue 实现
|
||||
// 此函数保留用于参考和测试场景
|
||||
// 设计文档公式:
|
||||
// R1 = R0 × T × [100% + Buff(n)]
|
||||
// R0 = 5 水晶/小时
|
||||
// R0 = 5 水晶/小时(默认,仅作参考)
|
||||
// T = 上架时长(小时)
|
||||
// Buff(n) 根据点赞数计算
|
||||
// 应用永久收益提升:revenueBoostBps (bps),如 500 = +5%
|
||||
func CalculateExhibitionRevenue(likeCount int, startTime, endTime int64, revenueBoostBps int) int64 {
|
||||
R0 := int64(5) // 水晶/小时
|
||||
R0 := int64(5) // 水晶/小时(默认参考值)
|
||||
|
||||
// 计算上架时长(毫秒转小时)
|
||||
T := (endTime - startTime) / 3600000
|
||||
|
||||
Loading…
Reference in New Issue
Block a user