OrderOper.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. <?php
  2. namespace app\api\model\order;
  3. use addons\shopro\exception\Exception;
  4. use addons\shopro\model\Cart;
  5. use addons\shopro\model\Config;
  6. use addons\shopro\model\Coupons;
  7. use addons\shopro\model\Share;
  8. use app\api\model\User;
  9. use app\api\model\GoodsComment;
  10. use app\api\model\order\Order;
  11. use addons\shopro\model\OrderAction;
  12. use addons\shopro\model\OrderItem;
  13. use addons\shopro\model\Store;
  14. use app\api\model\UserWalletLog;
  15. use addons\shopro\model\Verify;
  16. use app\admin\model\shopro\order\RefundLog;
  17. use think\Db;
  18. use think\Log;
  19. trait OrderOper
  20. {
  21. use OrderOperSendGet, OrderOperCreate;
  22. // 获取订单号
  23. public static function getSn($user_id)
  24. {
  25. $rand = $user_id < 9999 ? mt_rand(100000, 99999999) : mt_rand(100, 99999);
  26. $order_sn = date('Yhis') . $rand;
  27. $id = str_pad($user_id, (24 - strlen($order_sn)), '0', STR_PAD_BOTH);
  28. return $order_sn . $id;
  29. }
  30. // 计算订单
  31. public static function pre($params, $calc_type = 'pre')
  32. {
  33. self::$calc_type = $calc_type;
  34. $user = User::info();
  35. // 检测必要系统环境
  36. // checkEnv(['bcmath', 'queue']);
  37. // 获取请求参数 "order_type", "groupon_id", "buy_type"
  38. extract(self::preParams($params));
  39. // 检测并重新组装要购买的商品列表
  40. list(
  41. $new_goods_list, // 组合好的新的商品列表
  42. $activity_type, // 订单参与的活动类型,后面还需计算一次
  43. $activity_discounts, // 订单可能参与的所有活动
  44. $need_address, // 是否需要收货地址
  45. $user_address // 用户收货地址
  46. ) = self::preCheck($params);
  47. // 计算订单商品价格,所需积分,运费
  48. list(
  49. $new_goods_list, // 新的商品列表
  50. $goods_original_amount, // 商品原始总价
  51. $goods_amount, // 商品现在总价
  52. $dispatch_amount, // 订单运费(所有商品种最高的,后面还需根据活动再次计算)
  53. $score_amount, // 订单所需支付积分
  54. ) = self::preCalcAmount($params, $new_goods_list, $user_address);
  55. // 记录原始运费
  56. $origin_dispatch_amount = $dispatch_amount;
  57. // 计算订单优惠折扣,优惠券,邮费
  58. list(
  59. $new_goods_list, // 重新赋值活动, 商品上增加了 activity_type
  60. $activity_discount_infos, // 每个活动包含的优惠信息
  61. $activity_discount_money, // 促销活动优惠总金额
  62. $dispatch_discount_money, // 邮费总优惠金额
  63. $free_shipping_goods_ids, // 包邮的商品的ids
  64. $activity_type, // 全部参与的活动类型
  65. $dispatch_amount, // 重新计算的运费
  66. $user_coupons, // 使用的优惠券信息
  67. $coupon_money // 优惠券优惠金额
  68. ) = self::preCalcDiscount(
  69. $params,
  70. $new_goods_list,
  71. $activity_discounts,
  72. $activity_type,
  73. $goods_amount,
  74. $dispatch_amount,
  75. $user_address
  76. );
  77. // 计算订单总金额,需支付金额
  78. list(
  79. $new_goods_list,
  80. $total_amount,
  81. $discount_fee,
  82. $total_fee,
  83. $coupon_fee,
  84. ) = self::preCalcOrder(
  85. $new_goods_list,
  86. $goods_amount,
  87. $origin_dispatch_amount,
  88. $dispatch_amount,
  89. $activity_discount_infos,
  90. $activity_discount_money,
  91. $dispatch_discount_money,
  92. $free_shipping_goods_ids,
  93. $coupon_money
  94. );
  95. // 获取发票金额
  96. $invoice_amount = self::preCalcInvoiceAmount($total_fee, $goods_amount);
  97. // 处理返回结果
  98. return self::preReturnParams(
  99. $goods_original_amount,
  100. $goods_amount,
  101. $origin_dispatch_amount,
  102. $dispatch_amount,
  103. $total_amount,
  104. $total_fee,
  105. $discount_fee,
  106. $coupon_fee,
  107. $activity_discount_money,
  108. $dispatch_discount_money,
  109. $activity_type,
  110. $score_amount,
  111. $new_goods_list,
  112. $need_address,
  113. $activity_discount_infos,
  114. $user_coupons,
  115. $user_address,
  116. $invoice_amount,
  117. $user->score//用户积分
  118. );
  119. }
  120. // 获取可用优惠券列表
  121. public static function coupons($params, $goods_amount = 0)
  122. {
  123. $user = User::info();
  124. extract($params);
  125. $order_type = $order_type ?? 'goods';
  126. $groupon_id = $groupon_id ?? 0; // 拼团的 团 id
  127. $buy_type = $buy_type ?? 'alone'; // 拼团的 购买方式: alone=单独购买,groupon=开团
  128. // 商品总金额
  129. if (!$goods_amount) {
  130. // 下单的时候把计算好的 goods_amount 传进来了,接口直接获取可用列表的时候,需要计算
  131. foreach ($goods_list as $k => $goods) {
  132. $goods_amount += ($goods['goods_price'] * $goods['goods_num']);
  133. }
  134. }
  135. $coupons = Coupons::getCouponsList(Coupons::COUPONS_CAN_USE);
  136. $new_coupons = [];
  137. // 过滤,如果有一个产品不适用,则优惠券不允许使用,不显示
  138. foreach ($coupons as $key => $coupon) {
  139. if ($coupon['goods_ids'] === '0') {
  140. // 所有商品可用
  141. $can_use = true;
  142. } else {
  143. $goods_ids = explode(',', $coupon['goods_ids']);
  144. $can_use = true;
  145. foreach ($goods_list as $k => $goods) {
  146. if (!in_array($goods['goods_id'], $goods_ids)) {
  147. $can_use = false;
  148. break;
  149. }
  150. }
  151. }
  152. // 商品可用 并且 商品金额满足
  153. if ($can_use && $coupon->enough <= $goods_amount) {
  154. $new_coupons[] = $coupon;
  155. }
  156. }
  157. $new_coupons = array_values($new_coupons);
  158. return $new_coupons;
  159. }
  160. public static function createOrder($params)
  161. {
  162. $user = User::info();
  163. // 入参
  164. extract($params);
  165. $remark = $remark ?? null;
  166. $order_type = $order_type ?? 'goods';
  167. $groupon_id = $groupon_id ?? 0; // 拼团的 团 id
  168. $buy_type = $buy_type ?? 'alone'; // 拼团的 购买方式: alone=单独购买,groupon=开团
  169. $invoice = $invoice ?? null; // 发票申请
  170. // 订单计算数据
  171. extract(self::pre($params, "create"));
  172. // 用户使用积分
  173. if ($use_score > 0) {
  174. $score_amount = $use_score;
  175. $total_fee = (($total_fee - $score_amount / 100) > 0 ?: 0);
  176. }
  177. $order = Db::transaction(function () use (
  178. $user,
  179. $order_type,
  180. $groupon_id,
  181. $buy_type,
  182. $goods_original_amount,
  183. $goods_amount,
  184. $dispatch_amount,
  185. $total_amount,
  186. $score_amount,
  187. $total_fee,
  188. $discount_fee,
  189. $coupon_fee,
  190. $activity_discount_money,
  191. $dispatch_discount_money,
  192. $activity_discount_infos,
  193. $new_goods_list,
  194. $activity_type,
  195. $user_coupons,
  196. $user_address,
  197. $remark,
  198. $from,
  199. $invoice,
  200. $invoice_amount
  201. ) {
  202. // 订单创建前
  203. $data = [
  204. 'user' => $user,
  205. 'order_type' => $order_type,
  206. 'groupon_id' => $groupon_id,
  207. 'buy_type' => $buy_type,
  208. 'goods_original_amount' => $goods_original_amount,
  209. 'goods_amount' => $goods_amount,
  210. 'dispatch_amount' => $dispatch_amount,
  211. 'total_amount' => $total_amount,
  212. 'score_amount' => $score_amount,
  213. 'total_fee' => $total_fee,
  214. 'discount_fee' => $discount_fee,
  215. 'coupon_fee' => $coupon_fee,
  216. 'activity_discount_money' => $activity_discount_money,
  217. 'dispatch_discount_money' => $dispatch_discount_money,
  218. 'activity_discount_infos' => $activity_discount_infos,
  219. 'new_goods_list' => $new_goods_list,
  220. 'activity_type' => $activity_type,
  221. 'user_coupons' => $user_coupons,
  222. 'user_address' => $user_address,
  223. 'remark' => $remark,
  224. 'from' => $from
  225. ];
  226. // 如果是活动,这里面减掉 redis 库存
  227. // todo del 1.4
  228. // \think\Hook::listen('order_create_before', $data);
  229. // 团信息, 如果是参与旧团拼团才会不为 null,(开新团放到支付成功)
  230. $groupon = cache('grouponinfo-' . $user->id);
  231. $groupon = $groupon ? json_decode($groupon, true) : null;
  232. // 立即清除缓存
  233. cache('grouponinfo-' . $user->id, NULL);
  234. $orderData = [];
  235. $orderData['order_sn'] = self::getSn($user->id);
  236. $orderData['user_id'] = $user->id;
  237. $orderData['type'] = $order_type;
  238. $orderData['activity_type'] = $activity_type;
  239. $orderData['goods_amount'] = $goods_amount;
  240. $orderData['dispatch_amount'] = $dispatch_amount;
  241. $orderData['total_amount'] = $total_amount;
  242. $orderData['score_amount'] = $score_amount;
  243. $orderData['total_fee'] = $total_fee;
  244. $orderData['discount_fee'] = $discount_fee;
  245. $orderData['score_fee'] = $score_amount; // 记录score 支付数
  246. $orderData['coupon_fee'] = $coupon_fee;
  247. $orderData['activity_discount_money'] = $activity_discount_money;
  248. $orderData['dispatch_discount_money'] = $dispatch_discount_money;
  249. $orderData['goods_original_amount'] = $goods_original_amount;
  250. if ($user_address) {
  251. $orderData['phone'] = $user_address->phone;
  252. $orderData['consignee'] = $user_address->name;
  253. $orderData['province_name'] = city_name($user_address->province_id);//$user_address->province_name;
  254. $orderData['city_name'] = city_name($user_address->city_id);//$user_address->city_name;
  255. $orderData['area_name'] = city_name($user_address->area_id);//$user_address->area_name;
  256. $orderData['address'] = $user_address->address;
  257. $orderData['province_id'] = $user_address->province_id;
  258. $orderData['city_id'] = $user_address->city_id;
  259. $orderData['area_id'] = $user_address->area_id;
  260. }
  261. // 处理发票申请
  262. if ($invoice_amount > 0) {
  263. if (!empty($invoice) && $invoice['amount'] == $invoice_amount) {
  264. $orderData['invoice_status'] = 1; // 已申请
  265. } else {
  266. $orderData['invoice_status'] = 0; // 未申请
  267. }
  268. } else {
  269. $orderData['invoice_status'] = -1; // 不可开具发票
  270. }
  271. $orderData['status'] = 0;
  272. $orderData['remark'] = $remark;
  273. $orderData['coupons_id'] = $user_coupons ? $user_coupons->id : 0;
  274. $orderData['platform'] = request()->header('platform');
  275. //$ext = $activity_discount_infos ? ['activity_discount_infos' => $activity_discount_infos] : []; // 促销活动信息
  276. $ext = [];
  277. $orderData['ext'] = json_encode($ext);
  278. $order = new Order();
  279. $order->allowField(true)->save($orderData);
  280. // 将优惠券使用掉
  281. if ($user_coupons) {
  282. $user_coupons->use_order_id = $order->id;
  283. $user_coupons->usetime = time();
  284. $user_coupons->save();
  285. }
  286. // 如果需要支付积分,扣除积分
  287. if ($score_amount) {
  288. // $user 为 Common\Auth 对象
  289. User::score(-$score_amount, $user->id, 'score_pay', $order->id, '', [
  290. 'order_id' => $order->id,
  291. 'order_sn' => $order->order_sn,
  292. ]);
  293. }
  294. // 添加发票数据
  295. if ($order->invoice_status == 1) {
  296. \addons\shopro\model\OrderInvoice::create([
  297. 'order_id' => $order->id,
  298. 'user_id' => $order->user_id,
  299. 'amount' => $invoice['amount'],
  300. 'type' => $invoice['type'],
  301. 'header_name' => $invoice['header_name'],
  302. 'tax_no' => $invoice['tax_no'],
  303. 'mobile' => $invoice['mobile'],
  304. ]);
  305. }
  306. // 添加 订单 item
  307. foreach ($new_goods_list as $key => $buyinfo) {
  308. $detail = $buyinfo['detail'];
  309. $current_sku_price = $detail['current_sku_price'];
  310. $orderItem = new OrderItem();
  311. $orderItem->user_id = $user->id;
  312. $orderItem->order_id = $order->id;
  313. $orderItem->goods_id = $buyinfo['goods_id'];
  314. $orderItem->goods_type = $detail['type'];
  315. $orderItem->goods_sku_price_id = $buyinfo['sku_price_id'];
  316. $item_activity_type = (isset($current_sku_price['activity_type']) && $current_sku_price['activity_type']) ? $current_sku_price['activity_type'] : '';
  317. $item_activity_type .= $buyinfo['activity_type'] ? ',' . $buyinfo['activity_type'] : '';
  318. $item_activity_type = trim($item_activity_type, ',');
  319. $orderItem->activity_id = $current_sku_price['activity_id'] ?? 0; // 商品当前的活动类型
  320. $orderItem->activity_type = $item_activity_type ?: null; // 商品当前的活动类型
  321. // 当前商品规格对应的 活动下对应商品规格的 id
  322. $orderItem->item_goods_sku_price_id = isset($current_sku_price['item_goods_sku_price']) ?
  323. $current_sku_price['item_goods_sku_price']['id'] : 0;
  324. $orderItem->goods_sku_text = $current_sku_price['goods_sku_text'];
  325. $orderItem->goods_title = $detail->title;
  326. $orderItem->goods_image = empty($current_sku_price['image']) ? $detail->image : $current_sku_price['image'];
  327. $orderItem->goods_original_price = $detail->original_price;
  328. $orderItem->discount_fee = $buyinfo['discount_fee']; // 平均计算单件商品所享受的折扣
  329. $orderItem->pay_price = $buyinfo['pay_price']; // 平均计算单件商品不算运费,算折扣时候的金额
  330. $orderItem->goods_price = $detail->current_sku_price->price;
  331. $orderItem->goods_num = $buyinfo['goods_num'] ?? 1;
  332. $orderItem->goods_weight = $detail->current_sku_price->weight;
  333. $orderItem->dispatch_status = 0;
  334. $orderItem->dispatch_fee = $buyinfo['dispatch_amount'];
  335. $orderItem->dispatch_type = $buyinfo['dispatch_type'];
  336. $orderItem->dispatch_id = $buyinfo['dispatch_id'] ? $buyinfo['dispatch_id'] : 0;
  337. $orderItem->store_id = $buyinfo['store_id'] ? $buyinfo['store_id'] : 0;
  338. $orderItem->aftersale_status = 0;
  339. $orderItem->comment_status = 0;
  340. $orderItem->refund_status = 0;
  341. $ext = [];
  342. if (isset($buyinfo['dispatch_date'])) {
  343. $ext['dispatch_date'] = $buyinfo['dispatch_date'];
  344. }
  345. if (isset($buyinfo['dispatch_phone'])) {
  346. $ext['dispatch_phone'] = $buyinfo['dispatch_phone'];
  347. }
  348. $orderItem->ext = json_encode($ext);
  349. $orderItem->save();
  350. }
  351. // 订单创建后
  352. $data = [
  353. 'user' => $user,
  354. 'order' => $order,
  355. 'from' => $from,
  356. 'groupon' => $groupon,
  357. 'buy_type' => $buy_type,
  358. 'activity_type' => $activity_type,
  359. 'new_goods_list' => $new_goods_list
  360. ];
  361. \think\Hook::listen('order_create_after', $data);
  362. // self::orderCreateAfter($data);
  363. // 重新获取订单
  364. $order = Order::where('id', $order->id)->find();
  365. return $order;
  366. });
  367. return $order;
  368. }
  369. // 订单创建后
  370. public static function orderCreateAfter(&$params)
  371. {
  372. Log::write("eeeeeeeeeeeeeeee");
  373. $user = $params['user'];
  374. $order = $params['order'];
  375. $from = $params['from'];
  376. $groupon = $params['groupon'];
  377. $buy_type = $params['buy_type'];
  378. $new_goods_list = $params['new_goods_list'];
  379. // 删除购物车
  380. if ($from == 'cart') {
  381. foreach ($new_goods_list as $delCart) {
  382. Cart::where([
  383. 'user_id' => $user->id,
  384. 'goods_id' => $delCart['goods_id'],
  385. 'sku_price_id' => $delCart['sku_price_id'],
  386. ])->delete();
  387. }
  388. }
  389. // 更新订单扩展字段
  390. $order_ext = $order['ext_arr'];
  391. $order_ext['buy_type'] = $buy_type; // 购买方式,alone: 单独购买, groupon: 拼团
  392. $order_ext['groupon_id'] = $groupon['id'] ?? 0; // 如果是拼团,团 id
  393. // 判断需要支付的金额是否大于 0
  394. if ($order['total_fee'] <= 0) {
  395. // 更新订单扩展字段
  396. $order->ext = json_encode($order_ext);
  397. $order->save();
  398. $order = (new \addons\shopro\model\Order)->paymentProcess($order, [
  399. 'order_sn' => $order['order_sn'],
  400. 'transaction_id' => '',
  401. 'notify_time' => date('Y-m-d H:i:s'),
  402. 'buyer_email' => $user->id,
  403. 'payment_json' => json_encode([]),
  404. 'pay_fee' => $order->total_fee,
  405. 'pay_type' => $order['type'] == 'score' ? 'score' : 'wallet' // 支付方式 积分完全支付,或者不需要支付,使用 wallet
  406. ]);
  407. } else {
  408. // 默认取第一个商品
  409. $goods_one = $new_goods_list[0]['detail'];
  410. if (isset($detail['activity']) && $detail['activity']) {
  411. $activity_one = $goods_one['activity'];
  412. // 获取第一个商品的活动规则,活动不存在,自动会使用全局自动关闭
  413. $rules = $activity_one['rules'] ?? [];
  414. } else {
  415. $rules = [];
  416. }
  417. // 优先使用活动的订单关闭时间
  418. if (isset($rules['order_auto_close']) && $rules['order_auto_close'] > 0) {
  419. $close_minue = $rules['order_auto_close'];
  420. } else {
  421. // 添加自动关闭队列
  422. $config = Config::where('name', 'order')->cache(300)->find(); // 读取配置自动缓存 5 分钟
  423. $config = isset($config) ? json_decode($config['value'], true) : [];
  424. $close_minue = (isset($config['order_auto_close']) && $config['order_auto_close'] > 0)
  425. ? $config['order_auto_close'] : 15;
  426. }
  427. // 更新订单,将过期时间存入订单,前台展示支付倒计时
  428. $order_ext['expired_time'] = time() + ($close_minue * 60);
  429. // 队列关闭
  430. \think\Queue::later(($close_minue * 60), '\addons\shopro\job\OrderAutoOper@autoClose', ['order' => $order], 'shopro');
  431. // 更新订单扩展字段
  432. $order->ext = json_encode($order_ext);
  433. $order->save();
  434. }
  435. Log::write($order->ext);
  436. return $order;
  437. }
  438. // 订单列表
  439. public static function getList($params)
  440. {
  441. $user = User::info();
  442. extract($params);
  443. $type =$params['type'];
  444. $order = (new self())->where('user_id', $user->id)->with('item');
  445. switch ($type) {
  446. case 'all':
  447. $order = $order;
  448. break;
  449. case 'nopay':
  450. $order = $order->nopay();
  451. break;
  452. case 'nosend':
  453. $order = $order->payed()->nosend();
  454. break;
  455. case 'noget':
  456. $order = $order->payed()->noget();
  457. break;
  458. case 'nocomment':
  459. $order = $order->payed()->nocomment();
  460. break;
  461. case 'aftersale':
  462. $order = $order->payed()->aftersale(); // 个人中心售后单不在走这里,而是直接走的售后单列表
  463. break;
  464. case 'invalid':
  465. $order = $order->invalid();
  466. break;
  467. }
  468. $orders = $order->order('id', 'desc')->paginate(10);
  469. // 处理未支付订单 item status_code
  470. $orders = $orders->toArray();
  471. if ($orders['data']) {
  472. $data = $orders['data'];
  473. foreach ($data as $key => $od) {
  474. $data[$key] = self::setOrderItemStatusByOrder($od);
  475. $data[$key]['expired_time'] = $od['createtime'] + (15 * 60);
  476. $time = time()-$od['createtime'] + (15 * 60);
  477. if($time>0&&$od['status']==0){
  478. Db::name('shopro_order')->where('id' ,$od['id'])->update(['status'=>-1]);
  479. }
  480. }
  481. $orders['data'] = $data;
  482. }
  483. return $orders;
  484. }
  485. // 订单列表
  486. public static function getItemList($params)
  487. {
  488. $user = User::info();
  489. extract($params);
  490. $order = (new self())->where('user_id', $user->id)->where('status',1);
  491. if($params['type']==1){
  492. $in = '0,1';
  493. }
  494. else{
  495. $in ='-1,2';
  496. }
  497. $orders = $order->order('id','desc')->field('order_sn,id,createtime,status,ext,activity_type')->paginate(10);
  498. // 处理未支付订单 item status_code
  499. $orders = $orders->toArray();
  500. // print_r($in);die;
  501. if ($orders['data']) {
  502. $data = $orders['data'];
  503. foreach ($data as $key => $od) {
  504. $data[$key]['item'] = Db::name('shopro_order_item')->where('order_id',$data[$key]['id'])->whereIn('aftersale_status',$in)->select();
  505. $data[$key]['expired_time'] = $od['createtime'] + (15 * 60);
  506. if( empty($data[$key]['item'])){
  507. unset($data[$key]);
  508. }
  509. }
  510. $orders['data'] = $data;
  511. $orders['total'] = count($data);
  512. }
  513. return $orders;
  514. }
  515. // 订单详情
  516. public static function detail($params)
  517. {
  518. $user = User::info();
  519. extract($params);
  520. $order = (new self())->with('item')->where('user_id', $user->id);
  521. if (isset($order_sn)) {
  522. $order = $order->where('order_sn', $order_sn);
  523. }
  524. if (isset($id)) {
  525. $order = $order->where('id', $id);
  526. }
  527. $order = $order->find();
  528. if (!$order) {
  529. new Exception('订单不存在');
  530. }
  531. // 处理未支付订单 item status_code
  532. $order = self::setOrderItemStatusByOrder($order);
  533. $order['expired_time'] = $order['createtime'] + (15 * 60);
  534. return $order;
  535. }
  536. // 订单商品详情
  537. public static function itemDetail($params)
  538. {
  539. $user = User::info();
  540. extract($params);
  541. $type = $type ?? 'default';
  542. $order = (new self())->with(['item' => function ($query) use ($order_item_id) {
  543. $query->where('id', $order_item_id);
  544. }])->where('user_id', $user->id);
  545. if (isset($order_sn)) {
  546. $order = $order->where('order_sn', $order_sn);
  547. }
  548. if (isset($id)) {
  549. $order = $order->where('id', $id);
  550. }
  551. $order = $order->find();
  552. if (!$order || !$order->item) {
  553. new Exception('订单不存在');
  554. }
  555. // 处理未支付订单 item status_code
  556. $order = self::setOrderItemStatusByOrder($order);
  557. $item = $order['item'][0];
  558. unset($order['item']); // 移除订单表中的 item
  559. $item['order'] = $order; // 订单信息
  560. if ($type == 'dispatch') {
  561. $store = null;
  562. $verifies = [];
  563. if (in_array($item['dispatch_type'], ['selfetch', 'store']) && $item['store_id']) {
  564. // 自提,商家配送
  565. if (
  566. $item['dispatch_type'] == 'selfetch'
  567. && $item['dispatch_status'] == OrderItem::DISPATCH_STATUS_SENDED
  568. && $item['refund_status'] <= 0
  569. ) {
  570. // 关联核销码
  571. $verifies = Verify::where('order_id', $item['order_id'])
  572. ->where('order_item_id', $item['id'])->select();
  573. }
  574. $store = Store::where('id', $item['store_id'])->find();
  575. }
  576. $item['store'] = $store; // 门店信息
  577. $item['verify'] = $verifies; // 核销券列表
  578. // $item['autosend']
  579. // 订单详情,自动发货内容 待补充
  580. }
  581. return $item;
  582. }
  583. // 取消订单
  584. public static function operCancel($params)
  585. {
  586. $user = User::info();
  587. extract($params);
  588. $order = self::with('item')->where('user_id', $user->id)->where('id', $id)->nopay()->find();
  589. if (!$order) {
  590. new Exception('订单不存在或已取消');
  591. }
  592. // 订单取消
  593. $order = (new self)->doCancel($order, $user);
  594. return $order;
  595. }
  596. public function doCancel($order, $user, $type = 'user')
  597. {
  598. $order = Db::transaction(function () use ($order, $user, $type) {
  599. $data = ['order' => $order];
  600. \think\Hook::listen('order_cancel_before', $data);
  601. $order->status = Order::STATUS_CANCEL; // 取消订单
  602. $order->ext = json_encode($order->setExt($order, ['cancel_time' => time()])); // 取消时间
  603. $order->save();
  604. OrderAction::operAdd($order, null, $user, $type, ($type == 'user' ? '用户' : '管理员') . '取消订单');
  605. // 订单取消,退回库存,退回优惠券积分,等
  606. $data = ['order' => $order];
  607. \think\Hook::listen('order_cancel_after', $data);
  608. return $order;
  609. });
  610. return $order;
  611. }
  612. private static function getItem($order, $order_item_id)
  613. {
  614. if (!$order) {
  615. new Exception('当前订单不存在');
  616. }
  617. $order_item = null;
  618. foreach ($order->item as $item) {
  619. if ($item->id == $order_item_id) {
  620. $order_item = $item;
  621. break;
  622. }
  623. }
  624. if (!$order_item) {
  625. new Exception('订单商品不需要操作');
  626. }
  627. return $order_item;
  628. }
  629. // 确认收货订单
  630. public static function operConfirm($params)
  631. {
  632. $user = User::info();
  633. extract($params);
  634. $order = Db::transaction(function () use ($id, $order_item_id, $user) {
  635. // 加锁查询订单,exist 里面的子查询不会加锁,但是该语句需要等待锁释放才能正常查询,所以下面的获取 item 已经是更改过状态之后的了
  636. $order = self::noget()->where('user_id', $user->id)->where('id', $id)->lock(true)->find();
  637. // 获取要操作的 订单 item
  638. $item = self::getItem($order, $order_item_id);
  639. if ($item->dispatch_status == OrderItem::DISPATCH_STATUS_NOSEND) {
  640. new Exception('当前订单未发货');
  641. }
  642. if ($item->dispatch_status == OrderItem::DISPATCH_STATUS_GETED) {
  643. new Exception('当前订单已收货');
  644. }
  645. $superior = new \app\common\model\User();
  646. $share = new \addons\shopro\model\Share();
  647. $userlog = new \addons\shopro\model\UserWalletLog();
  648. $pid = $share->where('user_id', $user->id)->field('share_id')->find();
  649. if ($pid) {
  650. $before = $superior->where('id', $pid['share_id'])->find();
  651. $money = config('site.rebate') / 100;
  652. $number = $money * $item->goods_price;
  653. $after = $before['money'] + $number;
  654. $superior->where('id', $pid['share_id'])->update(['money' => $after]);
  655. $userlog->write($before, $number, $before['money'], $after, 'commission_income', '', 'money', '一级返利');
  656. $tpid = $share->where('user_id', $pid['share_id'])->field('share_id')->find();
  657. if ($tpid) {
  658. $tbefore = $superior->where('id', $tpid['share_id'])->find();
  659. $tmoney = config('site.trebate') / 100;
  660. $tnumber = $tmoney * $item->goods_price;
  661. $tafter = $tbefore['money'] + $tnumber;
  662. $superior->where('id', $tpid['share_id'])->update(['money' => $tafter]);
  663. $userlog->write($tbefore, $tnumber, $tbefore['money'], $tafter, 'commission_income', '', 'money', '二级返利');
  664. }
  665. }
  666. (new self())->getedItem($order, $item, ['oper_type' => 'user']);
  667. });
  668. return $order;
  669. }
  670. // 删除订单
  671. public static function operDelete($params)
  672. {
  673. $user = User::info();
  674. extract($params);
  675. $order = self::canDelete()->where('user_id', $user->id)->where('id', $id)->find();
  676. if (!$order) {
  677. new Exception('订单不存在或不可删除');
  678. }
  679. $order = Db::transaction(function () use ($order, $user) {
  680. $order->delete(); // 删除订单
  681. OrderAction::operAdd($order, null, $user, 'user', '用户删除订单');
  682. return $order;
  683. });
  684. return $order;
  685. }
  686. // 评价
  687. public static function operComment($params)
  688. {
  689. $user = User::info();
  690. $order = Db::transaction(function () use ($user, $params) {
  691. extract($params);
  692. // 加锁读取订单,直到订单评价状态改变
  693. $order = self::with('item')->payed()->where('user_id', $user->id)->where('id', $id)->lock(true)->find();
  694. // 获取要操作的 订单 item
  695. $item = self::getItem($order, $order_item_id);
  696. if ($item->comment_status == 1) {
  697. new Exception('当前商品已评价');
  698. }
  699. // 订单评价前
  700. $data = ['order' => $order, 'item' => $item];
  701. \think\Hook::listen('order_comment_before', $data); // 重新拿到更新过的订单
  702. $images = (isset($images) && $images) ? $images : null;
  703. // 获取用户评论配置
  704. $config = \addons\shopro\model\Config::where('name', 'order')->value('value');
  705. $config = json_decode($config, true);
  706. GoodsComment::create([
  707. 'goods_id' => $item->goods_id,
  708. 'order_id' => $order->id,
  709. 'order_item_id' => $item->id,
  710. 'user_id' => $user->id,
  711. 'level' => $level,
  712. 'content' => $content,
  713. 'images' => $images ? implode(',', $images) : $images,
  714. 'status' => !empty($config['user_reply']) ? 'show' : 'hidden'
  715. ]);
  716. $item->comment_status = OrderItem::COMMENT_STATUS_OK; // 评价成功
  717. $item->save();
  718. OrderAction::operAdd($order, $item, $user, 'user', '用户评价成功');
  719. // 订单评价后
  720. $data = ['order' => $order, 'item' => $item];
  721. \think\Hook::listen('order_comment_after', $data);
  722. return $order;
  723. });
  724. return $order;
  725. }
  726. // 个人中心订单数量
  727. public static function statusNum()
  728. {
  729. $user = User::info();
  730. $status_num['nopay'] = self::where('user_id', $user->id)->nopay()->count();
  731. $status_num['nosend'] = self::where('user_id', $user->id)->payed()->nosend()->count();
  732. $status_num['noget'] = self::where('user_id', $user->id)->payed()->noget()->count();
  733. $status_num['nocomment'] = self::where('user_id', $user->id)->payed()->nocomment()->count();
  734. // $status_num['aftersale'] = self::where('user_id', $user->id)->payed()->aftersale()->count();
  735. $status_num['aftersale'] = \addons\shopro\model\OrderAftersale::where('user_id', $user->id)->count();
  736. return $status_num;
  737. }
  738. public function paymentProcess($order, $notify)
  739. {
  740. $order->status = Order::STATUS_PAYED;
  741. $order->paytime = time();
  742. $order->transaction_id = $notify['transaction_id'];
  743. $order->payment_json = $notify['payment_json'];
  744. $order->pay_type = $notify['pay_type'];
  745. $order->pay_fee = $notify['pay_fee'];
  746. $order->save();
  747. $user = User::where('id', $order->user_id)->find();
  748. OrderAction::operAdd($order, null, $user, 'user', '用户支付成功');
  749. // 支付成功后续使用异步队列处理
  750. \think\Queue::push('\addons\shopro\job\OrderPayed@payed', ['order' => $order, 'user' => $user], 'shopro-high');
  751. return $order;
  752. }
  753. // 开始退款
  754. public static function startRefund($order, $item, $refund_money, $user = null, $remark = '')
  755. {
  756. // 订单退款前
  757. $data = ['order' => $order, 'item' => $item];
  758. \think\Hook::listen('order_refund_before', $data);
  759. $item->refund_status = \app\admin\model\shopro\order\OrderItem::REFUND_STATUS_OK; // 同意退款
  760. $item->refund_fee = $refund_money;
  761. $item->save();
  762. \addons\shopro\model\OrderAction::operAdd($order, $item, $user, ($user ? 'admin' : 'system'), $remark . ',退款金额:' . $refund_money);
  763. \app\admin\model\shopro\order\Order::refund($order, $item, $refund_money, $remark);
  764. // 订单退款后
  765. $data = ['order' => $order, 'item' => $item];
  766. \think\Hook::listen('order_refund_after', $data);
  767. }
  768. // 退款
  769. public static function refund($order, $item, $refund_money, $remark = '')
  770. {
  771. // 生成退款单
  772. $refundLog = new RefundLog();
  773. $refundLog->order_sn = $order->order_sn;
  774. $refundLog->refund_sn = RefundLog::getSn($order->user_id);
  775. $refundLog->order_item_id = $item->id;
  776. $refundLog->pay_fee = $order->pay_fee;
  777. $refundLog->refund_fee = $refund_money;
  778. $refundLog->pay_type = $order->pay_type;
  779. $refundLog->save();
  780. if ($order->pay_type == 'wechat' || $order->pay_type == 'alipay') {
  781. // 微信|支付宝退款
  782. // 退款数据
  783. $order_data = [
  784. 'out_trade_no' => $order->order_sn
  785. ];
  786. if ($order->pay_type == 'wechat') {
  787. $total_fee = $order->pay_fee * 100;
  788. $refund_fee = $refund_money * 100;
  789. $order_data = array_merge($order_data, [
  790. 'out_refund_no' => $refundLog->refund_sn,
  791. 'total_fee' => $total_fee,
  792. 'refund_fee' => $refund_fee,
  793. 'refund_desc' => $remark,
  794. ]);
  795. } else {
  796. $order_data = array_merge($order_data, [
  797. 'out_request_no' => $refundLog->refund_sn,
  798. 'refund_amount' => $refund_money,
  799. ]);
  800. }
  801. $notify_url = request()->domain() . '/addons/shopro/pay/notifyr/payment/' . $order->pay_type . '/platform/' . $order->platform;
  802. $pay = new \addons\shopro\library\PayService($order->pay_type, $order->platform, $notify_url);
  803. $result = $pay->refund($order_data);
  804. \think\Log::write('refund-result' . json_encode($result));
  805. if ($order->pay_type == 'wechat') {
  806. // 微信通知回调 pay->notifyr
  807. if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
  808. return true;
  809. } else {
  810. throw new \Exception($result['return_msg']);
  811. }
  812. } else {
  813. // 支付宝通知回调 pay->notifyx
  814. if ($result['code'] == "10000") {
  815. return true;
  816. } else {
  817. throw new \Exception($result['msg']);
  818. }
  819. }
  820. // { // 微信返回结果
  821. // "return_code":"SUCCESS",
  822. // "return_msg":"OK",
  823. // "appid":"wx39cd0799d4567dd0",
  824. // "mch_id":"1481069012",
  825. // "nonce_str":"huW9eIAb5BDPn0Ma",
  826. // "sign":"250316740B263FE53F5DFF50AF5A8FA1",
  827. // "result_code":"SUCCESS",
  828. // "transaction_id":"4200000497202004072822298902",
  829. // "out_trade_no":"202010300857029180027000",
  830. // "out_refund_no":"1586241595",
  831. // "refund_id":"50300603862020040700031444448",
  832. // "refund_channel":[],
  833. // "refund_fee":"1",
  834. // "coupon_refund_fee":"0",
  835. // "total_fee":"1",
  836. // "cash_fee":"1",
  837. // "coupon_refund_count":"0",
  838. // "cash_refund_fee":"1
  839. // }
  840. // { // 支付宝返回结果
  841. // "code": "10000",
  842. // "msg": "Success",
  843. // "buyer_logon_id": "157***@163.com",
  844. // "buyer_user_id": "2088902485164146",
  845. // "fund_change": "Y",
  846. // "gmt_refund_pay": "2020-08-15 16:11:45",
  847. // "out_trade_no": "202002460317545607015300",
  848. // "refund_fee": "0.01",
  849. // "send_back_fee": "0.00",
  850. // "trade_no": "2020081522001464141438570535"
  851. // }
  852. } else if ($order->pay_type == 'wallet') {
  853. // 余额退款
  854. if ($refund_money != 0) {
  855. \addons\shopro\model\User::money($refund_money, $order->user_id, 'wallet_refund', $order->id, '', [
  856. 'order_id' => $order->id,
  857. 'order_sn' => $order->order_sn,
  858. 'item_id' => $item->id,
  859. ]);
  860. }
  861. self::refundFinish($order, $item, $refundLog);
  862. return true;
  863. } else if ($order->pay_type == 'score') {
  864. // 积分退款,暂不支持积分退款
  865. }
  866. }
  867. public static function refundFinish($order, $item, $refundLog)
  868. {
  869. // 退款完成
  870. $refundLog->status = 1;
  871. $refundLog->save();
  872. // 退款完成
  873. $item->refund_status = \app\admin\model\shopro\order\OrderItem::REFUND_STATUS_FINISH; // 退款完成
  874. $item->save();
  875. \addons\shopro\model\OrderAction::operAdd($order, $item, null, 'admin', '退款成功');
  876. }
  877. public function setExt($order, $field, $origin = [])
  878. {
  879. $newExt = array_merge($origin, $field);
  880. $orderExt = $order['ext_arr'];
  881. return array_merge($orderExt, $newExt);
  882. }
  883. }