ItemService.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <?php
  2. namespace App\Module\GameItems\Services;
  3. use App\Module\GameItems\Dtos\ItemDto;
  4. use App\Module\GameItems\Dtos\ItemUserDto;
  5. use App\Module\GameItems\Logics\Item as ItemLogic;
  6. use App\Module\GameItems\Logics\ItemQuantity;
  7. use App\Module\GameItems\Models\Item;
  8. use App\Module\GameItems\Models\ItemUser;
  9. use Exception;
  10. use Illuminate\Support\Collection as SupportCollection;
  11. use UCore\Db\Helper;
  12. use UCore\Dto\Res;
  13. /**
  14. * 物品服务类
  15. *
  16. * 提供物品相关的服务,包括获取用户物品、添加物品到用户背包、消耗用户物品等功能。
  17. * 该类是物品模块对外提供服务的主要入口,封装了物品操作的复杂逻辑,
  18. * 通过调用ItemLogic类实现具体的业务逻辑处理。
  19. *
  20. * 所有方法均为静态方法,可直接通过类名调用。
  21. */
  22. class ItemService
  23. {
  24. /**
  25. * 获取用户物品列表
  26. *
  27. * @param int $userId 用户ID
  28. * @param array $filters 过滤条件
  29. * @param bool $includeExpired 是否包含已过期物品
  30. * @return SupportCollection|ItemUserDto[] 用户物品DTO集合
  31. */
  32. public static function getUserItems(int $userId, array $filters = [], bool $includeExpired = false): SupportCollection
  33. {
  34. $query = ItemUser::where('user_id', $userId)
  35. ->with(['item', 'instance']);
  36. // 应用过滤条件
  37. if (isset($filters['item_id'])) {
  38. $query->where('item_id', $filters['item_id']);
  39. }
  40. if (isset($filters['category_id'])) {
  41. $query->whereHas('item', function ($q) use ($filters) {
  42. $q->where('category_id', $filters['category_id']);
  43. });
  44. }
  45. if (isset($filters['type'])) {
  46. $query->whereHas('item', function ($q) use ($filters) {
  47. $q->where('type', $filters['type']);
  48. });
  49. }
  50. // 排除过期物品
  51. if (!$includeExpired) {
  52. $now = now();
  53. $query->where(function ($q) use ($now) {
  54. $q->whereNull('expire_at')
  55. ->orWhere('expire_at', '>', $now);
  56. })->whereHas('item', function ($q) use ($now) {
  57. $q->where(function ($subQ) use ($now) {
  58. $subQ->whereNull('global_expire_at')
  59. ->orWhere('global_expire_at', '>', $now);
  60. });
  61. });
  62. }
  63. // 获取模型集合
  64. $itemUsers = $query->get();
  65. // 转换为DTO集合
  66. return $itemUsers->map(function (ItemUser $itemUser) {
  67. return ItemUserDto::fromModel($itemUser);
  68. });
  69. }
  70. /**
  71. * 添加物品到用户背包
  72. *
  73. * @param int $userId 用户ID
  74. * @param int $itemId 物品ID
  75. * @param int $quantity 数量
  76. * @param array $options 选项
  77. * @return array 添加结果
  78. * @throws Exception
  79. */
  80. public static function addItem(int $userId, int $itemId, int $quantity, array $options = []): array
  81. {
  82. // 获取物品信息
  83. $item = Item::findOrFail($itemId);
  84. // 检查物品是否已过期(全局过期)
  85. if (ItemLogic::isExpired($item)) {
  86. throw new Exception("物品 {$itemId} 已全局过期");
  87. }
  88. // 处理单独属性物品
  89. if ($item->is_unique) {
  90. return ItemLogic::addUniqueItem($userId, $itemId, $options);
  91. }
  92. // 处理统一属性物品
  93. return ItemLogic::addNormalItem($userId, $itemId, $quantity, $options);
  94. }
  95. /**
  96. * 消耗用户物品
  97. *
  98. * @param int $userId 用户ID
  99. * @param int $itemId 物品ID
  100. * @param int|null $instanceId 物品实例ID(单独属性物品)
  101. * @param int $quantity 数量
  102. * @param array $options 选项
  103. * @return array 消耗结果
  104. * @throws Exception
  105. */
  106. public static function consumeItem(int $userId, int $itemId, ?int $instanceId, int $quantity, array $options = []): array
  107. {
  108. Helper::check_tr();
  109. // 获取物品信息(确保物品存在)
  110. Item::findOrFail($itemId);
  111. if ($instanceId) {
  112. // 消耗单独属性物品
  113. return ItemLogic::consumeUniqueItem($userId, $itemId, $instanceId, $options);
  114. } else {
  115. // 消耗统一属性物品
  116. return ItemLogic::consumeNormalItem($userId, $itemId, $quantity, $options);
  117. }
  118. }
  119. /**
  120. * 获取物品信息
  121. *
  122. * @param int $itemId 物品ID
  123. * @return ItemDto|null 物品信息DTO
  124. */
  125. public static function getItemInfo(int $itemId): ?ItemDto
  126. {
  127. $item = Item::find($itemId);
  128. if (!$item) {
  129. return null;
  130. }
  131. return ItemDto::fromModel($item);
  132. }
  133. /**
  134. * 获取物品数值属性
  135. *
  136. * @param int $itemId 物品ID
  137. * @param string $attributeName 属性名称
  138. * @param int $defaultValue 默认值
  139. * @return int 属性值
  140. */
  141. public static function getItemNumericAttribute(int $itemId, string $attributeName, int $defaultValue = 0): int
  142. {
  143. $item = Item::find($itemId);
  144. if (!$item) {
  145. return $defaultValue;
  146. }
  147. $numericAttributes = (array)$item->numeric_attributes;
  148. return $numericAttributes[$attributeName] ?? $defaultValue;
  149. }
  150. /**
  151. * 验证用户是否拥有足够数量的物品
  152. *
  153. * @param int $userId 用户ID
  154. * @param int $itemId 物品ID
  155. * @param int $quantity 需要的数量
  156. * @param int|null $instanceId 物品实例ID(可选,用于验证特定实例物品)
  157. * @param bool $includeAllTypes 是否包含所有类型的物品(普通物品和实例物品)
  158. * @param bool $includeExpired 是否包含已过期物品
  159. * @return Res 验证结果
  160. */
  161. public static function checkItemQuantity(int $userId, int $itemId, int $quantity, ?int $instanceId = null, bool $includeAllTypes = false, bool $includeExpired = false): Res
  162. {
  163. try {
  164. // 获取物品信息
  165. $item = Item::find($itemId);
  166. if (!$item) {
  167. return Res::error("物品不存在(ID: {$itemId})");
  168. }
  169. // 检查物品是否已过期(全局过期)
  170. if (ItemLogic::isExpired($item)) {
  171. return Res::error("物品已全局过期(ID: {$itemId})");
  172. }
  173. // 根据不同情况进行验证
  174. if ($instanceId !== null) {
  175. // 验证特定实例物品
  176. $hasInstance = ItemQuantity::hasInstanceItem($userId, $itemId, $instanceId, $includeExpired);
  177. if (!$hasInstance) {
  178. return Res::error("用户不拥有指定的实例物品(ID: {$instanceId})");
  179. }
  180. return Res::success("用户拥有指定的实例物品");
  181. } else if ($includeAllTypes) {
  182. // 验证所有类型物品(普通物品和实例物品)的总数量
  183. $totalQuantity = ItemQuantity::getUserTotalItemQuantity($userId, $itemId, $includeExpired);
  184. if ($totalQuantity < $quantity) {
  185. return Res::error("物品总数量不足,需要{$quantity}个,但只有{$totalQuantity}个", [
  186. 'required' => $quantity,
  187. 'available' => $totalQuantity
  188. ]);
  189. }
  190. return Res::success("物品总数量足够", [
  191. 'quantity' => $totalQuantity,
  192. 'required' => $quantity
  193. ]);
  194. } else {
  195. // 只验证普通物品数量
  196. $normalQuantity = ItemQuantity::getUserItemQuantity($userId, $itemId, $includeExpired);
  197. if ($normalQuantity < $quantity) {
  198. return Res::error("普通物品数量不足,需要{$quantity}个,但只有{$normalQuantity}个", [
  199. 'required' => $quantity,
  200. 'available' => $normalQuantity
  201. ]);
  202. }
  203. return Res::success("普通物品数量足够", [
  204. 'quantity' => $normalQuantity,
  205. 'required' => $quantity
  206. ]);
  207. }
  208. } catch (Exception $e) {
  209. return Res::error("验证物品数量时发生错误: " . $e->getMessage(), [
  210. 'error' => $e->getMessage(),
  211. 'trace' => $e->getTraceAsString()
  212. ]);
  213. }
  214. }
  215. }