# game-router 游戏路由服务

## 服务定位

游戏路由服务负责根据 `provider`、`game_code`、`config_version`、`session_id` 和服务健康状态，把游戏请求路由到对应子游戏服务实例。

第一阶段已经具备标准 Go 服务分层和内存版服务发现/路由表，可本地运行和测试。当前实现只做路由决策，不生成游戏结果、不改账本、不派彩。

## 核心职责

- 维护子游戏服务实例发现信息，第一阶段用内存 repository 模拟。
- 根据 `provider + game_code + config_version` 过滤候选实例。
- 过滤不健康实例，健康实例不足时返回 `NO_HEALTHY_INSTANCE`。
- 支持 `round_robin`、`consistent_hash`、`least_loaded` 三类策略。
- 使用 `route_id` 做幂等键，同一 `route_id` 和同一请求指纹返回原始路由结果。
- 保存路由决策，支持按 `route_id` 查询。

## 禁止事项

- 不能生成游戏结果。
- 不能直接访问账本表。
- 不能绕过 game-config-service 私自读取配置文件。
- 不能直接派彩，派彩仍由 settlement-service 和 wallet/ledger 链路负责。

## 目录结构

```text
游戏服务/game-router
├── api/openapi/game-router.openapi.yaml
├── cmd/game-router/main.go
├── configs/config.example.yaml
└── internal
    ├── api
    ├── architecture
    ├── bootstrap
    ├── config
    ├── domain
    ├── handler/http
    ├── middleware
    ├── model
    ├── repository/memory
    └── service
```

## HTTP 接口

### POST /game-router/route

请求示例：

```bash
curl -s http://127.0.0.1:8091/game-router/route \
  -H 'Content-Type: application/json' \
  -H 'X-Request-ID: req-route-001' \
  -d '{
    "route_id": "route-001",
    "provider": "pg",
    "game_code": "fortune-tiger",
    "config_version": "cfg-demo-v1",
    "session_id": "session-001",
    "strategy": "consistent_hash",
    "config_source": "game-config-service:active"
  }'
```

响应中的 `data` 是路由决策：

```json
{
  "route_id": "route-001",
  "provider": "pg",
  "game_code": "mahjong-hu-le",
  "config_version": "pg-mahjong-ways-data0-v1",
  "session_id": "session-001",
  "strategy": "consistent_hash",
  "instance_id": "pg-mahjong-hu-le-v1-a",
  "endpoint": "http://game-pg-mahjong:8080/providers/pg/games/mahjong-hu-le",
  "healthy": true,
  "config_source": "game-config-service:active",
  "discovery_source": "memory-bootstrap"
}
```

### GET /game-router/routes/{route_id}

```bash
curl -s http://127.0.0.1:8091/game-router/routes/route-001
```

用于 game-gateway 或排障工具回查某次请求实际选中的子游戏实例。

## 路由策略

| 策略 | 第一阶段行为 | 后续落地建议 |
| --- | --- | --- |
| round_robin | 对同一 `provider/game_code/config_version` 的健康实例轮询 | 可把轮询游标放入 Redis，避免多副本本地游标不一致 |
| consistent_hash | 对 `session_id` 做稳定哈希，保持同一会话命中同一实例 | 可升级为一致性哈希环，节点来自 K8s Endpoints |
| least_loaded | 选择 `current_load` 最低的健康实例 | 可由实例心跳或 sidecar 上报实时负载到 Redis |

## 服务发现建议

第一阶段内存 repository 字段已经保留：

- `instance_id`
- `provider`
- `game_code`
- `config_version`
- `endpoint`
- `healthy`
- `current_load`
- `discovery_source`
- `last_heartbeat_at`

后续生产建议：

1. K8s Endpoints/EndpointSlice 作为实例来源，按 `provider/game_code/config_version` label 过滤。
2. Redis 维护实例健康、负载、最近心跳和熔断状态，TTL 默认 15 秒。
3. game-router 本地只缓存短 TTL 快照，避免每次请求直接打 K8s API。
4. 子游戏服务滚动发布时由 game-config-service 先生成新 `config_version`，再逐步切流。

## 与周边服务关系

- `game-gateway`：入口服务。负责鉴权、商户上下文、请求验签，然后调用 game-router 获取目标子游戏实例。
- `game-config-service`：配置来源。game-router 使用它产出的 active `config_version` 或上游传入的明确版本，不私自读取配置文件。
- `game-session-service`：会话来源。game-router 使用 `session_id` 做稳定路由，但不创建、不续期、不关闭会话。
- 子游戏服务：执行真实游戏逻辑。game-router 只返回 endpoint，不生成开奖结果。
- `bet-order-service` / `settlement-service` / `wallet` / `ledger`：订单、派彩、账本链路。game-router 不直接调用这些服务改账。

## 本地命令

```bash
cd /Users/amumu/Desktop/beifen/golang新架构/游戏服务/game-router
go test ./...
go build ./cmd/game-router
PORT=8091 go run ./cmd/game-router
```

## 健康检查

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

## 成熟度检查

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

game-router 已从 `scripts/go-service-placeholder.allowlist` 移除，后续如果退回单文件健康检查骨架，成熟度脚本会报错。
