ItemRecipe.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. namespace App\Module\GameItems\Models;
  3. use App\Module\GameItems\Casts\TransactionDetailsCast;
  4. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  5. use Illuminate\Database\Eloquent\Relations\HasMany;
  6. use UCore\ModelCore;
  7. /**
  8. * 物品合成配方
  9. *
  10. * field start
  11. * @property int $id 配方ID,主键
  12. * @property string $name 配方名称
  13. * @property int $result_item_id 产出物品ID,外键关联kku_item_items表
  14. * @property int $result_min_quantity 最小产出数量
  15. * @property int $result_max_quantity 最大产出数量
  16. * @property float $success_rate 成功率(百分比,最大100)
  17. * @property object|array $coin_cost 货币成本,以JSON格式存储多种货币类型和数量
  18. * @property int $level_required 所需等级
  19. * @property int $is_default_unlocked 是否默认解锁(0:否, 1:是)
  20. * @property object|array $unlock_condition 解锁条件,以JSON格式存储
  21. * @property int $cooldown_seconds 冷却时间(秒)
  22. * @property int $category_id 配方分类ID
  23. * @property int $sort_order 排序权重
  24. * @property int $is_active 是否激活(0:否, 1:是)
  25. * @property \Carbon\Carbon $created_at 创建时间
  26. * @property \Carbon\Carbon $updated_at 更新时间
  27. * field end
  28. */
  29. class ItemRecipe extends ModelCore
  30. {
  31. /**
  32. * 与模型关联的表名
  33. *
  34. * @var string
  35. */
  36. protected $table = 'item_recipes';
  37. // attrlist start
  38. protected $fillable = [
  39. 'id',
  40. 'name',
  41. 'result_item_id',
  42. 'result_min_quantity',
  43. 'result_max_quantity',
  44. 'success_rate',
  45. 'coin_cost',
  46. 'level_required',
  47. 'is_default_unlocked',
  48. 'unlock_condition',
  49. 'cooldown_seconds',
  50. 'category_id',
  51. 'sort_order',
  52. 'is_active',
  53. ];
  54. // attrlist end
  55. /**
  56. * 可批量赋值的属性
  57. *
  58. * @var array
  59. */
  60. protected $fillable = [
  61. 'name',
  62. 'result_item_id',
  63. 'result_quantity',
  64. 'success_rate',
  65. 'coin_cost',
  66. 'cooldown_seconds',
  67. 'is_visible',
  68. 'unlock_condition',
  69. ];
  70. /**
  71. * 应该被转换为原生类型的属性
  72. *
  73. * @var array
  74. */
  75. protected $casts = [
  76. 'result_quantity' => 'integer',
  77. 'success_rate' => 'float',
  78. 'coin_cost' => TransactionDetailsCast::class,
  79. 'cooldown_seconds' => 'integer',
  80. 'is_visible' => 'boolean',
  81. 'unlock_condition' => TransactionDetailsCast::class,
  82. ];
  83. /**
  84. * 获取合成结果物品
  85. *
  86. * @return BelongsTo
  87. */
  88. public function resultItem(): BelongsTo
  89. {
  90. return $this->belongsTo(ItemItem::class, 'result_item_id');
  91. }
  92. /**
  93. * 获取配方材料
  94. *
  95. * @return HasMany
  96. */
  97. public function materials(): HasMany
  98. {
  99. return $this->hasMany(ItemRecipeMaterial::class, 'recipe_id');
  100. }
  101. /**
  102. * 获取用户配方解锁状态
  103. *
  104. * @return HasMany
  105. */
  106. public function userRecipes(): HasMany
  107. {
  108. return $this->hasMany(ItemUserRecipe::class, 'recipe_id');
  109. }
  110. /**
  111. * 获取合成记录
  112. *
  113. * @return HasMany
  114. */
  115. public function craftLogs(): HasMany
  116. {
  117. return $this->hasMany(ItemCraftLog::class, 'recipe_id');
  118. }
  119. /**
  120. * 检查用户是否已解锁该配方
  121. *
  122. * @param int $userId 用户ID
  123. * @return bool
  124. */
  125. public function isUnlockedByUser(int $userId): bool
  126. {
  127. $userRecipe = $this->userRecipes()
  128. ->where('user_id', $userId)
  129. ->first();
  130. return $userRecipe && $userRecipe->is_unlocked;
  131. }
  132. /**
  133. * 检查用户是否可以合成该配方
  134. *
  135. * @param int $userId 用户ID
  136. * @return array 检查结果,包含是否可合成和原因
  137. */
  138. public function canCraftByUser(int $userId): array
  139. {
  140. // 检查配方是否已解锁
  141. if (!$this->isUnlockedByUser($userId)) {
  142. return [
  143. 'can_craft' => false,
  144. 'reason' => '配方未解锁',
  145. ];
  146. }
  147. // 检查冷却时间
  148. if ($this->cooldown_seconds > 0) {
  149. $lastCraft = $this->craftLogs()
  150. ->where('user_id', $userId)
  151. ->where('created_at', '>', now()->subSeconds($this->cooldown_seconds))
  152. ->first();
  153. if ($lastCraft) {
  154. $remainingSeconds = $this->cooldown_seconds - now()->diffInSeconds($lastCraft->created_at);
  155. return [
  156. 'can_craft' => false,
  157. 'reason' => '冷却中',
  158. 'remaining_seconds' => $remainingSeconds,
  159. ];
  160. }
  161. }
  162. // 检查材料是否足够
  163. $materials = $this->materials;
  164. $missingMaterials = [];
  165. foreach ($materials as $material) {
  166. $userItem = ItemUser::where('user_id', $userId)
  167. ->where('item_id', $material->item_id)
  168. ->whereNull('instance_id')
  169. ->sum('quantity');
  170. if ($userItem < $material->quantity) {
  171. $missingMaterials[] = [
  172. 'item_id' => $material->item_id,
  173. 'item_name' => $material->item->name,
  174. 'required' => $material->quantity,
  175. 'available' => $userItem,
  176. ];
  177. }
  178. }
  179. if (!empty($missingMaterials)) {
  180. return [
  181. 'can_craft' => false,
  182. 'reason' => '材料不足',
  183. 'missing_materials' => $missingMaterials,
  184. ];
  185. }
  186. return [
  187. 'can_craft' => true,
  188. ];
  189. }
  190. }