AdvancedPaymentService.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <?php
  2. namespace App\Modules\Finance\Services;
  3. use App\Libs\Pay\SandPay;
  4. use App\Modules\Channel\Models\Channel;
  5. use App\Modules\Finance\Models\BatchPayments;
  6. use App\Modules\Finance\Models\FinancialConfig;
  7. use App\Modules\Finance\Models\FinancialStat;
  8. use App\Modules\Finance\Models\Payment;
  9. use App\Modules\Finance\Models\WithdrawCash;
  10. use App\Modules\Manage\Models\Manage;
  11. use Exception;
  12. use Illuminate\Support\Collection;
  13. use Illuminate\Support\Facades\DB;
  14. use Illuminate\Support\Facades\Log;
  15. use Redis;
  16. /**
  17. * 更优的打款结构
  18. */
  19. class AdvancedPaymentService
  20. {
  21. const wait_merge = '待合并';
  22. const wait_pay = '待打款';
  23. const auto_wait_pay = '自动待打款';
  24. const artifical_wait_pay = '人工待打款';
  25. const wait_pay_status = [
  26. self::wait_pay,
  27. self::auto_wait_pay,
  28. self::artifical_wait_pay,
  29. ];
  30. const artifical_success = '人工打款成功';
  31. const auto_success = '自动打款成功';
  32. const success = "打款成功";
  33. const success_status = [
  34. self::success,
  35. self::auto_success,
  36. self::artifical_success,
  37. ];
  38. const paying = "打款中";
  39. const auto_paying = "自动打款中";
  40. const artifical_paying = "人工打款中";
  41. const paying_status = [
  42. self::paying,
  43. self::auto_paying,
  44. self::artifical_paying,
  45. ];
  46. const auto_failure = '自动打款失败';
  47. public function findConfig(int $channel_id)
  48. {
  49. FinancialConfig::where('distribution_channel_id', $channel_id)->first();
  50. }
  51. public function findFinancialStat(int $channel_id)
  52. {
  53. return FinancialStat::where('distribution_channel_id', $channel_id)->first();
  54. }
  55. public function findWithdrawCash(int $withdraw_cash_id)
  56. {
  57. return WithdrawCash::where('id', $withdraw_cash_id)->first();
  58. }
  59. public function isWithdrawCashSuccess(string $status)
  60. {
  61. if (in_array($status, self::success_status)) {
  62. return true;
  63. }
  64. return false;
  65. }
  66. /**
  67. * 添加人工打款记录
  68. */
  69. private function addPayment(WithdrawCash $withdraw, string $trade_no, int $uid, string $remark)
  70. {
  71. $isCompanyName = $withdraw->is_company == 0 ? "对私" : "对公";
  72. $remark = "[" . $isCompanyName . "]" . $remark;
  73. Payment::create(
  74. [
  75. 'withdraw_cash_id' => $withdraw->id,
  76. 'amount' => (float) $withdraw->amount - (float) $withdraw->tallage,
  77. 'remark' => $remark,
  78. 'pay_time' => date('Y-m-d H:i:s'),
  79. 'pay_merchant_source' => "人工",
  80. 'trade_no' => $trade_no,
  81. 'status' => self::artifical_success,
  82. 'pay_merchant_source_msg' => '',
  83. 'is_company' => $withdraw->is_company,
  84. 'amount_person' => $uid,
  85. ]
  86. );
  87. }
  88. /**
  89. * 查找操作账号
  90. */
  91. private function findManageUser(int $id)
  92. {
  93. return Manage::find($id);
  94. }
  95. /**
  96. * 更新提现信息状态
  97. * @param WithdrawCash $withdraw
  98. * @param string $status
  99. * @param int $uid
  100. * @param string $serial_number
  101. * @param string $remark
  102. * @return WithdrawCash
  103. */
  104. public function updateWithdrawCashStatus(WithdrawCash $withdraw, string $status, int $uid = 0, string $serial_number = '', string $remark = '')
  105. {
  106. if (in_array($withdraw->status, self::success_status)) {
  107. return;
  108. }
  109. $withdraw->status = $status;
  110. if ($uid) {
  111. $user = $this->findManageUser($uid);
  112. $withdraw->check_user_id = $uid;
  113. $withdraw->check_user_name = $user ? $user->nickname : '';
  114. }
  115. if ($remark) {
  116. $withdraw->remark = $remark;
  117. }
  118. if ($serial_number) {
  119. if (!strpos($withdraw->serial_number, $serial_number)) {
  120. $serial_number = $withdraw->serial_number . "," . $serial_number;
  121. }
  122. $withdraw->serial_number = $serial_number;
  123. }
  124. $withdraw->save();
  125. if (in_array($status, self::success_status)) {
  126. //成功提现
  127. $financialStat = $this->findFinancialStat($withdraw->distribution_channel_id);
  128. //修改累计提现金额
  129. $financialStat->accumulative_withdrawal_amount = (float) $financialStat->accumulative_withdrawal_amount + (float) $withdraw->amount;
  130. //修改提现中金额
  131. $financialStat->withdraw_pending_amount = (float) $financialStat->withdraw_pending_amount - (float) $withdraw->amount;
  132. $financialStat->save();
  133. }
  134. return $withdraw;
  135. }
  136. /**
  137. * 创建人工打款
  138. * @param $userId
  139. * @param $withdrawCashId
  140. * @param $amount
  141. * @param $remark
  142. * @param $tradeNo
  143. * @param int $amount_person
  144. * @return bool
  145. */
  146. public function makePersonMadePayment($userId, $withdrawCashId, $remark, $trade_no, $amount_person = 0)
  147. {
  148. try {
  149. DB::beginTransaction();
  150. $withdraw = $this->findWithdrawCash($withdrawCashId);
  151. //插入人工打款记录
  152. $this->addPayment($withdraw, $trade_no, $amount_person, $remark);
  153. //更新当然提现记录为 人工打款成功状态
  154. $this->updateWithdrawCashStatus($withdraw, self::artifical_success, $userId, $trade_no, $remark);
  155. DB::commit();
  156. } catch (Exception $e) {
  157. Log::error("人工打款失败日志" . $e->getTraceAsString());
  158. DB::rollback();
  159. return false;
  160. }
  161. return true;
  162. }
  163. /**
  164. * 对私打款改为对公打款
  165. */
  166. public function changePrivateToPublicPayment(int $uid, int $batch_payment_id, string $trade_no, string $account_name, string $card_number, string $account_bank)
  167. {
  168. $withdraws = WithdrawCash::where('batch_payment_id', $batch_payment_id)->get();
  169. DB::beginTransaction();
  170. foreach ($withdraws as $withdraw) {
  171. $withdraw->account_name = $account_name;
  172. $withdraw->bank_account = $card_number;
  173. $withdraw->account_bank = $account_bank;
  174. $withdraw->batch_payment_id = 0;
  175. $withdraw->status = '人工待打款';
  176. $withdraw->is_company = 1;
  177. $withdraw->is_batch_operate = 0;
  178. $withdraw->save();
  179. if (!$this->makePersonMadePayment($uid, $withdraw->id, "[对私转对公]", $trade_no)) {
  180. DB::rollback();
  181. return;
  182. }
  183. }
  184. BatchPayments::where('id', $batch_payment_id)->delete();
  185. DB::commit();
  186. }
  187. /**
  188. * 更新合并打款对应的提现状态
  189. */
  190. private function updateBatchPaymentWithdrawCashStatus(int $batch_payment_id, string $status, int $uid = 0, string $serial_number = '', string $remark = '')
  191. {
  192. $withdraws = WithdrawCash::where('batch_payment_id', $batch_payment_id)->get();
  193. foreach ($withdraws as $withdraw) {
  194. $this->updateWithdrawCashStatus($withdraw, $status, $uid, $serial_number, $remark);
  195. if (in_array($status, self::success_status)) {
  196. Payment::create([
  197. 'withdraw_cash_id' => $withdraw->id,
  198. 'amount' => $withdraw->amount,
  199. 'remark' => '合并打款',
  200. 'pay_time' => date('Y-m-d H:i:s'),
  201. 'pay_merchant_source' => 'SANDPAY',
  202. 'trade_no' => $serial_number,
  203. 'status' => self::auto_success,
  204. 'is_company' => $withdraw->is_company
  205. ]);
  206. }
  207. }
  208. }
  209. /**
  210. * 更新合并打款状态
  211. */
  212. public function updateBatchPaymentStatus(BatchPayments $payment, string $status, int $uid = 0, array $query_result = [], string $serial_number = '')
  213. {
  214. try {
  215. DB::beginTransaction();
  216. $payment->serial_number = $serial_number ? $serial_number : ($payment->serial_number ?? '');
  217. $payment->status = $status;
  218. $payment->pay_merchant_source_msg = $query_result ? json_encode($query_result) : '';
  219. $payment->pay_merchant_source_result = in_array($status, self::success_status) ? '处理成功' : '';
  220. $payment->save();
  221. $this->updateBatchPaymentWithdrawCashStatus($payment->id, $status, $uid, $payment->serial_number);
  222. DB::commit();
  223. } catch (Exception $e) {
  224. myLog('test')->info($e->getTraceAsString());
  225. DB::rollback();
  226. }
  227. }
  228. /**
  229. * 查找自动待打款的批量提现记录
  230. */
  231. public function findPayingBatchPayments()
  232. {
  233. return BatchPayments::where('status', self::auto_paying)->get();
  234. }
  235. public function findBatchPayment(int $batch_payment_id)
  236. {
  237. return BatchPayments::find($batch_payment_id);
  238. }
  239. /**
  240. * 设置打款的redis缓存
  241. */
  242. private function setRedis(string $redis_key, int $id)
  243. {
  244. Redis::sAdd($redis_key, $id);
  245. Redis::expire($redis_key, 60 * 60 * 24);
  246. }
  247. /**
  248. * 判断是否在合并打款中
  249. */
  250. public function judgeIsPayment($id)
  251. {
  252. $key = 'batch_payment:' . date('Y-m-d');
  253. if (Redis::sIsmember($key, $id)) {
  254. return true;
  255. } else {
  256. $result = BatchPayments::where('id', $id)->whereIn('status', self::paying_status)->exists();
  257. if ($result) {
  258. $this->setRedis($key, $id);
  259. }
  260. return $result;
  261. }
  262. }
  263. /**
  264. * 合并打款
  265. * @param $uid
  266. * @param $batch_payment_id
  267. * @param $amount
  268. * @param $remark
  269. * @return bool
  270. */
  271. public function makeThreeSourcePayment(BatchPayments $batch_payment, int $uid, float $amount, string $remark)
  272. {
  273. $trade_no = date('YmdHis') . ($batch_payment->channel_user_id);
  274. $pay_service = new SandPay;
  275. //对私
  276. $balance = $pay_service->queryBalance([
  277. 'order_no' => date('YmdHis'),
  278. 'pay_time' => $batch_payment->pay_time,
  279. ]);
  280. if ($balance > 0 && $balance >= $amount) {
  281. try {
  282. DB::beginTransaction();
  283. $this->setRedis('batch_payment:' . date('Y-m-d'), $batch_payment->id);
  284. //修改状态打款中
  285. $batch_payment->trade_no = $trade_no;
  286. $batch_payment->status = self::auto_paying;
  287. $batch_payment->pay_time = now();
  288. $batch_payment->save();
  289. $this->updateBatchPaymentWithdrawCashStatus($batch_payment->id, self::auto_paying, $uid, '', $remark);
  290. $result = $pay_service->agentPay([
  291. 'order_no' => $trade_no,
  292. 'pay_time' => (string) $batch_payment->pay_time,
  293. 'account_name' => $batch_payment->account_name,
  294. 'bank_account' => $batch_payment->bank_account,
  295. 'account_bank' => $batch_payment->account_bank,
  296. 'remark' => $remark,
  297. 'amount' => $amount,
  298. ]);
  299. $batch_payment->pay_merchant_source_msg = json_encode($result);
  300. $batch_payment->save();
  301. DB::commit();
  302. return true;
  303. } catch (Exception $e) {
  304. Log::error($e->getTraceAsString());
  305. DB::rollback();
  306. }
  307. }
  308. }
  309. /**
  310. * 添加合并打款数据
  311. */
  312. private function addBatchPayment(string $status, float $amount, int $is_company, WithdrawCash $model, array $channel_ids, array $ids)
  313. {
  314. $payment = [
  315. 'amount' => $amount,
  316. 'status' => self::wait_pay,
  317. 'is_company' => $is_company,
  318. 'channel_user_id' => $model->channel_user_id,
  319. 'bank_account' => $model->bank_account,
  320. 'account_name' => $model->account_name,
  321. 'account_bank' => $model->account_bank,
  322. 'remark' => "channel_ids:" . (implode(',', $channel_ids)) . ';withdraw_cash_ids:' . implode(',', $ids)
  323. ];
  324. $added = BatchPayments::create($payment);
  325. WithdrawCash::whereIn('id', $ids)->update([
  326. 'batch_payment_id' => $added->id,
  327. 'status' => $status
  328. ]);
  329. }
  330. /**
  331. * 查找待合并提现记录
  332. * @return Collection
  333. */
  334. private function findWaitMergeWithdrawCash(int $is_company)
  335. {
  336. $withdraws = WithdrawCash::where('distribution_channel_id', '>', 0)
  337. ->where('is_batch_operate', 1)
  338. ->where('is_company', $is_company)
  339. ->where('status', self::wait_merge)
  340. ->where(function ($query) {
  341. $query->whereNull('batch_payment_id')->orwhere('batch_payment_id', 0);
  342. })
  343. ->get();
  344. $channel_ids = $withdraws->pluck('distribution_channel_id')->all();
  345. $channels = Channel::whereIn('id', $channel_ids)->select('id', 'channel_user_id')->get();
  346. $withdraws->transform(function ($item) use ($channels) {
  347. $channel = $channels->where('id', $item->distribution_channel_id)->first();
  348. $item->channel_user_id = $channel ? $channel->channel_user_id : 0;
  349. return $item;
  350. });
  351. return $withdraws->sortByDesc('amount')
  352. ->sortBy('bank_account')
  353. ->sortBy('channel_user_id')
  354. ->sortBy('pay_merchant_company_id')
  355. ->values();
  356. }
  357. public function mergePayments(int $is_company)
  358. {
  359. //获取排好序的待合并数据
  360. $withdrawCashes = $this->findWaitMergeWithdrawCash($is_company);
  361. $amount = 0;
  362. $temp = $withdrawCashes->first();
  363. $channel_ids = [];
  364. $ids = [];
  365. if (count($withdrawCashes) > 0) {
  366. try {
  367. DB::beginTransaction();
  368. foreach ($withdrawCashes as $withdrawCash) {
  369. if (
  370. $temp->channel_user_id == $withdrawCash->channel_user_id
  371. && $temp->bank_account == $withdrawCash->bank_account
  372. && $temp->pay_merchant_company_id == $withdrawCash->pay_merchant_company_id
  373. && $amount + $withdrawCash->amount < 50000
  374. ) {
  375. $amount += $withdrawCash->amount;
  376. $ids[] = $withdrawCash->id;
  377. if (!in_array($withdrawCash->distribution_channel_id, $channel_ids))
  378. $channel_ids[] = $withdrawCash->distribution_channel_id;
  379. } else {
  380. $this->addBatchPayment(self::auto_wait_pay, $amount, $is_company, $temp, $channel_ids, $ids);
  381. $amount = $withdrawCash->amount;
  382. $ids = [];
  383. $channel_ids = [];
  384. $ids[] = $withdrawCash->id;
  385. $channel_ids[] = $withdrawCash->distribution_channel_id;
  386. $temp = $withdrawCash;
  387. }
  388. }
  389. if ($amount > 0) {
  390. $this->addBatchPayment(self::auto_wait_pay, $amount, $is_company, $temp, $channel_ids, $ids);
  391. }
  392. DB::commit();
  393. } catch (Exception $e) {
  394. myLog('add_batch_payment')->error($e->getMessage());
  395. DB::rollBack();
  396. }
  397. }
  398. }
  399. }