You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

384 lines
14 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. <?php
  2. namespace App\JsonRpc;
  3. use App\Commons\Log;
  4. use App\Constants\v3\ErrorCode;
  5. use App\Constants\v3\LogLabel;
  6. use App\Constants\v3\OrderState;
  7. use App\Exception\ErrorCodeException;
  8. use App\Model\v3\Order;
  9. use App\Model\v3\OrderGoods;
  10. use App\Model\v3\OrderMain;
  11. use App\Model\v3\User;
  12. use App\Service\v3\Interfaces\FinancialRecordServiceInterface;
  13. use App\Service\v3\Interfaces\MiniprogramServiceInterface;
  14. use App\Service\v3\Interfaces\OrderOnlineServiceInterface;
  15. use App\Service\v3\Interfaces\SeparateAccountsServiceInterface;
  16. use EasyWeChat\Factory;
  17. use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
  18. use Hyperf\DbConnection\Db;
  19. use Hyperf\Guzzle\CoroutineHandler;
  20. use Hyperf\RpcServer\Annotation\RpcService;
  21. use Hyperf\Di\Annotation\Inject;
  22. /**
  23. * @RpcService(name="OrdersService", protocol="jsonrpc-http", server="jsonrpc-http", publishTo="")
  24. */
  25. class OrdersService implements OrdersServiceInterface
  26. {
  27. /**
  28. * @Inject
  29. * @var Log
  30. */
  31. protected $log;
  32. /**
  33. * @Inject
  34. * @var OrderOnlineServiceInterface
  35. */
  36. protected $orderOnlineService;
  37. /**
  38. * @Inject
  39. * @var SeparateAccountsServiceInterface
  40. */
  41. protected $separateAccountsService;
  42. /**
  43. * @Inject
  44. * @var FinancialRecordServiceInterface
  45. */
  46. protected $financialRecordService;
  47. /**
  48. * @Inject
  49. * @var \App\Service\v3\Interfaces\BadgeServiceInterface
  50. */
  51. protected $badgeService;
  52. /**
  53. * @Inject
  54. * @var MiniprogramServiceInterface
  55. */
  56. protected $miniprogramService;
  57. /**
  58. * 订单完成
  59. * @param $global_order_id
  60. * @param $user_id
  61. * @return array
  62. */
  63. public function onlineComplete($global_order_id, $user_id)
  64. {
  65. Db::beginTransaction();
  66. try {
  67. $this->orderOnlineService->doComplete($global_order_id, $user_id);
  68. $this->separateAccountsService->orderOnlineCompleted($global_order_id, $user_id);
  69. Db::commit();
  70. return [
  71. "status" => 200,
  72. "code" => 0,
  73. "result" => [],
  74. "message" => '处理成功'
  75. ];
  76. } catch (\Exception $e) {
  77. Db::rollBack();
  78. $this->log->event(LogLabel::ORDER_COMPLETE_LOG, ['jsonrpc_order_service_exception_onlineComplete' => $e->getMessage(), 'params' => json_encode([$global_order_id, $user_id])]);
  79. return [
  80. "status" => 200,
  81. "code" => $e->getCode() ?? ErrorCode::ORDER_COMPLETE_FAIL,
  82. "result" => [],
  83. "message" => $e->getMessage()
  84. ];
  85. }
  86. }
  87. /**
  88. * 线上订单退款,整个订单退,这个是专门用于处理用户的申请退款的同意退款操作
  89. * @param $global_order_id
  90. * @param $user_id
  91. * @return array
  92. */
  93. public function onlineRefund($global_order_id, $user_id)
  94. {
  95. try {
  96. $result = $this->orderOnlineService->doRefund($global_order_id, $user_id);
  97. return [
  98. "status" => 200,
  99. "code" => 0,
  100. "result" => [],
  101. "message" => '处理成功'
  102. ];
  103. } catch (\Exception $e) {
  104. return [
  105. "status" => 200,
  106. "code" => $e->getCode(),
  107. "result" => [],
  108. "message" => $e->getMessage()
  109. ];
  110. }
  111. }
  112. /**
  113. * 线上订单单笔退款,主要用于后台强行操作退单退款
  114. * 支持单商品、单店、整单
  115. * 按比例计算红包进行退款
  116. * 比如:两个子订单和子订单商品,分别是2元,98元,使用了10元优惠券
  117. * 退2元商品时,退款金额为
  118. * 红包 (2/(98+2)*10 = 0.2
  119. * 退款:2-0.2=1.8
  120. * @param $user_id *用户ID
  121. * @param $global_order_id *全局总订单ID
  122. * @param $order_child_id *主订单ID,
  123. * @param $order_goods_id *订单商品ID
  124. * @param $note
  125. * @return array
  126. */
  127. public function onlineSingleRefund($user_id, $note, $global_order_id, $order_child_id='', $order_goods_id='')
  128. {
  129. Db::beginTransaction();
  130. try {
  131. $params = [
  132. 'user_id' => $user_id,
  133. 'note' => $note,
  134. 'global_order_id' => $global_order_id,
  135. 'order_child_id' => $order_child_id,
  136. 'order_goods_id' => $order_goods_id,
  137. ];
  138. if (!$user_id || !$global_order_id || !$note) {
  139. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  140. 'jsonrpc_order_service_exception_onlineSingleRefund' => '参数不对',
  141. 'params' => json_encode($params)
  142. ]);
  143. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  144. }
  145. // 主订单
  146. $orderMain = OrderMain::query()
  147. ->where(['global_order_id' => $global_order_id, 'user_id' => $user_id])
  148. ->whereIn('state', array_merge(OrderState::CAN_REFUND_DIRECT, [OrderState::REFUNDED_DIRECT]))
  149. ->first();
  150. if (is_null($orderMain)) {
  151. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  152. 'jsonrpc_order_service_exception_onlineSingleRefund' => '订单不存在',
  153. 'params' => json_encode($params)
  154. ]);
  155. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  156. }
  157. // 子订单
  158. $orderChild = null;
  159. if ($order_child_id) {
  160. $orderChild = Order::query()->with('store')->where(['order_main_id' => $orderMain->global_order_id, 'id' => $order_child_id])->first();
  161. }
  162. // 订单商品
  163. $orderGoods = null;
  164. if ($order_goods_id) {
  165. if (!$order_child_id || is_null($orderChild)) {
  166. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  167. 'jsonrpc_order_service_exception_onlineSingleRefund' => '子订单参数异常[单品]',
  168. 'params' => json_encode($params)
  169. ]);
  170. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  171. }
  172. $orderGoods = OrderGoods::query()
  173. ->where(['order_id' => $orderChild->id, 'id' => $order_goods_id, 'status' => 1])
  174. ->first();
  175. if (is_null($orderGoods)) {
  176. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  177. 'jsonrpc_order_service_exception_onlineSingleRefund' => '订单商品参数异常[单品]',
  178. 'params' => json_encode($params)
  179. ]);
  180. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  181. }
  182. }
  183. $totalAmount = $orderMain->total_money; // 订单可退款金额,总订单金额不含配送费和服务费
  184. $preRefundAmount = 0; // 预退款金额
  185. $refundAmount = 0; // 实际退款金额
  186. $refundType = 'main'; // 退款类型, Main整单 Sub子单 Goods单品
  187. if ($orderGoods) { // 1. 如果订单商品存在则说明要退单品
  188. $preRefundAmount = bcmul($orderGoods->price, $orderGoods->number, 2);
  189. $refundType = 'goods';
  190. } elseif ($orderChild) { // 2. 否则如果存在子订单说明退子订单
  191. $preRefundAmount = $orderChild->money;
  192. $refundType = 'sub';
  193. } else { // 3. 再则如果存在主订单说明退主订单
  194. $preRefundAmount = $orderMain->total_money;
  195. $refundType = 'main';
  196. }
  197. // 占订单金额的比例
  198. $rate = bcdiv($preRefundAmount, $totalAmount, 6);
  199. // 计算优惠券所占金额
  200. $couponMoney = bcmul($orderMain->coupon_money, $rate, 6);
  201. // 计算本次退款实际退款金额
  202. $refundAmount = bcsub($preRefundAmount, $couponMoney, 2);
  203. if ($refundAmount <= 0) {
  204. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  205. 'jsonrpc_order_service_exception_onlineSingleRefund' => '没有可退款金额[实际退款金额]',
  206. 'params' => json_encode($params)
  207. ]);
  208. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  209. }
  210. // 开始退款
  211. $config = config('wxpay');
  212. $app = Factory::payment($config);
  213. $app['guzzle_handler'] = CoroutineHandler::class;
  214. $result = $app->refund->byOutTradeNumber(
  215. $orderMain->global_order_id,
  216. $orderMain->global_order_id.$order_child_id.$order_goods_id,
  217. bcmul($orderMain->money, 100, 0),
  218. bcmul($refundAmount, 100, 0),
  219. [
  220. 'refund_desc' => '订单协商退款[' . $refundType . ']',
  221. 'notify_url' => config('wechat.notify_url.refund_single'),
  222. ]
  223. );
  224. if ($result['return_code'] == 'FAIL') {
  225. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  226. 'jsonrpc_order_service_exception_onlineSingleRefund' => $result['return_msg'] . '[微信退款失败]',
  227. 'params' => json_encode($result)
  228. ]);
  229. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  230. }
  231. if ($result['result_code'] == 'FAIL') {
  232. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  233. 'jsonrpc_order_service_exception_onlineSingleRefund' => $result['err_code_des'] . '[微信退款失败]' . $result['err_code'],
  234. 'params' => json_encode($result)
  235. ]);
  236. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  237. }
  238. // 退款申请成功,查询退款状态,此处暂时无法传递和记录单品退的信息,所以直接查询退款状态处理,不做回调
  239. $refundResult = $app->refund->queryByRefundId($result['refund_id']);
  240. $this->log->event(LogLabel::ORDER_REFUND_LOG, [
  241. 'jsonrpc_order_service_exception_onlineSingleRefund' => '[微信退款查询]',
  242. 'params' => json_encode($refundResult)
  243. ]);
  244. // 退款成功
  245. if (
  246. !($refundResult['return_code'] == 'SUCCESS'
  247. && isset($refundResult['result_code'])
  248. && $refundResult['result_code'] == 'SUCCESS')
  249. ) {
  250. throw new ErrorCodeException(ErrorCode::ORDER_REFUND_FAIL);
  251. }
  252. $currentTime = time();
  253. // 处理订单状态
  254. $orderMain->state = OrderState::REFUNDED_DIRECT;
  255. $orderMain->total_refund_note = $note;
  256. $orderMain->refund_time = $currentTime;
  257. $orderMain->save();
  258. if ($refundType == 'sub') {
  259. $orderChild->status = 3;
  260. $orderChild->refund_note = $note;
  261. $orderChild->refund_time = $currentTime;
  262. $orderChild->save();
  263. } elseif ($refundType == 'goods') {
  264. $orderGoods->status = 3;
  265. $orderGoods->refund_note = $note;
  266. $orderGoods->refund_time = $currentTime;
  267. $orderGoods->save();
  268. }
  269. // 处理用户和商户流水
  270. $orderChildren = [];
  271. if ($refundType == 'main') { # 整单退的话还得处理所有子订单的商户
  272. $orderChildren = Order::query()->with('store:user_id')->where(['order_main_id' => $orderMain->global_order_id])->get();
  273. foreach ($orderChildren as $key => &$order) {
  274. // 占订单金额的比例
  275. $rate = bcdiv($order->money, $totalAmount, 6);
  276. // 计算优惠券所占金额
  277. $couponMoney = bcmul($orderMain->coupon_money, $rate, 6);
  278. // 计算本次退款实际退款金额
  279. $refundStoreAmount = bcsub($order->money, $couponMoney, 2);
  280. $this->financialRecordService->storeRefundDirect($order->store->user_id, $order->id, $refundStoreAmount);
  281. $this->miniprogramService->subscribeMsgForSingleRefund($order->id, $refundStoreAmount, '', $note);
  282. }
  283. } elseif ($refundType == 'sub'||$refundType == 'goods') { # 退子订单或者退单品的话,商户只有一个
  284. $orderChildren = Order::query()->with('store:user_id')->where(['id' => $order_child_id])->get();
  285. $this->financialRecordService->storeRefundDirect($orderChild->store->user_id, $orderChild->id, $refundAmount);
  286. $orderGoodsId = $order_goods_id ?: '';
  287. $this->miniprogramService->subscribeMsgForSingleRefund($order_child_id, $refundAmount, $orderGoodsId, $note);
  288. }
  289. $this->financialRecordService->userRefundDirect($orderMain->user_id, $orderMain->global_order_id, $refundAmount);
  290. Db::commit();
  291. // 记录badge
  292. $orderChildIds = array_values(array_column($orderChildren->toArray(), 'store_id'));
  293. $this->badgeService->doByOrder($orderMain->user_id, $orderChildIds, $orderMain->global_order_id, OrderState::REFUNDED);
  294. return [
  295. "status" => 200,
  296. "code" => 0,
  297. "result" => [],
  298. "message" => '处理成功'
  299. ];
  300. } catch (\Exception $e) {
  301. Db::rollBack();
  302. return [
  303. "status" => 200,
  304. "code" => $e->getCode(),
  305. "result" => [],
  306. "message" => '[退款失败]'.$e->getMessage()
  307. ];
  308. }
  309. }
  310. /**
  311. * 配送开始的时候,管理后台点击配送并分配配送员
  312. * 做一些处理:
  313. * 1、发送订阅消息给用户
  314. * 2、发送订阅消息给骑手
  315. * @param $global_order_id
  316. */
  317. public function onlineDeliveryStart($global_order_id)
  318. {
  319. try {
  320. $this->miniprogramService->subscribeMsgForDeliveryStart($global_order_id);
  321. return [
  322. "status" => 200,
  323. "code" => 0,
  324. "result" => [],
  325. "message" => '处理成功'
  326. ];
  327. } catch (\Exception $e) {
  328. return [
  329. "status" => 200,
  330. "code" => $e->getCode(),
  331. "result" => [],
  332. "message" => $e->getMessage()
  333. ];
  334. }
  335. }
  336. }