UserRechargeServices.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\user;
  13. use app\services\BaseServices;
  14. use app\dao\user\UserRechargeDao;
  15. use app\services\pay\RechargeServices;
  16. use app\services\system\config\SystemGroupDataServices;
  17. use app\services\wechat\WechatUserServices;
  18. use crmeb\exceptions\AdminException;
  19. use app\jobs\RoutineTemplateJob;
  20. use app\jobs\WechatTemplateJob as TemplateJob;
  21. use crmeb\services\{
  22. FormBuilder as Form, MiniProgramService, WechatService
  23. };
  24. use think\exception\ValidateException;
  25. use think\facade\Route as Url;
  26. /**
  27. *
  28. * Class UserRechargeServices
  29. * @package app\services\user
  30. * @method be($map, string $field = '') 查询一条数据是否存在
  31. * @method getDistinctCount(array $where, $field, ?bool $search = true)
  32. * @method getTrendData($time, $type, $timeType)
  33. */
  34. class UserRechargeServices extends BaseServices
  35. {
  36. /**
  37. * UserRechargeServices constructor.
  38. * @param UserRechargeDao $dao
  39. */
  40. public function __construct(UserRechargeDao $dao)
  41. {
  42. $this->dao = $dao;
  43. }
  44. /**
  45. * 获取单条数据
  46. * @param int $id
  47. * @param array $field
  48. */
  49. public function getRecharge(int $id, array $field = [])
  50. {
  51. return $this->dao->get($id, $field);
  52. }
  53. /**
  54. * 获取统计数据
  55. * @param array $where
  56. * @param string $field
  57. * @return float
  58. */
  59. public function getRechargeSum(array $where, string $field = '')
  60. {
  61. $whereData = [];
  62. if (isset($where['data'])) {
  63. $whereData['time'] = $where['data'];
  64. }
  65. if (isset($where['paid']) && $where['paid'] != '') {
  66. $whereData['paid'] = $where['paid'];
  67. }
  68. if (isset($where['nickname']) && $where['nickname']) {
  69. $whereData['like'] = $where['nickname'];
  70. }
  71. if (isset($where['recharge_type']) && $where['recharge_type']) {
  72. $whereData['recharge_type'] = $where['recharge_type'];
  73. }
  74. return $this->dao->getWhereSumField($whereData, $field);
  75. }
  76. /**
  77. * 获取充值列表
  78. * @param array $where
  79. * @param string $field
  80. * @return array
  81. */
  82. public function getRechargeList(array $where, string $field = '*', $is_page = true)
  83. {
  84. $whereData = [];
  85. if (isset($where['data'])) {
  86. $whereData['time'] = $where['data'];
  87. }
  88. if (isset($where['paid']) && $where['paid'] != '') {
  89. $whereData['paid'] = $where['paid'];
  90. }
  91. if (isset($where['nickname']) && $where['nickname']) {
  92. $whereData['like'] = $where['nickname'];
  93. }
  94. [$page, $limit] = $this->getPageValue($is_page);
  95. $list = $this->dao->getList($whereData, $field, $page, $limit);
  96. $count = $this->dao->count($whereData);
  97. foreach ($list as &$item) {
  98. switch ($item['recharge_type']) {
  99. case 'routine':
  100. $item['_recharge_type'] = '小程序充值';
  101. break;
  102. case 'weixin':
  103. $item['_recharge_type'] = '公众号充值';
  104. break;
  105. default:
  106. $item['_recharge_type'] = '其他充值';
  107. break;
  108. }
  109. $item['_pay_time'] = $item['pay_time'] ? date('Y-m-d H:i:s', $item['pay_time']) : '暂无';
  110. $item['_add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', $item['add_time']) : '暂无';
  111. $item['paid_type'] = $item['paid'] ? '已支付' : '未支付';
  112. unset($item['user']);
  113. }
  114. return compact('list', 'count');
  115. }
  116. /**
  117. * 获取用户充值数据
  118. * @return array
  119. */
  120. public function user_recharge(array $where)
  121. {
  122. $data = [];
  123. $data['sumPrice'] = $this->getRechargeSum($where, 'price');
  124. $data['sumRefundPrice'] = $this->getRechargeSum($where, 'refund_price');
  125. $where['recharge_type'] = 'routine';
  126. $data['sumRoutinePrice'] = $this->getRechargeSum($where, 'price');
  127. $where['recharge_type'] = 'weixin';
  128. $data['sumWeixinPrice'] = $this->getRechargeSum($where, 'price');
  129. return [
  130. [
  131. 'name' => '充值总金额',
  132. 'field' => '元',
  133. 'count' => $data['sumPrice'],
  134. 'className' => 'logo-yen',
  135. 'col' => 6,
  136. ],
  137. [
  138. 'name' => '充值退款金额',
  139. 'field' => '元',
  140. 'count' => $data['sumRefundPrice'],
  141. 'className' => 'logo-usd',
  142. 'col' => 6,
  143. ],
  144. [
  145. 'name' => '小程序充值金额',
  146. 'field' => '元',
  147. 'count' => $data['sumRoutinePrice'],
  148. 'className' => 'logo-bitcoin',
  149. 'col' => 6,
  150. ],
  151. [
  152. 'name' => '公众号充值金额',
  153. 'field' => '元',
  154. 'count' => $data['sumWeixinPrice'],
  155. 'className' => 'ios-bicycle',
  156. 'col' => 6,
  157. ],
  158. ];
  159. }
  160. /**退款表单
  161. * @param $id
  162. * @return mixed|void
  163. */
  164. public function refund_edit(int $id)
  165. {
  166. $UserRecharge = $this->getRecharge($id);
  167. if (!$UserRecharge) {
  168. throw new AdminException('数据不存在!');
  169. }
  170. if ($UserRecharge['paid'] != 1) {
  171. throw new AdminException('订单未支付');
  172. }
  173. if ($UserRecharge['price'] == $UserRecharge['refund_price']) {
  174. throw new AdminException('已退完支付金额!不能再退款了');
  175. }
  176. if ($UserRecharge['recharge_type'] == 'balance') {
  177. throw new AdminException('佣金转入余额,不能退款');
  178. }
  179. $f = array();
  180. $f[] = Form::input('order_id', '退款单号', $UserRecharge->getData('order_id'))->disabled(true);
  181. $f[] = Form::radio('refund_price', '状态', 1)->options([['label' => '本金(扣赠送余额)', 'value' => 1], ['label' => '仅本金', 'value' => 0]]);
  182. // $f[] = Form::number('refund_price', '退款金额', (float)$UserRecharge->getData('price'))->precision(2)->min(0)->max($UserRecharge->getData('price'));
  183. return create_form('编辑', $f, Url::buildUrl('/finance/recharge/' . $id), 'PUT');
  184. }
  185. /**
  186. * 退款操作
  187. * @param int $id
  188. * @param $refund_price
  189. * @return mixed
  190. */
  191. public function refund_update(int $id, $refund_price)
  192. {
  193. $UserRecharge = $this->getRecharge($id);
  194. if (!$UserRecharge) {
  195. throw new AdminException('数据不存在!');
  196. }
  197. if ($UserRecharge['price'] == $UserRecharge['refund_price']) {
  198. throw new AdminException('已退完支付金额!不能再退款了');
  199. }
  200. if ($UserRecharge['recharge_type'] == 'balance') {
  201. throw new AdminException('佣金转入余额,不能退款');
  202. }
  203. // $data['refund_price'] = bcadd($refund_price, $UserRecharge['refund_price'], 2);
  204. $data['refund_price'] = $UserRecharge['price'];
  205. // $bj = bccomp((string)$UserRecharge['price'], (string)$data['refund_price'], 2);
  206. // if ($bj < 0) {
  207. // throw new AdminException('退款金额大于支付金额,请修改退款金额');
  208. // }
  209. $refund_data['pay_price'] = $UserRecharge['price'];
  210. $refund_data['refund_price'] = $UserRecharge['price'];
  211. // $refund_data['refund_account']='REFUND_SOURCE_RECHARGE_FUNDS';
  212. if ($refund_price == 1) {
  213. $number = bcadd($UserRecharge['price'], $UserRecharge['give_price'], 2);
  214. } else {
  215. $number = $UserRecharge['price'];
  216. }
  217. try {
  218. $recharge_type = $UserRecharge['recharge_type'];
  219. if ($recharge_type == 'weixin') {
  220. WechatService::payOrderRefund($UserRecharge['order_id'], $refund_data);
  221. } else {
  222. MiniProgramService::payOrderRefund($UserRecharge['order_id'], $refund_data);
  223. }
  224. } catch (\Exception $e) {
  225. throw new AdminException($e->getMessage());
  226. }
  227. if (!$this->dao->update($id, $data)) {
  228. throw new AdminException('修改提现数据失败');
  229. }
  230. /** @var UserServices $userServices */
  231. $userServices = app()->make(UserServices::class);
  232. $userInfo = $userServices->getUserInfo($UserRecharge['uid']);
  233. $userServices->cutNowMoney($UserRecharge['uid'], $userInfo['now_money'], $number);
  234. /** @var WechatUserServices $wechatServices */
  235. $wechatServices = app()->make(WechatUserServices::class);
  236. switch (strtolower($UserRecharge['recharge_type'])) {
  237. case 'weixin':
  238. $openid = $wechatServices->uidToOpenid($UserRecharge['uid'], 'wechat');
  239. TemplateJob::dispatchDo('sendRechargeRefundStatus', [$openid, $data, $UserRecharge]);
  240. break;
  241. case 'routine':
  242. // $openid = $wechatServices->uidToOpenid($UserRecharge['uid'], 'routine');
  243. // Queue::instance()->do('sendOrderRefundSuccess')->job(RoutineTemplateJob::class)->data($openid, $UserRecharge, $refund_price)->push();
  244. break;
  245. }
  246. $billData = ['title' => '系统退款', 'number' => $number, 'link_id' => $id, 'balance' => $userInfo['now_money'], 'mark' => '退款给用户' . $UserRecharge['price'] . '元'];
  247. /** @var UserBillServices $userBillService */
  248. $userBillService = app()->make(UserBillServices::class);
  249. $userBillService->expendNowMoney($UserRecharge['uid'], 'user_recharge_refund', $billData);
  250. return true;
  251. }
  252. /**
  253. * 删除
  254. * @param int $id
  255. * @return bool
  256. */
  257. public function delRecharge(int $id)
  258. {
  259. $rechargInfo = $this->getRecharge($id);
  260. if (!$rechargInfo) throw new AdminException('订单未找到');
  261. if ($rechargInfo->paid) {
  262. throw new AdminException('已支付的订单记录无法删除');
  263. }
  264. if ($this->dao->delete($id))
  265. return true;
  266. else
  267. throw new AdminException('删除失败');
  268. }
  269. /**
  270. * 生成充值订单号
  271. * @return bool|string
  272. */
  273. public function getOrderId()
  274. {
  275. return 'wx' . date('YmdHis', time()) . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
  276. }
  277. /**
  278. * 导入佣金到余额
  279. * @param int $uid
  280. * @param $price
  281. * @return bool
  282. */
  283. public function importNowMoney(int $uid, $price)
  284. {
  285. /** @var UserServices $userServices */
  286. $userServices = app()->make(UserServices::class);
  287. $user = $userServices->getUserInfo($uid);
  288. if (!$user) {
  289. throw new ValidateException('数据不存在');
  290. }
  291. /** @var UserBrokerageFrozenServices $frozenPrices */
  292. $frozenPrices = app()->make(UserBrokerageFrozenServices::class);
  293. $broken_commission = array_bc_sum($frozenPrices->getUserFrozenPrice($uid));
  294. $commissionCount = bcsub($user['brokerage_price'], $broken_commission, 2);
  295. if ($price > $commissionCount) {
  296. throw new ValidateException('转入金额不能大于可提现佣金!');
  297. }
  298. $edit_data = [];
  299. $edit_data['now_money'] = bcadd((string)$user['now_money'], (string)$price, 2);
  300. $edit_data['brokerage_price'] = $user['brokerage_price'] > $price ? bcsub((string)$user['brokerage_price'], (string)$price, 2) : 0;
  301. if (!$userServices->update($uid, $edit_data, 'uid')) {
  302. throw new ValidateException('修改用户信息失败');
  303. }
  304. //写入充值记录
  305. $rechargeInfo = [
  306. 'uid' => $uid,
  307. 'order_id' => $this->getOrderId(),
  308. 'recharge_type' => 'balance',
  309. 'price' => $price,
  310. 'give_price' => 0,
  311. 'paid' => 1,
  312. 'pay_time' => time(),
  313. 'add_time' => time()
  314. ];
  315. if (!$re = $this->dao->save($rechargeInfo)) {
  316. throw new ValidateException('写入余额充值失败');
  317. }
  318. $bill_data = ['title' => '用户佣金转入余额', 'link_id' => $re['id'], 'number' => $price, 'balance' => $user['now_money'], 'mark' => '成功转入余额' . floatval($price) . '元'];
  319. /** @var UserBillServices $userBill */
  320. $userBill = app()->make(UserBillServices::class);
  321. //余额充值
  322. $userBill->incomeNowMoney($uid, 'recharge', $bill_data);
  323. //写入提现记录
  324. $extractInfo = [
  325. 'uid' => $uid,
  326. 'real_name' => $user['nickname'],
  327. 'extract_type' => 'balance',
  328. 'extract_price' => $price,
  329. 'balance' => $user['brokerage_price'],
  330. 'add_time' => time(),
  331. 'status' => 1
  332. ];
  333. /** @var UserExtractServices $userExtract */
  334. $userExtract = app()->make(UserExtractServices::class);
  335. if (!$re = $userExtract->save($extractInfo)) {
  336. throw new ValidateException('写入佣金提现失败');
  337. }
  338. //佣金提现
  339. $userBill->income('brokerage_to_nowMoney', $uid, $price, $user['brokerage_price'], $re['id']);
  340. return true;
  341. }
  342. /**
  343. * 申请充值
  344. * @param int $uid
  345. * @return mixed
  346. */
  347. public function recharge(int $uid, $price, $recharId, $type, $from)
  348. {
  349. /** @var UserServices $userServices */
  350. $userServices = app()->make(UserServices::class);
  351. $user = $userServices->getUserInfo($uid);
  352. if (!$user) {
  353. throw new ValidateException('数据不存在');
  354. }
  355. switch ((int)$type) {
  356. case 0: //支付充值余额
  357. $paid_price = 0;
  358. if ($recharId) {
  359. /** @var SystemGroupDataServices $systemGroupData */
  360. $systemGroupData = app()->make(SystemGroupDataServices::class);
  361. $data = $systemGroupData->getDateValue($recharId);
  362. if ($data === false) {
  363. return app('json')->fail('您选择的充值方式已下架!');
  364. } else {
  365. $paid_price = $data['give_money'] ?? 0;
  366. }
  367. }
  368. $recharge_data = [];
  369. $recharge_data['order_id'] = $this->getOrderId();
  370. $recharge_data['uid'] = $uid;
  371. $recharge_data['price'] = $price;
  372. $recharge_data['recharge_type'] = $from;
  373. $recharge_data['paid'] = 0;
  374. $recharge_data['add_time'] = time();
  375. $recharge_data['give_price'] = $paid_price;
  376. $recharge_data['channel_type'] = $user['user_type'];
  377. if (!$rechargeOrder = $this->dao->save($recharge_data)) {
  378. throw new ValidateException('充值订单生成失败');
  379. }
  380. try {
  381. /** @var RechargeServices $recharge */
  382. $recharge = app()->make(RechargeServices::class);
  383. $order_info = $recharge->recharge((int)$rechargeOrder->id);
  384. } catch (\Exception $e) {
  385. throw new ValidateException($e->getMessage());
  386. }
  387. return ['msg' => '', 'type' => $from, 'data' => $order_info];
  388. break;
  389. case 1: //佣金转入余额
  390. $this->importNowMoney($uid, $price);
  391. return ['msg' => '转入余额成功', 'type' => $from, 'data' => []];
  392. break;
  393. default:
  394. throw new ValidateException('缺少参数');
  395. break;
  396. }
  397. }
  398. /**
  399. * //TODO用户充值成功后
  400. * @param $orderId
  401. */
  402. public function rechargeSuccess($orderId)
  403. {
  404. $order = $this->dao->getOne(['order_id' => $orderId, 'paid' => 0]);
  405. if (!$order) {
  406. throw new ValidateException('订单失效或者不存在');
  407. }
  408. /** @var UserServices $userServices */
  409. $userServices = app()->make(UserServices::class);
  410. $user = $userServices->getUserInfo((int)$order['uid']);
  411. if (!$user) {
  412. throw new ValidateException('数据不存在');
  413. }
  414. $price = bcadd((string)$order['price'], (string)$order['give_price'], 2);
  415. if (!$this->dao->update($order['id'], ['paid' => 1, 'pay_time' => time()], 'id')) {
  416. throw new ValidateException('修改订单失败');
  417. }
  418. $mark = '成功充值余额' . floatval($order['price']) . '元' . ($order['give_price'] ? ',赠送' . $order['give_price'] . '元' : '');
  419. $bill_data = ['title' => '用户余额充值', 'number' => $price, 'link_id' => $order['id'], 'balance' => $user['now_money'], 'mark' => $mark];
  420. /** @var UserBillServices $userBill */
  421. $userBill = app()->make(UserBillServices::class);
  422. $userBill->incomeNowMoney($order['uid'], 'recharge', $bill_data);
  423. $now_money = bcadd((string)$user['now_money'], (string)$price, 2);
  424. if (!$userServices->update((int)$order['uid'], ['now_money' => $now_money], 'uid')) {
  425. throw new ValidateException('修改用户信息失败');
  426. }
  427. $wechatServices = app()->make(WechatUserServices::class);
  428. switch (strtolower($order['recharge_type'])) {
  429. case 'weixin':
  430. break;
  431. case 'routine':
  432. $openid = $wechatServices->uidToOpenid($order['uid'], 'routine');
  433. RoutineTemplateJob::dispatchDo('sendRechargeSuccess', [$openid, $order, $now_money]);
  434. break;
  435. }
  436. return true;
  437. }
  438. /**根据查询用户充值金额
  439. * @param array $where
  440. * @return float|int
  441. */
  442. public function getRechargeMoneyByWhere(array $where, string $rechargeSumField, string $selectType, string $group = "")
  443. {
  444. switch ($selectType) {
  445. case "sum" :
  446. return $this->dao->getWhereSumField($where, $rechargeSumField);
  447. case "group" :
  448. return $this->dao->getGroupField($where, $rechargeSumField, $group);
  449. }
  450. }
  451. }