6.0 KiB
6.0 KiB
经验升级系统实现计划
Context
后端目前有 FanProfile.Level 和 FanProfile.Experience 两个字段,但没有任何根据经验值自动计算等级的逻辑。用户希望:
- 等级阈值可配置(数据库配置)
- 经验值增加后自动计算并更新等级
- 在
AddExperience的事务内部触发升级检查(保证原子性) - 10级满级,满级后溢出丢弃
- 等级变化需要返回给前端
关键文件
- backend/pkg/models/user.go —
FanProfile模型定义,包含Level和Experience - backend/services/userService/repository/fan_profile_repository.go —
UpdateExperience方法,最佳插入点 - backend/services/userService/repository/fan_profile_repository.go — 新增
GetLevelThreshold方法
实现方案
1. 数据库等级阈值表
新建 backend/services/userService/repository/level_threshold.go:
type LevelThreshold struct {
Level int32 `gorm:"primaryKey;column:level"`
ExpRequired int64 `gorm:"not null;column:exp_required"`
}
func (r *fanProfileRepository) GetLevelThresholds() (map[int32]int64, error) {
var thresholds []LevelThreshold
if err := r.db.Order("level ASC").Find(&thresholds).Error; err != nil {
return nil, err
}
result := make(map[int32]int64)
for _, t := range thresholds {
result[t.Level] = t.ExpRequired
}
return result, nil
}
建表 SQL(需执行):
CREATE TABLE IF NOT EXISTS level_thresholds (
level INT PRIMARY KEY,
exp_required BIGINT NOT NULL
);
INSERT INTO level_thresholds (level, exp_required) VALUES
(1, 0), (2, 100), (3, 300), (4, 600), (5, 1000),
(6, 1500), (7, 2100), (8, 2800), (9, 3600), (10, 4500);
2. 新增 CalculateLevel 函数
const MaxLevel = 10
// 根据经验值计算当前等级(10级满级,溢出返回10)
func CalculateLevel(experience int64, thresholds map[int32]int64) int32 {
for level := MaxLevel; level >= 1; level-- {
if exp >= thresholds[level] {
return level
}
}
return 1
}
3. 修改 UpdateExperience 事务
在 fan_profile_repository.go:396-441 的事务末尾:
// 计算新等级
newLevel := CalculateLevel(newExperience, thresholds) // thresholds 从缓存或传入
// 如果等级有提升,更新 level 字段
if newLevel > profile.Level {
tx.Model(&models.FanProfile{}).
Where("user_id = ? AND star_id = ?", userID, starID).
Updates(map[string]interface{}{
"experience": newExperience,
"level": newLevel,
})
} else {
tx.Model(&models.FanProfile{}).
Where("user_id = ? AND star_id = ?", userID, starID).
Update("experience", newExperience)
}
4. 修改 AddExperienceResponse
proto/user.proto 的 AddExperienceResponse 增加 new_level 字段:
message AddExperienceResponse {
topfans.common.BaseResponse base = 1;
int64 new_experience = 2;
int32 new_level = 3; // 新增
}
重新生成 user.pb.go 和 user.triple.go。
5. 修改 userService.AddExperience
user_service.go:854-861 返回 newLevel:
// 4. 计算新等级
newLevel := CalculateLevel(newExperience, thresholds)
// 5. 构建响应
return &pb.AddExperienceResponse{
Base: &pbCommon.BaseResponse{...},
NewExperience: newExperience,
NewLevel: newLevel,
}, nil
6. 修改 user_rpc_client.go
user_rpc_client.go:43-58 返回值增加 newLevel:
func (c *userServiceClient) AddExperience(ctx context.Context, userID, starID int64, delta int64) (int64, int32, error) {
resp, err := c.client.AddExperience(ctx, &pbUser.AddExperienceRequest{
UserId: userID, StarId: starID, Delta: delta,
})
if err != nil {
return 0, 0, err
}
if resp.Base.Code != pbCommon.StatusCode_STATUS_OK {
return 0, 0, fmt.Errorf("AddExperience failed with code: %d", resp.Base.Code)
}
return resp.NewExperience, resp.NewLevel, nil
}
7. 修改 ClaimDailyTaskResponse 和 ClaimAllDailyTasksResponse
在 proto/task.proto 中两个 response 增加 new_level 字段,重新生成 pb 文件。
验证方案
- 编译验证:
cd backend && go build ./... - 数据库:确认
level_thresholds表存在且有数据 - 手动测试:调用
/api/v1/tasks/daily/claim,观察返回的experience和level是否正确 - 边界测试:
- 经验刚好达到阈值时是否升级
- 满级后经验是否溢出丢弃
涉及修改的文件
| 文件 | 修改内容 |
|---|---|
backend/services/userService/repository/level_threshold.go |
新建 — 等级阈值表模型和查询方法 |
backend/services/userService/repository/fan_profile_repository.go |
修改 UpdateExperience 事务,增加等级计算和更新 |
backend/services/userService/service/user_service.go |
修改 AddExperience 返回 newLevel |
backend/services/userService/config/ |
可能需要新增缓存配置 |
backend/proto/user.proto |
AddExperienceResponse 增加 new_level 字段 |
backend/proto/task.proto |
ClaimDailyTaskResponse / ClaimAllDailyTasksResponse 增加 new_level |
backend/pkg/proto/user/user.pb.go |
重新生成 |
backend/pkg/proto/user/user.triple.go |
重新生成 |
backend/pkg/proto/task/task.pb.go |
重新生成 |
backend/pkg/proto/task/task.triple.go |
重新生成 |
backend/services/taskService/client/user_rpc_client.go |
AddExperience 返回值增加 newLevel |
backend/services/taskService/service/daily_task_service.go |
调用处增加 newLevel 处理 |