MexAccountLogic.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. namespace App\Module\Mex\Logic;
  3. use App\Module\Fund\Services\FundService;
  4. use App\Module\Fund\Enums\FUND_TYPE;
  5. use App\Module\Fund\Enums\FUND_CURRENCY_TYPE;
  6. use App\Module\Game\Enums\REWARD_SOURCE_TYPE;
  7. use App\Module\GameItems\Services\ItemService;
  8. use App\Module\Mex\Logic\MexTransactionLogic;
  9. use App\Module\Mex\Logic\MexWarehouseLogic;
  10. use App\Module\Mex\Logic\FundLogic;
  11. use App\Module\Mex\Enums\TransactionType;
  12. use Illuminate\Support\Facades\DB;
  13. use Illuminate\Support\Facades\Log;
  14. /**
  15. * 农贸市场账户流转逻辑
  16. *
  17. * 处理用户与仓库账户之间的资金和物品流转
  18. * 仓库账户USER_ID为15,调控账户USER_ID为16
  19. * 支持多币种交易,默认使用钻石币种
  20. */
  21. class MexAccountLogic
  22. {
  23. // 仓库账户ID
  24. const WAREHOUSE_USER_ID = 15;
  25. // 调控账户ID
  26. const REGULATION_USER_ID = 16;
  27. /**
  28. * 处理用户卖出订单的账户流转
  29. * 用户卖出:资金从仓库转出到用户、物品从用户转入仓库
  30. *
  31. * @param int $userId 用户ID
  32. * @param int $itemId 商品ID
  33. * @param int $quantity 数量
  34. * @param string $price 单价
  35. * @param string $totalAmount 总金额
  36. * @param int $orderId 订单ID
  37. * @param FUND_CURRENCY_TYPE|null $currencyType 币种类型,默认使用钻石
  38. * @return array 操作结果
  39. */
  40. public static function processSellOrder(int $userId, int $itemId, int $quantity, string $price, string $totalAmount, int $orderId, ?FUND_CURRENCY_TYPE $currencyType = null): array
  41. {
  42. try {
  43. DB::beginTransaction();
  44. // 获取币种类型,默认使用钻石
  45. $currencyType = $currencyType ?? FundLogic::getDefaultCurrency();
  46. // 获取对应的账户类型
  47. $availableAccountType = FundLogic::getAvailableAccountType($currencyType);
  48. if (!$availableAccountType) {
  49. DB::rollBack();
  50. return ['success' => false, 'message' => '不支持的币种类型'];
  51. }
  52. // 1. 验证用户是否有足够的物品
  53. $checkResult = ItemService::checkItemQuantity($userId, $itemId, $quantity);
  54. if (!$checkResult->success) {
  55. DB::rollBack();
  56. return ['success' => false, 'message' => $checkResult->message];
  57. }
  58. // 2. 从用户扣除物品
  59. $consumeResult = ItemService::consumeItem($userId, $itemId, null, $quantity, [
  60. 'reason' => 'mex_sell',
  61. 'order_id' => $orderId,
  62. 'remark' => "农贸市场卖出物品,订单ID:{$orderId}"
  63. ]);
  64. if (!$consumeResult['success']) {
  65. DB::rollBack();
  66. return ['success' => false, 'message' => '扣除用户物品失败:' . ($consumeResult['message'] ?? '未知错误')];
  67. }
  68. // 3. 仓库账户是虚拟账户,不实际存储物品,只更新统计数据
  69. // 物品已从用户扣除,直接进入虚拟仓库统计
  70. // 4. 从仓库账户转出资金给用户
  71. $warehouseFundService = new FundService(self::WAREHOUSE_USER_ID, $availableAccountType->value);
  72. $precision = $currencyType->getPrecision();
  73. $fundAmount = (int)bcmul($totalAmount, bcpow('10', $precision), 0); // 根据币种精度转换
  74. $transferResult = $warehouseFundService->trade(
  75. $userId,
  76. $fundAmount,
  77. 'mex_sell',
  78. $orderId,
  79. "农贸市场卖出收款,订单ID:{$orderId}"
  80. );
  81. if (is_string($transferResult)) {
  82. DB::rollBack();
  83. return ['success' => false, 'message' => '资金转移失败:' . $transferResult];
  84. }
  85. // 5. 更新仓库统计
  86. $warehouseResult = MexWarehouseLogic::addStock($itemId, $quantity, $totalAmount);
  87. if (!$warehouseResult) {
  88. DB::rollBack();
  89. return ['success' => false, 'message' => '更新仓库统计失败'];
  90. }
  91. // 6. 创建成交记录
  92. $transactionResult = MexTransactionLogic::createTransaction([
  93. 'sell_order_id' => $orderId,
  94. 'buyer_id' => self::WAREHOUSE_USER_ID,
  95. 'seller_id' => $userId,
  96. 'item_id' => $itemId,
  97. 'currency_type' => $currencyType->value,
  98. 'quantity' => $quantity,
  99. 'price' => $price,
  100. 'total_amount' => $totalAmount,
  101. 'transaction_type' => TransactionType::USER_SELL,
  102. 'is_admin_operation' => false,
  103. ]);
  104. if (!$transactionResult) {
  105. DB::rollBack();
  106. return ['success' => false, 'message' => '创建成交记录失败'];
  107. }
  108. DB::commit();
  109. return [
  110. 'success' => true,
  111. 'message' => '卖出订单处理成功',
  112. 'transaction_id' => $transactionResult->id,
  113. 'fund_transfer' => $transferResult,
  114. 'item_consume' => $consumeResult,
  115. 'warehouse_add' => $addResult
  116. ];
  117. } catch (\Exception $e) {
  118. DB::rollBack();
  119. Log::error('Mex卖出订单处理失败', [
  120. 'user_id' => $userId,
  121. 'item_id' => $itemId,
  122. 'quantity' => $quantity,
  123. 'order_id' => $orderId,
  124. 'error' => $e->getMessage(),
  125. 'trace' => $e->getTraceAsString()
  126. ]);
  127. return ['success' => false, 'message' => '系统错误:' . $e->getMessage()];
  128. }
  129. }
  130. /**
  131. * 处理用户买入订单的账户流转
  132. * 用户买入:资金从用户转入仓库、物品从仓库转出到用户
  133. *
  134. * @param int $userId 用户ID
  135. * @param int $itemId 商品ID
  136. * @param int $quantity 数量
  137. * @param string $price 单价
  138. * @param string $totalAmount 总金额
  139. * @param int $orderId 订单ID
  140. * @param FUND_CURRENCY_TYPE|null $currencyType 币种类型,默认使用钻石
  141. * @return array 操作结果
  142. */
  143. public static function processBuyOrder(int $userId, int $itemId, int $quantity, string $price, string $totalAmount, int $orderId, ?FUND_CURRENCY_TYPE $currencyType = null): array
  144. {
  145. try {
  146. DB::beginTransaction();
  147. // 获取币种类型,默认使用钻石
  148. $currencyType = $currencyType ?? FundLogic::getDefaultCurrency();
  149. // 获取对应的账户类型
  150. $availableAccountType = FundLogic::getAvailableAccountType($currencyType);
  151. if (!$availableAccountType) {
  152. DB::rollBack();
  153. return ['success' => false, 'message' => '不支持的币种类型'];
  154. }
  155. // 1. 验证仓库是否有足够的物品
  156. if (!MexWarehouseLogic::checkStockSufficient($itemId, $quantity)) {
  157. DB::rollBack();
  158. return ['success' => false, 'message' => '仓库库存不足'];
  159. }
  160. // 2. 验证用户是否有足够的资金
  161. $userFundService = new FundService($userId, $availableAccountType->value);
  162. $precision = $currencyType->getPrecision();
  163. $fundAmount = (int)bcmul($totalAmount, bcpow('10', $precision), 0); // 根据币种精度转换
  164. if ($userFundService->balance() < $fundAmount) {
  165. DB::rollBack();
  166. return ['success' => false, 'message' => '用户资金不足'];
  167. }
  168. // 3. 从用户转出资金到仓库
  169. $transferResult = $userFundService->trade(
  170. self::WAREHOUSE_USER_ID,
  171. $fundAmount,
  172. 'mex_buy',
  173. $orderId,
  174. "农贸市场买入付款,订单ID:{$orderId}"
  175. );
  176. if (is_string($transferResult)) {
  177. DB::rollBack();
  178. return ['success' => false, 'message' => '资金转移失败:' . $transferResult];
  179. }
  180. // 4. 从仓库扣除物品
  181. $consumeResult = ItemService::consumeItem(self::WAREHOUSE_USER_ID, $itemId, null, $quantity, [
  182. 'reason' => 'mex_warehouse_sell',
  183. 'order_id' => $orderId,
  184. 'remark' => "农贸市场仓库出售物品,订单ID:{$orderId}"
  185. ]);
  186. if (!$consumeResult['success']) {
  187. DB::rollBack();
  188. return ['success' => false, 'message' => '仓库扣除物品失败:' . ($consumeResult['message'] ?? '未知错误')];
  189. }
  190. // 5. 给用户添加物品
  191. $addResult = ItemService::addItem($userId, $itemId, $quantity, [
  192. 'reason' => 'mex_buy',
  193. 'source_type' => REWARD_SOURCE_TYPE::MEX_BUY,
  194. 'source_id' => $orderId,
  195. 'order_id' => $orderId,
  196. 'remark' => "农贸市场买入物品,订单ID:{$orderId}"
  197. ]);
  198. if (!$addResult['success']) {
  199. DB::rollBack();
  200. return ['success' => false, 'message' => '用户添加物品失败:' . ($addResult['message'] ?? '未知错误')];
  201. }
  202. // 6. 更新仓库统计
  203. $warehouseResult = MexWarehouseLogic::reduceStock($itemId, $quantity, $totalAmount);
  204. if (!$warehouseResult) {
  205. DB::rollBack();
  206. return ['success' => false, 'message' => '更新仓库统计失败'];
  207. }
  208. // 7. 创建成交记录
  209. $transactionResult = MexTransactionLogic::createTransaction([
  210. 'buy_order_id' => $orderId,
  211. 'buyer_id' => $userId,
  212. 'seller_id' => self::WAREHOUSE_USER_ID,
  213. 'item_id' => $itemId,
  214. 'currency_type' => $currencyType->value,
  215. 'quantity' => $quantity,
  216. 'price' => $price,
  217. 'total_amount' => $totalAmount,
  218. 'transaction_type' => TransactionType::USER_BUY,
  219. 'is_admin_operation' => false,
  220. ]);
  221. if (!$transactionResult) {
  222. DB::rollBack();
  223. return ['success' => false, 'message' => '创建成交记录失败'];
  224. }
  225. DB::commit();
  226. return [
  227. 'success' => true,
  228. 'message' => '买入订单处理成功',
  229. 'transaction_id' => $transactionResult->id,
  230. 'fund_transfer' => $transferResult,
  231. 'item_consume' => $consumeResult,
  232. 'user_add' => $addResult
  233. ];
  234. } catch (\Exception $e) {
  235. DB::rollBack();
  236. Log::error('Mex买入订单处理失败', [
  237. 'user_id' => $userId,
  238. 'item_id' => $itemId,
  239. 'quantity' => $quantity,
  240. 'order_id' => $orderId,
  241. 'error' => $e->getMessage(),
  242. 'trace' => $e->getTraceAsString()
  243. ]);
  244. return ['success' => false, 'message' => '系统错误:' . $e->getMessage()];
  245. }
  246. }
  247. /**
  248. * 检查仓库账户资金余额
  249. *
  250. * @param FUND_CURRENCY_TYPE|null $currencyType 币种类型,默认使用钻石
  251. * @return int 余额(按币种精度存储的整数)
  252. */
  253. public static function getWarehouseFundBalance(?FUND_CURRENCY_TYPE $currencyType = null): int
  254. {
  255. $currencyType = $currencyType ?? FundLogic::getDefaultCurrency();
  256. $availableAccountType = FundLogic::getAvailableAccountType($currencyType);
  257. if (!$availableAccountType) {
  258. return 0;
  259. }
  260. $warehouseFundService = new FundService(self::WAREHOUSE_USER_ID, $availableAccountType->value);
  261. return $warehouseFundService->balance();
  262. }
  263. /**
  264. * 检查调控账户资金余额
  265. *
  266. * @param FUND_CURRENCY_TYPE|null $currencyType 币种类型,默认使用钻石
  267. * @return int 余额(按币种精度存储的整数)
  268. */
  269. public static function getRegulationFundBalance(?FUND_CURRENCY_TYPE $currencyType = null): int
  270. {
  271. $currencyType = $currencyType ?? FundLogic::getDefaultCurrency();
  272. $availableAccountType = FundLogic::getAvailableAccountType($currencyType);
  273. if (!$availableAccountType) {
  274. return 0;
  275. }
  276. $regulationFundService = new FundService(self::REGULATION_USER_ID, $availableAccountType->value);
  277. return $regulationFundService->balance();
  278. }
  279. }