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.

422 lines
16 KiB

  1. <?php
  2. namespace App\Service;
  3. use App\Model\Coupon;
  4. use App\Model\CouponUserRec;
  5. use App\Model\CouponUserUse;
  6. use App\Model\Goods;
  7. use App\Model\Order;
  8. use App\Model\OrderGoods;
  9. use App\Model\OrderMain;
  10. use App\Model\SpecCombination;
  11. use App\Model\Users;
  12. use Exception;
  13. use Hyperf\DbConnection\Db;
  14. use Hyperf\Snowflake\IdGeneratorInterface;
  15. use Hyperf\Utils\ApplicationContext;
  16. use Hyperf\Di\Annotation\Inject;
  17. class OrderService implements OrderServiceInterface
  18. {
  19. /**
  20. * @Inject
  21. * @var CoupnoServiceInterface
  22. */
  23. protected $couponService;
  24. /**
  25. * @inheritDoc
  26. */
  27. public function addOnlineOrder($data)
  28. {
  29. bcscale(6);
  30. // 订单判重
  31. $dataMain = $data;
  32. if ($orderMainId = $this->existsByOrderNum($data['order_num'])) {
  33. return $orderMainId;
  34. }
  35. Db::beginTransaction();
  36. try {
  37. // 计算当前订单可用红包优惠金额
  38. $couponMoney = 0;
  39. if (isset($data['receive_coupon_ids'])&&$data['receive_coupon_ids']) {
  40. $receiveCouponIds = explode(',', str_replace(',',',',$data['receive_coupon_ids']));
  41. $couponMoney = $this->getCouponAmount($receiveCouponIds, $data['money'], $data['user_id'], $data['market_id']);
  42. }
  43. $dataMain['yhq_money2'] = $couponMoney;
  44. // 获取分布式全局ID
  45. $generator = ApplicationContext::getContainer()->get(IdGeneratorInterface::class);
  46. $dataMain['global_order_id'] = $generator->generate();
  47. // 店铺IDs
  48. $dataMain['store_ids'] = '';
  49. $storeList = json_decode(json_encode($data['store_list']), true);
  50. if (!is_array($storeList)||empty($storeList)) {
  51. Db::rollBack();
  52. return '订单中商品不存在或已失效';
  53. }
  54. // 获取商户IDs
  55. foreach ($storeList as &$item) {
  56. $dataMain['store_ids'] .= empty($dataMain['store_ids']) ? $item['store_id'] : ','.$item['store_id'];
  57. }
  58. // 主订单插入数据
  59. $currentTime = time();
  60. $dataMain['time'] = date('Y-m-d H:i:s', $currentTime);
  61. $dataMain['time_add'] = $currentTime;
  62. $dataMain['state'] = OrderMain::ORDER_STATE_UNPAY;
  63. $dataMain['code'] = $dataMain['global_order_id'];
  64. // 主订单模型保存
  65. $orderMain = OrderMain::create($dataMain);
  66. $orderMainId = $orderMain->id;
  67. // 统计订单中所有店铺当日订单数,做店铺订单序号
  68. $countsArr = Order::query()->select('COUNT(*) AS count, id')
  69. ->whereIn('store_id', explode(',', $dataMain['store_ids']))
  70. ->where(['type' => OrderMain::ORDER_TYPE_ONLINE])
  71. ->whereBetween('time', [date('Y-m-d 00:00:00'), date('Y-m-d 23:59:59')])
  72. ->get()
  73. ->toArray();
  74. $storeOrderCounts = [];
  75. foreach ($countsArr as $key => &$row) {
  76. $storeOrderCounts[$row['id']] = $row['count'];
  77. }
  78. // 循环处理订单总额、子订单总额、商品、商户订单等信息
  79. $orderAmountTotal = 0; # 总订单金额
  80. foreach ($storeList as $key => &$item) {
  81. // 子订单数据处理
  82. $dataChild = [
  83. 'uniacid' => $data['uniacid'],
  84. 'order_num' => 's'.date('YmdHis', time()) . rand(1111, 9999),
  85. 'user_id' => $orderMain->user_id,
  86. 'store_id' => $item['store_id'],
  87. 'order_main_id' => $orderMain,
  88. 'state' => OrderMain::ORDER_STATE_UNPAY,
  89. 'tel' => $orderMain->tel,
  90. 'name' => $orderMain->name,
  91. 'address' => $orderMain->address,
  92. 'area' => $orderMain->area,
  93. 'time' => date("Y-m-d H:i:s"),
  94. 'note' => $item['note'],
  95. 'delivery_time' => $orderMain->delivery_time,
  96. 'type' => $orderMain->type,
  97. 'lat' => $orderMain->lat,
  98. 'lng' => $orderMain->lng,
  99. 'pay_type' => $orderMain->pay_type,
  100. 'order_type' => $orderMain->order_type,
  101. 'money' => floatval($item['subtotal']),
  102. 'box_money' => floatval($item['box_money']),
  103. 'mj_money' => floatval($item['mj_money']),
  104. 'yhq_money' => floatval($item['yhq_money']),
  105. 'yhq_money2' => floatval($item['yhq_money2']),
  106. 'zk_money' => floatval($item['zk_money']),
  107. 'coupon_id' => $item['coupon_id'],
  108. 'coupon_id2' => $item['coupon_id2'],
  109. 'xyh_money' => floatval($item['xyh_money']),
  110. 'oid' => (isset($storeOrderCounts[$item['store_id']]) ? $item['store_id'] : 0) + 1,
  111. 'time_add' => date("Y-m-d H:i:s"),
  112. ];
  113. $order = Order::create($dataChild);
  114. $orderChildId = $order->id;
  115. // 子订单内商品处理
  116. $goodsAmountTotal = 0;
  117. $orderGoods = [];
  118. if (!is_array($item['good_list'])||empty($item['good_list'])) {
  119. Db::rollBack();
  120. return '订单商品异常';
  121. }
  122. foreach ($item['good_list'] as &$goods) {
  123. $goodsAmount = bcadd(floatval($goods['money']), floatval($goods['box_money']));
  124. $goodsAmount = bcmul($goodsAmount, $goods['num']);
  125. $goodsAmountTotal = bcadd($goodsAmountTotal, $goodsAmount);
  126. $orderGoods[$goods['id']] = $goods;
  127. $orderGoods[$goods['id']]['uniacid'] = $data['uniacid'];
  128. $orderGoods[$goods['id']]['order_id'] = $orderChildId;
  129. $orderGoods[$goods['id']]['user_id'] = $dataMain['user_id'];
  130. $orderGoods[$goods['id']]['store_id'] = $item['store_id'];
  131. }
  132. // 子订单优惠总额
  133. $discountAmountTotal = bcadd($dataChild['mj_money'], $dataChild['yhq_money']);
  134. $discountAmountTotal = bcadd($discountAmountTotal, $dataChild['yhq_money2']);
  135. $discountAmountTotal = bcadd($discountAmountTotal, $dataChild['zk_money']);
  136. $discountAmountTotal = bcadd($discountAmountTotal, $dataChild['xyh_money']);
  137. $goodsAmountTotal = bcsub($goodsAmountTotal, $discountAmountTotal, 2);
  138. $orderAmountTotal = bcadd($orderAmountTotal, $goodsAmountTotal, 2);
  139. // 校验子订单金额
  140. if ($goodsAmountTotal != $dataChild['money']) {
  141. Db::rollBack();
  142. return '店铺订单总金额错误';
  143. }
  144. }
  145. // 校验库存
  146. foreach ($orderGoods as $Key=>&$goodsItem) {
  147. $goodsItem['combination_id'] = intval($goodsItem['combination_id']);
  148. // 存在规格,则去规格处查库存,整个接口还有很多别的问题,目前
  149. $goods = (object)[];
  150. if ($goodsItem['combination_id'] > 0) {
  151. $goods = SpecCombination::query()
  152. ->select('id, number AS inventory')
  153. ->where(['id' => $goodsItem['combination_id']])
  154. ->first();
  155. $goods->name = $goods->goods->name;
  156. $goods->is_max = $goods->goods->is_max;
  157. } else {
  158. $goods = Goods::query()
  159. ->select('id, name, is_max, inventory')
  160. ->where(['id' => $goodsItem['good_id']])
  161. ->first();
  162. }
  163. if (!$goods) {
  164. Db::rollBack();
  165. return '缺少商品';
  166. }
  167. if($goodsItem['num'] > $goods->inventory && $goods->is_max != Goods::INVENTORY_NOLIMIT){
  168. Db::rollBack();
  169. return '商品 '.$goods->name.' 库存不足!';
  170. }
  171. }
  172. // 校验总订单金额
  173. $deliveryAmount = 0; # 配送费用
  174. if($dataMain['order_type'] == OrderMain::ORDER_TYPE_ONLINE){
  175. $deliveryAmount = $dataMain['dada_fee'];
  176. }
  177. $orderAmountTotal = bcadd($orderAmountTotal, $deliveryAmount);
  178. # 总订单优惠总额
  179. $discountAmountTotal = bcadd($dataMain['mj_money'], $dataMain['yhq_money']);
  180. $discountAmountTotal = bcadd($discountAmountTotal, $dataMain['yhq_money2']);
  181. $discountAmountTotal = bcadd($discountAmountTotal, $dataMain['zk_money']);
  182. $discountAmountTotal = bcadd($discountAmountTotal, $dataMain['xyh_money']);
  183. $orderAmountTotal = bcsub($orderAmountTotal, $discountAmountTotal, 2);
  184. if ($orderAmountTotal != bcsub(bcadd($dataMain['money'], $deliveryAmount), $discountAmountTotal, 2)) {
  185. Db::rollBack();
  186. return '订单总金额错误';
  187. }
  188. // 添加订单商品
  189. $tempGoods = $orderGoods;
  190. $orderGoods = [];
  191. foreach ($tempGoods as $key => &$value) {
  192. $goods['good_id'] = $value['good_id'];
  193. $goods['img'] = $value['logo'];
  194. $goods['number'] = $value['num'];
  195. $goods['order_id'] = $value['order_id'];
  196. $goods['name'] = $value['name'];
  197. $goods['money'] = $value['money'];
  198. $goods['dishes_id'] = $value['dishes_id'];
  199. $goods['spec'] = $value['spec'];
  200. $goods['is_qg'] = $value['is_qg'];
  201. $goods['good_unit'] = $value['good_unit'];
  202. $goods['uniacid'] = $value['uniacid'];
  203. $goods['combination_id'] = $value['combination_id'];
  204. $orderGoods[] = $goods;
  205. }
  206. $addOrderGoods = OrderGoods::query()->insert($orderGoods);
  207. if (!$addOrderGoods) {
  208. Db::rollBack();
  209. return '订单商品异常';
  210. }
  211. // 修改总订单金额,金额是计算来的
  212. // TODO 这部分其实可以结合处理优化一下,循环前后关联处理太多
  213. $updateOrderMain = OrderMain::query()->where(['id' => $orderMainId])->update(['money' => $orderAmountTotal, 'total_money' => $dataMain['money']]);
  214. if (!$updateOrderMain) {
  215. Db::rollBack();
  216. return '订单总金额记录失败';
  217. }
  218. // 处理红包的使用
  219. $canUseConpons = $this->couponService->getOrderCanUseCoupons(
  220. $data['money'],
  221. $data['market_id'],
  222. $data['user_id'],
  223. [
  224. 'receive.id',
  225. 'receive.user_id',
  226. 'receive.number',
  227. 'receive.number_remain',
  228. 'receive.system_coupon_user_id',
  229. 'coupon.discounts',
  230. 'coupon.discount_type',
  231. ]
  232. );
  233. if (is_array($canUseConpons)&&!empty($canUseConpons)) {
  234. # 使用记录、更新当前优惠券
  235. foreach ($canUseConpons as $key => &$coupon) {
  236. $couponUse = [
  237. 'user_id' => $coupon['user_id'],
  238. 'user_receive_id' => $coupon['id'],
  239. 'system_coupon_id' => $coupon['system_coupon_user_id'],
  240. 'order_main_id' => $orderMainId,
  241. 'use_time' => $currentTime,
  242. 'return_time' => 0,
  243. 'number' => 1,
  244. 'status' => 1,
  245. 'update_time' => 0,
  246. ];
  247. $insertRes = CouponUserUse::query()->insert($couponUse);
  248. if ($insertRes) {
  249. $numberRemain = $coupon['number_remain'] - 1;
  250. if ($numberRemain == 0) {
  251. $status = 2;
  252. } elseif ($numberRemain > 0 && $numberRemain < $coupon['number']) {
  253. $status = 1;
  254. } elseif ($numberRemain == $coupon['number']) {
  255. $status = 0;
  256. }
  257. $upRes = CouponUserRec::query()->where(['id' => $coupon['id']])->update(['number_remain' => $numberRemain, 'status' => $status]);
  258. if (!$upRes) {
  259. Db::rollBack();
  260. return '优惠券使用失败';
  261. }
  262. // 缓存使用记录
  263. $usedRes = $this->couponService->cacheTodayCouponUsed($coupon['user_id'], $coupon['system_coupon_user_id'], $coupon['id']);
  264. if (!$usedRes) {
  265. Db::rollBack();
  266. return '优惠券使用失败';
  267. }
  268. } else {
  269. Db::rollBack();
  270. return '优惠券使用失败';
  271. }
  272. }
  273. }
  274. Db::commit();
  275. } catch (Exception $e) {
  276. Db::rollBack();
  277. return $e->getMessage();
  278. }
  279. // 订单成功后处理
  280. if ($orderMainId) {
  281. // 处理喇叭播报
  282. }
  283. return $orderMainId;
  284. }
  285. /**
  286. * @inheritDoc
  287. */
  288. public function addOfflineOrder()
  289. {
  290. // TODO: Implement addOfflineOrder() method.
  291. }
  292. /**
  293. * 计算和校验当前订单可用红包及金额
  294. * @param $couponIds
  295. * @param $orderAmount
  296. * @param $userId
  297. * @param $marketId
  298. * @throws Exception
  299. */
  300. protected function getCouponAmount($couponIds, $orderAmount, $userId, $marketId)
  301. {
  302. // 用户当前订单可用优惠券
  303. $couponsCanUse = $this->couponService->getOrderCanUseCoupons(
  304. $orderAmount,
  305. $marketId,
  306. $userId,
  307. [
  308. 'receive.id',
  309. 'receive.user_id',
  310. 'receive.number',
  311. 'receive.number_remain',
  312. 'receive.system_coupon_user_id',
  313. 'coupon.discounts',
  314. 'coupon.discount_type',
  315. ]
  316. );
  317. $couponCanUseIds = array_column($couponsCanUse, 'id');
  318. $couponCanUseIds = array_intersect($couponCanUseIds, $couponIds);
  319. $couponCannotUseIds = array_diff($couponIds, $couponCanUseIds);
  320. if (empty($couponCanUseIds)||!empty($couponCannotUseIds)) {
  321. throw new Exception('您的订单中有优惠券已经失效');
  322. }
  323. // 计算红包折扣金额
  324. $couponMoney = 0;
  325. foreach ($couponsCanUse as $key => $coupon) {
  326. if (!in_array($coupon->id, $couponIds)) {
  327. continue;
  328. }
  329. if ($coupon->discount_type == Coupon::DISCOUNT_TYPE_CASH) {
  330. $couponMoney = bcadd($couponMoney, $coupon->discounts, 2);
  331. } elseif ($coupon->discount_type == Coupon::DISCOUNT_TYPE_RATE) {
  332. $discountRate = bcdiv($coupon->discounts,10);
  333. $discountRate = bcsub(1,$discountRate);
  334. $discountMoney = bcmul($orderAmount, $discountRate);
  335. $couponMoney = bcadd($couponMoney, $discountMoney, 2);
  336. }
  337. }
  338. return $couponMoney;
  339. }
  340. /**
  341. * 订单是否存在
  342. * @param $orderNum
  343. * @return \Hyperf\Utils\HigherOrderTapProxy|mixed|void|null
  344. */
  345. public function existsByOrderNum($orderNum)
  346. {
  347. return OrderMain::query()->where(['order_num' => $orderNum])->value('id');
  348. }
  349. }