HigoWithdrawLog.php 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. <?php
  2. namespace addons\HiGo\common\models;
  3. use addons\HiGo\common\enums\HiGoEnum;
  4. use addons\HiGo\common\logic\HiBeiLogic;
  5. use addons\HiGo\common\logic\WalletLogic;
  6. use common\components\export\CsvTool;
  7. use common\enums\DownloadEnum;
  8. use common\enums\RedisKeyEnum;
  9. use common\enums\PayTypeEnum;
  10. use common\enums\StatusEnum;
  11. use common\enums\UserWalletEnum;
  12. use common\globals\GlobalInvoke;
  13. use common\helpers\LockHelper;
  14. use common\models\common\Download;
  15. use common\models\common\PaidSinglePayLog;
  16. use common\models\common\PaymentTransfer;
  17. use common\enums\AssetsType;
  18. use common\models\base\BaseActiveRecord;
  19. use common\models\user\User;
  20. use common\models\user\UserInfo;
  21. use services\common\PayService;
  22. use services\common\UserWalletService;
  23. use Exception;
  24. use Yii;
  25. use yii\helpers\Json;
  26. /**
  27. * This is the model class for table "{{%higo_withdraw_log}}".
  28. *
  29. * @property int $id
  30. * @property int $mall_id
  31. * @property int $user_id
  32. * @property string $order_no 提现订单号
  33. * @property float $num 提现金额
  34. * @property float $actual_num 实际到账金额
  35. * @property float $poundage_num 手续费扣除金额
  36. * @property float $poundage_ratio 手续费比例
  37. * @property int $from 提现来源1用户;2商户
  38. * @property string $type 提现方式 auto--自动打款 wechat--微信打款 alipay--支付宝打款 bank--银行转账 balance--打款到余额
  39. * @property string $extra 额外信息 例如微信账号、支付宝账号等
  40. * @property string $content 用户额外输入信息
  41. * @property int $status 提现状态 0--申请 1--同意 2--已打款 3--驳回 4--失败
  42. * @property string|null $transfer 支付宝打款信息
  43. * @property int $created_at 添加时间戳
  44. * @property int $updated_at 更新时间戳
  45. */
  46. class HigoWithdrawLog extends BaseActiveRecord
  47. {
  48. const FROM_BALANCE = 2;
  49. const FROM_INCOME = 1;
  50. const STATUS_APPLY = 0;
  51. const STATUS_AGREE = 1;
  52. const STATUS_ALREADY_TRANS = 2;
  53. const STATUS_ALREADY_REJECT = 3;
  54. const STATUS_FAIL = 4;
  55. const STATUS_WAIT = 5;
  56. //提现状态
  57. public static $status_msg = [
  58. self::STATUS_APPLY => '申请',
  59. self::STATUS_AGREE => '同意',
  60. self::STATUS_ALREADY_TRANS => '已打款',
  61. self::STATUS_ALREADY_REJECT => '驳回',
  62. self::STATUS_FAIL => '失败',
  63. self::STATUS_WAIT => '打款中'
  64. ];
  65. //打款类型
  66. public static $type_msg = [
  67. 'auto' => '自动打款',
  68. 'wechat' => '微信',
  69. 'alipay' => '支付宝',
  70. 'bank' => '银行转账',
  71. 'balance' => '打款到余额'
  72. ];
  73. /**
  74. * @var float|int|mixed|null
  75. */
  76. /**
  77. * {@inheritdoc}
  78. */
  79. public static function tableName()
  80. {
  81. return '{{%addons_higo_withdraw_log}}';
  82. }
  83. /**
  84. * {@inheritdoc}
  85. */
  86. public function rules()
  87. {
  88. return [
  89. [['mall_id', 'user_id', 'num', 'actual_num', 'extra', 'content', 'from', 'type'], 'required'],
  90. [['id', 'mall_id', 'user_id', 'from', 'status', 'created_at', 'updated_at'], 'integer'],
  91. [['num', 'actual_num', 'poundage_num', 'poundage_ratio',], 'number'],
  92. [['extra', 'content', 'transfer'], 'string'],
  93. [['order_no'], 'string', 'max' => 255],
  94. [['type'], 'string', 'max' => 45],
  95. [['from'], 'in', 'range' => [1, 2]],
  96. ];
  97. }
  98. /**
  99. * {@inheritdoc}
  100. */
  101. public function attributeLabels()
  102. {
  103. return [
  104. 'id' => 'ID',
  105. 'mall_id' => 'Mall ID',
  106. 'user_id' => 'User ID',
  107. 'order_no' => 'Order No',
  108. 'num' => 'Num',
  109. 'actual_num' => 'Actual Num',
  110. 'poundage_num' => 'Poundage Num',
  111. 'poundage_ratio' => 'Poundage Ratio',
  112. 'from' => 'From',
  113. 'type' => 'Type',
  114. 'extra' => 'Extra',
  115. 'content' => 'Content',
  116. 'status' => 'Status',
  117. 'transfer' => 'Transfer',
  118. 'created_at' => 'Created At',
  119. 'updated_at' => 'Updated At',
  120. ];
  121. }
  122. public static function fieldsList($field = null)
  123. {
  124. $fields = [
  125. 'user_id' => '会员ID',
  126. // 'user_info' => '基本信息',
  127. 'username' => '会员编号',
  128. 'nickname' => '会员昵称',
  129. 'user_mobile' => '手机号码',
  130. 'from' => '提现类型',
  131. // 'type' => '账户信息',
  132. 'alipay_name' => '支付宝姓名',
  133. 'mobile' => '支付宝账号',
  134. 'bank_account_type' => '账户类型',
  135. 'name' => '持卡/开户人',
  136. 'bank_name' => '开户行',
  137. 'bank_account' => '银行卡号',
  138. 'num' => '提现金额',
  139. 'actual_num' => '实际到账金额',
  140. 'poundage_num' => '手续费',
  141. 'status' => '状态',
  142. 'updated_at' => '时间',
  143. 'remark' => '备注',
  144. ];
  145. if ($field === null) return $fields;
  146. $temp = [];
  147. foreach ($field as $val) {
  148. if (isset($fields[$val])) $temp[$val] = $fields[$val];
  149. }
  150. return $temp;
  151. }
  152. public function saveRecord($params)
  153. {
  154. if ($params['price'] <= 0) {
  155. return [
  156. 'state' => false,
  157. 'msg' => '提现金额必须大于0!'
  158. ];
  159. }
  160. $exists = self::find()->where([
  161. 'mall_id' => $params['mall_id'], 'status' => 0, 'user_id' => $params['user_id'], 'from' => $params['from']
  162. ])->asArray()->one();
  163. if ($exists) {
  164. return [
  165. 'state' => false,
  166. 'msg' => '尚有未审核的提现申请!'
  167. ];
  168. }
  169. $higo_wallet = HigoWallet::getOne([['user_id' => $params['user_id']], ['status' => StatusEnum::ENABLED], ['mall_id' => $params['mall_id']]], true, [], false, 'hi_bei');
  170. $hi_bei = $higo_wallet['hi_bei'] ?? 0;
  171. if ($hi_bei < $params['price']) {
  172. return [
  173. 'state' => false,
  174. 'msg' => '提现金额超出可提现金额!'
  175. ];
  176. }
  177. $withdraw = Yii::$app->services->setting->addons->get($this->mall_id, HiGoEnum::ADDONS_NAME, 'withdraw');
  178. $cash_status = $withdraw['cash_status'] ? json_decode($withdraw['cash_status'], true) : 0;
  179. $cash_status = is_array($cash_status) ? $cash_status['value'] : $cash_status;
  180. if (!$cash_status) {
  181. return [
  182. 'state' => false,
  183. 'msg' => '暂未开启提现功能!'
  184. ];
  185. }
  186. $cash_service_fee = $withdraw['cash_service_fee'] ? json_decode($withdraw['cash_service_fee'], true) : 0;
  187. $poundage_ratio = is_array($cash_service_fee) ? $cash_service_fee['value'] : $cash_service_fee;
  188. $wallet_type = HiGoEnum::WALLET_TYPE_HI_BEI;
  189. $balance_config = \Yii::$app->services->setting->mall->get($params['mall_id'], 'pay.balance');
  190. if ($balance_config) {
  191. $balance_config = Json::decode((string)$balance_config);
  192. if ($balance_config['pay_password_status'] == 1) {
  193. $transactionPassword = $params['transaction_password'];
  194. $password = User::getOne([['id' => $params['user_id']]], true, '', '', 'transaction_password');
  195. if (!$params['transaction_password']) {
  196. return [
  197. 'state' => false,
  198. 'msg' => '请输入支付密码!'
  199. ];
  200. }
  201. if (empty($password['transaction_password'])) {
  202. return [
  203. 'state' => false,
  204. 'msg' => '您未设置支付密码!'
  205. ];
  206. }
  207. if (empty($transactionPassword)) {
  208. return [
  209. 'state' => false,
  210. 'msg' => '请输入交易密码!'
  211. ];
  212. }
  213. if (!\Yii::$app->getSecurity()->validatePassword(trim($transactionPassword), $password['transaction_password'])) {
  214. return [
  215. 'state' => false,
  216. 'msg' => '支付密码错误!'
  217. ];
  218. }
  219. }
  220. }
  221. if (!$params['type']) {
  222. return [
  223. 'state' => false,
  224. 'msg' => '请选择提现方式!'
  225. ];
  226. }
  227. if ($params['type'] == 'wechat') {
  228. if (!isset($params['wechat_qrcode'])) {
  229. return [
  230. 'state' => false,
  231. 'msg' => '请上传微信收款码!'
  232. ];
  233. }
  234. }
  235. if ($params['type'] == 'alipay') {
  236. if (!$params['mobile']) {
  237. return [
  238. 'state' => false,
  239. 'msg' => '请填写支付宝账号!'
  240. ];
  241. }
  242. if (!$params['name']) {
  243. return [
  244. 'state' => false,
  245. 'msg' => '请填写支付宝姓名!'
  246. ];
  247. }
  248. }
  249. if ($params['type'] == 'third_party_address') {
  250. if (!$params['address']) {
  251. return [
  252. 'state' => false,
  253. 'msg' => '请填写第三地址提现地址'
  254. ];
  255. }
  256. }
  257. if ($params['type'] == 'paid') { //代付银行卡
  258. if (!$params['name']) {
  259. return [
  260. 'state' => false,
  261. 'msg' => '请填写持卡人!'
  262. ];
  263. }
  264. if (!$params['bank_account']) {
  265. return [
  266. 'state' => false,
  267. 'msg' => '请填写银行卡号!'
  268. ];
  269. }
  270. }
  271. if ($params['type'] == 'alipay_tax') {
  272. if (!$params['mobile']) {
  273. return [
  274. 'state' => false,
  275. 'msg' => '请填写支付宝账号(手机号)'
  276. ];
  277. }
  278. if (!$params['name']) {
  279. return [
  280. 'state' => false,
  281. 'msg' => '请填写支付宝姓名'
  282. ];
  283. }
  284. if (!$params['id_card']) {
  285. return [
  286. 'state' => false,
  287. 'msg' => '请填写身份证信息'
  288. ];
  289. }
  290. $service = new \addons\AvoidTax\common\service\UserService(['mall_id' => $this->mall_id]);
  291. $res = $service->checkSign($this->user_id);
  292. if ($res === false) {
  293. $res = $service->goToSing($this->user_id, $params['mobile'], $params['id_card'], $params['name']);
  294. if (!$res) {
  295. return [
  296. 'state' => false,
  297. 'msg' => $service->error
  298. ];
  299. }
  300. // 跳转url签约
  301. return [
  302. 'state' => true,
  303. 'data' => $res
  304. ];
  305. }
  306. }
  307. $hi_bei_now_price = HiBeiLogic::getNowPrice($params['mall_id']);
  308. $cash = new self();
  309. $cash->mall_id = $params['mall_id'];
  310. $cash->user_id = $params['user_id'];
  311. $cash->num = $params['price'];
  312. $cash->actual_num = $params['price'];
  313. $cash->content = Json::encode(['user_content' => $params['content']]);
  314. $cash->from = $params['from'];
  315. $cash->type = $params['type'];
  316. $cash->extra = Json::encode([
  317. 'name' => $params['name'],
  318. 'mobile' => $params['mobile'],
  319. 'bank_name' => $params['bank_name'],
  320. 'bank_account' => (string)$params['bank_account'],
  321. 'bank_account_type' => $params['bank_account_type'],
  322. 'wechat_qrcode' => $params['wechat_qrcode'],
  323. 'third_party_address' => $params['address'],
  324. 'now_price' => $hi_bei_now_price
  325. ], JSON_UNESCAPED_UNICODE);
  326. if ($poundage_ratio < 0 || $poundage_ratio > 100) {
  327. return [
  328. 'state' => false,
  329. 'msg' => '手续费设置不正确!'
  330. ];
  331. }
  332. //手续费
  333. $poundage = 0;
  334. if ($poundage_ratio > 0 && $poundage_ratio < 100) {
  335. //手续费:取小数点2位,截断
  336. $poundage = bcmul($poundage_ratio / 100, $params['price'], 4);
  337. // 四舍五入,保留2位
  338. $poundage = number_format($poundage, 2, '.', '');
  339. $cash->actual_num = bcsub($params['price'], $poundage, 2);
  340. $cash->poundage_num = $poundage;
  341. $cash->poundage_ratio = $poundage_ratio;
  342. }
  343. if (intval(bcmul($cash->actual_num, 100)) < 1) {
  344. return [
  345. 'state' => false,
  346. 'msg' => '提现金额扣除费用后金额不足!'
  347. ];
  348. }
  349. //计算当前价格
  350. $cash->actual_num = bcmul($cash->actual_num,$hi_bei_now_price,2);
  351. $t = \Yii::$app->db->beginTransaction();
  352. $basic = HiGoEnum::getHigoWalletText($params['mall_id']);
  353. $hi_bei_name = $basic[HiGoEnum::WALLET_TYPE_HI_BEI];
  354. try {
  355. $cash->order_no = $this->getCashOrder($params['mall_id']);
  356. $desc = $hi_bei_name . '提现';
  357. $desc .= $cash->num;
  358. if ($poundage > 0) {
  359. $desc .= ':手续费¥' . $poundage;
  360. }
  361. if (!$cash->save()) {
  362. throw new Exception($cash->getErrorMessage());
  363. }
  364. $res_data = [
  365. 'state' => true,
  366. 'data' => ['cash_id' => $cash->id],
  367. 'msg' => ''
  368. ];
  369. $t->commit();
  370. $wallet_log = [
  371. 'mall_id' => $params['mall_id'],
  372. 'user_id' => $params['user_id'],
  373. 'change_num' => bcmul($params['price'], -1, 20),
  374. 'desc' => $desc,
  375. 'source_table' => self::tableName(),
  376. 'source_table_id' => $cash->id,
  377. 'from_type' => HiGoEnum::FROM_TYPE_WITHDRAW_HI_BEI,
  378. 'wallet_type' => $wallet_type,
  379. 'is_send' => false
  380. ];
  381. $result = WalletLogic::handleInQueue($wallet_log);
  382. if (!$result) {
  383. $cash->status = 4;
  384. $cash->save();
  385. return [
  386. 'state' => false,
  387. 'msg' => '提现失败,系统繁忙!'
  388. ];
  389. }
  390. } catch (Exception $e) {
  391. $t->rollBack();
  392. \Yii::error($e->getMessage());
  393. return [
  394. 'state' => false,
  395. 'msg' => '提现失败!' . $e->getMessage()
  396. ];
  397. }
  398. return $res_data;
  399. }
  400. private function getCashOrder($mall_id): string
  401. {
  402. $order_no = null;
  403. while (true) {
  404. $order_no = 'HG' . date('YmdHis') . rand(10000, 99999);
  405. $exist = self::find()->where(['mall_id' => $mall_id, 'order_no' => $order_no,])->asArray()->one();
  406. if (!$exist) {
  407. break;
  408. }
  409. }
  410. return $order_no;
  411. }
  412. public function searchLogList($get): array
  413. {
  414. $mall_id = $get['mall_id'];
  415. $params['alias'] = 'b';
  416. $params['joinWith'] = ['user'];
  417. if ($get['status'] && $get['status'] >= 0) {
  418. $params['where'][] = ['=', 'b.status', $get['status']];
  419. }
  420. if ($get['status'] == 0) {
  421. $params['where'][] = ['=', 'b.status', $get['status']];
  422. }
  423. if (!empty($get['keyword'])) {
  424. $params['where'] = [[
  425. 'or',
  426. ['like', 'nickname', $get['keyword']],
  427. ['like', 'mobile', $get['keyword']]
  428. ]];
  429. }
  430. if (!empty($get['user_id'])) {
  431. $params['where'][] = ['=', 'user_id', $get['user_id']];
  432. }
  433. if (!empty($get['withdraw_type'])) {
  434. $params['where'][] = ['=', 'b.type', $get['withdraw_type']];
  435. }
  436. if (!empty($get['withdraw_from'])) {
  437. $params['where'][] = ['=', 'b.from', $get['withdraw_from']];
  438. }
  439. if (!empty($get['start_date']) && !empty($get['end_date'])) {
  440. $get['start_date'] = str_replace('+', ' ', $get['start_date']);
  441. $get['end_date'] = str_replace('+', ' ', $get['end_date']);
  442. $params['where'][] = ['>=', 'b.created_at', strtotime($get['start_date'])];
  443. $params['where'][] = ['<=', 'b.created_at', strtotime($get['end_date'])];
  444. }
  445. $params['where'][] = ['=', 'b.mall_id', $mall_id];
  446. $params['order'] = ['status' => SORT_ASC, 'updated_at' => SORT_DESC];
  447. $params['limit'] = 10;
  448. $list = self::listPage($params, false, false);
  449. $newList = [];
  450. foreach ($list['list'] as $item) {
  451. $serviceCharge = $item->poundage_num;
  452. $fact_price = $item->actual_num;
  453. $extra = $item->extra ? json_decode($item->extra, true) : [];
  454. if (!empty($extra)) $extra['bank_account'] = (string)$extra['bank_account'];
  455. if ($item->type == 'paid' || $item->type == 'joinpay') {
  456. //汇聚代付
  457. $pay_log = PaidSinglePayLog::getOne([['mall_id' => $item->mall_id, 'order_no' => $item->order_no]]);
  458. if ($pay_log) {
  459. if ($pay_log->status == PaidSinglePayLog::STATUS_SUCCESS) {
  460. $joinpay_msg = '打款成功';
  461. } else {
  462. $joinpay_msg = $pay_log->error_msg ?: '打款中';
  463. }
  464. }
  465. }
  466. $total_num = bcsub($item->num,$serviceCharge,2);
  467. $now_price = bcdiv($fact_price,$total_num,2);
  468. $newItem = [
  469. 'id' => $item->id,
  470. 'from' => $item->from,
  471. 'order_no' => $item->order_no,
  472. 'type' => $item->type,
  473. 'status' => $item->status,
  474. 'extra' => $extra,
  475. 'created_at' => $item->created_at,
  476. 'updated_at' => $item->updated_at,
  477. 'content' => $item->content ? Json::decode($item->content) : [],
  478. 'user' => [
  479. 'avatar' => isset($item->user->avatar_url) ? $item->user->avatar_url : '',
  480. 'nickname' => isset($item->user->nickname) ? $item->user->nickname : '',
  481. 'id' => isset($item->user->id) ? $item->user->id : '',
  482. 'mobile' => isset($item->user->mobile) ? $item->user->mobile : '',
  483. ],
  484. 'cash' => [
  485. 'price' => round($item->num, 2),
  486. 'service_fee_rate' => bcadd($serviceCharge,0,2),
  487. 'fact_price' => bcadd($fact_price,0,2),
  488. 'now_price' => $now_price
  489. ],
  490. 'joinpay_msg' => $joinpay_msg
  491. ];
  492. $newList[] = $newItem;
  493. }
  494. $export_list = self::fieldsList();
  495. return [
  496. 'code' => 0,
  497. 'data' => [
  498. 'list' => $newList,
  499. 'export_list' => $export_list,
  500. 'pagination' => $list['pagination'],
  501. ]
  502. ];
  503. }
  504. /**
  505. * @throws Exception
  506. */
  507. public function updateLog(): bool
  508. {
  509. $where = [['id' => $this->id]];
  510. $cash = self::getOne($where);
  511. if (!$cash) {
  512. throw new \Exception('提现记录不存在');
  513. }
  514. if ($cash->status == 2) {
  515. throw new \Exception('提现已打款');
  516. }
  517. if ($cash->status == 3) {
  518. throw new \Exception('提现已被驳回');
  519. }
  520. if ($this->status <= $cash->status) {
  521. throw new \Exception('状态错误, 请刷新重试');
  522. }
  523. $lock_key = RedisKeyEnum::suffix(RedisKeyEnum::LOCK_USER_CASH, $cash->user_id);
  524. $identification = uniqid();
  525. if (!LockHelper::lock($lock_key, $identification, 100, 100)) {
  526. \Yii::error('cashForm 获取锁(' . $lock_key . ')失败阻塞,提现打款:cash,操作:save,金额:' . $cash->actual_num);
  527. return false;
  528. }
  529. $t = \Yii::$app->db->beginTransaction();
  530. try {
  531. switch ($this->status) {
  532. case 1:
  533. $this->validateCash($cash);
  534. break;
  535. case 2:
  536. $this->remit($cash);
  537. break;
  538. case 3:
  539. $this->reject($cash);
  540. break;
  541. default:
  542. throw new \Exception('错误的提现类型');
  543. }
  544. $t->commit();
  545. LockHelper::uLock($lock_key, $identification);
  546. } catch (\Exception $exception) {
  547. $t->rollBack();
  548. LockHelper::uLock($lock_key, $identification);
  549. \Yii::error("finance cashForm error=" . $exception->getFile() . ";line:" . $exception->getLine() . ";message:" . $exception->getMessage());
  550. throw new \Exception($exception->getMessage());
  551. }
  552. return true;
  553. }
  554. /**
  555. * 申请
  556. * @param $cash
  557. * @return bool
  558. * @throws Exception
  559. */
  560. private function validateCash($cash): bool
  561. {
  562. if (!$cash->content) {
  563. $content = [];
  564. } else {
  565. $content = Json::decode($cash->content);
  566. }
  567. $cash->status = 1;
  568. $content['validate_content'] = $this->content ?: '审核通过';
  569. $cash->content = Json::encode($content);
  570. if (!$cash->save()) {
  571. throw new \Exception('失败');
  572. }
  573. return true;
  574. }
  575. /**
  576. * @Author: 广东七件事 ganxiaohao
  577. * @Date: 2020-05-30
  578. * @Time: 11:22
  579. * @Note:拒绝
  580. * @param $cash
  581. * @return void
  582. * @throws \Exception
  583. */
  584. private function reject($cash): void
  585. {
  586. if (!$this->content) {
  587. throw new \Exception('请填写备注');
  588. }
  589. if (!$cash->content) {
  590. $content = [];
  591. } else {
  592. $content = Json::decode($cash->content);
  593. }
  594. $cash->status = 3;
  595. $content['reject_content'] = $this->content;
  596. $cash->content = Json::encode($content);
  597. if (!$cash->save()) {
  598. throw new \Exception('失败');
  599. }
  600. $wallet_type = HiGoEnum::WALLET_TYPE_HI_BEI;
  601. $basic = HiGoEnum::getHigoWalletText($cash->mall_id);
  602. $hi_bei_name = $basic[HiGoEnum::WALLET_TYPE_HI_BEI];
  603. $desc = $hi_bei_name."提现申请被驳回,驳回原因:" . $this->content ?? '';
  604. $wallet_log = [
  605. 'mall_id' => $cash->mall_id,
  606. 'user_id' => $cash->user_id,
  607. 'change_num' => abs($cash->num),
  608. 'desc' => $desc,
  609. 'source_table' => self::tableName(),
  610. 'source_table_id' => $cash->id,
  611. 'from_type' => HiGoEnum::FROM_TYPE_WITHDRAW_REFUSE_HI_BEI,
  612. 'wallet_type' => $wallet_type,
  613. 'is_send' => false
  614. ];
  615. $result = WalletLogic::handleInQueue($wallet_log);
  616. if ($result === false) {
  617. throw new Exception('系统繁忙,请稍后再试');
  618. }
  619. }
  620. public function getUser(): \yii\db\ActiveQuery
  621. {
  622. return $this->hasOne(User::class, ['id' => 'user_id']);
  623. }
  624. /**
  625. * @throws Exception
  626. */
  627. private function remit($cash): void
  628. {
  629. $res = false;
  630. if ($cash->type === 'auto') {
  631. //todo 待测试联调
  632. /**
  633. * @var UserInfo $userInfo ;
  634. */
  635. $userInfo = UserInfo::getOne([["user_id" => $cash->user_id, "platform" => [User::PLATFORM_WECHAT, User::PLATFORM_MP_WX], "status" => StatusEnum::ENABLED]]);
  636. if (empty($userInfo)) {
  637. throw new \Exception('该用户不存在微信相关信息!');
  638. }
  639. // $wechatPaySetting = OptionLogic::get(Option::NAME_PAYMENT, \Yii::$app->mall->id, Option::GROUP_APP);
  640. // if (!$wechatPaySetting) {
  641. // throw new \Exception('系统未开启微信支付!');
  642. // }
  643. // if (!$wechatPaySetting['wechat_status']) {
  644. // throw new \Exception('系统未开启微信支付!');
  645. // }
  646. try {
  647. $payment = new PayService();
  648. $paymentTransfer = new PaymentTransfer();
  649. $paymentTransfer->orderNo = PaymentTransfer::generate_order_no("TR");
  650. $paymentTransfer->user = $userInfo;
  651. $paymentTransfer->title = "收益提现";
  652. $paymentTransfer->amount = $cash->actual_num;
  653. $paymentTransfer->transferType = PayTypeEnum::WECHAT;
  654. // $paymentTransfer->transferType = PaymentTransfer::TRANSFER_TYPE_WECHAT;
  655. $paymentTransfer->cashInfo = $cash;
  656. $res = $payment->transfer($paymentTransfer);
  657. } catch (Exception $e) {
  658. throw new \Exception($e->getMessage());
  659. }
  660. } else if ($cash->type === 'balance') {
  661. $userInfo = User::getOne([['id' => $cash->user_id, 'status' => StatusEnum::ENABLED]]);
  662. if (!$userInfo) {
  663. throw new \Exception('该用户不存在!');
  664. }
  665. $desc = "用户收益提现到余额:" . $cash->actual_num . "元";
  666. $data = [
  667. 'message_id' => 0,
  668. 'mall_id' => $cash->mall_id, 'user_id' => $cash->user_id,
  669. 'wallet_type' => UserWalletService::WALLET_TYPE_BALANCE,
  670. 'is_frozen' => false, 'unfrozen' => false, 'money' => abs($cash->actual_num), 'desc' => $desc,
  671. 'source_table' => AssetsType::CASH_OUT, 'source_table_id' => $cash->id,
  672. 'from_type' => UserWalletEnum::RECHARGE,
  673. 'capital_type' => UserWalletEnum::RECHARGE_COMMISSION,
  674. ];
  675. $res = UserWalletService::handle(0, $data);
  676. //免审核,免打款
  677. $withdraw_config = \Yii::$app->services->setting->mall->get($cash->mall_id, 'withdraw_income');
  678. if (isset($withdraw_config['is_examine']) && $withdraw_config['is_examine']) {
  679. $cash->content = Json::encode(['validate_content' => '提现免审核,免打款']);
  680. }
  681. } elseif ($cash->type === 'paid' || $cash->type === 'joinpay') { //代付
  682. $res = GlobalInvoke::exec(GlobalInvoke::INVOKE_WITHDRAW_EXTENSION_PAY, $cash->mall_id, ['cash' => $cash]);
  683. if ($res) {
  684. // 提交成功
  685. self::remit_wait($cash, "提交成功");
  686. return;
  687. } else {
  688. // 提交失败
  689. $msg = '代付打款失败';
  690. throw new \Exception($msg);
  691. }
  692. } else if ($cash->type == 'alipay' || $cash->type == 'wechat' || $cash->type == 'bank' || $cash->type == 'third_party_address') { //线下转账
  693. $res = true;
  694. }
  695. if ($res) {
  696. self::remit_success($cash);
  697. } else {
  698. $msg = '打款失败';
  699. throw new \Exception($msg);
  700. }
  701. }
  702. /**
  703. * 明确打款成功
  704. *
  705. * @param HigoWithdrawLog $cash
  706. * @param string $msg
  707. * @return void
  708. */
  709. public static function remit_success(HigoWithdrawLog $cash, $msg = '打款成功')
  710. {
  711. // 保存提现信息
  712. if ($cash->content) {
  713. $content = Json::decode($cash->content);
  714. } else {
  715. $content = [];
  716. }
  717. $cash->status = 2;
  718. $content['remit_at'] = date('Y-m-d H:i:s', time());
  719. $content['remit_content'] = $msg;
  720. $cash->content = Json::encode($content);
  721. if (!$cash->save()) {
  722. throw new \Exception();
  723. }
  724. return true;
  725. }
  726. /**
  727. * 打款失败,变成已同意状态,让客户自己去驳回
  728. *
  729. * @param HigoWithdrawLog $cash
  730. * @param string $msg
  731. * @return void
  732. */
  733. public static function remit_fail(HigoWithdrawLog $cash, $msg)
  734. {
  735. $content = empty($cash->content) ? [] : Json::decode($cash->content);
  736. $content['remit_content'] = $msg;
  737. $res = HigoWithdrawLog::updateAll([
  738. 'status' => 1,
  739. 'content' => Json::encode($content),
  740. 'updated_at' => time()
  741. ], ['id' => $cash->id]);
  742. }
  743. /**
  744. * 打款中
  745. *
  746. * @param [type] $cash
  747. * @param [type] $msg
  748. * @return void
  749. */
  750. public static function remit_wait(HigoWithdrawLog $cash, $msg)
  751. {
  752. $content = empty($cash['content']) ? [] : Json::decode($cash['content']);
  753. $content['remit_content'] = $msg;
  754. $res = self::updateAll(['content' => Json::encode($content), 'status' => 5], ['id' => $cash['id']]);
  755. }
  756. public static function exportHandle(array $data)
  757. {
  758. $download_id = $data['download_id'] ?? 0;
  759. $download = Download::findOne(['id' => $download_id]);
  760. if ($download) {
  761. try {
  762. $params = json_decode($download->params, true);
  763. $filename = $download->name;
  764. $type = $download->type;
  765. $user_withdrawlog_model = HigoWithdrawLog::find();
  766. $user_withdrawlog_model->where(['mall_id' => $params['mall_id']]);
  767. if (!empty($params['keyword'])) {
  768. $user = User::find()->where([
  769. 'OR',
  770. ['like', 'nickname', $params['keyword'] . '%', false],
  771. ['like', 'mobile', $params['keyword'] . '%', false],
  772. ])->all();
  773. $where[] = ['in', 'user_id', array_column($user, 'id')];
  774. $user_withdrawlog_model->andWhere(['in', 'user_id', array_column($user, 'id')]);
  775. }
  776. if (!empty($params['user_id'])) $user_withdrawlog_model->andWhere(['=', 'user_id', $params['user_id']]);
  777. if (!empty($params['type'])) $user_withdrawlog_model->andWhere(['=', 'type', $params['withdraw_type']]);
  778. if (!empty($params['withdraw_from'])) $user_withdrawlog_model->andWhere(['=', 'from', $params['withdraw_from']]);
  779. if (!empty($params['start_date'])) $user_withdrawlog_model->andWhere(['>=', 'created_at', strtotime($params['start_date'])]);
  780. if (!empty($params['end_date'])) $user_withdrawlog_model->andWhere(['<=', 'created_at', strtotime($params['end_date'])]);
  781. if (isset($params['status'])) {
  782. $user_withdrawlog_model->andWhere(['=', 'status', $params['status']]);
  783. } else {
  784. $user_withdrawlog_model->andWhere(['>=', 'status', StatusEnum::DISABLED]);
  785. }
  786. $fields = $params['fields'];
  787. $fields_arr = self::fieldsList();
  788. $have_select_field_arr = [];
  789. foreach ($fields as $value) {
  790. $have_select_field_arr[] = $fields_arr[$value];
  791. }
  792. $csv = new CsvTool();
  793. $csv->filename = $filename;
  794. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($type);
  795. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  796. $csv->header = $have_select_field_arr;
  797. $user_withdrawlog_model->with(['user' => function ($query) {
  798. $query->select('id,nickname,username,avatar_url,mobile');
  799. }]);
  800. $user_withdrawlog_model->orderBy('status asc,created_at desc');
  801. foreach ($user_withdrawlog_model->batch() as $withdrawlog) {
  802. $csv->data = array_map(function ($withdrawlog) use ($fields) {
  803. $row = [];
  804. $extra = json_decode($withdrawlog['extra'], true) ?: [];
  805. foreach ($fields as $field) {
  806. switch (true) {
  807. case (in_array($field, ['updated_at'])):
  808. $row[] = !empty($withdrawlog[$field]) ? date('Y-m-d H:i:s', $withdrawlog[$field]) : '';
  809. break;
  810. case ($field == 'username'):
  811. $row[] = $withdrawlog['user']['username'] ?: '';
  812. break;
  813. case ($field == 'nickname'):
  814. $row[] = $withdrawlog['user']['nickname'] ?: '';
  815. break;
  816. case ($field == 'user_mobile'):
  817. $row[] = $withdrawlog['user']['mobile'] ?: '';
  818. break;
  819. case ($field == 'from'):
  820. $row[] = $withdrawlog[$field] == 1 ? '佣金收益提现' : '余额提现';
  821. break;
  822. case ($field == 'alipay_name'):
  823. $row[] = $extra['name'] ?: '';
  824. break;
  825. case ($field == 'mobile'):
  826. $row[] = empty($extra['mobile']) ? '' : $extra['mobile'] . "\t";
  827. break;
  828. case ($field == 'bank_account_type'):
  829. $row[] = $extra['bank_account_type'] == 1 ? '对私账户' : ($extra['bank_account_type'] == 2 ? '对公账号' : '');
  830. break;
  831. case ($field == 'name'):
  832. $row[] = $extra['name'] ?: '';
  833. break;
  834. case ($field == 'bank_name'):
  835. $row[] = $extra['bank_name'] ?: '';
  836. break;
  837. case ($field == 'bank_account'):
  838. $row[] = empty($extra['bank_account']) ? '' : $extra['bank_account'] . "\t";
  839. break;
  840. case ($field == 'wechat_qrcode'):
  841. $row[] = $extra['wechat_qrcode'] ?: '';
  842. break;
  843. case ($field == 'third_party_address'):
  844. $row[] = $extra['third_party_address'] ?: '';
  845. break;
  846. case ($field == 'status'):
  847. $str = '';
  848. switch ($withdrawlog[$field]) {
  849. case 0:
  850. $str = '待审核';
  851. break;
  852. case 1:
  853. $str = '待打款';
  854. break;
  855. case 2:
  856. $str = '已打款';
  857. break;
  858. case 5:
  859. $str = '打款中';
  860. break;
  861. case 3:
  862. $str = '已驳回';
  863. break;
  864. }
  865. $row[] = $str;
  866. break;
  867. case ($field == 'remark'):
  868. $withdrawlog['content'] = json_decode($withdrawlog['content'], true);
  869. $str = '';
  870. if (!empty($withdrawlog['content']['reject_content'])) $str .= '驳回备注:' . $withdrawlog['content']['reject_content'];
  871. if (!empty($withdrawlog['content']['validate_content']) && in_array($withdrawlog['status'], [1, 2])) $str .= ';审核备注:' . $withdrawlog['content']['validate_content'];
  872. if (!empty($withdrawlog['content']['remit_content']) && in_array($withdrawlog['status'], [1, 2])) $str .= ';打款备注:' . $withdrawlog['content']['remit_content'];
  873. if (!empty($withdrawlog['content']['joinpay_msg'])) $str .= ';汇聚备注:' . $withdrawlog['content']['joinpay_msg'];
  874. $row[] = $str;
  875. break;
  876. default:
  877. $row[] = !empty($withdrawlog[$field]) ? $withdrawlog[$field] : '';
  878. }
  879. }
  880. return $row;
  881. }, $withdrawlog);
  882. $csv->export();
  883. }
  884. $csv->updateDown($download, $params['mall_id']);
  885. return true;
  886. } catch (\Exception $e) {
  887. $download->status = 3;
  888. $res = $download->save();
  889. self::setStaticError($e->getMessage());
  890. return $e->getMessage();
  891. }
  892. }
  893. }
  894. }