RefundController.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. /**
  3. * 用途:商城顾客售后申请接口
  4. * 谁用:mallApp 申请售后页、退款售后列表/详情
  5. * 解决:顾客提交/查看/撤销售后申请;列表展示店铺与商品快照;真正退款由商家在 hdApp 审核通过后执行
  6. */
  7. namespace mall\controllers;
  8. use bizHd\distribution\classes\DistributionOrderClass;
  9. use bizHd\order\classes\OrderClass as HdOrderClass;
  10. use bizHd\refund\classes\HdRefundClass;
  11. use bizHd\refund\classes\HdRefundMallClass;
  12. use bizMall\order\classes\OrderClass;
  13. use common\components\util;
  14. use Yii;
  15. class RefundController extends BaseController
  16. {
  17. /**
  18. * 申请页初始化数据:订单商品 + 可退金额 + 已有申请进度
  19. */
  20. public function actionGetRefundData()
  21. {
  22. $id = (int)Yii::$app->request->get('id', 0);
  23. $order = OrderClass::getOrderById($id);
  24. if (empty($order)) {
  25. util::fail('没有找到订单');
  26. }
  27. $this->assertOrderOwner($order);
  28. $couldRefundPrice = (float)HdRefundMallClass::remainRefundCash($order);
  29. $apply = HdRefundClass::getLatestByOrderId($id);
  30. if (!empty($apply) && is_array($apply)) {
  31. $apply['statusText'] = HdRefundMallClass::statusText($apply['status'] ?? 0);
  32. if (!empty($apply['product']) && is_string($apply['product'])) {
  33. $apply['productList'] = json_decode($apply['product'], true) ?: [];
  34. }
  35. }
  36. // 分销售后截止提示(仅提示,真正拦截在提交时)
  37. $refundDeadlineTip = '';
  38. if (DistributionOrderClass::isDistOrderSettled($id)) {
  39. $refundDeadlineTip = '该订单分销佣金已结算,无法申请售后';
  40. } else {
  41. $distOrder = DistributionOrderClass::getByCondition(['orderId' => $id]);
  42. if (!empty($distOrder)) {
  43. $finishTime = $distOrder['finishTime'] ?? '';
  44. if (!empty($finishTime) && $finishTime !== '0000-00-00 00:00:00') {
  45. $rule = \bizHd\distribution\classes\DistributionRuleClass::getRule(
  46. (int)($order['shopId'] ?? 0),
  47. (int)($order['mainId'] ?? $this->mainId)
  48. );
  49. $settleDays = max(0, (int)($rule['settleDays'] ?? 0));
  50. $deadline = date('Y-m-d H:i', strtotime($finishTime) + $settleDays * 86400);
  51. $refundDeadlineTip = "分销订单完成后{$settleDays}天内可申请售后,截止:{$deadline}";
  52. }
  53. }
  54. }
  55. util::success([
  56. 'orderInfo' => $order,
  57. 'goodsInfoList' => $order['goodsInfoList'] ?? [],
  58. 'itemInfoList' => $order['itemList'] ?? [],
  59. 'couldRefundPrice' => $couldRefundPrice,
  60. 'apply' => $apply,
  61. 'refundDeadlineTip' => $refundDeadlineTip,
  62. 'canApply' => $this->canApplyRefund($order, $apply),
  63. ]);
  64. }
  65. /**
  66. * 顾客提交售后申请(仅落待审核记录)
  67. * 若该订单最新申请为已取消,则把原记录状态改回待审核,不新开一条
  68. */
  69. public function actionCreateOrder()
  70. {
  71. $post = Yii::$app->request->post();
  72. $id = (int)($post['id'] ?? 0);
  73. util::checkRepeatCommit('mall_refund_' . $id, 5);
  74. $order = HdOrderClass::getById($id, true);
  75. if (empty($order)) {
  76. util::fail('没有找到订单');
  77. }
  78. $this->assertOrderOwner($order);
  79. if ((int)$order->payStatus !== 1) {
  80. util::fail('订单未付款,无法申请售后');
  81. }
  82. if ((int)$order->status === HdOrderClass::ORDER_STATUS_UN_PAY) {
  83. util::fail('待付款订单无法申请售后');
  84. }
  85. if ((int)$order->status === HdOrderClass::ORDER_STATUS_CANCEL) {
  86. util::fail('已取消订单无法申请售后');
  87. }
  88. // 前端 ORDER_STATUS 含已退款=6,全额退完后可能落此状态
  89. if ((int)$order->status === 6) {
  90. util::fail('已退款订单无法申请售后');
  91. }
  92. if ((int)($order->forward ?? 0) === 1) {
  93. util::fail('售后收入单无法申请售后');
  94. }
  95. if (HdRefundClass::hasPendingByOrderId($id)) {
  96. util::fail('已有待审核的售后申请,请勿重复提交');
  97. }
  98. // 分销关联订单:完成后超过 settleDays 不可再申请
  99. DistributionOrderClass::assertRefundNotExpired(
  100. $id,
  101. (int)$order->shopId,
  102. (int)($order->mainId ?? $this->mainId)
  103. );
  104. $connection = Yii::$app->db;
  105. $transaction = $connection->beginTransaction();
  106. try {
  107. $extra = [
  108. 'userId' => (int)$this->userId,
  109. 'hdId' => (int)$this->hdId,
  110. 'customId' => (int)$this->customId,
  111. 'shopId' => (int)$order->shopId,
  112. 'mainId' => (int)($order->mainId ?? $this->mainId),
  113. ];
  114. $apply = HdRefundMallClass::createApply($post, $order, $extra);
  115. $transaction->commit();
  116. util::success([
  117. 'id' => (int)$apply->id,
  118. 'status' => (int)$apply->status,
  119. 'statusText' => HdRefundMallClass::statusText($apply->status),
  120. ], '提交成功,请等待商家审核');
  121. } catch (\Exception $e) {
  122. $transaction->rollBack();
  123. Yii::info('商城售后申请失败:' . $e->getMessage());
  124. // util::fail 已输出时不会走到这里;其它异常统一提示
  125. if (strpos($e->getMessage(), '操作失败') === false) {
  126. util::fail($e->getMessage() ?: '申请失败');
  127. }
  128. util::fail('申请失败');
  129. }
  130. }
  131. /**
  132. * 顾客售后申请列表(mallApp 退款售后 Tab)
  133. * status:all 全部;0/pending 处理中;1/refunded 已退款;closed 已关闭(驳回+取消)
  134. */
  135. public function actionList()
  136. {
  137. $tab = Yii::$app->request->get('status', 'all');
  138. if ($tab === '' || $tab === null) {
  139. $tab = 'all';
  140. }
  141. $list = HdRefundMallClass::getCustomApplyList((int)$this->userId, (string)$tab);
  142. util::success($list);
  143. }
  144. /**
  145. * 顾客售后申请详情(列表「查看详情」)
  146. */
  147. public function actionDetail()
  148. {
  149. $id = (int)Yii::$app->request->get('id', 0);
  150. $apply = HdRefundClass::getById($id, true);
  151. HdRefundClass::validByCustom($apply, (int)$this->customId, (int)$this->userId);
  152. $data = HdRefundMallClass::formatCustomDetail($apply->toArray());
  153. util::success($data);
  154. }
  155. /**
  156. * 顾客撤销待审核申请
  157. */
  158. public function actionCancelOrder()
  159. {
  160. $post = Yii::$app->request->post();
  161. $id = (int)($post['id'] ?? 0);
  162. $apply = HdRefundClass::getById($id, true);
  163. HdRefundClass::validByCustom($apply, (int)$this->customId, (int)$this->userId);
  164. $connection = Yii::$app->db;
  165. $transaction = $connection->beginTransaction();
  166. try {
  167. HdRefundMallClass::cancel($apply);
  168. $transaction->commit();
  169. util::complete('已撤销申请');
  170. } catch (\Exception $e) {
  171. $transaction->rollBack();
  172. Yii::info('撤销售后申请失败:' . $e->getMessage());
  173. util::fail($e->getMessage() ?: '撤销失败');
  174. }
  175. }
  176. /**
  177. * 校验订单归属当前登录顾客
  178. * @param array|object $order
  179. */
  180. protected function assertOrderOwner($order)
  181. {
  182. $customId = is_object($order) ? (int)($order->customId ?? 0) : (int)($order['customId'] ?? 0);
  183. $userId = is_object($order) ? (int)($order->userId ?? 0) : (int)($order['userId'] ?? 0);
  184. if ($customId > 0 && (int)$this->customId > 0 && $customId === (int)$this->customId) {
  185. return;
  186. }
  187. // 兼容早期仅绑 userId 的订单
  188. if ($userId > 0 && $userId === (int)$this->userId) {
  189. return;
  190. }
  191. util::fail('不是你的订单,无法操作');
  192. }
  193. /**
  194. * 是否允许新开售后申请
  195. * 现金为 0 时:红包仍挂单(hbId>0)且红包金额大于订单实付(hbAmount>mainPay)才允许申请
  196. * @param array $order
  197. * @param array|null $apply
  198. * @return int 1可申请 0不可
  199. */
  200. protected function canApplyRefund($order, $apply)
  201. {
  202. if ((int)($order['payStatus'] ?? 0) !== 1) {
  203. return 0;
  204. }
  205. $status = (int)($order['status'] ?? 0);
  206. if (in_array($status, [HdOrderClass::ORDER_STATUS_UN_PAY, HdOrderClass::ORDER_STATUS_CANCEL, 6], true)) {
  207. return 0;
  208. }
  209. if ((int)($order['forward'] ?? 0) === 1) {
  210. return 0;
  211. }
  212. if (!empty($apply) && (int)($apply['status'] ?? -1) === HdRefundClass::STATUS_PENDING) {
  213. return 0;
  214. }
  215. if (DistributionOrderClass::isDistOrderSettled((int)($order['id'] ?? 0))) {
  216. return 0;
  217. }
  218. // 可退现金>0 可申请;现金为 0 时需红包未退回且红包金额大于实付
  219. if (!HdRefundMallClass::hasRefundableAmount($order)) {
  220. return 0;
  221. }
  222. return 1;
  223. }
  224. }