OrderSettledService.php 33 KB

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