| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- <?php
- namespace App\Module\GameItems\Services;
- use App\Module\GameItems\Dtos\ItemDto;
- use App\Module\GameItems\Dtos\ItemUserDto;
- use App\Module\GameItems\Enums\FREEZE_REASON_TYPE;
- use App\Module\GameItems\Logics\Item as ItemLogic;
- use App\Module\GameItems\Logics\ItemFreeze;
- use App\Module\GameItems\Logics\ItemQuantity;
- use App\Module\GameItems\Models\Item;
- use App\Module\GameItems\Models\ItemUser;
- use Exception;
- use Illuminate\Support\Collection as SupportCollection;
- use UCore\Db\Helper;
- use UCore\Dto\Res;
- /**
- * 物品服务类
- *
- * 提供物品相关的服务,包括获取用户物品、添加物品到用户背包、消耗用户物品等功能。
- * 该类是物品模块对外提供服务的主要入口,封装了物品操作的复杂逻辑,
- * 通过调用ItemLogic类实现具体的业务逻辑处理。
- *
- * 所有方法均为静态方法,可直接通过类名调用。
- */
- class ItemService
- {
- /**
- * 获取用户物品列表
- *
- * @param int $userId 用户ID
- * @param array $filters 过滤条件
- * @param bool $includeExpired 是否包含已过期物品
- * @return SupportCollection|ItemUserDto[] 用户物品DTO集合
- */
- public static function getUserItems(int $userId, array $filters = [], bool $includeExpired = false): SupportCollection
- {
- $query = ItemUser::where('user_id', $userId)
- ->with(['item', 'instance']);
- // 应用过滤条件
- if (isset($filters['item_id'])) {
- $query->where('item_id', $filters['item_id']);
- }
- if (isset($filters['category_id'])) {
- $query->whereHas('item', function ($q) use ($filters) {
- $q->where('category_id', $filters['category_id']);
- });
- }
- if (isset($filters['type'])) {
- $query->whereHas('item', function ($q) use ($filters) {
- $q->where('type', $filters['type']);
- });
- }
- // 排除过期物品
- if (!$includeExpired) {
- $now = now();
- $query->where(function ($q) use ($now) {
- $q->whereNull('expire_at')
- ->orWhere('expire_at', '>', $now);
- })->whereHas('item', function ($q) use ($now) {
- $q->where(function ($subQ) use ($now) {
- $subQ->whereNull('global_expire_at')
- ->orWhere('global_expire_at', '>', $now);
- });
- });
- }
- // 获取模型集合
- $itemUsers = $query->get();
- // 转换为DTO集合
- return $itemUsers->map(function (ItemUser $itemUser) {
- return ItemUserDto::fromModel($itemUser);
- });
- }
- /**
- * 添加物品到用户背包
- *
- * @param int $userId 用户ID
- * @param int $itemId 物品ID
- * @param int $quantity 数量
- * @param array $options 选项
- * @return array 添加结果
- * @throws Exception
- */
- public static function addItem(int $userId, int $itemId, int $quantity, array $options = []): array
- {
- // 获取物品信息
- $item = Item::findOrFail($itemId);
- // 检查物品是否已过期(全局过期)
- if (ItemLogic::isExpired($item)) {
- throw new Exception("物品 {$itemId} 已全局过期");
- }
- // 处理单独属性物品
- if ($item->is_unique) {
- return ItemLogic::addUniqueItem($userId, $itemId, $options);
- }
- // 处理统一属性物品
- return ItemLogic::addNormalItem($userId, $itemId, $quantity, $options);
- }
- /**
- * 消耗用户物品
- *
- * @param int $userId 用户ID
- * @param int $itemId 物品ID
- * @param int|null $instanceId 物品实例ID(单独属性物品)
- * @param int $quantity 数量
- * @param array $options 选项
- * @return array 消耗结果
- * @throws Exception
- */
- public static function consumeItem(int $userId, int $itemId, ?int $instanceId, int $quantity, array $options = []): array
- {
- Helper::check_tr();
- // 获取物品信息(确保物品存在)
- Item::findOrFail($itemId);
- if ($instanceId) {
- // 消耗单独属性物品
- return ItemLogic::consumeUniqueItem($userId, $itemId, $instanceId, $options);
- } else {
- // 消耗统一属性物品
- return ItemLogic::consumeNormalItem($userId, $itemId, $quantity, $options);
- }
- }
- /**
- * 获取物品信息
- *
- * @param int $itemId 物品ID
- * @return ItemDto|null 物品信息DTO
- */
- public static function getItemInfo(int $itemId): ?ItemDto
- {
- $item = Item::find($itemId);
- if (!$item) {
- return null;
- }
- return ItemDto::fromModel($item);
- }
- /**
- * 获取物品数值属性
- *
- * @param int $itemId 物品ID
- * @param string $attributeName 属性名称
- * @param int $defaultValue 默认值
- * @return int 属性值
- */
- public static function getItemNumericAttribute(int $itemId, string $attributeName, int $defaultValue = 0): int
- {
- /**
- * @var Item $item
- */
- $item = Item::find($itemId);
- if (!$item) {
- return $defaultValue;
- }
- $numericAttributes = (array)$item->numeric_attributes;
- return $numericAttributes[$attributeName] ?? $defaultValue;
- }
- /**
- * 验证用户是否拥有足够数量的物品
- *
- * @param int $userId 用户ID
- * @param int $itemId 物品ID
- * @param int $quantity 需要的数量
- * @param int|null $instanceId 物品实例ID(可选,用于验证特定实例物品)
- * @param bool $includeAllTypes 是否包含所有类型的物品(普通物品和实例物品)
- * @param bool $includeExpired 是否包含已过期物品
- * @return Res 验证结果
- */
- public static function checkItemQuantity(int $userId, int $itemId, int $quantity, ?int $instanceId = null, bool $includeAllTypes = false, bool $includeExpired = false): Res
- {
- try {
- // 获取物品信息
- $item = Item::find($itemId);
- if (!$item) {
- return Res::error("物品不存在(ID: {$itemId})");
- }
- // 检查物品是否已过期(全局过期)
- if (ItemLogic::isExpired($item)) {
- return Res::error("物品已全局过期(ID: {$itemId})");
- }
- // 根据不同情况进行验证
- if ($instanceId ) {
- // 验证特定实例物品
- $hasInstance = ItemQuantity::hasInstanceItem($userId, $itemId, $instanceId, $includeExpired);
- if (!$hasInstance) {
- return Res::error("用户不拥有指定的实例物品(ID: {$instanceId})");
- }
- return Res::success("用户拥有指定的实例物品");
- } else if ($includeAllTypes) {
- // 验证所有类型物品(普通物品和实例物品)的总数量
- $totalQuantity = ItemQuantity::getUserTotalItemQuantity($userId, $itemId, $includeExpired);
- if ($totalQuantity < $quantity) {
- return Res::error("物品总数量不足,需要{$quantity}个,但只有{$totalQuantity}个", [
- 'required' => $quantity,
- 'available' => $totalQuantity
- ]);
- }
- return Res::success("物品总数量足够", [
- 'quantity' => $totalQuantity,
- 'required' => $quantity
- ]);
- } else {
- // 只验证普通物品数量
- $normalQuantity = ItemQuantity::getUserItemQuantity($userId, $itemId, $includeExpired);
- if ($normalQuantity < $quantity) {
- return Res::error("普通物品数量不足,需要{$quantity}个,但只有{$normalQuantity}个", [
- 'required' => $quantity,
- 'available' => $normalQuantity
- ]);
- }
- return Res::success("普通物品数量足够", [
- 'quantity' => $normalQuantity,
- 'required' => $quantity
- ]);
- }
- } catch (Exception $e) {
- return Res::error("验证物品数量时发生错误: " . $e->getMessage(), [
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
- }
- }
- /**
- * 获取用户拥有的种子物品
- *
- * @param int $userId 用户ID
- * @param bool $includeExpired 是否包含已过期物品
- * @return array 种子物品数组,键为物品ID,值为数量
- */
- public static function getUserSeedItems(int $userId, bool $includeExpired = false): array
- {
- try {
- // 首先获取所有种子的物品ID
- $seedItemIds = \App\Module\Farm\Models\FarmSeed::pluck('item_id')->toArray();
- if (empty($seedItemIds)) {
- return [];
- }
- // 查询用户拥有的种子物品
- $query = ItemUser::where('user_id', $userId)
- ->whereIn('item_id', $seedItemIds)
- ->where('quantity', '>', 0);
- // 排除过期物品
- if (!$includeExpired) {
- $now = now();
- $query->where(function ($q) use ($now) {
- $q->whereNull('expire_at')
- ->orWhere('expire_at', '>', $now);
- })->whereHas('item', function ($q) use ($now) {
- $q->where(function ($subQ) use ($now) {
- $subQ->whereNull('global_expire_at')
- ->orWhere('global_expire_at', '>', $now);
- });
- });
- }
- $userItems = $query->get();
- // 按物品ID分组并汇总数量
- $seedItems = [];
- foreach ($userItems as $userItem) {
- $itemId = $userItem->item_id;
- if (!isset($seedItems[$itemId])) {
- $seedItems[$itemId] = 0;
- }
- $seedItems[$itemId] += $userItem->quantity;
- }
- return $seedItems;
- } catch (Exception $e) {
- \Illuminate\Support\Facades\Log::error('获取用户种子物品失败', [
- 'user_id' => $userId,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
- return [];
- }
- }
- /**
- * 冻结物品
- *
- * @param int $userId 用户ID
- * @param int $itemId 物品ID
- * @param int|null $instanceId 物品实例ID(单独属性物品)
- * @param int $quantity 数量
- * @param string $reason 冻结原因
- * @param array $options 选项
- * @return array 冻结结果
- * @throws Exception
- */
- public static function freezeItem(
- int $userId,
- int $itemId,
- ?int $instanceId,
- int $quantity,
- string $reason,
- array $options = []
- ): array {
- Helper::check_tr();
- // 解析冻结原因
- $reasonEnum = FREEZE_REASON_TYPE::tryFrom($options['reason_type'] ?? FREEZE_REASON_TYPE::SYSTEM_FREEZE->value);
- if (!$reasonEnum) {
- $reasonEnum = FREEZE_REASON_TYPE::SYSTEM_FREEZE;
- }
- $sourceId = $options['source_id'] ?? null;
- $sourceType = $options['source_type'] ?? null;
- $operatorId = $options['operator_id'] ?? null;
- if ($instanceId) {
- // 冻结单独属性物品
- return ItemFreeze::freezeUniqueItem(
- $userId,
- $itemId,
- $instanceId,
- $reasonEnum,
- $sourceId,
- $sourceType,
- $operatorId
- );
- } else {
- // 冻结统一属性物品
- return ItemFreeze::freezeNormalItem(
- $userId,
- $itemId,
- $quantity,
- $reasonEnum,
- $sourceId,
- $sourceType,
- $operatorId
- );
- }
- }
- /**
- * 解冻物品
- *
- * @param int $freezeLogId 冻结日志ID
- * @return array 解冻结果
- * @throws Exception
- */
- public static function unfreezeItem(int $freezeLogId): array
- {
- Helper::check_tr();
- return ItemFreeze::unfreezeByLogId($freezeLogId);
- }
- /**
- * 获取用户可用物品数量(排除冻结的)
- *
- * @param int $userId 用户ID
- * @param int $itemId 物品ID
- * @param int|null $instanceId 实例ID
- * @return int 可用数量
- */
- public static function getAvailableQuantity(int $userId, int $itemId, ?int $instanceId = null): int
- {
- return ItemFreeze::getAvailableQuantity($userId, $itemId, $instanceId);
- }
- /**
- * 获取用户冻结物品列表
- *
- * @param int $userId 用户ID
- * @param array $filters 过滤条件
- * @return SupportCollection 冻结物品集合
- */
- public static function getFrozenItems(int $userId, array $filters = []): SupportCollection
- {
- return ItemFreeze::getFrozenItems($userId, $filters);
- }
- /**
- * 批量冻结物品
- *
- * @param int $userId 用户ID
- * @param array $items 物品列表
- * @param string $reason 冻结原因
- * @param array $options 选项
- * @return array 冻结结果
- * @throws Exception
- */
- public static function batchFreezeItems(
- int $userId,
- array $items,
- string $reason,
- array $options = []
- ): array {
- Helper::check_tr();
- // 解析冻结原因
- $reasonEnum = FREEZE_REASON_TYPE::tryFrom($options['reason_type'] ?? FREEZE_REASON_TYPE::SYSTEM_FREEZE->value);
- if (!$reasonEnum) {
- $reasonEnum = FREEZE_REASON_TYPE::SYSTEM_FREEZE;
- }
- $sourceId = $options['source_id'] ?? null;
- $sourceType = $options['source_type'] ?? null;
- $operatorId = $options['operator_id'] ?? null;
- return ItemFreeze::batchFreezeItems(
- $userId,
- $items,
- $reasonEnum,
- $sourceId,
- $sourceType,
- $operatorId
- );
- }
- /**
- * 获取冻结统计信息
- *
- * @param int $userId 用户ID
- * @return array 统计信息
- */
- public static function getFreezeStatistics(int $userId): array
- {
- return ItemFreeze::getFreezeStatistics($userId);
- }
- }
|