# ranking-service 排行榜服务

## 服务定位

`ranking-service` 负责游戏内日榜、周榜、大奖榜等实时排名能力。第一阶段已经从健康检查占位升级为可运行 Go 服务，提供 HTTP 写分与查询接口，并用内存仓储模拟后续生产形态中的 Redis ZSET 热榜和 ClickHouse 分数事实表。

该服务只负责展示和运营侧排名，不作为资金结算依据，不直接修改注单，也不把热榜数据当作唯一长期存储。

## 当前接口

### 1. 写入分数

```bash
curl -X POST http://127.0.0.1:8100/rankings/daily_slot/scores \
  -H 'Content-Type: application/json' \
  -H 'X-Request-ID: req_demo_001' \
  -d '{
    "score_event_id": "score_evt_001",
    "merchant_id": "MERCHANT_DEMO",
    "player_id": "player_1001",
    "game_code": "slot_fortune",
    "score": 168000,
    "period": "2026-06-06",
    "dimension": "daily",
    "policy": "highest"
  }'
```

路径：

- `POST /rankings/{board_id}/scores`

字段：

- `score_event_id`：分数事件幂等键，在 `merchant_id + game_code + board_id + period + dimension` 作用域内生效；同一作用域重复提交且载荷一致只返回重复标记，不重复写事实；同一键载荷不同返回 `409 IDEMPOTENCY_CONFLICT`。
- `merchant_id`：商户 ID。
- `player_id`：玩家 ID。
- `game_code`：游戏编码。
- `board_id`：榜单 ID，取路径参数。
- `score`：本次分数，必须大于等于 0。
- `period`：榜单周期，例如 `2026-06-06`、`2026-W23`。
- `dimension`：榜单维度，例如 `daily`、`weekly`、`jackpot`。
- `policy`：可选，`highest` 取同玩家最高分，`cumulative` 按同玩家累计分；缺省为 `highest`。

### 2. 查询 Top N

```bash
curl 'http://127.0.0.1:8100/rankings/daily_slot?merchant_id=MERCHANT_DEMO&game_code=slot_fortune&period=2026-06-06&dimension=daily&limit=10'
```

路径：

- `GET /rankings/{board_id}`

排序规则：

- 先按 `score` 倒序。
- 分数相同时按 `player_id` 升序，保证第一阶段内存实现可重复测试。
- `limit` 缺省 50，最大 1000。

### 3. 查询玩家名次

```bash
curl 'http://127.0.0.1:8100/rankings/daily_slot/players/player_1001?merchant_id=MERCHANT_DEMO&game_code=slot_fortune&period=2026-06-06&dimension=daily'
```

路径：

- `GET /rankings/{board_id}/players/{player_id}`

该接口不受 Top N 截断影响，会在完整榜单中返回玩家当前 `rank`、`score`、`updated_at`。

## Redis ZSET 设计

生产形态建议把热榜写入 Redis ZSET：

```text
key: ranking:{merchant_id}:{game_code}:{board_id}:{period}:{dimension}
member: player_id
score: 当前排名分
```

写入策略：

- `highest`：读取玩家当前 score，本次分数更高才 `ZADD` 覆盖。
- `cumulative`：用 `ZINCRBY` 或 Lua 脚本按 `score_event_id` 幂等后累加。
- 幂等键：`ranking_event:{merchant_id}:{game_code}:{board_id}:{period}:{dimension}:{score_event_id}`，建议设置覆盖周期的 TTL，防止同一结算事件重复累计；命中幂等时必须比较事件指纹。
- 查询 Top N：`ZREVRANGE key 0 limit-1 WITHSCORES`。
- 查询玩家名次：`ZREVRANK key player_id` 和 `ZSCORE key player_id`。

第一阶段 `internal/repository/memory` 用 map 模拟上述结构，同时维护 `ScoreFact` 切片模拟 ClickHouse 明细事实，便于测试幂等、排序和策略。

## 与周边服务关系

- `game-pg-mahjong`：PG Mahjong Ways 子游戏在结算/得分后生成 `score_event_id`，调用本服务写入玩家分数。
- `bet-order-service`：注单结算完成后可携带 `merchant_id`、`player_id`、`game_code`、`round_id` 等上下文，派生排行榜写分事件；排行榜不能反向影响注单状态。
- `warehouse-writer`：后续生产中排行榜分数事实应进入数仓，供运营复盘、活动审计和离线榜单快照使用；当前内存仓储已保留 ClickHouse fact 模型。
- `report-service`：报表服务查询经营统计，本服务查询实时排名。两者可以共享数仓事实，但接口职责分离，避免排行榜查询混入经营报表聚合逻辑。

## 本地命令

```bash
cd /Users/amumu/Desktop/beifen/golang新架构/实时服务/ranking-service
go test ./...
go build ./cmd/ranking-service
PORT=8100 go run ./cmd/ranking-service
```

健康检查：

```bash
curl http://127.0.0.1:8100/health
curl http://127.0.0.1:8100/ready
```

成熟度检查：

```bash
cd /Users/amumu/Desktop/beifen/golang新架构
./scripts/check-go-service-maturity.sh
```

## 配置与契约

- 配置示例：`实时服务/ranking-service/configs/config.example.yaml`
- OpenAPI：`实时服务/ranking-service/api/openapi/ranking-service.openapi.yaml`
- 默认策略环境变量：`RANKING_DEFAULT_POLICY`
- 最大查询条数环境变量：`RANKING_MAX_LIMIT`
