You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

200 lines
6.7 KiB

5 years ago
5 years ago
5 years ago
  1. <?php
  2. namespace App\Controller\v3;
  3. use App\Commons\Log;
  4. use App\Constants\v3\ErrorCode;
  5. use App\Constants\v3\LogLabel;
  6. use App\Constants\v3\UserType;
  7. use App\Controller\BaseController;
  8. use App\Exception\ErrorCodeException;
  9. use App\Model\v3\FinancialRecord;
  10. use App\Model\v3\Store;
  11. use App\Model\v3\StoreWithdrawal;
  12. use App\Model\v3\User;
  13. use App\Model\v3\UserBalance;
  14. use App\Service\v3\Interfaces\FinancialRecordServiceInterface;
  15. use App\Service\v3\Interfaces\PaymentServiceInterface;
  16. use Hyperf\DbConnection\Db;
  17. use Hyperf\Di\Annotation\Inject;
  18. use Hyperf\Snowflake\IdGeneratorInterface;
  19. use Hyperf\Utils\ApplicationContext;
  20. use Hyperf\Validation\ValidationException;
  21. class WithdrawController extends BaseController
  22. {
  23. /**
  24. * @Inject
  25. * @var Log
  26. */
  27. protected $log;
  28. /**
  29. * @Inject
  30. * @var FinancialRecordServiceInterface
  31. */
  32. protected $financialService;
  33. /**
  34. * @Inject
  35. * @var PaymentServiceInterface
  36. */
  37. protected $paymentService;
  38. /**
  39. * 商户提现页数据
  40. * 1、可提现金额
  41. * 2、获得的奖励金额(历史总奖励金额)
  42. * 3、用户须知
  43. * 4、提现须知
  44. */
  45. public function pageByStore()
  46. {
  47. $validator = $this->validationFactory->make(
  48. $this->request->all(),
  49. [
  50. 'user_id' => 'required|nonempty|integer',
  51. 'store_id' => 'required|nonempty|integer',
  52. ],
  53. ['*.*' => ':attribute 参数不正确']
  54. );
  55. if ($validator->fails()) {
  56. throw new ValidationException($validator);
  57. }
  58. $userId = $this->request->input('user_id');
  59. $storeId = $this->request->input('store_id');
  60. $store = Store::query()->where(['user_id' => $userId, 'id' => $storeId])->first();
  61. if (empty($store)) {
  62. throw new ErrorCodeException(ErrorCode::STORE_NOT_AVAILABLE);
  63. }
  64. $data['balance'] = UserBalance::query()
  65. ->where(['source_id' => $store->user_id, 'user_type' => UserType::STORE])
  66. ->value('balance');
  67. $data['award'] = $this->financialService->sumAmount(
  68. $store->user_id,
  69. UserType::STORE,
  70. [FinancialRecord::MONEY_TYPE_STORE_PLAT_NEW_USER, FinancialRecord::MONEY_TYPE_STORE_FIRST_ORDER]
  71. );
  72. $data['notice'] = [
  73. [
  74. 'title' => '用户须知',
  75. 'content' => '<p>请仔细填写相关信息,如果由于您填写的信息有误导致资金流失,平台概不负责。</p><p>注意:因微信限制当前用户账户余额满<span style="color:red;">1</span>元后才可提现</p>'
  76. ],
  77. [
  78. 'title' => '提现须知',
  79. 'content' => '<p>一次提现金额最低为<span style="color:red;">1</span>元</p>'
  80. .'<p>一次提现金额最高为<span style="color:red;">5000</span>元</p>'
  81. .'<p>每天可提现<span style="color:red;">10</span>次</p>'
  82. ],
  83. ];
  84. return $this->success($data);
  85. }
  86. /**
  87. * 商户提现申请
  88. * 1、商户提现参数校验
  89. * 2、商户提现是否超过可提现金额
  90. * 3、提现是否秒到账,秒到账则打款并且做流水,否则就打到后台审核
  91. */
  92. public function applyByStore()
  93. {
  94. $isDirect = config('wechat.withdrawal.is_direct');
  95. $min = config('wechat.withdrawal.min_amount');
  96. $max = config('wechat.withdrawal.max_amount');
  97. $validator = $this->validationFactory->make(
  98. $this->request->all(),
  99. [
  100. 'user_id' => 'required|nonempty|integer',
  101. 'store_id' => 'required|nonempty|integer',
  102. 'money' => "required|nonempty|numeric|between:{$min}, {$max}",
  103. ]
  104. );
  105. if ($validator->fails()) {
  106. throw new ValidationException($validator);
  107. }
  108. $money = $this->request->input('money');
  109. $userId = $this->request->input('user_id');
  110. $storeId = $this->request->input('store_id');
  111. $store = Store::query()->where(['user_id' => $userId, 'id' => $storeId])->first();
  112. if (empty($store)) {
  113. throw new ErrorCodeException(ErrorCode::STORE_NOT_AVAILABLE);
  114. }
  115. // 校验余额
  116. $balance = UserBalance::query()
  117. ->where(['source_id' => $store->user_id, 'user_type' => UserType::STORE])
  118. ->first();
  119. if ($money > $balance->balance) {
  120. throw new ErrorCodeException(ErrorCode::STORE_WITHDRAW_INSUFFICIENT_BALANCE);
  121. }
  122. Db::beginTransaction();
  123. try {
  124. $storeUser = User::query()->find($store->user_id);
  125. $currentTime = time();
  126. // 获取分布式全局ID
  127. $generator = ApplicationContext::getContainer()->get(IdGeneratorInterface::class);
  128. $globalOrderId = $generator->generate();
  129. // 记录提现申请记录
  130. $withdraw = new StoreWithdrawal();
  131. $withdraw->trade_no = $globalOrderId;
  132. $withdraw->store_id = $store->id;
  133. $withdraw->name = $storeUser->real_name ?? $storeUser->nick_name;
  134. $withdraw->tel = $storeUser->tel;
  135. $withdraw->apply_cash = $money;
  136. $withdraw->save();
  137. // 先扣除余额
  138. $balance->balance = bcsub($balance->balance, $money, 2);
  139. $balance->save();
  140. // 如果秒到账无需审核的话
  141. if ($isDirect) {
  142. // 打款
  143. $res = $this->paymentService->payToWx(
  144. bcmul($withdraw->apply_cash, 100 , 0),
  145. $withdraw->trade_no,
  146. $storeUser->openid,
  147. $withdraw->name,
  148. '商户提现打款'
  149. );
  150. // 更新打款金额,审核时间等
  151. $withdraw->check_time = time();
  152. $withdraw->real_cash = $money;
  153. $withdraw->state = 2;
  154. $withdraw->save();
  155. // 打款成功,写流水
  156. if ($res === true) {
  157. $this->financialService->storeWithdrawByWx($store->user_id, 0, $withdraw->real_cash);
  158. }
  159. }
  160. Db::commit();
  161. return $this->success([]);
  162. } catch (\Exception $e) {
  163. Db::rollBack();
  164. $this->log->event(LogLabel::STORE_WITHDRAW_FAIL_LOG, [
  165. 'exception' => $e->getMessage(),
  166. 'withdraw' => json_encode($withdraw),
  167. 'params' => json_encode(['balance' => $balance->balance, 'user_id' => $userId, 'store_id' => $storeId]),
  168. ]);
  169. throw new ErrorCodeException(ErrorCode::STORE_WITHDRAW_FAIL, $e->getMessage());
  170. }
  171. }
  172. }