OrderSettledService.php 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. <?php
  2. namespace app\api\service\order\settled;
  3. use app\api\model\order\Order as OrderModel;
  4. use app\api\model\order\OrderProduct;
  5. use app\api\model\order\OrderAddress as OrderAddress;
  6. use app\api\model\plus\coupon\UserCoupon as UserCouponModel;
  7. use app\common\enum\order\OrderPayTypeEnum;
  8. use app\common\enum\order\OrderSourceEnum;
  9. use app\common\enum\order\OrderTypeEnum;
  10. use app\common\model\settings\Setting as SettingModel;
  11. use app\api\service\points\PointsDeductService;
  12. use app\api\service\coupon\ProductDeductService;
  13. use app\common\model\store\Store as StoreModel;
  14. use app\api\service\user\UserService;
  15. use app\common\enum\settings\DeliveryTypeEnum;
  16. use app\common\library\helper;
  17. use app\common\service\delivery\ExpressService;
  18. use app\common\service\BaseService;
  19. use app\common\service\product\factory\ProductFactory;
  20. use app\api\model\shop\FullReduce as FullReduceModel;
  21. use app\api\service\fullreduce\FullDeductService;
  22. /**
  23. * 订单结算服务基类
  24. */
  25. abstract class OrderSettledService extends BaseService
  26. {
  27. /* $model OrderModel 订单模型 */
  28. public $model;
  29. // 当前应用id
  30. protected $app_id;
  31. protected $user;
  32. // 订单结算商品列表
  33. protected $supplierData = [];
  34. protected $params;
  35. /**
  36. * 订单结算的规则
  37. * 主商品默认规则
  38. */
  39. protected $settledRule = [
  40. 'is_coupon' => true, // 优惠券抵扣
  41. 'is_use_points' => true, // 是否使用积分抵扣
  42. 'force_points' => false, // 强制使用积分,积分兑换
  43. 'is_user_grade' => true, // 会员等级折扣
  44. 'is_agent' => true, // 商品是否开启分销,最终还是支付成功后判断分销活动是否开启
  45. 'is_reduce' => true, //是否满减
  46. ];
  47. /**
  48. * 订单结算数据
  49. */
  50. protected $commonOrderData = [];
  51. /**
  52. * 订单结算数据
  53. */
  54. protected $orderData = [];
  55. /**
  56. * 订单来源
  57. */
  58. protected $orderSource;
  59. /**
  60. * 构造函数
  61. */
  62. public function __construct($user, $supplierData, $params)
  63. {
  64. $this->model = new OrderModel;
  65. $this->app_id = OrderModel::$app_id;
  66. $this->user = $user;
  67. $this->supplierData = $supplierData;
  68. $this->params = $params;
  69. }
  70. /**
  71. * 订单确认-结算台
  72. */
  73. public function settlement()
  74. {
  75. // 验证商品状态, 是否允许购买
  76. $this->validateProductList();
  77. $orderTotalNum = 0;
  78. $orderTotalPrice = 0;
  79. $orderPayPrice = 0;
  80. $expressPrice = 0;
  81. $totalPointsMoney = 0;
  82. $totalPoints = 0;
  83. $this->commonOrderData = $this->getCommonOrderData();
  84. // 供应商
  85. foreach ($this->supplierData as &$supplier) {
  86. // 整理订单数据
  87. $this->orderData = $this->getOrderData($supplier['shop_supplier_id']);
  88. // 订单商品总数量
  89. $orderTotalNum += helper::getArrayColumnSum($supplier['productList'], 'total_num');
  90. // 设置订单商品会员折扣价
  91. $this->setOrderGrade($supplier['productList']);
  92. // 设置订单商品总金额(不含优惠折扣)
  93. $this->setOrderTotalPrice($supplier['productList']);
  94. $orderTotalPrice += $this->orderData['order_total_price'];
  95. // 先计算满减、自动满减,查找店铺满减
  96. if($this->settledRule['is_reduce']){
  97. $reduce = FullReduceModel::getReductList($this->orderData['order_total_price'], $orderTotalNum, $supplier['shop_supplier_id']);
  98. // 设置满减
  99. $this->orderData['reduce'] = $reduce;
  100. $reduce && $this->setOrderFullreduceMoney($reduce, $supplier['productList']);
  101. }
  102. if($this->settledRule['is_coupon']) {
  103. // 当前用户可用的优惠券列表
  104. $couponList = $this->getUserCouponList($this->orderData['order_total_price'], $supplier['shop_supplier_id']);
  105. foreach ($couponList as $i => $coupon){
  106. if(!$this->checkCouponCanUse($coupon, $supplier['productList'])){
  107. unset($couponList[$i]);
  108. }
  109. }
  110. // 计算优惠券抵扣,如果没有选择,则默认为第一个,折扣最多的
  111. $this->orderData['coupon_id'] = 0;
  112. $this->orderData['couponList'] = $couponList;
  113. if (isset($this->params['supplier'])) {
  114. $this->orderData['coupon_id'] = $this->params['supplier'][$supplier['shop_supplier_id']]['coupon_id'];
  115. } else {
  116. if (count($couponList) > 0) {
  117. $this->orderData['coupon_id'] = current($couponList)['user_coupon_id'];
  118. }
  119. }
  120. $this->setOrderCouponMoney($couponList, $this->orderData['coupon_id'], $supplier['productList']);
  121. }
  122. // 计算可用积分抵扣
  123. $this->setOrderPoints($supplier['productList']);
  124. $totalPointsMoney += $this->orderData['points_money'];
  125. $totalPoints += $this->orderData['points_num'];
  126. // 计算订单商品的实际付款金额
  127. $this->setOrderProductPayPrice($supplier['productList']);
  128. // 设置默认配送方式
  129. if(!isset($this->params['supplier'])){
  130. $this->orderData['delivery'] = $supplier['productList'][0]['is_virtual'] == 1?30:current(SettingModel::getItem('store')['delivery_type']);
  131. }else{
  132. if($supplier['productList'][0]['is_virtual'] == 1){
  133. $this->orderData['delivery'] = 30;
  134. }else {
  135. $this->orderData['delivery'] = $this->params['supplier'][$supplier['shop_supplier_id']]['delivery'];
  136. }
  137. $this->orderData['store_id'] = $this->params['supplier'][$supplier['shop_supplier_id']]['store_id'];
  138. }
  139. // 处理配送方式
  140. if ($this->orderData['delivery'] == DeliveryTypeEnum::EXPRESS) {
  141. $this->setOrderExpress($supplier['productList']);
  142. $expressPrice += $this->orderData['express_price'];
  143. } elseif ($this->orderData['delivery'] == DeliveryTypeEnum::EXTRACT) {
  144. $this->orderData['store_id'] > 0 && $this->orderData['extract_store'] = StoreModel::detail($this->params['supplier'][$supplier['shop_supplier_id']]['store_id']);
  145. }
  146. // 计算订单最终金额
  147. $this->setOrderPayPrice($supplier['productList']);
  148. $orderPayPrice += $this->orderData['order_pay_price'];
  149. $supplier['orderData'] = $this->orderData;
  150. }
  151. // 平台优惠券
  152. $couponList = $this->getUserCouponList($orderTotalPrice, 0);
  153. // 计算优惠券抵扣,如果没有选择,则默认为第一个,折扣最多的
  154. $coupon_id = 0;
  155. if($this->params['coupon_id'] > 0){
  156. $coupon_id = $this->params['coupon_id'];
  157. }else if($this->params['coupon_id'] == -1){
  158. // 传-1取最高的抵扣
  159. if(count($couponList) > 0){
  160. $coupon_id = current($couponList)['user_coupon_id'];
  161. }
  162. }
  163. $this->setOrderSysCouponMoney($coupon_id, $couponList);
  164. //最终价格
  165. $orderPayPrice = $this->setOrderFinalPrice();
  166. // 计算订单积分赠送数量
  167. $this->setOrderPointsBonus();
  168. //订单数据
  169. $this->commonOrderData = array_merge([
  170. 'order_total_num' => $orderTotalNum, // 商品总数量
  171. 'order_total_price' => helper::number2($orderTotalPrice), // 商品总价
  172. 'order_pay_price' => helper::number2($orderPayPrice), // 商品总价,最终支付
  173. 'coupon_list' => $couponList,
  174. 'coupon_id_sys' => $coupon_id,
  175. 'coupon_money_sys' => 0,
  176. 'points_money' => $totalPointsMoney,
  177. 'points_num' => $totalPoints,
  178. // 房间id
  179. 'room_id' => isset($this->params['room_id']) && $this->params['room_id'] > 0 ? $this->params['room_id'] : 0,
  180. ], $this->commonOrderData, $this->settledRule);
  181. // 返回订单数据
  182. return [
  183. 'supplierList' => $this->supplierData,
  184. 'orderData' => $this->commonOrderData
  185. ];
  186. }
  187. /**
  188. * 整理订单数据(结算台初始化),公共部分
  189. */
  190. private function getCommonOrderData()
  191. {
  192. // 积分设置
  193. $pointsSetting = SettingModel::getItem('points');
  194. return [
  195. // 默认地址
  196. 'address' => $this->user['address_default'],
  197. // 是否存在收货地址
  198. 'exist_address' => $this->user['address_id'] > 0,
  199. // 是否允许使用积分抵扣
  200. 'is_allow_points' => true,
  201. // 是否使用积分抵扣
  202. 'is_use_points' => $this->params['is_use_points'],
  203. // 支付方式
  204. 'pay_type' => isset($this->params['pay_type']) ? $this->params['pay_type'] : OrderPayTypeEnum::WECHAT,
  205. 'pay_source' => isset($this->params['pay_source']) ? $this->params['pay_source'] : '',
  206. // 系统设置
  207. 'setting' => [
  208. 'points_name' => $pointsSetting['points_name'], // 积分名称
  209. ],
  210. ];
  211. }
  212. /**
  213. * 验证订单商品的状态
  214. * @return bool
  215. */
  216. abstract function validateProductList();
  217. /**
  218. * 创建新订单
  219. */
  220. public function createOrder($order)
  221. {
  222. // 表单验证
  223. if (!$this->validateOrderForm($order, $this->params)) {
  224. return false;
  225. }
  226. // 余额支付标记订单已支付
  227. if ($order['orderData']['pay_type'] == OrderPayTypeEnum::BALANCE) {
  228. // 验证余额支付时用户余额是否满足
  229. if ($this->user['balance'] < $order['orderData']['order_pay_price']) {
  230. $this->error = '用户余额不足,无法使用余额支付';
  231. return false;
  232. }
  233. }
  234. $order_arr = [];
  235. // 创建新的订单
  236. foreach ($order['supplierList'] as $supplier){
  237. $this->model = new OrderModel;
  238. $this->model->transaction(function () use ($order, $supplier) {
  239. // 创建订单事件
  240. $this->createOrderEvent($order['orderData'], $supplier);
  241. if ($order['orderData']['pay_type'] == OrderPayTypeEnum::BALANCE) {
  242. $this->model->onPaymentByBalance($this->model['order_no']);
  243. }
  244. });
  245. array_push($order_arr, $this->model);
  246. }
  247. return $order_arr;
  248. }
  249. /**
  250. * 设置订单的商品总金额(不含优惠折扣)
  251. */
  252. private function setOrderTotalPrice($productList)
  253. {
  254. // 订单商品的总金额(不含优惠券折扣)
  255. $this->orderData['order_total_price'] = helper::number2(helper::getArrayColumnSum($productList, 'total_price'));
  256. }
  257. /**
  258. * 当前用户可用的优惠券列表
  259. */
  260. private function getUserCouponList($orderTotalPrice, $shop_supplier_id)
  261. {
  262. // 是否开启优惠券折扣
  263. if (!$this->settledRule['is_coupon']) {
  264. return [];
  265. }
  266. return UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice, $shop_supplier_id);
  267. }
  268. /**
  269. * 设置订单优惠券抵扣信息
  270. */
  271. private function setOrderCouponMoney($couponList, $couponId, $productList)
  272. {
  273. // 设置默认数据:订单信息
  274. helper::setDataAttribute($this->orderData, [
  275. 'coupon_id' => 0, // 用户优惠券id
  276. 'coupon_money' => 0, // 优惠券抵扣金额
  277. ], false);
  278. // 设置默认数据:订单商品列表
  279. helper::setDataAttribute($productList, [
  280. 'coupon_money' => 0, // 优惠券抵扣金额
  281. ], true);
  282. // 是否开启优惠券折扣
  283. if (!$this->settledRule['is_coupon']) {
  284. return false;
  285. }
  286. // 如果没有可用的优惠券,直接返回
  287. if ($couponId <= 0 || empty($couponList)) {
  288. return true;
  289. }
  290. // 获取优惠券信息
  291. $couponInfo = helper::getArrayItemByColumn($couponList, 'user_coupon_id', $couponId);
  292. if ($couponInfo == false) {
  293. $this->error = '未找到优惠券信息';
  294. return false;
  295. }
  296. // 计算订单商品优惠券抵扣金额
  297. $productListTemp = helper::getArrayColumns($productList, ['total_price']);
  298. $CouponMoney = new ProductDeductService( 'coupon_money', 'total_price');
  299. $completed = $CouponMoney->setProductCouponMoney($productListTemp, $couponInfo['reduced_price']);
  300. // 分配订单商品优惠券抵扣金额
  301. foreach ($productList as $key => &$product) {
  302. $product['coupon_money'] = $completed[$key]['coupon_money'] / 100;
  303. }
  304. // 记录订单优惠券信息
  305. $this->orderData['coupon_id'] = $couponId;
  306. $this->orderData['coupon_money'] = helper::number2($CouponMoney->getActualReducedMoney() / 100);
  307. return true;
  308. }
  309. /**
  310. * 计算订单商品的实际付款金额
  311. */
  312. private function setOrderProductPayPrice($productList)
  313. {
  314. // 商品总价 - 优惠抵扣
  315. foreach ($productList as &$product) {
  316. // 减去优惠券抵扣金额
  317. $value = helper::bcsub($product['total_price'], $product['coupon_money']);
  318. // 减去积分抵扣金额
  319. if ($this->orderData['is_allow_points'] && $this->commonOrderData['is_use_points'] && !$this->settledRule['force_points']) {
  320. $value = helper::bcsub($value, $product['points_money']);
  321. }
  322. // 减去满减金额
  323. if($this->settledRule['is_reduce'] && $this->orderData['reduce']){
  324. $value = helper::bcsub($value, $product['fullreduce_money']);
  325. }
  326. $product['total_pay_price'] = helper::number2($value);
  327. }
  328. return true;
  329. }
  330. /**
  331. * 整理订单数据(结算台初始化)
  332. */
  333. private function getOrderData($shop_supplier_id)
  334. {
  335. // 系统支持的配送方式 (后台设置)
  336. $deliveryType = SettingModel::getItem('store')['delivery_type'];
  337. // 积分设置
  338. $pointsSetting = SettingModel::getItem('points');
  339. if(isset($this->params['supplier'])){
  340. $delivery = $this->params['supplier'][$shop_supplier_id]['delivery'];
  341. }else{
  342. $delivery = $deliveryType[0];
  343. }
  344. return [
  345. // 配送类型
  346. 'delivery' => $delivery,
  347. // 默认地址
  348. 'address' => $this->user['address_default'],
  349. // 是否存在收货地址
  350. 'exist_address' => $this->user['address_id'] > 0,
  351. // 配送费用
  352. 'express_price' => 0.00,
  353. // 当前用户收货城市是否存在配送规则中
  354. 'intra_region' => true,
  355. // 自提门店信息
  356. 'extract_store' => [],
  357. // 是否允许使用积分抵扣
  358. 'is_allow_points' => false,
  359. // 是否使用积分抵扣
  360. 'is_use_points' => $this->params['is_use_points'],
  361. // 支付方式
  362. 'pay_type' => isset($this->params['pay_type']) ? $this->params['pay_type'] : OrderPayTypeEnum::WECHAT,
  363. // 系统设置
  364. 'setting' => [
  365. 'delivery' => $deliveryType, // 支持的配送方式
  366. 'points_name' => $pointsSetting['points_name'], // 积分名称
  367. ],
  368. // 记忆的自提联系方式
  369. //'last_extract' => UserService::getLastExtract($this->user['user_id']),
  370. 'deliverySetting' => $deliveryType,
  371. //门店id
  372. 'store_id' => 0,
  373. //优惠券id
  374. 'coupon_id' => 0,
  375. //优惠金额
  376. 'coupon_money'=>0
  377. ];
  378. }
  379. /**
  380. * 订单配送-快递配送
  381. */
  382. private function setOrderExpress($productList)
  383. {
  384. // 设置默认数据:配送费用
  385. helper::setDataAttribute($productList, [
  386. 'express_price' => 0,
  387. ], true);
  388. // 当前用户收货城市id
  389. $cityId = $this->user['address_default'] ? $this->user['address_default']['city_id'] : null;
  390. // 初始化配送服务类
  391. $ExpressService = new ExpressService(
  392. $this->app_id,
  393. $cityId,
  394. $productList,
  395. OrderTypeEnum::MASTER
  396. );
  397. // 获取不支持当前城市配送的商品
  398. $notInRuleProduct = $ExpressService->getNotInRuleProduct();
  399. // 验证商品是否在配送范围
  400. $this->orderData['intra_region'] = ($notInRuleProduct === false);
  401. if (!$this->orderData['intra_region']) {
  402. $notInRuleProductName = $notInRuleProduct['product_name'];
  403. $this->error = "很抱歉,您的收货地址不在商品 [{$notInRuleProductName}] 的配送范围内";
  404. return false;
  405. } else {
  406. // 计算配送金额
  407. $ExpressService->setExpressPrice();
  408. }
  409. // 订单总运费金额
  410. $this->orderData['express_price'] = helper::number2($ExpressService->getTotalFreight());
  411. return true;
  412. }
  413. /**
  414. * 设置订单的实际支付金额(含配送费)
  415. */
  416. private function setOrderPayPrice($productList)
  417. {
  418. // 订单金额(含优惠折扣)
  419. $this->orderData['order_price'] = helper::number2(helper::getArrayColumnSum($productList, 'total_pay_price'));
  420. // 订单实付款金额(订单金额 + 运费)
  421. $this->orderData['order_pay_price'] = helper::number2(helper::bcadd($this->orderData['order_price'], $this->orderData['express_price']));
  422. }
  423. /**
  424. * 表单验证 (订单提交)
  425. */
  426. private function validateOrderForm(&$order)
  427. {
  428. // 余额支付时,判断用户余额是否足够
  429. if ($order['orderData']['pay_type'] == OrderPayTypeEnum::BALANCE) {
  430. if ($this->user['balance'] < $order['orderData']['order_pay_price']) {
  431. $this->error = '用户余额不足,无法使用余额支付';
  432. return false;
  433. }
  434. }
  435. //如果是积分兑换,判断用户积分是否足够
  436. if ($this->settledRule['force_points']) {
  437. if ($this->user['points'] < $order['orderData']['points_num']) {
  438. $this->error = '用户积分不足,无法使用积分兑换';
  439. return false;
  440. }
  441. }
  442. return true;
  443. }
  444. /**
  445. * 创建订单事件
  446. */
  447. private function createOrderEvent($commomOrder, $supplier)
  448. {
  449. // 新增订单记录
  450. $status = $this->add($commomOrder, $supplier);
  451. if ($supplier['orderData']['delivery'] == DeliveryTypeEnum::EXPRESS) {
  452. // 记录收货地址
  453. $this->saveOrderAddress($commomOrder['address'], $status);
  454. } elseif ($supplier['orderData']['delivery'] == DeliveryTypeEnum::EXTRACT) {
  455. // 记录自提信息
  456. $this->saveOrderExtract($commomOrder['address']['name'], $commomOrder['address']['phone']);
  457. }
  458. // 保存订单商品信息
  459. $this->saveOrderProduct($supplier, $status);
  460. // 更新商品库存 (针对下单减库存的商品)
  461. ProductFactory::getFactory($this->orderSource['source'])->updateProductStock($supplier['productList']);
  462. // 设置优惠券使用状态
  463. UserCouponModel::setIsUse($this->params['coupon_id']);
  464. // 积分兑换扣除用户积分
  465. if ($commomOrder['force_points']) {
  466. $describe = "用户积分兑换消费:{$this->model['order_no']}";
  467. $this->user->setIncPoints(-$commomOrder['points_num'], $describe);
  468. } else {
  469. // 积分抵扣情况下扣除用户积分
  470. if ($commomOrder['is_allow_points'] && $commomOrder['is_use_points'] && $commomOrder['points_num'] > 0) {
  471. $describe = "用户消费:{$this->model['order_no']}";
  472. $this->user->setIncPoints(-$commomOrder['points_num'], $describe);
  473. }
  474. }
  475. return $status;
  476. }
  477. /**
  478. * 新增订单记录
  479. */
  480. private function add($commomOrder, $supplier)
  481. {
  482. $order = $supplier['orderData'];
  483. // 订单数据
  484. $data = [
  485. 'user_id' => $this->user['user_id'],
  486. 'order_no' => $this->model->orderNo(),
  487. 'total_price' => $order['order_total_price'],
  488. 'order_price' => $order['order_price'],
  489. 'coupon_id' => $supplier['orderData']['coupon_id'],
  490. 'coupon_money' => $supplier['orderData']['coupon_money'],
  491. 'coupon_id_sys'=>$supplier['orderData']['coupon_id_sys'],
  492. 'coupon_money_sys'=>$supplier['orderData']['coupon_money_sys'],
  493. 'points_money' => $supplier['orderData']['points_money'],
  494. 'points_num' => $supplier['orderData']['points_num'],
  495. 'pay_price' => $order['order_pay_price'],
  496. 'delivery_type' => $supplier['orderData']['delivery'],
  497. 'pay_type' => $commomOrder['pay_type'],
  498. 'pay_source' => $commomOrder['pay_source'],
  499. 'buyer_remark' => $this->params['supplier'][$supplier['shop_supplier_id']]['remark'],
  500. 'order_source' => $this->orderSource['source'],
  501. 'points_bonus' => $supplier['orderData']['points_bonus'],
  502. 'is_agent' => $this->settledRule['is_agent']? 1:0,
  503. 'shop_supplier_id' => $supplier['shop_supplier_id'],
  504. 'supplier_money' => $order['supplier_money'],
  505. 'sys_money' => $order['sys_money'],
  506. 'app_id' => $this->app_id,
  507. 'room_id' => $commomOrder['room_id'],
  508. 'virtual_auto' => $supplier['productList'][0]['virtual_auto'],
  509. ];
  510. if ($supplier['orderData']['delivery'] == DeliveryTypeEnum::EXPRESS) {
  511. $data['express_price'] = $order['express_price'];
  512. } elseif ($supplier['orderData']['delivery'] == DeliveryTypeEnum::EXTRACT) {
  513. $data['extract_store_id'] = $order['extract_store']['store_id'];
  514. }
  515. // 结束支付时间
  516. if($this->orderSource['source'] == OrderSourceEnum::SECKILL){
  517. //如果是秒杀
  518. $config = SettingModel::getItem('seckill');
  519. $closeMinters = $config['order_close'];
  520. $data['pay_end_time'] = time() + ((int)$closeMinters * 60);
  521. }else{
  522. //随主订单配置
  523. $config = SettingModel::getItem('trade');
  524. $closeDays = $config['order']['close_days'];
  525. $data['pay_end_time'] = time() + ((int)$closeDays * 86400);
  526. }
  527. // 如果是满减
  528. if(isset($order['reduce'])&&$order['reduce']){
  529. $data['fullreduce_money'] = $order['reduce']['reduced_price'];
  530. $data['fullreduce_remark'] = $order['reduce']['active_name'];
  531. }
  532. UserCouponModel::setIsUse($supplier['orderData']['coupon_id']);
  533. // 保存订单记录
  534. $this->model->save($data);
  535. return $this->model['order_id'];
  536. }
  537. /**
  538. * 记录收货地址
  539. */
  540. private function saveOrderAddress($address, $order_id)
  541. {
  542. $model = new OrderAddress();
  543. if ($address['region_id'] == 0 && !empty($address['district'])) {
  544. $address['detail'] = $address['district'] . ' ' . $address['detail'];
  545. }
  546. return $model->save([
  547. 'order_id' => $order_id,
  548. 'user_id' => $this->user['user_id'],
  549. 'app_id' => $this->app_id,
  550. 'name' => $address['name'],
  551. 'phone' => $address['phone'],
  552. 'province_id' => $address['province_id'],
  553. 'city_id' => $address['city_id'],
  554. 'region_id' => $address['region_id'],
  555. 'detail' => $address['detail'],
  556. ]);
  557. }
  558. /**
  559. * 保存上门自提联系人
  560. */
  561. private function saveOrderExtract($linkman, $phone)
  562. {
  563. // 记忆上门自提联系人(缓存),用于下次自动填写
  564. UserService::setLastExtract($this->model['user_id'], trim($linkman), trim($phone));
  565. // 保存上门自提联系人(数据库)
  566. return $this->model->extract()->save([
  567. 'linkman' => trim($linkman),
  568. 'phone' => trim($phone),
  569. 'user_id' => $this->model['user_id'],
  570. 'app_id' => $this->app_id,
  571. ]);
  572. }
  573. /**
  574. * 保存订单商品信息
  575. */
  576. private function saveOrderProduct($supplier, $status)
  577. {
  578. // 订单商品列表
  579. $productList = [];
  580. foreach ($supplier['productList'] as $product) {
  581. $item = [
  582. 'order_id' => $status,
  583. 'user_id' => $this->user['user_id'],
  584. 'app_id' => $this->app_id,
  585. 'product_id' => $product['product_id'],
  586. 'product_name' => $product['product_name'],
  587. 'image_id' => $product['image'][0]['image_id'],
  588. 'deduct_stock_type' => $product['deduct_stock_type'],
  589. 'spec_type' => $product['spec_type'],
  590. 'spec_sku_id' => $product['product_sku']['spec_sku_id'],
  591. 'product_sku_id' => isset($product['product_sku']['product_sku_id'])? $product['product_sku']['product_sku_id'] : $product['product_sku_id'],
  592. 'product_attr' => $product['product_sku']['product_attr'],
  593. 'content' => $product['content'],
  594. 'product_no' => $product['product_sku']['product_no'],
  595. 'product_price' => $product['product_sku']['product_price'],
  596. 'line_price' => $product['product_sku']['line_price'],
  597. 'product_weight' => $product['product_sku']['product_weight'],
  598. 'is_user_grade' => (int)$product['is_user_grade'],
  599. 'grade_ratio' => $product['grade_ratio'],
  600. 'grade_product_price' => isset($product['grade_product_price'])?$product['grade_product_price']:0,
  601. 'grade_total_money' => $product['grade_total_money'],
  602. 'coupon_money' => isset($product['coupon_money'])?$product['coupon_money']:0,
  603. 'points_money' => isset($product['points_money'])?$product['points_money']:0,
  604. 'points_num' => isset($product['points_num'])?$product['points_num']:0,
  605. 'points_bonus' => isset($product['points_bonus'])?$product['points_bonus']:0,
  606. 'total_num' => $product['total_num'],
  607. 'total_price' => $product['total_price'],
  608. 'total_pay_price' => $product['total_pay_price'],
  609. 'supplier_money' => $product['supplier_money'],
  610. 'is_agent' => $product['is_agent'],
  611. 'is_ind_agent' => $product['is_ind_agent'],
  612. 'agent_money_type' => $product['agent_money_type'],
  613. 'first_money' => $product['first_money'],
  614. 'second_money' => $product['second_money'],
  615. 'third_money' => $product['third_money'],
  616. 'fullreduce_money' => isset($product['fullreduce_money'])?$product['fullreduce_money']:0,
  617. 'virtual_content' => $product['virtual_content'],
  618. ];
  619. // 记录订单商品来源id
  620. $item['product_source_id'] = isset($product['product_source_id']) ? $product['product_source_id'] : 0;
  621. // 记录订单商品sku来源id
  622. $item['sku_source_id'] = isset($product['sku_source_id']) ? $product['sku_source_id'] : 0;
  623. // 记录拼团类的商品来源id
  624. $item['bill_source_id'] = isset($product['bill_source_id']) ? $product['bill_source_id'] : 0;
  625. $productList[] = $item;
  626. }
  627. $model = new OrderProduct();
  628. return $model->saveAll($productList);
  629. }
  630. /**
  631. * 计算订单可用积分抵扣
  632. */
  633. private function setOrderPoints($productList)
  634. {
  635. $this->orderData['points_money'] = 0;
  636. // 积分抵扣总数量
  637. $this->orderData['points_num'] = 0;
  638. // 允许积分抵扣
  639. $this->orderData['is_allow_points'] = false;
  640. // 积分商城兑换
  641. if (isset($this->settledRule['force_points']) && $this->settledRule['force_points']) {
  642. // 积分抵扣金额,商品价格-兑换金额
  643. $this->orderData['points_money'] = $productList[0]['points_money'];
  644. // 积分抵扣总数量
  645. $this->orderData['points_num'] = $productList[0]['points_num'];
  646. // 允许积分抵扣
  647. $this->orderData['is_allow_points'] = true;
  648. if ($this->user['points'] < $productList[0]['points_num']) {
  649. $this->error = '积分不足,去多赚点积分吧!';
  650. return false;
  651. }
  652. return true;
  653. }
  654. // 积分设置
  655. $setting = SettingModel::getItem('points');
  656. // 条件:后台开启下单使用积分抵扣
  657. if (!$setting['is_shopping_discount']) {
  658. return false;
  659. }
  660. // 条件:订单金额满足[?]元
  661. if (helper::bccomp($setting['discount']['full_order_price'], $this->orderData['order_total_price']) === 1) {
  662. return false;
  663. }
  664. // 计算订单商品最多可抵扣的积分数量
  665. $this->setOrderProductMaxPointsNum($productList);
  666. // 订单最多可抵扣的积分总数量
  667. $maxPointsNumCount = helper::getArrayColumnSum($productList, 'max_points_num');
  668. // 实际可抵扣的积分数量
  669. $actualPointsNum = min($maxPointsNumCount, $this->user['points']);
  670. if ($actualPointsNum < 1) {
  671. $this->orderData['points_money'] = 0;
  672. // 积分抵扣总数量
  673. $this->orderData['points_num'] = 0;
  674. // 允许积分抵扣
  675. $this->orderData['is_allow_points'] = true;
  676. return false;
  677. }
  678. // 计算订单商品实际抵扣的积分数量和金额
  679. $ProductDeduct = new PointsDeductService($productList);
  680. $ProductDeduct->setProductPoints($maxPointsNumCount, $actualPointsNum);
  681. // 积分抵扣总金额
  682. $orderPointsMoney = helper::getArrayColumnSum($productList, 'points_money');
  683. $this->orderData['points_money'] = helper::number2($orderPointsMoney);
  684. // 积分抵扣总数量
  685. $this->orderData['points_num'] = $actualPointsNum;
  686. // 允许积分抵扣
  687. $this->orderData['is_allow_points'] = true;
  688. return true;
  689. }
  690. /**
  691. * 计算订单商品最多可抵扣的积分数量
  692. */
  693. private function setOrderProductMaxPointsNum($productList)
  694. {
  695. // 积分设置
  696. $setting = SettingModel::getItem('points');
  697. foreach ($productList as &$product) {
  698. // 积分兑换
  699. if ($this->settledRule['force_points']) {
  700. $product['max_points_num'] = $product['points_num'];
  701. } else {
  702. // 商品不允许积分抵扣
  703. if (!$product['is_points_discount']) continue;
  704. // 积分抵扣比例
  705. $deductionRatio = helper::bcdiv($setting['discount']['max_money_ratio'], 100);
  706. // 最多可抵扣的金额
  707. $maxPointsMoney = helper::bcmul($product['total_price'], $deductionRatio);
  708. // 最多可抵扣的积分数量
  709. $product['max_points_num'] = helper::bcdiv($maxPointsMoney, $setting['discount']['discount_ratio'], 0);
  710. // 如果超过商品最大抵扣数量
  711. if($product['max_points_discount'] != -1 && $product['max_points_num'] > $product['max_points_discount']){
  712. $product['max_points_num'] = $product['max_points_discount'];
  713. }
  714. }
  715. }
  716. return true;
  717. }
  718. /**
  719. * 计算订单积分赠送数量
  720. */
  721. private function setOrderPointsBonus()
  722. {
  723. // 初始化商品积分赠送数量
  724. foreach ($this->supplierData as &$supplier){
  725. foreach ($supplier['productList'] as $product){
  726. $product['points_bonus'] = 0;
  727. }
  728. $supplier['orderData']['points_bonus'] = 0;
  729. }
  730. // 积分设置
  731. $setting = SettingModel::getItem('points');
  732. // 条件:后台开启开启购物送积分
  733. if (!$setting['is_shopping_gift']) {
  734. return false;
  735. }
  736. // 设置商品积分赠送数量
  737. foreach ($this->supplierData as &$supplier) {
  738. foreach ($supplier['productList'] as &$product) {
  739. // 积分赠送比例
  740. $ratio = $setting['gift_ratio'] / 100;
  741. // 计算抵扣积分数量
  742. $product['points_bonus'] = !$product['is_points_gift'] ? 0 : helper::bcmul($product['total_pay_price'], $ratio, 0);
  743. }
  744. // 订单积分赠送数量
  745. $supplier['orderData']['points_bonus'] = helper::getArrayColumnSum($supplier['productList'], 'points_bonus');
  746. }
  747. return true;
  748. }
  749. /**
  750. * 设置订单商品会员折扣价
  751. */
  752. private function setOrderGrade($productList)
  753. {
  754. // 设置默认数据
  755. helper::setDataAttribute($productList, [
  756. // 标记参与会员折扣
  757. 'is_user_grade' => false,
  758. // 会员等级抵扣的金额
  759. 'grade_ratio' => 0,
  760. // 会员折扣的商品单价
  761. 'grade_goods_price' => 0.00,
  762. // 会员折扣的总额差
  763. 'grade_total_money' => 0.00,
  764. ], true);
  765. // 是否开启会员等级折扣
  766. if (!$this->settledRule['is_user_grade']) {
  767. return false;
  768. }
  769. // 计算抵扣金额
  770. foreach ($productList as &$product) {
  771. // 判断商品是否参与会员折扣
  772. if (!$product['is_enable_grade']) {
  773. continue;
  774. }
  775. $alone_grade_type = 10;
  776. // 商品单独设置了会员折扣
  777. if ($product['is_alone_grade'] && isset($product['alone_grade_equity'][$this->user['grade_id']])) {
  778. if($product['alone_grade_type'] == 10){
  779. // 折扣比例
  780. $discountRatio = helper::bcdiv($product['alone_grade_equity'][$this->user['grade_id']], 100);
  781. }else{
  782. $alone_grade_type = 20;
  783. $discountRatio = helper::bcdiv($product['alone_grade_equity'][$this->user['grade_id']], $product['product_price'], 2);
  784. }
  785. } else {
  786. // 折扣比例
  787. $discountRatio = helper::bcdiv($this->user['grade']['equity'], 100);
  788. }
  789. if ($discountRatio < 1) {
  790. // 会员折扣后的商品总金额
  791. if($alone_grade_type == 20){
  792. // 固定金额
  793. $gradeTotalPrice = $product['alone_grade_equity'][$this->user['grade_id']] * $product['total_num'];
  794. $grade_product_price = $product['alone_grade_equity'][$this->user['grade_id']];
  795. }else{
  796. $gradeTotalPrice = max(0.01, helper::bcmul($product['total_price'], $discountRatio));
  797. $grade_product_price = helper::number2(helper::bcmul($product['product_price'], $discountRatio), true);
  798. }
  799. helper::setDataAttribute($product, [
  800. 'is_user_grade' => true,
  801. 'grade_ratio' => $discountRatio,
  802. 'grade_product_price' => $grade_product_price,
  803. 'grade_total_money' => helper::number2(helper::bcsub($product['total_price'], $gradeTotalPrice)),
  804. 'total_price' => $gradeTotalPrice,
  805. ], false);
  806. }
  807. }
  808. return true;
  809. }
  810. /**
  811. * 设置订单满减抵扣信息
  812. */
  813. private function setOrderFullreduceMoney($reduce, $productList)
  814. {
  815. // 计算订单商品满减抵扣金额
  816. $productListTemp = helper::getArrayColumns($productList, ['total_price']);
  817. $service = new FullDeductService;
  818. $completed = $service->setProductFullreduceMoney($productListTemp, $reduce['reduced_price']);
  819. // 分配订单商品优惠券抵扣金额
  820. foreach ($productList as $key => &$product) {
  821. $product['fullreduce_money'] = $completed[$key]['fullreduce_money'] / 100;
  822. }
  823. return true;
  824. }
  825. /**
  826. * 系统优惠券抵扣
  827. */
  828. private function setOrderSysCouponMoney($couponId, $couponList){
  829. // 设置默认数据:订单信息
  830. helper::setDataAttribute($this->commonOrderData, [
  831. 'coupon_id_sys' => 0, // 用户优惠券id
  832. 'coupon_money_sys' => 0, // 优惠券抵扣金额
  833. ], false);
  834. $productList = [];
  835. foreach ($this->supplierData as &$supplier){
  836. foreach ($supplier['productList'] as $product){
  837. array_push($productList, $product);
  838. }
  839. $supplier['orderData']['coupon_id_sys'] = 0;
  840. $supplier['orderData']['coupon_money_sys'] = 0;
  841. }
  842. // 设置默认数据:订单商品列表
  843. helper::setDataAttribute($productList, [
  844. 'coupon_money_sys' => 0, // 优惠券抵扣金额
  845. ], true);
  846. // 是否开启优惠券折扣
  847. if (!$this->settledRule['is_coupon']) {
  848. return false;
  849. }
  850. // 如果没有可用的优惠券,直接返回
  851. if ($couponId <= 0 || empty($couponList)) {
  852. return true;
  853. }
  854. // 获取优惠券信息
  855. $couponInfo = helper::getArrayItemByColumn($couponList, 'user_coupon_id', $couponId);
  856. if ($couponInfo == false) {
  857. $this->error = '未找到优惠券信息';
  858. return false;
  859. }
  860. // 计算订单商品优惠券抵扣金额
  861. $productListTemp = helper::getArrayColumns($productList, ['total_pay_price']);
  862. $CouponMoney = new ProductDeductService('coupon_money_sys', 'total_pay_price');
  863. $completed = $CouponMoney->setProductCouponMoney($productListTemp, $couponInfo['reduced_price']);
  864. // 分配订单商品优惠券抵扣金额
  865. foreach ($productList as $key => &$product) {
  866. $product['coupon_money_sys'] = $completed[$key]['coupon_money_sys'] / 100;
  867. }
  868. // 统计供应商的分配额度
  869. foreach ($this->supplierData as &$supplier){
  870. $supplier['orderData']['coupon_id_sys'] = $couponId;
  871. $supplier['orderData']['coupon_money_sys'] = helper::getArrayColumnSum($supplier['productList'], 'coupon_money_sys');
  872. }
  873. // 记录订单优惠券信息
  874. $this->commonOrderData['coupon_id_sys'] = $couponId;
  875. $this->commonOrderData['coupon_money_sys'] = helper::number2($CouponMoney->getActualReducedMoney() / 100);
  876. return true;
  877. }
  878. /**
  879. * 获取所有支付价格
  880. */
  881. private function setOrderFinalPrice(){
  882. $config = SettingModel::getItem('store');
  883. $sys_percent = intval($config['commission_rate']);
  884. $supplier_percent = 100 - $sys_percent;
  885. foreach ($this->supplierData as &$supplier){
  886. $coupon_money_sys = helper::getArrayColumnSum($supplier['productList'], 'coupon_money_sys');
  887. $supplier['orderData']['order_pay_price'] -= $coupon_money_sys;
  888. // 供应商结算金额,包括运费
  889. $supplier['orderData']['supplier_money'] = helper::number2($supplier['orderData']['order_price'] * $supplier_percent/100 + $supplier['orderData']['express_price']);
  890. // 平台分佣金额
  891. $supplier['orderData']['sys_money'] = helper::number2($supplier['orderData']['order_price'] * $sys_percent/100);
  892. // 产品价格
  893. // 结算金额不包括运费
  894. foreach ($supplier['productList'] as &$product){
  895. $product['total_pay_price'] -= $product['coupon_money_sys'];
  896. $product['supplier_money'] = helper::number2($product['total_pay_price'] * $supplier_percent/100);
  897. $product['sys_money'] = helper::number2($product['total_pay_price'] * $sys_percent/100);
  898. }
  899. }
  900. $price = 0;
  901. foreach ($this->supplierData as &$supplier){
  902. $price += $supplier['orderData']['order_pay_price'];
  903. }
  904. return $price;
  905. }
  906. /**
  907. * 检查优惠券是否可以使用
  908. */
  909. private function checkCouponCanUse($coupon, $productList){
  910. // 0无限制
  911. if($coupon['free_limit'] == 1){
  912. //不可与促销同时,目前只有满减
  913. if($this->orderData['reduce']){
  914. return false;
  915. }
  916. } else if($coupon['free_limit'] == 2){
  917. //不可与等级优惠同时
  918. foreach ($productList as $product){
  919. if($product['is_user_grade']){
  920. return false;
  921. }
  922. }
  923. } else if($coupon['free_limit'] == 3){
  924. //不可与促销和等级同时
  925. if($this->orderData['reduce']){
  926. return false;
  927. }
  928. foreach ($productList as $product){
  929. if($product['is_user_grade']){
  930. return false;
  931. }
  932. }
  933. }
  934. // 是否限制商品使用
  935. if($coupon['apply_range'] == 20){
  936. $product_ids = explode(',', $coupon['product_ids']);
  937. foreach ($productList as $product){
  938. if(!in_array($product['product_id'], $product_ids)){
  939. return false;
  940. }
  941. }
  942. }
  943. return true;
  944. }
  945. }