ConsumeService.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. namespace App\Module\Game\Services;
  3. use App\Module\Fund\Logic\User as FundLogic;
  4. use App\Module\Fund\Enums\LOG_TYPE as FUND_LOG_TYPE;
  5. use App\Module\Game\Enums\CONSUME_TYPE;
  6. use App\Module\Game\Enums\REWARD_SOURCE_TYPE;
  7. use App\Module\Game\Logics\ConsumeProcessors\CurrencyConsume;
  8. use App\Module\Game\Logics\ConsumeProcessors\FundConfigConsume;
  9. use App\Module\Game\Logics\ConsumeProcessors\FundConfigsConsume;
  10. use App\Module\Game\Logics\ConsumeProcessors\ItemConsume;
  11. use App\Module\Game\Logics\ConsumeProcessors\ProcessorDispatcher;
  12. use App\Module\Game\Models\GameConsumeGroup;
  13. use App\Module\Game\Models\GameConsumeItem;
  14. use App\Module\Game\Dtos\ConsumeGroupDto;
  15. use App\Module\GameItems\Services\ItemService;
  16. use Illuminate\Support\Facades\Log;
  17. use UCore\Dto\Res;
  18. /**
  19. * 消耗服务类
  20. *
  21. * 提供消耗组相关的服务,包括检查用户是否满足消耗条件、执行消耗等功能
  22. */
  23. class ConsumeService
  24. {
  25. /**
  26. * 获取消耗组信息
  27. *
  28. * @param int|string $consumeGroupCode 消耗组ID或编码
  29. * @param bool $withItems 是否包含消耗项
  30. * @return ConsumeGroupDto|null 消耗组DTO,不存在时返回null
  31. */
  32. public static function getConsumeGroup($consumeGroupCode, bool $withItems = true): ?ConsumeGroupDto
  33. {
  34. try {
  35. // 获取消耗组
  36. $consumeGroup = is_numeric($consumeGroupCode)
  37. ? GameConsumeGroup::with($withItems ? ['consumeItems'] : [])->find($consumeGroupCode)
  38. : GameConsumeGroup::with($withItems ? ['consumeItems'] : [])->where('code', $consumeGroupCode)->first();
  39. if (!$consumeGroup) {
  40. return null;
  41. }
  42. return ConsumeGroupDto::fromModel($consumeGroup, $withItems);
  43. } catch (\Exception $e) {
  44. Log::error('获取消耗组失败', [
  45. 'consume_group_code' => $consumeGroupCode,
  46. 'error' => $e->getMessage()
  47. ]);
  48. return null;
  49. }
  50. }
  51. /**
  52. * 获取消耗组并转换为Protobuf Deduct对象
  53. *
  54. * @param int|string $consumeGroupCode 消耗组ID或编码
  55. * @return \Uraus\Kku\Common\Deduct|null Protobuf Deduct对象,不存在时返回null
  56. */
  57. public static function getConsumeGroupAsDeduct($consumeGroupCode): ?\Uraus\Kku\Common\Deduct
  58. {
  59. $consumeGroupDto = self::getConsumeGroup($consumeGroupCode, true);
  60. if (!$consumeGroupDto) {
  61. return null;
  62. }
  63. return ProtobufConverter::convertConsumeGroupToDeduct($consumeGroupDto);
  64. }
  65. /**
  66. * 检查用户是否满足消耗组的条件
  67. *
  68. * @param int $userId 用户ID
  69. * @param string|int $consumeGroupCode 消耗组编码或ID
  70. * @param float $multiplier 倍数,用于验证几倍消耗,默认为1
  71. * @return array 检查结果,包含success字段表示是否满足条件,message字段表示错误信息
  72. */
  73. public static function checkConsume(int $userId, $consumeGroupCode, float $multiplier = 1.0): Res
  74. {
  75. try {
  76. // 获取消耗组
  77. /**
  78. * @var GameConsumeGroup $consumeGroup
  79. */
  80. $consumeGroup = is_numeric($consumeGroupCode)
  81. ? GameConsumeGroup::find($consumeGroupCode)
  82. : GameConsumeGroup::where('code', $consumeGroupCode)->first();
  83. if (!$consumeGroup) {
  84. return Res::error("消耗组不存在: {$consumeGroupCode}");
  85. }
  86. // 获取消耗组中的所有消耗项
  87. $consumeItems = $consumeGroup->consumeItems;
  88. if ($consumeItems->isEmpty()) {
  89. return Res::success();
  90. }
  91. // 检查每个消耗项
  92. foreach ($consumeItems as $item) {
  93. $checkResult = self::checkConsumeItem($userId, $item, $multiplier);
  94. if (!$checkResult['success']) {
  95. return Res::error($checkResult['message'], $checkResult);
  96. }
  97. }
  98. // 所有条件都满足,返回成功; 返回消耗组信息
  99. return Res::success('',
  100. ConsumeGroupService::consumeItems2Array($consumeItems)
  101. );
  102. } catch (\Exception $e) {
  103. Log::error('检查消耗条件失败', [
  104. 'user_id' => $userId,
  105. 'consume_group' => $consumeGroupCode,
  106. 'error' => $e->getMessage()
  107. ]);
  108. return Res::error('检查消耗条件时发生错误: ' . $e->getMessage());
  109. }
  110. }
  111. /**
  112. * 执行消耗
  113. *
  114. * @param int $userId 用户ID
  115. * @param string|int $consumeGroupCode 消耗组编码或ID
  116. * @param string $source 消耗来源
  117. * @param int $sourceId 消耗来源ID
  118. * @param bool $check 是否先检查消耗条件
  119. * @param float $multiplier 倍数,用于执行几倍消耗,默认为1
  120. * @return Res 执行结果
  121. */
  122. public static function executeConsume(int $userId, $consumeGroupCode, REWARD_SOURCE_TYPE $source, int $sourceId = 0, $check = true, float $multiplier = 1.0): Res
  123. {
  124. if ($check) {
  125. // 先检查是否满足消耗条件(使用相同的倍数)
  126. $checkResult = self::checkConsume($userId, $consumeGroupCode, $multiplier);
  127. if (!$checkResult->success) {
  128. return $checkResult;
  129. }
  130. }
  131. try {
  132. // 获取消耗组
  133. /**
  134. * @var GameConsumeGroup $consumeGroup
  135. */
  136. $consumeGroup = is_numeric($consumeGroupCode)
  137. ? GameConsumeGroup::find($consumeGroupCode)
  138. : GameConsumeGroup::where('code', $consumeGroupCode)->first();
  139. if (!$consumeGroup) {
  140. return Res::error("消耗组不存在: {$consumeGroupCode}");
  141. }
  142. // 获取消耗组中的所有消耗项
  143. $consumeItems = $consumeGroup->consumeItems;
  144. if ($consumeItems->isEmpty()) {
  145. return Res::success('消耗组为空,无需执行消耗', [
  146. 'consume_group' => [
  147. 'id' => $consumeGroup->id,
  148. 'code' => $consumeGroup->code,
  149. 'name' => $consumeGroup->name
  150. ],
  151. 'list' => []
  152. ]);
  153. }
  154. // 验证事务是否已开启(由调用者负责事务管理)
  155. \UCore\Db\Helper::check_tr();
  156. // 执行每个消耗项
  157. foreach ($consumeItems as $item) {
  158. $consumeResult = ProcessorDispatcher::process($userId, $item, $source, $sourceId, $multiplier);
  159. if (!$consumeResult['success']) {
  160. return Res::error($consumeResult['message'], $consumeResult);
  161. }
  162. }
  163. return Res::success('消耗执行成功', [
  164. 'consume_group' => [
  165. 'id' => $consumeGroup->id,
  166. 'code' => $consumeGroup->code,
  167. 'name' => $consumeGroup->name
  168. ],
  169. 'list' => ConsumeGroupService::consumeItems2Array($consumeItems)
  170. ]);
  171. } catch (\Exception $e) {
  172. Log::error('执行消耗失败', [
  173. 'user_id' => $userId,
  174. 'consume_group' => $consumeGroupCode,
  175. 'source' => $source,
  176. 'source_id' => $sourceId,
  177. 'error' => $e->getMessage()
  178. ]);
  179. return Res::error('执行消耗时发生错误: ' . $e->getMessage());
  180. }
  181. }
  182. /**
  183. * 检查单个消耗项
  184. *
  185. * @param int $userId 用户ID
  186. * @param GameConsumeItem $consumeItem 消耗项
  187. * @param float $multiplier 倍数,用于验证几倍消耗,默认为1
  188. * @return array 检查结果
  189. */
  190. protected static function checkConsumeItem(int $userId, GameConsumeItem $consumeItem, float $multiplier = 1.0): array
  191. {
  192. switch ($consumeItem->consume_type) {
  193. case CONSUME_TYPE::ITEM->value:
  194. $result = ItemConsume::checkItemConsume($userId,$consumeItem,$multiplier);
  195. // 将 Res 对象转换为数组格式
  196. return [
  197. 'success' => $result->success,
  198. 'message' => $result->message,
  199. 'item_id' => $result->data['item_id'] ?? null,
  200. 'required' => $result->data['required'] ?? null,
  201. 'actual' => $result->data['actual'] ?? null
  202. ];
  203. case CONSUME_TYPE::FUND_CONFIG->value:
  204. $result = FundConfigConsume::checkFundConfigConsume($userId,$consumeItem,$multiplier);
  205. // 将 Res 对象转换为数组格式
  206. return [
  207. 'success' => $result->success,
  208. 'message' => $result->message,
  209. 'fund_config_id' => $result->data['fund_config_id'] ?? null,
  210. 'required' => $result->data['required'] ?? null,
  211. 'actual' => $result->data['actual'] ?? null
  212. ];
  213. case CONSUME_TYPE::CURRENCY->value:
  214. return CurrencyConsume::checkCurrencyConsume($userId,$consumeItem,$multiplier);
  215. case CONSUME_TYPE::FUND_CONFIGS->value:
  216. $result = FundConfigsConsume::checkFundConfigsConsume($userId,$consumeItem,$multiplier);
  217. // 将 Res 对象转换为数组格式
  218. return [
  219. 'success' => $result->success,
  220. 'message' => $result->message,
  221. 'fund_config_ids' => $result->data['fund_config_ids'] ?? null,
  222. 'required' => $result->data['required'] ?? null,
  223. 'actual' => $result->data['actual'] ?? null
  224. ];
  225. default:
  226. return [
  227. 'success' => false,
  228. 'message' => "不支持的消耗类型: {$consumeItem->consume_type}"
  229. ];
  230. }
  231. }
  232. }