topfans/docs/generic-moseying-bird.md
2026-04-15 17:04:42 +08:00

6.0 KiB
Raw Permalink Blame History

经验升级系统实现计划

Context

后端目前有 FanProfile.LevelFanProfile.Experience 两个字段,但没有任何根据经验值自动计算等级的逻辑。用户希望:

  1. 等级阈值可配置(数据库配置
  2. 经验值增加后自动计算并更新等级
  3. AddExperience 的事务内部触发升级检查(保证原子性)
  4. 10级满级满级后溢出丢弃
  5. 等级变化需要返回给前端

关键文件

实现方案

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.protoAddExperienceResponse 增加 new_level 字段:

message AddExperienceResponse {
  topfans.common.BaseResponse base = 1;
  int64 new_experience = 2;
  int32 new_level = 3;  // 新增
}

重新生成 user.pb.gouser.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. 修改 ClaimDailyTaskResponseClaimAllDailyTasksResponse

proto/task.proto 中两个 response 增加 new_level 字段,重新生成 pb 文件。

验证方案

  1. 编译验证cd backend && go build ./...
  2. 数据库:确认 level_thresholds 表存在且有数据
  3. 手动测试:调用 /api/v1/tasks/daily/claim,观察返回的 experiencelevel 是否正确
  4. 边界测试
    • 经验刚好达到阈值时是否升级
    • 满级后经验是否溢出丢弃

涉及修改的文件

文件 修改内容
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 处理