|
|
@@ -31,7 +31,7 @@ GameItems模块主要负责游戏内所有物品的管理,包括但不限于
|
|
|
|
|
|
### 2.1 数据表设计
|
|
|
|
|
|
-#### items表(物品基础信息)
|
|
|
+#### items 表(统一属性物品)
|
|
|
|
|
|
| 字段名 | 类型 | 说明 |
|
|
|
| --- | --- | --- |
|
|
|
@@ -43,40 +43,48 @@ GameItems模块主要负责游戏内所有物品的管理,包括但不限于
|
|
|
| icon | varchar | 物品图标路径 |
|
|
|
| max_stack | int | 最大堆叠数量 |
|
|
|
| sell_price | int | 出售价格 |
|
|
|
+| display_attributes | json | 展示属性,以JSON格式存储键值对,用于界面展示和描述的属性 |
|
|
|
+| numeric_attributes | json | 数值属性,以JSON格式存储键值对,用于计算和游戏逻辑的属性 |
|
|
|
| min_drop_count | int | 宝箱最小掉落物品数量(仅宝箱类型物品有效) |
|
|
|
| max_drop_count | int | 宝箱最大掉落物品数量(仅宝箱类型物品有效) |
|
|
|
| global_expire_at | timestamp | 物品全局过期时间(可为空) |
|
|
|
| created_at | timestamp | 创建时间 |
|
|
|
| updated_at | timestamp | 更新时间 |
|
|
|
|
|
|
-#### user_items表(用户物品关联)
|
|
|
+#### unique_items 表(单独属性物品)
|
|
|
|
|
|
| 字段名 | 类型 | 说明 |
|
|
|
| --- | --- | --- |
|
|
|
-| id | int | 记录ID,主键 |
|
|
|
-| user_id | int | 用户ID,外键 |
|
|
|
-| item_id | int | 物品ID,外键 |
|
|
|
-| quantity | int | 数量 |
|
|
|
-| expire_at | timestamp | 用户物品过期时间(可为空) |
|
|
|
-| created_at | timestamp | 获取时间 |
|
|
|
+| id | int | 唯一物品ID,主键 |
|
|
|
+| item_id | int | 关联的基础物品ID,外键关联items表 |
|
|
|
+| name | varchar | 物品名称(可以与基础物品不同,如“锐利的钢刀”) |
|
|
|
+| display_attributes | json | 展示属性,以JSON格式存储键值对,用于界面展示和描述的属性 |
|
|
|
+| numeric_attributes | json | 数值属性,以JSON格式存储键值对,用于计算和游戏逻辑的属性 |
|
|
|
+| expire_at | timestamp | 物品过期时间(可为空) |
|
|
|
+| created_at | timestamp | 创建时间 |
|
|
|
| updated_at | timestamp | 更新时间 |
|
|
|
|
|
|
-#### item_attributes表(物品属性)
|
|
|
+#### user_items 表(用户物品关联)
|
|
|
|
|
|
| 字段名 | 类型 | 说明 |
|
|
|
| --- | --- | --- |
|
|
|
| id | int | 记录ID,主键 |
|
|
|
-| item_id | int | 物品ID,外键 |
|
|
|
-| attribute_key | varchar | 属性键名 |
|
|
|
-| attribute_value | varchar | 属性值 |
|
|
|
+| user_id | int | 用户ID,外键 |
|
|
|
+| item_id | int | 统一属性物品ID,外键关联items表(当该字段有值时,unique_item_id应为空) |
|
|
|
+| unique_item_id | int | 单独属性物品ID,外键关联unique_items表(当该字段有值时,item_id应为空) |
|
|
|
+| quantity | int | 数量(对于单独属性物品,该值始终为1) |
|
|
|
+| expire_at | timestamp | 用户物品过期时间(可为空) |
|
|
|
+| created_at | timestamp | 获取时间 |
|
|
|
+| updated_at | timestamp | 更新时间 |
|
|
|
|
|
|
-#### chest_items表(宝箱内容配置)
|
|
|
+#### chest_items 表(宝箱内容配置)
|
|
|
|
|
|
| 字段名 | 类型 | 说明 |
|
|
|
| --- | --- | --- |
|
|
|
| id | int | 记录ID,主键 |
|
|
|
| chest_id | int | 宝箱物品ID,外键关联items表 |
|
|
|
| item_id | int | 可能获得的物品ID |
|
|
|
+| is_unique | tinyint | 是否生成单独属性物品(0:否, 1:是) |
|
|
|
| min_quantity | int | 最小数量 |
|
|
|
| max_quantity | int | 最大数量 |
|
|
|
| weight | int | 权重,决定获取概率 |
|
|
|
@@ -93,48 +101,213 @@ GameItems模块主要负责游戏内所有物品的管理,包括但不限于
|
|
|
| items | id | 主键 | 物品ID主键索引 |
|
|
|
| items | type | 普通索引 | 加速按物品类型查询 |
|
|
|
| items | global_expire_at | 普通索引 | 加速过期物品查询 |
|
|
|
-| user_items | user_id, item_id | 复合索引 | 加速用户物品查询 |
|
|
|
+| unique_items | id | 主键 | 唯一物品ID主键索引 |
|
|
|
+| unique_items | item_id | 普通索引 | 加速根据基础物品查询唯一物品 |
|
|
|
+| unique_items | expire_at | 普通索引 | 加速过期物品查询 |
|
|
|
+| user_items | user_id, item_id | 复合索引 | 加速用户统一属性物品查询 |
|
|
|
+| user_items | user_id, unique_item_id | 复合索引 | 加速用户单独属性物品查询 |
|
|
|
| user_items | expire_at | 普通索引 | 加速过期物品查询 |
|
|
|
-| item_attributes | item_id | 普通索引 | 加速物品属性查询 |
|
|
|
| chest_items | chest_id | 普通索引 | 加速宝箱内容查询 |
|
|
|
| chest_items | item_id | 普通索引 | 加速物品在宝箱中的查询 |
|
|
|
+| chest_items | is_unique | 普通索引 | 加速查询生成唯一物品的宝箱配置 |
|
|
|
+
|
|
|
+### 2.3 可视图的方式
|
|
|
+
|
|
|
+以下是数据模型之间的关联关系图:
|
|
|
+
|
|
|
+```mermaid
|
|
|
+graph TD
|
|
|
+ User["User"] --- UserItems{"user_items<br>M:N"}
|
|
|
+ UserItems --- Item["Item<br>统一属性物品"]
|
|
|
+ UserItems --- UniqueItem["unique_items<br>单独属性物品"]
|
|
|
+ UniqueItem -->|"N:1"| Item
|
|
|
+
|
|
|
+ subgraph "宝箱系统"
|
|
|
+ ChestItem["Item<br>(type=5)宝箱"] -->|"1:N"| ChestConfig["chest_items<br>宝箱配置"]
|
|
|
+ ChestConfig -->|"N:1"| ItemDrop["Item<br>掉落物品"]
|
|
|
+ ChestConfig -->|"is_unique=1"| UniqueItem
|
|
|
+ end
|
|
|
+
|
|
|
+ classDef entity fill:#f9f,stroke:#333,stroke-width:2px;
|
|
|
+ classDef junction fill:#bbf,stroke:#33f,stroke-width:2px;
|
|
|
+
|
|
|
+ class User,Item,UniqueItem,ChestItem,ItemDrop entity;
|
|
|
+ class UserItems,ChestConfig junction;
|
|
|
+```
|
|
|
|
|
|
-### 2.3 模型关系
|
|
|
+图表说明:
|
|
|
+- **User 与 Item/UniqueItem**:用户可以拥有统一属性物品和单独属性物品,通过 user_items 表关联
|
|
|
+- **UniqueItem 与 Item**:多对一关系,每个单独属性物品都关联到一个基础物品模板
|
|
|
+- **宝箱物品与掉落物品**:宝箱(Item type=5)与 chest_items 表是一对多关系
|
|
|
+- **chest_items 与物品**:可以配置生成统一属性物品或单独属性物品(通过is_unique字段控制)
|
|
|
|
|
|
-使用Laravel Eloquent ORM实现数据模型之间的关联关系:
|
|
|
+### 2.4 单独属性物品实现
|
|
|
+
|
|
|
+为了支持“每个用户拥有的同类物品可以有不同属性”的需求(如不同的刀有不同的属性),我们采用了两种物品类型的设计:
|
|
|
+
|
|
|
+#### 实现方式说明
|
|
|
+
|
|
|
+该设计允许我们实现以下功能:
|
|
|
+
|
|
|
+1. **物品类型分离**
|
|
|
+ - `items`表存储统一属性物品,属性以JSON格式存储
|
|
|
+ - `unique_items`表存储单独属性物品,属性以JSON格式存储
|
|
|
+ - 每个单独属性物品都关联到一个基础物品模板
|
|
|
+
|
|
|
+2. **用户物品关联的灵活性**
|
|
|
+ - 用户可以同时拥有统一属性物品和单独属性物品
|
|
|
+ - `user_items`表中的`item_id`和`unique_item_id`字段互斥,一个记录只能关联其中一种物品
|
|
|
+ - 对于单独属性物品,`quantity`始终为1
|
|
|
+
|
|
|
+3. **宝箱生成机制**
|
|
|
+ - 宝箱可以配置生成统一属性物品或单独属性物品
|
|
|
+ - 通过`chest_items`表中的`is_unique`字段控制生成类型
|
|
|
+
|
|
|
+#### 使用示例
|
|
|
|
|
|
```php
|
|
|
-// Item模型与User多对多关系
|
|
|
-Item::belongsToMany(User::class, 'user_items')
|
|
|
- ->withPivot('quantity', 'expire_at')
|
|
|
- ->withTimestamps();
|
|
|
+// 为用户创建一把具有单独属性的刀
|
|
|
+public function createUniqueWeapon($userId, $baseItemId)
|
|
|
+{
|
|
|
+ // 开始事务
|
|
|
+ DB::beginTransaction();
|
|
|
|
|
|
-// Item模型与ItemAttribute一对多关系
|
|
|
-Item::hasMany(ItemAttribute::class);
|
|
|
+ try {
|
|
|
+ // 1. 获取基础物品信息
|
|
|
+ $baseItem = Item::findOrFail($baseItemId);
|
|
|
+
|
|
|
+ // 2. 生成随机属性
|
|
|
+ // 继承基础物品的展示属性
|
|
|
+ $baseDisplayAttributes = json_decode($baseItem->display_attributes, true) ?? [];
|
|
|
+ $displayAttributes = $baseDisplayAttributes;
|
|
|
+
|
|
|
+ // 添加自定义展示属性
|
|
|
+ $customDisplayAttributes = [
|
|
|
+ 'appearance' => '闪亮的刀身上刻有精美的纹理',
|
|
|
+ 'material' => '特殊合金打造',
|
|
|
+ 'description' => '这把刀经过精心锻造,锐利无比'
|
|
|
+ ];
|
|
|
|
|
|
-// 宝箱物品与其内容物品的关系
|
|
|
-Item::hasMany(ChestItem::class, 'chest_id')
|
|
|
- ->where('type', 5); // 类型5为宝箱
|
|
|
+ // 合并展示属性,自定义属性会覆盖基础属性
|
|
|
+ $displayAttributes = array_merge($displayAttributes, $customDisplayAttributes);
|
|
|
+
|
|
|
+ // 继承基础物品的数值属性
|
|
|
+ $baseNumericAttributes = json_decode($baseItem->numeric_attributes, true) ?? [];
|
|
|
+ $numericAttributes = $baseNumericAttributes;
|
|
|
+
|
|
|
+ // 生成随机数值属性
|
|
|
+ $randomNumericAttributes = [
|
|
|
+ 'attack' => mt_rand(10, 20),
|
|
|
+ 'durability' => mt_rand(80, 100),
|
|
|
+ 'sharpness' => mt_rand(1, 5),
|
|
|
+ 'critical_chance' => mt_rand(5, 15) / 100 // 暴击几率
|
|
|
+ ];
|
|
|
|
|
|
-// ChestItem模型与宝箱物品的关系
|
|
|
-ChestItem::belongsTo(Item::class, 'chest_id');
|
|
|
+ // 合并数值属性,随机属性会覆盖基础属性
|
|
|
+ $numericAttributes = array_merge($numericAttributes, $randomNumericAttributes);
|
|
|
|
|
|
-// ChestItem模型与内容物品的关系
|
|
|
-ChestItem::belongsTo(Item::class, 'item_id');
|
|
|
+ // 3. 创建单独属性物品
|
|
|
+ $uniqueItem = new UniqueItem();
|
|
|
+ $uniqueItem->item_id = $baseItemId;
|
|
|
+ $uniqueItem->name = "锐利的" . $baseItem->name; // 可以自定义名称
|
|
|
+ $uniqueItem->display_attributes = json_encode($displayAttributes);
|
|
|
+ $uniqueItem->numeric_attributes = json_encode($numericAttributes);
|
|
|
+ $uniqueItem->save();
|
|
|
|
|
|
-// User模型与Item多对多关系
|
|
|
-User::belongsToMany(Item::class, 'user_items')
|
|
|
- ->withPivot('quantity', 'expire_at')
|
|
|
- ->withTimestamps();
|
|
|
+ // 4. 关联到用户
|
|
|
+ $userItem = new UserItem();
|
|
|
+ $userItem->user_id = $userId;
|
|
|
+ $userItem->unique_item_id = $uniqueItem->id; // 注意这里使用unique_item_id而非item_id
|
|
|
+ $userItem->quantity = 1; // 单独属性物品数量始终为1
|
|
|
+ $userItem->save();
|
|
|
+
|
|
|
+ DB::commit();
|
|
|
+ return [
|
|
|
+ 'user_item' => $userItem,
|
|
|
+ 'unique_item' => $uniqueItem
|
|
|
+ ];
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ DB::rollBack();
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取用户的所有单独属性武器
|
|
|
+public function getUserUniqueWeapons($userId, $baseItemId = null)
|
|
|
+{
|
|
|
+ // 构建查询
|
|
|
+ $query = UserItem::where('user_id', $userId)
|
|
|
+ ->whereNotNull('unique_item_id'); // 只查询单独属性物品
|
|
|
+
|
|
|
+ // 如果指定了基础物品ID,则只查询该类型的武器
|
|
|
+ if ($baseItemId) {
|
|
|
+ $query->whereHas('uniqueItem', function($q) use ($baseItemId) {
|
|
|
+ $q->where('item_id', $baseItemId);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取用户物品记录,并预加载单独属性物品信息
|
|
|
+ $userItems = $query->with('uniqueItem.baseItem')->get();
|
|
|
+
|
|
|
+ $result = [];
|
|
|
+
|
|
|
+ foreach ($userItems as $userItem) {
|
|
|
+ $uniqueItem = $userItem->uniqueItem;
|
|
|
+ $baseItem = $uniqueItem->baseItem;
|
|
|
+
|
|
|
+ $weaponData = [
|
|
|
+ 'id' => $userItem->id,
|
|
|
+ 'unique_item_id' => $uniqueItem->id,
|
|
|
+ 'base_item_id' => $baseItem->id,
|
|
|
+ 'name' => $uniqueItem->name,
|
|
|
+ 'base_name' => $baseItem->name,
|
|
|
+ 'display_attributes' => json_decode($uniqueItem->display_attributes, true),
|
|
|
+ 'numeric_attributes' => json_decode($uniqueItem->numeric_attributes, true),
|
|
|
+ 'obtained_at' => $userItem->created_at->format('Y-m-d H:i:s')
|
|
|
+ ];
|
|
|
+
|
|
|
+ $result[] = $weaponData;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $result;
|
|
|
+}
|
|
|
+
|
|
|
+// 为用户添加统一属性物品
|
|
|
+public function addItemToUser($userId, $itemId, $quantity = 1)
|
|
|
+{
|
|
|
+ // 查找用户是否已有该物品
|
|
|
+ $userItem = UserItem::where('user_id', $userId)
|
|
|
+ ->where('item_id', $itemId)
|
|
|
+ ->whereNull('unique_item_id') // 确保是统一属性物品
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if ($userItem) {
|
|
|
+ // 如果已有,增加数量
|
|
|
+ $userItem->quantity += $quantity;
|
|
|
+ $userItem->save();
|
|
|
+ } else {
|
|
|
+ // 如果没有,创建新记录
|
|
|
+ $userItem = new UserItem();
|
|
|
+ $userItem->user_id = $userId;
|
|
|
+ $userItem->item_id = $itemId;
|
|
|
+ $userItem->quantity = $quantity;
|
|
|
+ $userItem->save();
|
|
|
+ }
|
|
|
+
|
|
|
+ return $userItem;
|
|
|
+}
|
|
|
```
|
|
|
|
|
|
-### 2.4 数据库事务
|
|
|
+### 2.5 数据库事务
|
|
|
|
|
|
在涉及多表操作的关键业务逻辑中,使用数据库事务确保数据一致性:
|
|
|
|
|
|
1. **宝箱开启操作**:包含宝箱扣除和物品添加的原子性操作
|
|
|
2. **物品交易**:确保物品转移的原子性
|
|
|
3. **批量物品操作**:如批量删除过期物品
|
|
|
+4. **单独属性物品创建**:包含创建 unique_items 记录和关联到用户的原子性操作
|
|
|
+5. **宝箱生成单独属性物品**:当宝箱配置为生成单独属性物品时,需要保证创建和关联的原子性
|
|
|
|
|
|
## 3. 与其他模块的交互
|
|
|
|