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.

227 lines
8.5 KiB

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