StatisticsService.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  1. <?php
  2. namespace backend\modules\mall\services;
  3. use addons\JoinPay\common\enums\JoinPayTypeEnum;
  4. use addons\YunfastPay\common\enums\YunfastPayTypeEnum;
  5. use common\components\export\CsvTool;
  6. use common\enums\AddonsEnum;
  7. use common\enums\AliPayPayTypeEnum;
  8. use common\enums\DownloadEnum;
  9. use common\enums\PayTypeEnum;
  10. use common\enums\RefundStatusEnum;
  11. use common\enums\StatusEnum;
  12. use common\enums\WechatPayTypeEnum;
  13. use common\helpers\DateHelper;
  14. use common\helpers\FormatHelper;
  15. use common\models\common\Download;
  16. use common\models\mall\MallAddons;
  17. use common\models\order\Order;
  18. use common\models\order\OrderRefund;
  19. use common\models\statistics\PaymentTypeRefundStatistics;
  20. use common\models\statistics\PaymentTypeRefundStatisticsDetails;
  21. use common\models\statistics\PaymentTypeRefundStatisticsLogs;
  22. use common\models\user\User;
  23. class StatisticsService extends BaseService
  24. {
  25. /**
  26. * 每种支付方式退款统计列表
  27. *
  28. * @param array $get
  29. * @param bool $isGetAll
  30. *
  31. * @return array
  32. */
  33. public function allPayTypeRefund(array $get, bool $isGetAll = false): array
  34. {
  35. $where = [
  36. ['status' => StatusEnum::ENABLED, 'mall_id' => $this->mall_id]
  37. ];
  38. $this->getAllPayTypeRefundListWhere($get, $where);
  39. // 默认本月
  40. $get['daterange'] = !empty($get['daterange']) ? $get['daterange'] : array_values(DateHelper::thisMonth());
  41. array_walk($get['daterange'], function (&$item) {
  42. $item = date('Ymd', !is_numeric($item) ? strtotime($item) : $item);
  43. });
  44. if ($isGetAll) {
  45. $list = PaymentTypeRefundStatistics::lists([
  46. 'where' => $where
  47. ]);
  48. $this->formatAllPayTypeRefundList($list, $get['daterange']);
  49. } else {
  50. $list = PaymentTypeRefundStatistics::listPage([
  51. 'where' => $where
  52. ]);
  53. $this->formatAllPayTypeRefundList($list['list'], $get['daterange']);
  54. // 返回总数
  55. $list['total_num'] = $this->getTotalNum($get);
  56. }
  57. return $list;
  58. }
  59. /**
  60. * 返回搜索下拉 可选择的具体支付方式(如 汇聚-微信)
  61. *
  62. * @return array
  63. */
  64. public function getPayIdentifierOption(): array
  65. {
  66. $statisticsList = PaymentTypeRefundStatistics::find()
  67. ->where(['mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED])
  68. ->select('payment_identifier,is_pay_group')
  69. ->asArray()
  70. ->all();
  71. $payTypeOption = [];
  72. // 拆分,并组装搜索的select(格式:7|WEIXIN_XCX,16|W06-CASHIER)
  73. foreach ($statisticsList as $item) {
  74. $payTypeOption[] = [
  75. 'value' => $item['payment_identifier'],
  76. 'label' => implode('+', $this->getPaymentTypeByPaymentIdentifier($item['payment_identifier']))
  77. ];
  78. }
  79. return $payTypeOption;
  80. }
  81. /**
  82. * 返回搜索下拉 可选择的支付方式
  83. *
  84. * @return array
  85. */
  86. public function getPayTypeOption(): array
  87. {
  88. $payTypeMap = PayTypeEnum::getMap();
  89. $payType = [];
  90. foreach ($payTypeMap as $key => $value) {
  91. $showPayType = [
  92. PayTypeEnum::WECHAT,
  93. PayTypeEnum::ALI,
  94. PayTypeEnum::JOINPAY,
  95. PayTypeEnum::MIXED,
  96. PayTypeEnum::HUIFUPAY,
  97. PayTypeEnum::BALANCE,
  98. PayTypeEnum::SCORE,
  99. PayTypeEnum::ON_LINE,
  100. PayTypeEnum::OFFLINE,
  101. PayTypeEnum::JOINPAY_FAST_PAY
  102. ];
  103. if (!in_array($key, $showPayType)) {
  104. continue;
  105. }
  106. $payType[] = [
  107. 'label' => $value,
  108. 'value' => $key
  109. ];
  110. }
  111. return $payType;
  112. }
  113. /**
  114. * 返回支付方式 具体的支付方式文字(如 汇聚-微信)
  115. *
  116. * @param int $payType
  117. * @param string $tradeType
  118. *
  119. * @return string
  120. */
  121. private function getTradeType(int $payType, string $tradeType): string
  122. {
  123. $map = [];
  124. switch ($payType) {
  125. case PayTypeEnum::WECHAT:
  126. $map = WechatPayTypeEnum::getMap();
  127. break;
  128. case PayTypeEnum::ALI:
  129. $map = AliPayPayTypeEnum::getMap();
  130. break;
  131. case PayTypeEnum::JOINPAY:
  132. if (MallAddons::isInstall($this->mall_id, AddonsEnum::ADDONS_NAME_JOIN_PAY)) {
  133. $map = JoinPayTypeEnum::getMap();
  134. }
  135. break;
  136. case PayTypeEnum::YUNFAST:
  137. if (MallAddons::isInstall($this->mall_id, 'YunfastPay')) {
  138. $map = YunfastPayTypeEnum::getMap();
  139. }
  140. break;
  141. case PayTypeEnum::HUIFUPAY:
  142. if (MallAddons::isInstall($this->mall_id, AddonsEnum::ADDONS_NAME_HUIFU_PAY)) {
  143. $map = [
  144. 'h5_wechat' => '微信h5',
  145. 'h5_alipay' => '支付宝h5',
  146. 'app_wechat' => '微信app',
  147. 'app_alipay' => '支付宝app',
  148. 'wechat' => '微信公众号',
  149. 'T_MINIAPP' => '微信小程序',
  150. 'mp_wechat' => '微信小程序',
  151. 'fast_pay' => '快捷支付',
  152. 'big_pay' => '大额支付',
  153. 'balance_pay' => '余额支付',
  154. ];
  155. }
  156. break;
  157. }
  158. return $map[$tradeType] ?? '';
  159. }
  160. private function getAllPayTypeRefundListWhere(array $params, array &$where)
  161. {
  162. // 主表筛选
  163. if (isset($params['payment_type']) && is_numeric($params['payment_type'])) {
  164. $where[] = ['=', 'payment_type', $params['payment_type']];
  165. }
  166. if (!empty($params['payment_identifier'])) {
  167. $where[] = ['=', 'payment_identifier', $params['payment_identifier']];
  168. }
  169. if (!empty($params['id'])) {
  170. $where[] = ['=', 'id', $params['id']];
  171. }
  172. }
  173. /**
  174. * @param array $list
  175. * @param array $date
  176. *
  177. * @return void
  178. */
  179. private function formatAllPayTypeRefundList(array &$list, array $date)
  180. {
  181. $baseWhere = [
  182. [
  183. 'status' => StatusEnum::ENABLED,
  184. 'mall_id' => $this->mall_id,
  185. 'statistics_id' => array_column($list, 'id')
  186. ]
  187. ];
  188. foreach ($list as &$item) {
  189. $paymentIdentifierArr = explode(',', $item['payment_identifier']);
  190. foreach ($paymentIdentifierArr as $paymentIdentifierItem) {
  191. ['payType' => $payType, 'tradeType' => $tradeType] = $this->returnPaymentTypeAndTradeType($paymentIdentifierItem);
  192. $label = $this->getPaymentTypeAndTradeTypeText($payType, $tradeType);
  193. $item['total_refund_arr'][$paymentIdentifierItem] = [
  194. 'label' => $label,
  195. 'money' => 0
  196. ];
  197. $item['total_pay_arr'][$paymentIdentifierItem] = [
  198. 'label' => $label,
  199. 'money' => 0
  200. ];
  201. $item['total_refund_cumulative_arr'][$paymentIdentifierItem] = [
  202. 'label' => $label,
  203. 'money' => 0
  204. ];
  205. $item['total_pay_cumulative_arr'][$paymentIdentifierItem] = [
  206. 'label' => $label,
  207. 'money' => 0
  208. ];
  209. }
  210. $item['payment_identifier_text'] = implode('+', $this->getPaymentTypeByPaymentIdentifier($item['payment_identifier']));
  211. if ($item['is_pay_group']) {
  212. $item['payment_identifier_text'] .= '(组合)';
  213. }
  214. }
  215. $group = 'statistics_id, payment_type, trade_type';
  216. $select = 'statistics_id, payment_type, trade_type, SUM(total_refund_money) total_refund_money, SUM(total_pay_money) total_pay_money';
  217. $list = array_column($list, null, 'id');
  218. // 累计退款总额
  219. $cumulativeDetails = PaymentTypeRefundStatisticsDetails::lists([
  220. 'group' => $group,
  221. 'select' => $select,
  222. 'where' => $baseWhere
  223. ]);
  224. $this->setMoneyTextArr($list, $cumulativeDetails, true);
  225. // 售后退货总额
  226. $details = PaymentTypeRefundStatisticsDetails::lists([
  227. 'group' => $group,
  228. 'select' => $select,
  229. 'where' => array_merge($baseWhere, $date ? [['between', 'date', $date[0], $date[1]]] : [])
  230. ]);
  231. $this->setMoneyTextArr($list, $details);
  232. $list = array_values($list);
  233. array_walk($list, function (&$item) {
  234. $item['total_refund_arr'] = array_values($item['total_refund_arr']);
  235. $item['total_pay_arr'] = array_values($item['total_pay_arr']);
  236. $item['total_refund_cumulative_arr'] = array_values($item['total_refund_cumulative_arr']);
  237. $item['total_pay_cumulative_arr'] = array_values($item['total_pay_cumulative_arr']);
  238. });
  239. }
  240. /**
  241. * 将唯一识别符拆分为支付方式和具体的支付方式文字返回(示例:5,18|T_MINIAPP)
  242. *
  243. * @param string $paymentIdentifier
  244. *
  245. * @return array
  246. */
  247. private function getPaymentTypeByPaymentIdentifier(string $paymentIdentifier): array
  248. {
  249. $paymentIdentifierArr = explode(',', $paymentIdentifier);
  250. $strArr = [];
  251. foreach ($paymentIdentifierArr as $paymentIdentifierItem) {
  252. ['payType' => $payType, 'tradeType' => $tradeType] = $this->returnPaymentTypeAndTradeType($paymentIdentifierItem);
  253. $strArr[] = $this->getPaymentTypeAndTradeTypeText(intval($payType), $tradeType);
  254. }
  255. return $strArr;
  256. }
  257. /**
  258. * 传入支付方式和具体的实付类型 返回如:汇付天下-微信小程序
  259. *
  260. * @param int $paymentType
  261. * @param string $tradeType
  262. *
  263. * @return string
  264. */
  265. private function getPaymentTypeAndTradeTypeText(int $paymentType, string $tradeType): string
  266. {
  267. $payTypeMap = PayTypeEnum::getMap();
  268. $payTypeText = $payTypeMap[$paymentType] ?? '';
  269. $tradeTypeText = $this->getTradeType($paymentType, $tradeType);
  270. return $payTypeText . ($tradeTypeText ? '-' . $tradeTypeText : '');
  271. }
  272. /**
  273. * 示例:传入5,18|T_MINIAPP 返回支付方式和具体的支付类型
  274. *
  275. * @param string $paymentIdentifierItem
  276. *
  277. * @return array
  278. */
  279. private function returnPaymentTypeAndTradeType(string $paymentIdentifierItem): array
  280. {
  281. $paymentIdentifierItemArr = explode('|', $paymentIdentifierItem);
  282. $payType = $paymentIdentifierItemArr[0] ?? 0;
  283. $tradeType = $paymentIdentifierItemArr[1] ?? '';
  284. return compact('payType', 'tradeType');
  285. }
  286. /**
  287. * 设置列表支付方式和金额显示的文字
  288. *
  289. * @param array $list
  290. * @param array $details
  291. * @param bool $isCumulative
  292. */
  293. public function setMoneyTextArr(array &$list, array $details, bool $isCumulative = false)
  294. {
  295. if (!$details) {
  296. return;
  297. }
  298. $cumulativeField = $isCumulative ? 'cumulative_' : '';
  299. $refundMoneyField = sprintf('total_refund_%sarr', $cumulativeField);
  300. $payMoneyField = sprintf('total_pay_%sarr', $cumulativeField);
  301. foreach ($details as $detail) {
  302. if (!isset($list[$detail['statistics_id']])) {
  303. continue;
  304. }
  305. $identifier = $this->makePaymentIdentifier($detail['payment_type'], $detail['trade_type']);
  306. $list[$detail['statistics_id']][$refundMoneyField][$identifier]['money'] = bcadd(
  307. $list[$detail['statistics_id']][$refundMoneyField][$identifier]['money'],
  308. $detail['total_refund_money'],
  309. 2
  310. );
  311. $list[$detail['statistics_id']][$payMoneyField][$identifier]['money'] = bcadd(
  312. $list[$detail['statistics_id']][$payMoneyField][$identifier]['money'],
  313. $detail['total_pay_money'],
  314. 2
  315. );
  316. }
  317. }
  318. /**
  319. * 生成数据唯一性标识符
  320. *
  321. * @param int $payType
  322. * @param string $tradeType
  323. *
  324. * @return string
  325. */
  326. public function makePaymentIdentifier(int $payType, string $tradeType): string
  327. {
  328. return $payType . ($tradeType ? '|' . $tradeType : '');
  329. }
  330. /**
  331. * 统计总数
  332. *
  333. * @param array $params
  334. *
  335. * @return array
  336. */
  337. private function getTotalNum(array $params): array
  338. {
  339. $baseWhere = [
  340. [
  341. 'status' => StatusEnum::ENABLED,
  342. 'mall_id' => $this->mall_id,
  343. ]
  344. ];
  345. $select = 'SUM(total_refund_money) total_refund_money, SUM(total_pay_money) total_pay_money';
  346. // 有条件则过滤某个支付方式,没条件则不过滤
  347. $findStatisticsIdWhere = [];
  348. $this->getAllPayTypeRefundListWhere($params, $findStatisticsIdWhere);
  349. // 得到statistics_id
  350. if ($findStatisticsIdWhere) {
  351. $findStatisticsIdWhere = array_merge($findStatisticsIdWhere, $baseWhere);
  352. $paymentTypeRefundStatisticsFind = PaymentTypeRefundStatistics::find();
  353. PaymentTypeRefundStatistics::paramPack([
  354. 'where' => $findStatisticsIdWhere,
  355. 'select' => 'id'
  356. ], $paymentTypeRefundStatisticsFind);
  357. $baseWhere = array_merge($baseWhere, [['in', 'statistics_id', $paymentTypeRefundStatisticsFind->column()]]);
  358. }
  359. // 累计总额
  360. $cumulativeDetails = PaymentTypeRefundStatisticsDetails::getOne(
  361. $baseWhere,
  362. true,
  363. [],
  364. null,
  365. $select
  366. );
  367. // 按时间统计总额
  368. $dateDetails = PaymentTypeRefundStatisticsDetails::getOne(
  369. array_merge($baseWhere, !empty($params['daterange']) ? [['between', 'date', $params['daterange'][0], $params['daterange'][1]]] : []),
  370. true,
  371. [],
  372. null,
  373. $select
  374. );
  375. return compact('cumulativeDetails', 'dateDetails');
  376. }
  377. /**
  378. * 导出支付通道统计
  379. *
  380. * @return void
  381. */
  382. public function allPayTypeRefundExport(array $data)
  383. {
  384. ini_set('memory_limit', '1024M');
  385. $download_id = $data['download_id'] ?? 0;
  386. if (!$download_id) {
  387. return;
  388. }
  389. $download = Download::findOne(['id' => $download_id]);
  390. if (!$download) {
  391. return;
  392. }
  393. $params = json_decode($download->params, true);
  394. $this->mall_id = $download->mall_id;
  395. $list = $this->allPayTypeRefund($params, true);
  396. $exportData = [];
  397. // 组装导出格式
  398. foreach ($list as $item) {
  399. $exportData[] = [
  400. 'payment_identifier' => $item['payment_identifier_text'],
  401. 'total_refund_cumulative' => implode('、', $this->mergeLabelAndMoney($item['total_refund_cumulative_arr'])),
  402. 'total_pay_cumulative' => implode('、', $this->mergeLabelAndMoney($item['total_pay_cumulative_arr'])),
  403. 'total_refund' => implode('、', $this->mergeLabelAndMoney($item['total_refund_arr'])),
  404. 'total_pay' => implode('、', $this->mergeLabelAndMoney($item['total_pay_arr'])),
  405. ];
  406. }
  407. unset($goodsList, $statisticsTeam);
  408. $header = [];
  409. $this->getExportInfoByExportField($exportData, $header, $params['export_field'], $this->getAllPayTypeRefundExportField());
  410. try {
  411. $csv = new CsvTool();
  412. $csv->filename = $download->name;
  413. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($download->type);
  414. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  415. $csv->header = $header;
  416. $csv->data = $exportData;
  417. $csv->export();
  418. $csv->updateDown($download, $this->mall_id);
  419. } catch (\Exception $e) {
  420. $download->status = 3;
  421. $download->save();
  422. \Yii::$app->custom->logs(FormatHelper::exception($e, '导出售后统计数据失败'), compact('data'));
  423. return;
  424. }
  425. }
  426. /**
  427. * [['label' => '标题', 'money': 1], ['label' => '标题', 'money': 1]] 转为 ['标题: 1', '标题: 1']
  428. *
  429. * @param array $labelMoneyArr
  430. *
  431. * @return array
  432. */
  433. private function mergeLabelAndMoney(array $labelMoneyArr): array
  434. {
  435. $arr = [];
  436. foreach ($labelMoneyArr as $item) {
  437. $arr[] = $item['label'] . ':' . $item['money'];
  438. }
  439. return $arr;
  440. }
  441. /**
  442. * @param array $params
  443. * @param bool $isGetAll
  444. *
  445. * @return array
  446. */
  447. public function allPayTypeRefundDetails(array $params, bool $isGetAll = false): array
  448. {
  449. $baseWhere = [
  450. ['status' => StatusEnum::ENABLED, 'mall_id' => $this->mall_id]
  451. ];
  452. // 默认本月
  453. $params['daterange'] = !empty($params['daterange']) ? $params['daterange'] : array_values(DateHelper::thisMonth());
  454. // 得到当前支付方式退款的记录
  455. $PaymentTypeRefundStatisticsLogsFind = PaymentTypeRefundStatisticsLogs::find();
  456. PaymentTypeRefundStatisticsLogs::paramPack([
  457. 'where' => array_merge($baseWhere, [['statistics_id' => $params['id']]]),
  458. 'select' => 'refund_id',
  459. ], $PaymentTypeRefundStatisticsLogsFind);
  460. $params['refund_ids'] = $PaymentTypeRefundStatisticsLogsFind->column();
  461. $where = [];
  462. $this->getAllPayTypeRefundListDetailsWhere($baseWhere, $params, $where);
  463. $where = array_merge($where, $baseWhere);
  464. $with = [
  465. 'order' => function ($query) {
  466. $query
  467. ->select('id, order_no, shipping_money, out_trade_no')
  468. ->with([
  469. 'trade' => function ($query) {
  470. $query
  471. ->select('out_trade_no, id, is_pay_group, pay_type, trade_type')
  472. ->with([
  473. 'tradeItem' => function ($query) {
  474. $query
  475. ->orderBy('pay_type')
  476. ->select('id, trade_id, pay_type, pay_fee, trade_type')
  477. ->with([
  478. 'paymentTradeItemRefund' => function ($query) {
  479. $query->select('refund_fee, item_id');
  480. }
  481. ]);
  482. }
  483. ]);
  484. }
  485. ]);
  486. },
  487. 'base_user'
  488. ];
  489. $listParams = [
  490. 'where' => $where,
  491. 'select' => 'id, user_id, order_id, reality_refund_price, order_no, refund_at, created_at, refund_status',
  492. 'with' => $with
  493. ];
  494. if ($isGetAll) {
  495. $list = OrderRefund::lists($listParams);
  496. $this->formatAllPayTypeRefundDetailsList($list);
  497. } else {
  498. $list = OrderRefund::listPage($listParams);
  499. $this->formatAllPayTypeRefundDetailsList($list['list']);
  500. // 返回总数
  501. $list['total_num'] = $this->getOrderRefundTotalNum($where);
  502. }
  503. return $list;
  504. }
  505. /**
  506. * 组装支付方式退款统计明细
  507. *
  508. * @param array $baseWhere
  509. * @param array $params
  510. * @param array $where
  511. *
  512. * @return void
  513. */
  514. private function getAllPayTypeRefundListDetailsWhere(array $baseWhere, array $params, array &$where)
  515. {
  516. $where[] = ['in', 'id', $params['refund_ids']];
  517. // 用户id 手机号 搜索
  518. if (!empty($params['user_keyword'])) {
  519. $userFind = User::find();
  520. User::paramPack([
  521. 'select' => 'id',
  522. 'where' => array_merge(
  523. $baseWhere,
  524. [
  525. [
  526. 'OR',
  527. ['=', 'id', $params['user_keyword']],
  528. ['like', 'mobile', $params['user_keyword']],
  529. ]
  530. ]
  531. )
  532. ], $userFind);
  533. $userIds = $userFind->column();
  534. $where[] = ['in', 'user_id', $userIds];
  535. }
  536. if ($params['daterange']) {
  537. array_walk($params['daterange'], function (&$item) {
  538. $item = !is_numeric($item) ? strtotime($item) : $item;
  539. });
  540. $where[] = ['between', 'refund_at', $params['daterange'][0], $params['daterange'][1]];
  541. }
  542. }
  543. /**
  544. * @param array $list
  545. *
  546. * @return void
  547. */
  548. private function formatAllPayTypeRefundDetailsList(array &$list)
  549. {
  550. array_walk($list, function (&$item) {
  551. $item['created_at_text'] = date('Y-m-d H:i:s', $item['created_at']);
  552. $item['refund_at_text'] = date('Y-m-d H:i:s', $item['refund_at']);
  553. $item['refund_status_text'] = RefundStatusEnum::getMap()[$item['refund_status']] ?? '-';
  554. // 存储显示的支付通道
  555. $paymentIdentifier= [];
  556. if (!empty($item['order']['trade']['is_pay_group'])) {
  557. if ($item['payment_identifier_text'] !== '') {
  558. $item['payment_identifier_text'] .= '(组合)';
  559. }
  560. // 显示组合支付每种支付类型的支付的价格
  561. if (!empty($item['order']['trade']['tradeItem'])) {
  562. foreach ($item['order']['trade']['tradeItem'] as &$tradeItem) {
  563. $tradeItem['payment_text'] = $this->getPaymentTypeAndTradeTypeText((int)$tradeItem['pay_type'], $tradeItem['trade_type']);
  564. $paymentIdentifier[] = $tradeItem['payment_text'];
  565. }
  566. }
  567. } else {
  568. $paymentIdentifier[] = $this->getPaymentTypeAndTradeTypeText((int)$item['order']['trade']['pay_type'], $item['order']['trade']['trade_type']);
  569. }
  570. $item['payment_identifier_text'] = implode(' + ', $paymentIdentifier);
  571. });
  572. }
  573. /**
  574. * 导出支付通道统计明细
  575. *
  576. * @return void
  577. */
  578. public function allPayTypeRefundDetailsExport(array $data)
  579. {
  580. ini_set('memory_limit', '1024M');
  581. $download_id = $data['download_id'] ?? 0;
  582. if (!$download_id) {
  583. return;
  584. }
  585. $download = Download::findOne(['id' => $download_id]);
  586. if (!$download) {
  587. return;
  588. }
  589. $params = json_decode($download->params, true);
  590. $this->mall_id = $download->mall_id;
  591. $list = $this->allPayTypeRefundDetails($params, true);
  592. $exportData = [];
  593. // 组装导出格式
  594. foreach ($list as $item) {
  595. $exportData[] = [
  596. 'user' => sprintf('昵称:%s 手机号:%s', $item['base_user']['nickname'] ?? '', $item['base_user']['mobile'] ?? ''),
  597. 'payment_identifier' => $item['payment_identifier_text'] ?? '',
  598. 'pay_money' => $item['order']['pay_money'] ?? 0,
  599. 'shipping_money' => $item['order']['shipping_money'] ?? 0,
  600. 'reality_refund_price' => $item['reality_refund_price'] ?? 0,
  601. 'order_no' => $item['order']['order_no'] ?? '',
  602. 'refund_no' => $item['order_no'] ?? '',
  603. 'refund_at' => $item['refund_at_text'] ?? '',
  604. 'refund_status' => $item['refund_status_text'] ?? '',
  605. ];
  606. }
  607. unset($goodsList, $statisticsTeam);
  608. if (empty($exportData)) {
  609. return;
  610. }
  611. // 得到导出标题
  612. $header = [];
  613. $this->getExportInfoByExportField($exportData, $header, $params['export_field'], $this->getAllPayTypeRefundDetailsExportField());
  614. try {
  615. $csv = new CsvTool();
  616. $csv->filename = $download->name;
  617. $csv->file_dirname = DownloadEnum::getTypeStorageDirMap($download->type);
  618. $csv->export_mode = CsvTool::EXPORT_MODE_ASYNC;
  619. $csv->header = $header;
  620. $csv->data = $exportData;
  621. $csv->export();
  622. $csv->updateDown($download, $this->mall_id);
  623. } catch (\Exception $e) {
  624. $download->status = 3;
  625. $download->save();
  626. \Yii::$app->custom->logs(FormatHelper::exception($e, '导出售后统计数据失败'), compact('data'));
  627. return;
  628. }
  629. }
  630. /**
  631. * 导出支付通道统计数据可选择的字段
  632. *
  633. * @return array[]
  634. */
  635. public function getAllPayTypeRefundExportField()
  636. {
  637. return [
  638. [
  639. 'label' => '支付通道',
  640. 'value' => 'payment_identifier'
  641. ],
  642. [
  643. 'label' => '累计退款总额',
  644. 'value' => 'total_refund_cumulative'
  645. ],
  646. [
  647. 'label' => '累计实付总额',
  648. 'value' => 'total_pay_cumulative'
  649. ],
  650. [
  651. 'label' => '售后退款总额',
  652. 'value' => 'total_refund'
  653. ],
  654. [
  655. 'label' => '原实付总额',
  656. 'value' => 'total_pay'
  657. ],
  658. ];
  659. }
  660. /**
  661. * 导出支付通道统计明细数据时可选择的字段
  662. *
  663. * @return array[]
  664. */
  665. public function getAllPayTypeRefundDetailsExportField(): array
  666. {
  667. return [
  668. [
  669. 'label' => '用户',
  670. 'value' => 'user'
  671. ],
  672. [
  673. 'label' => '支付通道',
  674. 'value' => 'payment_identifier'
  675. ],
  676. [
  677. 'label' => '原实付金额',
  678. 'value' => 'pay_money'
  679. ],
  680. [
  681. 'label' => '运费',
  682. 'value' => 'shipping_money'
  683. ],
  684. [
  685. 'label' => '退款金额',
  686. 'value' => 'reality_refund_price'
  687. ],
  688. [
  689. 'label' => '订单编号',
  690. 'value' => 'order_no'
  691. ],
  692. [
  693. 'label' => '售后单号',
  694. 'value' => 'refund_no'
  695. ],
  696. [
  697. 'label' => '售后申请时间',
  698. 'value' => 'refund_at'
  699. ],
  700. [
  701. 'label' => '售后状态',
  702. 'value' => 'refund_status'
  703. ]
  704. ];
  705. }
  706. /**
  707. * 返回需要导出的指定字段
  708. *
  709. * @param array $exportData
  710. * @param array $header
  711. * @param array $exportField
  712. * @param array $defaultExportField
  713. *
  714. * @return void
  715. */
  716. private function getExportInfoByExportField(array &$exportData, array &$header, array $exportField, array $defaultExportField)
  717. {
  718. $keyMap = array_column($defaultExportField, 'label', 'value');
  719. // 得到具体要导出的字段
  720. $defaultExportField = array_column($defaultExportField, 'value');
  721. $exportField = !empty($exportField) && array_intersect($exportField, $defaultExportField) ? $exportField : $defaultExportField;
  722. // 按照要导出的字段重新过滤
  723. if (count($exportData[0]) !== count($exportField)) {
  724. array_walk($exportData, function (&$item) use ($exportField) {
  725. $res = [];
  726. foreach ($exportField as $field) {
  727. if (isset($item[$field])) {
  728. $res[$field] = $item[$field];
  729. }
  730. }
  731. $item = $res;
  732. });
  733. }
  734. // 得到导出标题
  735. $exportDataKey = array_keys($exportData[0]);
  736. foreach ($exportDataKey as $key) {
  737. $header[] = $keyMap[$key] ?? '';
  738. }
  739. }
  740. /**
  741. * 在订单退款表查询统计数据
  742. *
  743. * @param array $where
  744. *
  745. * @return array
  746. */
  747. private function getOrderRefundTotalNum(array $where)
  748. {
  749. $dateDetails = $this->getTotalRefundMoneyAndTotalPayMoney($where);
  750. // 累积的,忽略时间筛选
  751. foreach ($where as $key => $item) {
  752. /*
  753. * 这个查询是这个格式
  754. * [
  755. * 0 => 'between'
  756. * 1 => 'refund_at'
  757. * 2 => 1730390400
  758. * 3 => 1732982399
  759. * ]
  760. */
  761. if (isset($item[1]) && $item[1] === 'refund_at') {
  762. unset($where[$key]);
  763. break;
  764. }
  765. }
  766. $cumulativeDetails = $this->getTotalRefundMoneyAndTotalPayMoney($where);
  767. return compact('cumulativeDetails', 'dateDetails');
  768. }
  769. /**
  770. * @param array $where
  771. *
  772. * @return array
  773. */
  774. private function getTotalRefundMoneyAndTotalPayMoney(array $where): array
  775. {
  776. $defaultRes = [
  777. 'total_refund_money' => 0,
  778. 'total_pay_money' => 0,
  779. ];
  780. $orderRefundFind = OrderRefund::find();
  781. OrderRefund::paramPack([
  782. 'select' => 'SUM(reality_refund_price) total_refund_money, GROUP_CONCAT(order_id) order_ids',
  783. 'where' => $where
  784. ], $orderRefundFind);
  785. $refundOne = $orderRefundFind->asArray()->one();
  786. if (empty($refundOne)) {
  787. return $defaultRes;
  788. }
  789. $defaultRes['total_refund_money'] = $refundOne['total_refund_money'];
  790. $orderIds = explode(',', $refundOne['order_ids']);
  791. if (empty($orderIds)) {
  792. return $defaultRes;
  793. }
  794. $defaultRes['total_pay_money'] = Order::find()
  795. ->where([
  796. 'status' => StatusEnum::ENABLED,
  797. 'mall_id' => $this->mall_id,
  798. 'id' => $orderIds
  799. ])
  800. ->sum('pay_money');
  801. return $defaultRes;
  802. }
  803. }