# Ent Edges 使用说明（game 模块）

本文说明 `app/game/rpc/internal/models/schema` 下 Edges 的定义方式、生成方式，以及在业务代码中如何使用。

## 1. 当前 game 模块关系设计

基于 `desc/sql/game` 的表结构，当前主要关系：

- `players` 1:N `player_services`
  - `player_services.player_id -> players.id`
- `shops` 1:N `shop_players`
  - `shop_players.shop_id -> shops.id`
- `players` 1:N `shop_players`（逻辑关联）
  - `shop_players.player_id -> players.id`
- `shops` 1:N `shop_invitations`
  - `shop_invitations.shop_id -> shops.id`
- `players` 1:N `shop_invitations`（收到邀请）
  - `shop_invitations.player_id -> players.id`
- `players` 1:N `shop_invitations`（发起邀请）
  - `shop_invitations.invited_by -> players.id`

## 2. Edge 定义规则

### 2.1 在子表（持有外键字段）上定义 `edge.From(...).Field(...)`

示例（`player_services`）：

- `edge.From("player", Players.Type).Ref("services").Field("player_id").Required().Unique()`

含义：

- 当前实体（`player_services`）通过 `player_id` 指向一个 `players`
- `Unique()` 表示每条 `player_services` 记录只对应一个 player（典型 N:1 子侧定义）

### 2.2 在父表定义反向 `edge.To(...)`

示例（`players`）：

- `edge.To("services", PlayerServices.Type)`

含义：

- 一个 player 可以有多条 service（1:N）

## 3. 代码生成

在项目根目录执行：

```bash
go run entgo.io/ent/cmd/ent generate ./app/game/rpc/internal/models/schema
```

如果你后续为 schema 增加了注解或字段，重复执行同一命令即可刷新生成代码。

## 4. 业务查询示例

> 以下为典型写法，具体包路径以你项目实际生成结果为准。

### 4.1 查询某个玩家及其所有服务

```go
player, err := client.Players.
    Query().
    Where(players.ID(playerID)).
    WithServices().
    Only(ctx)
```

### 4.2 查询某个店铺及其成员关系记录

```go
shop, err := client.Shops.
    Query().
    Where(shops.ID(shopID)).
    WithMemberships().
    Only(ctx)
```

### 4.3 查询某玩家收到的邀请

```go
invitations, err := client.Players.
    QueryReceivedInvitations(player).
    All(ctx)
```

## 5. 常见坑

- `edge.From(...).Field(...)` 的字段名必须是当前 schema 里真实存在的字段。
- `Ref("...")` 名称要和对端 `edge.To("...")` 保持一致。
- 同一实体指向同一目标实体的多条边（例如邀请的 `player` 和 `inviter`）必须使用不同 edge 名称，避免代码生成冲突。
- 如果 SQL 没有声明外键但你定义了带 `Field(...)` 的 edge，迁移时可能会生成外键约束；若不想生成，请保持和 SQL 一致或在迁移层显式控制。

## 6. 当前文件位置

- Schema 目录：`app/game/rpc/internal/models/schema`
- SQL 来源：`desc/sql/game`
