MexOrderValidator.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. namespace App\Module\Mex\Validators;
  3. use App\Module\GameItems\Logics\ItemQuantity;
  4. use App\Module\Fund\Services\FundService;
  5. use App\Module\Fund\Enums\FUND_CURRENCY_TYPE;
  6. use App\Module\Mex\Logic\FundLogic;
  7. use UCore\Validator;
  8. use Uraus\Kku\Common\MEX_DIRECTION;
  9. use Illuminate\Support\Facades\Log;
  10. /**
  11. * 农贸市场订单验证器
  12. *
  13. * 验证订单创建的各种条件,包括资金、物品数量等
  14. * 支持多币种验证,默认使用钻石币种
  15. */
  16. class MexOrderValidator extends Validator
  17. {
  18. /**
  19. * 验证订单创建条件
  20. *
  21. * @param mixed $value 交易方向
  22. * @param array $data 所有数据
  23. * @return bool 验证是否通过
  24. */
  25. public function validate(mixed $value, array $data): bool
  26. {
  27. $direction = (int)$value;
  28. // 从 args 获取字段名
  29. $userIdKey = $this->args[0] ?? 'user_id';
  30. $itemIdKey = $this->args[1] ?? 'itemId';
  31. $numKey = $this->args[2] ?? 'num';
  32. $priceKey = $this->args[3] ?? 'price';
  33. $currencyTypeKey = $this->args[4] ?? 'currency_type'; // 新增币种字段
  34. $userId = $data[$userIdKey] ?? null;
  35. $itemId = $data[$itemIdKey] ?? null;
  36. $quantity = $data[$numKey] ?? null;
  37. $price = $data[$priceKey] ?? null;
  38. $currencyTypeValue = $data[$currencyTypeKey] ?? null;
  39. if (!$userId || !$itemId || !$quantity || !$price) {
  40. $this->addError('订单参数不完整');
  41. return false;
  42. }
  43. // 解析币种类型
  44. $currencyType = null;
  45. if ($currencyTypeValue !== null) {
  46. $currencyType = FUND_CURRENCY_TYPE::tryFrom($currencyTypeValue);
  47. }
  48. // 如果没有指定币种或币种无效,使用默认币种(钻石)
  49. $currencyType = $currencyType ?? FundLogic::getDefaultCurrency();
  50. try {
  51. if ($direction === MEX_DIRECTION::SELL) {
  52. return $this->validateSellOrder($userId, $itemId, $quantity, $price);
  53. } elseif ($direction === MEX_DIRECTION::BUY) {
  54. return $this->validateBuyOrder($userId, $itemId, $quantity, $price, $currencyType);
  55. } else {
  56. $this->addError('无效的交易方向');
  57. return false;
  58. }
  59. } catch (\Exception $e) {
  60. Log::error('农贸市场订单验证失败', [
  61. 'user_id' => $userId,
  62. 'item_id' => $itemId,
  63. 'quantity' => $quantity,
  64. 'price' => $price,
  65. 'direction' => $direction,
  66. 'currency_type' => $currencyType?->value,
  67. 'error' => $e->getMessage()
  68. ]);
  69. $this->addError('订单验证时发生错误');
  70. return false;
  71. }
  72. }
  73. /**
  74. * 验证卖出订单
  75. *
  76. * @param int $userId 用户ID
  77. * @param int $itemId 物品ID
  78. * @param int $quantity 数量
  79. * @param float $price 价格
  80. * @return bool
  81. */
  82. private function validateSellOrder(int $userId, int $itemId, int $quantity, float $price): bool
  83. {
  84. // 验证用户是否拥有足够的物品
  85. if (!$this->validateUserItemQuantity($userId, $itemId, $quantity)) {
  86. return false;
  87. }
  88. return true;
  89. }
  90. /**
  91. * 验证买入订单
  92. *
  93. * @param int $userId 用户ID
  94. * @param int $itemId 物品ID
  95. * @param int $quantity 数量
  96. * @param float $price 价格
  97. * @param FUND_CURRENCY_TYPE|null $currencyType 币种类型,默认使用钻石
  98. * @return bool
  99. */
  100. private function validateBuyOrder(int $userId, int $itemId, int $quantity, float $price, ?FUND_CURRENCY_TYPE $currencyType = null): bool
  101. {
  102. // 计算所需资金
  103. $totalCost = $quantity * $price;
  104. // 验证用户是否有足够的资金(使用指定币种)
  105. if (!$this->validateUserFundBalance($userId, $totalCost, $currencyType)) {
  106. return false;
  107. }
  108. return true;
  109. }
  110. /**
  111. * 验证用户物品数量(检查可用数量,排除已冻结的物品)
  112. *
  113. * @param int $userId 用户ID
  114. * @param int $itemId 物品ID
  115. * @param int $requiredQuantity 需要的数量
  116. * @return bool
  117. */
  118. private function validateUserItemQuantity(int $userId, int $itemId, int $requiredQuantity): bool
  119. {
  120. // 使用ItemService获取可用数量(排除已冻结的物品)
  121. $availableQuantity = \App\Module\GameItems\Services\ItemService::getAvailableQuantity($userId, $itemId);
  122. if ($availableQuantity < $requiredQuantity) {
  123. $this->addError("可用物品数量不足,当前可用 {$availableQuantity} 个,需要 {$requiredQuantity} 个");
  124. return false;
  125. }
  126. return true;
  127. }
  128. /**
  129. * 验证用户资金余额
  130. * 使用Fund模块的验证器进行数额验证,信任Fund模块的数据处理
  131. *
  132. * @param int $userId 用户ID
  133. * @param float $requiredAmount 需要的金额
  134. * @param FUND_CURRENCY_TYPE|null $currencyType 币种类型,默认使用钻石
  135. * @return bool
  136. */
  137. private function validateUserFundBalance(int $userId, float $requiredAmount, ?FUND_CURRENCY_TYPE $currencyType = null): bool
  138. {
  139. // 获取币种类型,默认使用钻石
  140. $currencyType = $currencyType ?? FundLogic::getDefaultCurrency();
  141. // 获取对应的可用账户类型
  142. $availableAccountType = FundLogic::getAvailableAccountType($currencyType);
  143. if (!$availableAccountType) {
  144. $this->addError('不支持的币种类型');
  145. return false;
  146. }
  147. // 使用Fund模块的FundService进行资金验证,信任Fund模块的数据处理
  148. $fundService = new FundService($userId, $availableAccountType->value);
  149. $balance = $fundService->balance();
  150. // 直接比较,不进行精度转换,信任Fund模块的数据处理
  151. if ($balance < $requiredAmount) {
  152. $this->addError("资金不足,当前{$currencyType->name}余额 {$balance},需要 {$requiredAmount}");
  153. return false;
  154. }
  155. return true;
  156. }
  157. }