ShopOrderService.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. <?php
  2. namespace app\common\service;
  3. use app\api\services\ExpressServices;
  4. use app\common\model\ShopCartModel;
  5. use app\common\model\ShopGoods;
  6. use app\common\model\ShopGoodsModel;
  7. use app\common\model\ShopGoodsSpec;
  8. use app\common\model\ShopOrder;
  9. use app\common\model\ShopOrderGoodsModel;
  10. use app\common\model\ShopOrderShippingModel;
  11. use app\common\model\UserAddressModel;
  12. use jobs\ShopOrderJob;
  13. use think\Exception;
  14. use think\facade\Db;
  15. use utils\Queue;
  16. use utils\RedisCache;
  17. /**
  18. * 商城订单服务 by wes
  19. * Class ShopOrderService
  20. * @package app\common\service
  21. */
  22. class ShopOrderService
  23. {
  24. protected static $instance = null;
  25. protected $model = null;
  26. public function __construct()
  27. {
  28. $this->model = new ShopOrder();
  29. }
  30. /**
  31. * 静态化入口
  32. * @return static|null
  33. */
  34. public static function make()
  35. {
  36. if (!self::$instance) {
  37. self::$instance = new static();
  38. }
  39. return self::$instance;
  40. }
  41. /**
  42. * 商城下单
  43. * @param $uid 用户ID
  44. * @param $buyGoods 购买的商品数组
  45. * @param $payType 支付方式
  46. * @param $request 请求数据
  47. * @param int $cartIds 购物车ID数组
  48. * @param int $cls 赠送月卡功能开关
  49. * @return array
  50. * @throws Exception
  51. */
  52. public function createOrder($uid, $buyGoods, $payType, $request, $cartIds = 0, $cls = 0)
  53. {
  54. // 获取收货地址信息
  55. $orderType = 1; // 订单类型 1商城订单 2积分订单 3.兑换券 4.锁定积分兑换 5.福袋发货订单
  56. $addressId = $request->post('address', '');
  57. $remark = $request->post('order_remark', '');
  58. if (!$addressId || !$userAddress = UserAddressModel::getAddressIdDetails((int)$addressId)) {
  59. throw new Exception('获取收货地址失败');
  60. }
  61. $addressUid = isset($userAddress['uid']) ? $userAddress['uid'] : 0;
  62. if ($addressUid != $uid) {
  63. throw new Exception('收货地址信息异常');
  64. }
  65. if (empty($buyGoods)) {
  66. throw new Exception('商品参数错误');
  67. }
  68. // 未支付订单数量验证
  69. $count = ShopOrderService::make()->checkBuyCountByTime($uid, 1, 0);
  70. $noPayLimit = env('common.ORDER_NO_PAY_LIMIT', 5);
  71. if ($count >= $noPayLimit) {
  72. throw new Exception("2小时内未支付的订单不能超过{$noPayLimit}个,下单失败");
  73. }
  74. // 订单商品数据处理
  75. $i = 0;
  76. $nowTime = time();
  77. $orderId = $this->model->max('order_id') + 1;
  78. $orderDatas = $orderGoods = $orders = $shippingList = [];
  79. $expireTime = time() + intval(env('common.ORDER_EXPIRES_TIME'));
  80. Db::startTrans();
  81. try {
  82. foreach ($buyGoods as $k => $v) {
  83. $goodsSn = isset($v['goods_sn']) ? trim($v['goods_sn']) : 0;
  84. $specId = isset($v['spec_id']) ? trim($v['spec_id']) : 0;
  85. $num = isset($v['num']) ? intval($v['num']) : 0;
  86. if ($num < 1) {
  87. throw new Exception('请先选择商品数量哦');
  88. }
  89. // 商品信息验证
  90. $goodsInfo = ShopGoodsService::make()->getCacheInfo($v['goods_sn']);
  91. if (!$goodsInfo) {
  92. throw new Exception('商品参数错误或已下架,请返回刷新列表');
  93. }
  94. if ($goodsInfo['goods_type'] == 2) {
  95. throw new Exception('商福袋商品不能购买');
  96. }
  97. // 商品限购验证
  98. if ($goodsInfo['restrictions_num'] > 0) {
  99. $count = ShopOrderGoodsService::make()->getCountByUserAndGoods($uid, $goodsSn);
  100. if ($count > $goodsInfo['restrictions_num']) {
  101. throw new Exception('超过限购');
  102. }
  103. if ($v['num'] + $count > $goodsInfo['restrictions_num']) {
  104. throw new Exception('超过限购数量,还可购买:' . ($goodsInfo['restrictions_num'] - $count) . '件');
  105. }
  106. }
  107. // 商品库存验证
  108. if ($goodsSn && $specId && $num > 0) {
  109. $stock = ShopGoodsSpecService::make()->getStock($goodsSn, $specId);
  110. if ($num > $stock) {
  111. throw new Exception('下单失败,所选商品含有库存不足的商品:SN-' . $goodsSn);
  112. }
  113. }
  114. // 是否赠送月卡
  115. $giveVip = 0;
  116. $goodsGiveVip = isset($goodsInfo['give_vip']) ? $goodsInfo['give_vip'] : 0;
  117. if ($cls == 1 && $goodsGiveVip) {
  118. $giveVip = $goodsGiveVip;
  119. }
  120. // 同商品订单数据
  121. if (!isset($orderDatas[$goodsSn]) || empty($orderDatas[$goodsSn])) {
  122. $orderSn = createdOrderSn();
  123. $orders[] = $orderSn;
  124. $orderId = $orderId + $i;
  125. $orderDatas[$goodsSn] = [
  126. 'order_id' => $orderId,
  127. 'order_sn' => $orderSn,
  128. 'total_price' => 0,
  129. 'payment' => 0,
  130. 'user_id' => $uid,
  131. 'status' => 0, // 待付款
  132. 'ship_postfee' => 0,
  133. 'rebate_score' => 0, //总返利积分
  134. 'created_time' => date('Y-m-d H:i:s', $nowTime + $i),
  135. 'updated_time' => date('Y-m-d H:i:s', $nowTime + $i),
  136. 'give_vip' => $giveVip,
  137. 'order_remark' => $remark,
  138. 'pay_type' => $payType,
  139. 'order_type' => $orderType,
  140. 'coupon_number' => '',
  141. 'expires_time' => sr_getcurtime($expireTime + $i),
  142. 'supplier_name' => isset($goodsInfo['supplier_name']) ? $goodsInfo['supplier_name'] : '',
  143. ];
  144. // 构建订单配送数据
  145. $shippingList[$orderSn] = [
  146. 'order_id' => $orderId,
  147. 'sp_name' => $userAddress['name'],
  148. 'sp_mobile' => $userAddress['mobile'],
  149. 'sp_province' => $userAddress['sp_province'],
  150. 'sp_city' => $userAddress['sp_city'],
  151. 'sp_county' => $userAddress['sp_county'],
  152. 'sp_remark' => $userAddress['remark'],
  153. 'sp_mergename' => $userAddress['mergename'],
  154. 'created_time' => date('Y-m-d H:i:s', $nowTime + $i),
  155. 'updated_time' => date('Y-m-d H:i:s', $nowTime + $i),
  156. ];
  157. $i++;
  158. }
  159. // 订单商品规格数据
  160. $goodsSpec = ShopGoodsSpecService::make()->getDataByGoods($goodsInfo['goods_id'], $specId);
  161. $goodsSpecId = isset($goodsSpec['goods_spec_id']) ? $goodsSpec['goods_spec_id'] : 0;
  162. $weight = isset($goodsSpec['weight']) ? $goodsSpec['weight'] : 0;
  163. $price = isset($goodsSpec['price']) ? $goodsSpec['price'] : 0;
  164. // 同一个商品数据合计
  165. $totalFee = bcmul($num, $price, 3);
  166. $totalWeight = bcmul($num, $weight, 3);
  167. $orderDatas[$goodsSn]['total_weight'] = isset($orderDatas[$goodsSn]['total_weight']) ? bcadd($orderDatas[$goodsSn]['total_weight'], $totalWeight, 3) : $totalWeight;
  168. $orderDatas[$goodsSn]['total_price'] = isset($orderDatas[$goodsSn]['total_price']) ? bcadd($orderDatas[$goodsSn]['total_price'], $totalFee, 3) : $totalFee;
  169. $orderDatas[$goodsSn]['num'] = isset($orderDatas[$goodsSn]['num']) ? intval($orderDatas[$goodsSn]['num'] + $num) : $num;
  170. // 返还积分/金额合计
  171. $rebateScore = isset($goodsInfo['rebate_score']) ? bcmul($num, $goodsInfo['rebate_score'], 3) : 0;
  172. $rebateMoney = isset($goodsInfo['rebate_money']) ? bcmul($num, $goodsInfo['rebate_money'], 3) : 0;
  173. $orderDatas[$goodsSn]['rebate_score'] = isset($orderDatas[$goodsSn]['rebate_score']) ? bcadd($orderDatas[$goodsSn]['rebate_score'], $rebateScore, 3) : $rebateScore;
  174. $orderDatas[$goodsSn]['rebate_money'] = isset($orderDatas[$goodsSn]['rebate_money']) ? bcadd($orderDatas[$goodsSn]['rebate_money'], $rebateScore, 3) : $rebateMoney;
  175. $orderDatas[$goodsSn]['rebate_money'] = 0; // 临时覆盖值
  176. // 邮费计算合计
  177. $postFee = 0;
  178. $costPostFee = true;
  179. if ($costPostFee) {
  180. $city = isset($userAddress['city']) ? $userAddress['city'] : '';
  181. $postData = [
  182. 'goods_sn' => $goodsInfo['goods_sn'],
  183. 'num' => $orderDatas[$goodsSn]['num'],
  184. 'total_price' => $orderDatas[$goodsSn]['total_price'],
  185. 'total_weight' => $orderDatas[$goodsSn]['total_weight'],
  186. ];
  187. $postTemplateId = isset($goodsInfo['post_template_id']) ? $goodsInfo['post_template_id'] : 0;
  188. $postFee = ShopOrderService::make()->postFee($city, $postData, $postTemplateId); // 邮费
  189. }
  190. $orderDatas[$goodsSn]['ship_postfee'] = $postFee;
  191. $orderDatas[$goodsSn]['payment'] = bcadd($orderDatas[$goodsSn]['total_price'], $postFee, 3);
  192. // 订单商品
  193. $orderGoods[] = [
  194. 'order_id' => $orderDatas[$goodsSn]['order_id'],
  195. 'goods_id' => $goodsInfo['goods_id'],
  196. 'goods_name' => $goodsInfo['goods_name'],
  197. 'goods_category' => $goodsInfo['category'],
  198. 'goods_img' => isset($goodsSpec['picture']) && $goodsSpec['picture'] ? $goodsSpec['picture'] : $goodsInfo['goods_img'],
  199. 'num' => $num,
  200. 'price' => $price,
  201. 'total_fee' => $totalFee,
  202. 'goods_spec_id' => $goodsSpecId,
  203. 'spec_ids' => isset($goodsSpec['spec_ids']) ? $goodsSpec['spec_ids'] : '',
  204. 'spec_text' => isset($goodsSpec['spec_text']) ? $goodsSpec['spec_text'] : '',
  205. 'rebate_score' => isset($goodsSpec['rebate_score']) ? $goodsSpec['rebate_score'] : 0,
  206. 'total_rebate_score' => $orderDatas[$goodsSn]['rebate_score'],
  207. 'uid' => $uid
  208. ];
  209. // 扣库存
  210. if (!ShopGoodsSpec::where(['goods_spec_id' => $goodsSpecId])->dec('stock', $num)->update()) {
  211. throw new Exception('商品库存处理失败');
  212. }
  213. // 扣除商品总库存
  214. if (!ShopGoods::where(['goods_id' => $goodsInfo['goods_id']])->dec('inventory', $num)->update()) {
  215. throw new Exception('商品库存处理失败');
  216. }
  217. // 增加销量
  218. if (in_array($orderType, [3, 4, 5])) {
  219. if (!ShopGoodsModel::where(['goods_id' => $goodsInfo['goods_id']])
  220. ->inc('sales_volume', $num)
  221. ->inc('real_sales_volume', $num)
  222. ->update()) {
  223. throw new Exception('商品销量处理失败');
  224. }
  225. }
  226. // 清除商品相关实时数据缓存
  227. RedisCache::keyDel('caches:goods:list_*');
  228. RedisCache::keyDel('caches:goodsSpec:list_*');
  229. }
  230. if (empty($orders) || empty($orderGoods)) {
  231. throw new Exception('订单数据错误,下单失败');
  232. }
  233. // 批量写入订单数据
  234. $data = array_values($orderDatas);
  235. if (!$this->model->insertAll($data)) {
  236. Db::rollback();
  237. throw new Exception('下单失败');
  238. }
  239. // 批量写入订单商品数据
  240. $data = array_values($orderGoods);
  241. if (!ShopOrderGoodsModel::insertAll($data)) {
  242. Db::rollback();
  243. throw new Exception('下单失败,订单商品处理错误');
  244. }
  245. // 写入配送单数据
  246. $data = array_values($shippingList);
  247. if (!ShopOrderShippingModel::insertAll($data)) {
  248. Db::rollback();
  249. throw new Exception('下单失败,订单配送数据处理错误');
  250. }
  251. // 超时过期订单队列任务处理
  252. if (in_array($orderType, [1, 3]) && $orders) {
  253. $ex_time = intval(env('common.ORDER_EXPIRES_TIME', 60 * 60));
  254. Queue::instance()->log('订单过期执行成功')
  255. ->job(ShopOrderJob::class)
  256. ->do('ShopOrderExpired')
  257. ->secs($ex_time)
  258. ->push([$orders[0]]);
  259. }
  260. // 验证检查购物车数据
  261. if ($cartIds && is_array($cartIds)) {
  262. ShopCartModel::where('id', 'in', $cartIds)->where(['user_id' => $uid])->delete();
  263. }
  264. // 清除单一购物车数据
  265. $cartId = $request->post('cart_id', 0);
  266. if ($cartId) {
  267. ShopCartModel::where('id', $cartId)->delete();
  268. }
  269. $cacheKey = "caches:orders:u_{$uid}_{$orderType}:create_{$payType}_" . date('YmdHis') . rand(10, 99);
  270. RedisCache::set($cacheKey, ['uid' => $uid, 'orderDatas' => $orderDatas, 'shipping' => $shippingList, 'orderGoods' => $orderGoods, 'post' => $request->post()], 7200);
  271. Db::commit();
  272. return $orders;
  273. } catch (\Exception $e) {
  274. throw new Exception($e->getMessage());
  275. }
  276. }
  277. /**
  278. * 下单数据
  279. * @param $buyGoods 购买商品
  280. * @param $request
  281. * @return array
  282. * @throws Exception
  283. * @throws \think\db\exception\DataNotFoundException
  284. * @throws \think\db\exception\DbException
  285. * @throws \think\db\exception\ModelNotFoundException
  286. */
  287. public function buyDetail($buyGoods, $request)
  288. {
  289. $addressId = $request->post('address_id', 0);
  290. if (!$address = UserAddressModel::getAddressIdDetails($addressId, $request->uid, 1)) {
  291. throw new Exception('订单地址错误');
  292. }
  293. $details = $goodsSnArr = [];
  294. $paymentTotal = 0; // 总订单金额
  295. $totalRebateScore = 0; // 总返还积分
  296. $totalNum = 0; // 商品总数量
  297. $curXzmoney = SystemConfigService::make()->getConfigByName('xz_cur_money', '', 'xzconfig');
  298. foreach ($buyGoods as $k => $v) {
  299. $goodsSn = isset($v['goods_sn']) ? $v['goods_sn'] : '';
  300. $specId = isset($v['spec_id']) ? $v['spec_id'] : 0;
  301. $num = isset($v['num']) ? intval($v['num']) : 0;
  302. $totalNum += $num;
  303. if ($num <= 0) {
  304. throw new Exception('下单失败,请先选择商品数量');
  305. }
  306. // 商品信息
  307. if (!$goodsSn || !$goodsInfo = ShopGoodsService::make()->getCacheInfo($goodsSn)) {
  308. throw new Exception('下单失败,商品参数错误');
  309. }
  310. // 订单商品规格数据
  311. $goodsSnArr[] = $goodsSn;
  312. $goodsSpec = ShopGoodsSpecService::make()->getDataByGoods($goodsInfo['goods_id'], $specId);
  313. $weight = isset($goodsSpec['weight']) ? $goodsSpec['weight'] : 0;
  314. $price = isset($goodsSpec['price']) ? $goodsSpec['price'] : 0;
  315. if (empty($goodsSpec)) {
  316. throw new Exception('下单失败,存在无效商品');
  317. }
  318. // 商品数据
  319. $postFee = '0.00';
  320. $rebateScore = bcmul($num, $goodsSpec['rebate_score'], 2);
  321. $totalFee = bcmul($num, $price, 2);
  322. $totalWeight = bcmul($num, $weight, 3);
  323. // 组装订单商品数据
  324. $details[$goodsSn]['total_rebate_score'] = isset($details[$goodsSn]['total_rebate_score']) ? bcadd($details[$goodsSn]['total_rebate_score'], $rebateScore, 3) : $rebateScore; //总返利积分
  325. $details[$goodsSn]['total_num'] = isset($details[$goodsSn]['total_num']) ? bcadd($details[$goodsSn]['total_num'], $num, 3) : $num; //总数量
  326. $details[$goodsSn]['total_price'] = isset($details[$goodsSn]['total_price']) ? bcadd($details[$goodsSn]['total_price'], $totalFee, 3) : $totalFee; // 总金额
  327. $details[$goodsSn]['total_weight'] = isset($details[$goodsSn]['total_weight']) ? bcadd($details[$goodsSn]['total_weight'], $totalWeight, 3) : $totalWeight; // 总重量
  328. $details[$goodsSn]['post_fee'] = $postFee;
  329. $details[$goodsSn]['payment'] = bcadd($details[$goodsSn]['total_price'], $details[$goodsSn]['post_fee'], 2);
  330. $details[$goodsSn]['supplier_name'] = $goodsInfo['supplier_name'];
  331. $goodsName = isset($goodsInfo['goods_name']) ? $goodsInfo['goods_name'] : '';
  332. $goodsName = $goodsName ? str_replace(' ', '', $goodsName) : '';
  333. $goodsName = mb_strlen($goodsName, 'utf-8') > 20 ? mb_substr($goodsName, 0, 19, 'utf-8') : $goodsName;
  334. $goodsType = isset($goodsInfo['goods_type']) ? $goodsInfo['goods_type'] : 0;
  335. if ($goodsType == 5) {
  336. $price = getXzPirceWithPrice($curXzmoney, $price);
  337. $totalFee = getXzPirceWithPrice($curXzmoney, $totalFee);
  338. }
  339. $goods = [
  340. 'goods_sn' => $goodsInfo['goods_sn'],
  341. 'goods_id' => $goodsInfo['goods_id'],
  342. 'goods_type' => $goodsType,
  343. 'goods_name' => $goodsName,
  344. 'picture' => $goodsSpec['picture'] ?: $goodsInfo['goods_img'],
  345. 'num' => $num, //数量
  346. 'price' => $price,//单价
  347. 'weight' => $goodsSpec['weight'], //重量
  348. 'total_fee' => $totalFee, //总价
  349. 'rebate_score' => $goodsSpec['rebate_score'],//单个返利积分
  350. 'total_items_rebate_score' => $rebateScore,//总返利积分
  351. 'spec_ids' => $goodsSpec['spec_ids'],
  352. 'spec_text' => $goodsSpec['spec_text'],
  353. 'post_template_id' => $goodsInfo['post_template_id'], //是否包邮,否就是运费模板ID
  354. 'stock' => $goodsSpec['stock'],
  355. 'supplier_name' => $goodsInfo['supplier_name']
  356. ];
  357. // 参数合计
  358. $totalNum += $num;
  359. $paymentTotal += bcadd($totalFee, $postFee, 3);
  360. $totalRebateScore = bcadd($totalRebateScore, $rebateScore, 3);
  361. $details[$goodsSn]['goods'][] = $goods;
  362. }
  363. $total = [
  364. 'count_payment' => sprintf("%.2f", $paymentTotal),
  365. 'count_rebate_score' => $totalRebateScore,
  366. 'count_num' => $totalNum,
  367. ];
  368. return ['address' => $address, 'buyDetail' => array_values($details), 'total' => $total];
  369. }
  370. /**
  371. * 取消订单
  372. */
  373. public function cancelOrder($data, $userId)
  374. {
  375. $nowTime = date('Y-m-d H:i:s', time());
  376. $data = $this->model->where(['order_sn' => $data['order_sn'], 'user_id' => $userId])->field('order_id,status,order_sn')->findOrEmpty();
  377. if (empty($data)) {
  378. throw new Exception('参数错误');
  379. }
  380. if ($data['status'] != 0) {
  381. throw new Exception('订单状态错误');
  382. }
  383. Db::startTrans();
  384. if (!$this->model->where(['order_sn' => $data['order_sn']])->save(['status' => 3, 'updated_time' => $nowTime])) {
  385. Db::rollback();
  386. throw new Exception('取消失败');
  387. }
  388. // 恢复库存
  389. $updateStock = $this->model->alias('a')->where(['a.order_sn' => $data['order_sn'], 'a.user_id' => $userId])
  390. ->leftJoin('shop_order_goods og', 'og.order_id=a.order_id')
  391. ->leftJoin('shop_goods_spec gs', 'gs.goods_spec_id=og.goods_spec_id')
  392. ->leftJoin('shop_goods g', 'g.goods_id=og.goods_id')
  393. ->update([
  394. 'g.inventory'=> Db::raw('g.inventory + og.num'),
  395. 'gs.stock'=> Db::raw('gs.stock + og.num'),
  396. ]);
  397. if (!$updateStock) {
  398. Db::rollback();
  399. throw new Exception('取消失败,库存处理错误');
  400. }
  401. Db::commit();
  402. return true;
  403. }
  404. /**
  405. * 确定订单
  406. */
  407. public function receiveOrder($data, $userId)
  408. {
  409. $nowTime = date('Y-m-d H:i:s', time());
  410. $data = $this->model->where(['order_sn' => $data['order_sn'], 'user_id' => $userId])->findOrEmpty()->toArray();
  411. if (empty($data)) {
  412. throw new Exception('参数错误');
  413. }
  414. if ($data['status'] != 2) {
  415. throw new Exception('订单状态错误');
  416. }
  417. return $this->model->where(['order_sn' => $data['order_sn']])->save(['status' => 4, 'updated_time' => $nowTime]);
  418. }
  419. /**
  420. * 计算运费
  421. * @param $address 用户地址
  422. * @param $data 商品数据
  423. * @param $express_template_id 模板ID
  424. * 获取运费
  425. */
  426. public function postFee($address, $data, $express_template_id)
  427. {
  428. //获取运费模板
  429. $expressDelivery = ExpressDeliveryService::make()->getDataByTemplate($express_template_id);
  430. //获取运费模板列表
  431. $expressDeliveryItems = ExpressShippingMethodService::make()->getListByTemplate($express_template_id);
  432. $price_method_field = '';
  433. switch ($expressDelivery['price_method']) {
  434. case 1:
  435. $price_method_field = $data['total_price']; //按金额
  436. break;
  437. case 2:
  438. $price_method_field = $data['num']; //按件数
  439. break;
  440. case 3:
  441. $price_method_field = $data['total_weight']; //按重量
  442. break;
  443. default:
  444. break;
  445. }
  446. if ($expressDelivery['price_method'] == 1) {
  447. //计算默认运费 【配送区域、免运费金额、达标时运费、未达标时运费;】
  448. if ($price_method_field < $expressDelivery['default_free_price']) {
  449. $default_postfee = $expressDelivery['default_fastandard_price'];
  450. } else {
  451. //达标时运费
  452. $default_postfee = $expressDelivery['default_price'];
  453. }
  454. if (empty($expressDeliveryItems)) {
  455. return $default_postfee;
  456. }
  457. //按指定地区计算邮费
  458. foreach ($expressDeliveryItems as $k => $node) {
  459. if (in_array($address, explode(',', $node['address_items_ids']))) {
  460. //未达标运费
  461. if ($price_method_field < $node['free_price']) {
  462. return $node['fastandard_price'];
  463. } else {
  464. //达标时运费
  465. return $node['price'];
  466. }
  467. } else {
  468. return $default_postfee;
  469. }
  470. }
  471. } else {
  472. //按件数 重量计费 【配送区域、首件、达标时运费、续件、未达标时运费】
  473. //计算默认运费
  474. if ($price_method_field <= $expressDelivery['default_pieces']) {
  475. $default_postfee = $expressDelivery['default_price'];
  476. } else {
  477. //达到续件条件时的运费
  478. if ($expressDelivery['add_pieces']) {
  479. //配置过续件费用
  480. //续件数 = (下单件数 - 首件) /续件数
  481. //总运费 = 首件运费 + (续件数 * 续件费用)
  482. $add_pieces = bcdiv(($price_method_field - $expressDelivery['default_pieces']), $expressDelivery['add_pieces'], 2);
  483. $default_postfee = bcadd($expressDelivery['default_price'], bcmul($add_pieces, $expressDelivery['add_price'], 2), 2);
  484. } else {
  485. //未配置过续件,按首件价格
  486. $default_postfee = $expressDelivery['default_price'];
  487. }
  488. }
  489. if (empty($expressDeliveryItems)) {
  490. return $default_postfee;
  491. }
  492. //按指定地区计算邮费
  493. foreach ($expressDeliveryItems as $k => $node) {
  494. if (in_array($address, explode(',', $node['address_items_ids']))) {
  495. //未达到续件条件时的运费
  496. if ($price_method_field <= $node['pieces']) {
  497. return $node['price'];
  498. } else {
  499. //达到续件条件时的运费
  500. if ($node['add_pieces']) {
  501. //配置过续件费用
  502. //续件数 = (下单件数 - 首件) /续件数
  503. //总运费 = 首件运费 + (续件数 * 续件费用)
  504. $add_pieces = bcdiv(($price_method_field - $node['pieces']), $node['add_pieces'], 2);
  505. return bcadd($node['price'], bcmul($add_pieces, $node['add_price'], 2), 2);
  506. } else {
  507. //未配置过续件,按首件价格
  508. return $node['price'];
  509. }
  510. }
  511. } else {
  512. return $default_postfee;
  513. }
  514. }
  515. }
  516. }
  517. /**
  518. * 验证商品库存
  519. * @param $data
  520. * @return bool
  521. */
  522. public function checkStock($data)
  523. {
  524. foreach ($data as $key => $value) {
  525. $num = array_sum(array_column($value, 'num'));
  526. $goodsSn = isset($value[0]['goods_sn']) ? $value[0]['goods_sn'] : 0;
  527. $specId = isset($value[0]['spec_id']) ? $value[0]['spec_id'] : 0;
  528. if ($goodsSn && $specId && $num > 0) {
  529. $stock = ShopGoodsSpecService::make()->getStock($goodsSn, $specId);
  530. if ($num > $stock) {
  531. return false;
  532. }
  533. } else {
  534. return false;
  535. }
  536. }
  537. return true;
  538. }
  539. /**
  540. * 2小时内未付款订单数
  541. * @param $uid
  542. * @param int $orderType
  543. * @param int $status
  544. * @return array|int|mixed
  545. * @throws \think\db\exception\DbException
  546. */
  547. public function checkBuyCountByTime($uid, $orderType = 1, $status = 0)
  548. {
  549. $cacheKey = "caches:orders:unpayCount:{$uid}_{$orderType}_{$status}";
  550. $data = RedisCache::get($cacheKey);
  551. if ($data) {
  552. return $data;
  553. }
  554. $data = $this->model->where(['user_id' => $uid, 'order_type' => $orderType, 'status' => $status])
  555. ->whereTime('created_time', '-2 hours')
  556. ->count('order_id');
  557. if ($data) {
  558. RedisCache::set($cacheKey, $data, rand(3, 5));
  559. }
  560. return $data;
  561. }
  562. /**
  563. * 根据单号获取订单信息(有缓存)
  564. * @param $orderSn 订单号
  565. * @param int $uid 所属用户ID
  566. * @param int $status 状态
  567. * @param string $field 返沪字段
  568. * @param bool $cache 是否缓存,默认是
  569. * @return array|mixed
  570. */
  571. public function getInfoBySn($orderSn, $uid=0, $status=0, $field='', $cache=false)
  572. {
  573. $cacheKey = "caches:orders:info:sn{$orderSn}_{$uid}".($field? '_'.md5($field):'');
  574. $data = RedisCache::get($cacheKey);
  575. if($data && $cache){
  576. return $data;
  577. }
  578. $where = ['order_sn'=> $orderSn];
  579. if($uid>0){
  580. $where['user_id'] = $uid;
  581. }
  582. if($status>0){
  583. $where['status'] = $status;
  584. }
  585. $field = $field? $field : 'order_id,order_sn,user_id,payment,total_price,status,pay_type,rebate_score,rebate_money,num,order_type,give_vip';
  586. $data = $this->model->where($where)->field($field)->findOrEmpty();
  587. $data = $data? $data->toArray() : [];
  588. if($data && $cache){
  589. RedisCache::set($cacheKey, $data, rand(5,10));
  590. }
  591. return $data;
  592. }
  593. /**
  594. * 根据单号获取订单信息(有缓存)
  595. * @param $orderSn 订单号
  596. * @param int $uid 所属用户ID
  597. * @param string $field 返沪字段
  598. * @param bool $cache 是否缓存,默认是
  599. * @return array|mixed
  600. */
  601. public function getInfoById($orderId, $uid=0, $field='', $cache=true)
  602. {
  603. $cacheKey = "caches:orders:info:id{$orderId}_{$uid}".($field? '_'.md5($field):'');
  604. $data = RedisCache::get($cacheKey);
  605. if($data && $cache){
  606. return $data;
  607. }
  608. $where = ['order_id'=> $orderId,'user_id'=> 0,'status'=>1];
  609. if($uid>0){
  610. $where['user_id'] = $uid;
  611. }else{
  612. unset($where['user_id']);
  613. }
  614. $field = $field? $field : 'order_id,order_sn,user_id,payment,total_price,status,pay_type,rebate_score,rebate_money,num,order_type,give_vip,coupon_number,hg_status,hg_enable';
  615. $data = $this->model->where($where)->field($field)->findOrEmpty();
  616. $data = $data? $data->toArray() : [];
  617. if($data && $cache){
  618. RedisCache::set($cacheKey, $data, rand(5,10));
  619. }
  620. return $data;
  621. }
  622. /**
  623. * 订单列表
  624. */
  625. public function getList ($params, $pageSize, $field = '')
  626. {
  627. $page = request()->post('page', 1);
  628. $userId = isset($params['user_id'])? $params['user_id'] : 0;
  629. $cacheKey = "caches:orders:list:{$userId}_{$page}_{$pageSize}".($field? '_'.md5($field):'');
  630. $data = RedisCache::get($cacheKey);
  631. if($data){
  632. return $data;
  633. }
  634. $where['user_id'] = $userId;
  635. $status = isset($params['status'])? $params['status'] : '-1';
  636. if($status>=0){
  637. $where['status'] = $status;
  638. }
  639. $orderType = isset($params['order_type'])? $params['order_type'] : 0;
  640. if($orderType){
  641. $where['order_type'] = $orderType;
  642. }
  643. $field = $field? $field:'order_id,status,order_sn,order_type,coupon_number,created_time,order_remark,payment,ship_postfee,rebate_score,ship_name,ship_code,ship_number,expires_time,rebate_lock_score';
  644. $data = $this->model->where($where)
  645. ->whereNotIn('status', [3])
  646. ->relation(['withOrderGoods'])
  647. ->field($field)
  648. ->order('order_id desc')
  649. ->paginate($pageSize)
  650. ->each(function($v, $k){
  651. foreach ($v['withOrderGoods'] as $key=>$val){
  652. $goods_sn = ShopGoods::where('goods_id', $val['goods_id'])->value('goods_sn');
  653. $v['withOrderGoods'][$key]['goods_sn'] = $goods_sn;
  654. }
  655. $v['withOrderShipping'] = ShopOrderShippingService::make()->getInfo($v['order_id']);
  656. $v['withOrderExpress'] = ExpressServices::instance()->orderId($v['order_id'])
  657. ->code($v['ship_code'])
  658. ->number($v['ship_number'])
  659. ->find();
  660. if (intval($v['status']) == 2){
  661. $v['express_url'] = "https://m.kuaidi100.com/result.jsp?nu=".$v['ship_number'];
  662. }else{
  663. $v['express_url'] = "";
  664. }
  665. if ($v['status'] == 1){
  666. if ($v['order_type'] == 1){
  667. $v['can_hg'] = 1;
  668. }
  669. }else{
  670. $v['can_hg'] = 2;
  671. }
  672. switch ($v['status']){
  673. case 0:
  674. $v['status_title'] = '待付款';
  675. break;
  676. case 1:
  677. $v['status_title'] = '待发货';
  678. break;
  679. case 2:
  680. $v['status_title'] = '已发货';
  681. break;
  682. case 3:
  683. $v['status_title'] = '已过期';
  684. break;
  685. case 4:
  686. $v['status_title'] = '已完成';
  687. break;
  688. case 5:
  689. $v['status_title'] = '已完成';
  690. break;
  691. case 6:
  692. $v['status_title'] = '已完成';
  693. break;
  694. }
  695. });
  696. $data = $data? $data->toArray() : [];
  697. if($data['data']){
  698. RedisCache::set($cacheKey, $data['data'], rand(2,3));
  699. }
  700. return $data['data'];
  701. }
  702. }