فهرست منبع

delete api fule

zhangdehua 1 سال پیش
والد
کامیت
893a29f573

+ 4 - 4
app/index/controller/Checkout.php

@@ -12,12 +12,12 @@ declare (strict_types=1);
 
 namespace app\index\controller;
 
-use app\api\model\Order as OrderModel;
-use app\api\service\order\PaySuccess;
+use app\index\model\Order as OrderModel;
+use app\index\service\order\PaySuccess;
 use app\index\service\User as UserService;
 use app\index\service\Cart as CartService;
-use app\api\service\order\Checkout as CheckoutService;
-use app\api\validate\order\Checkout as CheckoutValidate;
+use app\index\service\order\Checkout as CheckoutService;
+use app\index\validate\order\Checkout as CheckoutValidate;
 use app\common\enum\order\PayType as OrderPayTypeEnum;
 use app\common\library\paypal\PayPal;
 use cores\exception\BaseException;

+ 421 - 0
app/index/model/Order.php

@@ -0,0 +1,421 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\index\model;
+
+use app\common\library\paypal\PayPal;
+use app\api\model\{Goods as GoodsModel, OrderRefund as OrderRefundModel, Setting as SettingModel};
+use app\api\service\{User as UserService, Payment as PaymentService};
+use app\api\service\order\{PaySuccess as OrderPaySuccesService, source\Factory as OrderSourceFactory};
+use app\common\model\Order as OrderModel;
+use app\common\service\{Order as OrderService, order\Complete as OrderCompleteService};
+use app\common\enum\{
+    Setting as SettingEnum,
+    OrderType as OrderTypeEnum,
+    order\PayType as OrderPayTypeEnum,
+    order\PayStatus as PayStatusEnum,
+    order\OrderStatus as OrderStatusEnum,
+//    order\DeliveryType as DeliveryTypeEnum,
+    order\ReceiptStatus as ReceiptStatusEnum,
+    order\DeliveryStatus as DeliveryStatusEnum
+};
+use app\common\library\helper;
+use cores\exception\BaseException;
+
+/**
+ * 订单模型
+ * Class Order
+ * @package app\api\model
+ */
+class Order extends OrderModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'update_time'
+    ];
+
+    // 信息提示
+    private $message = '';
+
+    /**
+     * 待支付订单详情
+     * @param string $orderNo 订单号
+     * @return null|static
+     */
+    public static function getPayDetail(string $orderNo): ?Order
+    {
+        return self::detail(['order_no' => $orderNo, 'pay_status' => PayStatusEnum::PENDING, 'is_delete' => 0], ['goods', 'user']);
+    }
+
+    /**
+     * 订单支付事件
+     * @param int $payType
+     * @return bool
+     */
+    public function onPay(int $payType = OrderPayTypeEnum::WECHAT): bool
+    {
+        // 判断订单状态
+        $orderSource = OrderSourceFactory::getFactory($this['order_source']);
+        if (!$orderSource->checkOrderStatusOnPay($this)) {
+            $this->error = $orderSource->getError();
+            return false;
+        }
+        // 余额支付
+        if ($payType == OrderPayTypeEnum::BALANCE) {
+            return $this->onPaymentByBalance($this['order_no']);
+        }
+        return true;
+    }
+
+    /**
+     * 构建支付请求的参数
+     * @param self $order 订单信息
+     * @param int $payType 订单支付方式
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function onOrderPayment(self $order, int $payType): array
+    {
+        if ($payType == OrderPayTypeEnum::WECHAT) {
+            return $this->onPaymentByWechat($order);
+        }
+        if ($payType == OrderPayTypeEnum::PAYPAL) {
+            return $this->onPaymentByPaypal($order);
+        }
+        return [];
+    }
+
+    /**
+     * 构建微信支付请求
+     * @param self $order 订单详情
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    protected function onPaymentByWechat(self $order): array
+    {
+        return PaymentService::wechat(
+            $order['order_id'],
+            $order['order_no'],
+            $order['pay_price'],
+            OrderTypeEnum::ORDER
+        );
+    }
+
+    protected function onPaymentByPaypal(self $order): array
+    {
+        $conf = [
+            'client_id' => 'AS0FH780ZGtSAdpT1NTjwkFzryCPf69rZb_FR9Rt_rZdasB80cmjqTQ6CQELWiFVh_MU9e31CSnyz7Ai',     // ClientID
+            'secret' => 'EDqRQhgLNHCb5bxld98T8-JJJZKvMIeqxudO7lMwDFOxBfy138PjM5A21FnDNyb3q4yYUh8r7Qr2BnVi',      // ClientSecret
+        'web_hook_id'=>'3NP026061E6858914',
+        ];
+        $pp = new PayPal($conf);
+
+        $pp->unify($order['order_no'],$order['pay_price']);
+
+        return PaymentService::wechat(
+            $order['order_id'],
+            $order['order_no'],
+            $order['pay_price'],
+            OrderTypeEnum::ORDER
+        );
+    }
+
+    /**
+     * 立即购买:获取订单商品列表
+     * @param int $goodsId 商品ID
+     * @param string $goodsSkuId 商品SKU
+     * @param int $goodsNum 购买数量
+     * @return mixed
+     * @throws BaseException
+     */
+    public function getOrderGoodsListByNow(int $goodsId, string $goodsSkuId, int $goodsNum)
+    {
+        // 获取商品列表
+        $model = new GoodsModel;
+        $goodsList = $model->isGoodsGradeMoney(false)->getListByIdsFromApi([$goodsId]);
+        if ($goodsList->isEmpty()) {
+            throwError('未找到商品信息');
+        }
+        // 隐藏冗余的属性
+        $goodsList->hidden(array_merge($model->hidden, ['content', 'goods_images', 'images']));
+        foreach ($goodsList as &$item) {
+            // 商品sku信息
+            $goodsInfo['skuInfo'] = GoodsModel::getSkuInfo($item, $goodsSkuId, false);
+            // 商品单价
+            $item['goods_price'] = $item['skuInfo']['goods_price'];
+            // 商品购买数量
+            $item['total_num'] = $goodsNum;
+            // 商品SKU索引
+            $item['goods_sku_id'] = $item['skuInfo']['goods_sku_id'];
+            // 商品购买总金额
+            $item['total_price'] = helper::bcmul($item['goods_price'], $goodsNum);
+        }
+        return $goodsList;
+    }
+
+    /**
+     * 余额支付标记订单已支付
+     * @param string $orderNo 订单号
+     * @return bool
+     */
+    public function onPaymentByBalance(string $orderNo): bool
+    {
+        // 获取订单详情
+        $service = new OrderPaySuccesService($orderNo);
+        // 发起余额支付
+        $status = $service->onPaySuccess(OrderPayTypeEnum::BALANCE);
+        if (!$status) {
+            $this->error = $service->getError();
+        }
+        return $status;
+    }
+
+    /**
+     * 获取用户订单列表
+     * @param string $type 订单类型 (all全部 payment待付款 received待发货 deliver待收货 comment待评价)
+     * @return \think\Paginator
+     * @throws \think\db\exception\DbException
+     * @throws BaseException
+     */
+    public function getList(string $type = 'all'): \think\Paginator
+    {
+        // 筛选条件
+        $filter = [];
+        // 订单数据类型
+        switch ($type) {
+            case 'all':
+                break;
+            case 'payment':
+                $filter['pay_status'] = PayStatusEnum::PENDING;
+                $filter['order_status'] = OrderStatusEnum::NORMAL;
+                break;
+            case 'delivery':
+                $filter['pay_status'] = PayStatusEnum::SUCCESS;
+                $filter['delivery_status'] = DeliveryStatusEnum::NOT_DELIVERED;
+                $filter['order_status'] = OrderStatusEnum::NORMAL;
+                break;
+            case 'received':
+                $filter['pay_status'] = PayStatusEnum::SUCCESS;
+                $filter['delivery_status'] = DeliveryStatusEnum::DELIVERED;
+                $filter['receipt_status'] = ReceiptStatusEnum::NOT_RECEIVED;
+                $filter['order_status'] = OrderStatusEnum::NORMAL;
+                break;
+            case 'comment':
+                $filter['is_comment'] = 0;
+                $filter['order_status'] = OrderStatusEnum::COMPLETED;
+                break;
+        }
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 查询列表数据
+        return $this->with(['goods.image'])
+            ->where($filter)
+            ->where('user_id', '=', $userId)
+            ->where('is_delete', '=', 0)
+            ->order(['create_time' => 'desc'])
+            ->paginate(15);
+    }
+
+    /**
+     * 取消订单
+     * @return bool|mixed
+     */
+    public function cancel()
+    {
+        if ($this['delivery_status'] == DeliveryStatusEnum::DELIVERED) {
+            $this->error = '已发货订单不可取消';
+            return false;
+        }
+        // 订单是否已支付
+        $isPay = $this['pay_status'] == PayStatusEnum::SUCCESS;
+        // 提示信息
+        $this->message = $isPay ? '订单已申请取消,需等待后台审核' : '订单已取消成功';
+        // 订单取消事件
+        return $this->transaction(function () use ($isPay) {
+            // 订单取消事件
+            $isPay == false && OrderService::cancelEvent($this);
+            // 更新订单状态: 已付款的订单设置为"待取消", 等待后台审核
+            return $this->save(['order_status' => $isPay ? OrderStatusEnum::APPLY_CANCEL : OrderStatusEnum::CANCELLED]);
+        });
+    }
+
+    /**
+     * 确认收货
+     * @return bool|mixed
+     */
+    public function receipt()
+    {
+        // 验证订单是否合法
+        // 条件1: 订单必须已发货
+        // 条件2: 订单必须未收货
+        if ($this['delivery_status'] != 20 || $this['receipt_status'] != 10) {
+            $this->error = '该订单不合法';
+            return false;
+        }
+        return $this->transaction(function () {
+            // 更新订单状态
+            $status = $this->save([
+                'receipt_status' => 20,
+                'receipt_time' => time(),
+                'order_status' => 30
+            ]);
+            // 执行订单完成后的操作
+            $OrderCompleteService = new OrderCompleteService();
+            $OrderCompleteService->complete([$this], static::$storeId);
+            return $status;
+        });
+    }
+
+    /**
+     * 获取当前用户订单数量
+     * @param string $type 订单类型 (all全部 payment待付款 received待发货 deliver待收货 comment待评价)
+     * @return int
+     * @throws BaseException
+     */
+    public function getCount(string $type = 'all'): int
+    {
+        // 筛选条件
+        $filter = [];
+        // 订单数据类型
+        switch ($type) {
+            case 'all':
+                break;
+            case 'payment':
+                $filter['pay_status'] = PayStatusEnum::PENDING;
+                break;
+            case 'received':
+                $filter['pay_status'] = PayStatusEnum::SUCCESS;
+                $filter['delivery_status'] = DeliveryStatusEnum::DELIVERED;
+                $filter['receipt_status'] = ReceiptStatusEnum::NOT_RECEIVED;
+                break;
+            case 'delivery':
+                $filter['pay_status'] = PayStatusEnum::SUCCESS;
+                $filter['delivery_status'] = DeliveryStatusEnum::NOT_DELIVERED;
+                $filter['order_status'] = OrderStatusEnum::NORMAL;
+                break;
+            case 'comment':
+                $filter['is_comment'] = 0;
+                $filter['order_status'] = OrderStatusEnum::COMPLETED;
+                break;
+        }
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 查询数据
+        return $this->where('user_id', '=', $userId)
+            ->where('order_status', '<>', 20)
+            ->where($filter)
+            ->where('is_delete', '=', 0)
+            ->count();
+    }
+
+    /**
+     * 获取用户订单详情(含关联数据)
+     * @param int $orderId 订单ID
+     * @return Order|array|null
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function getUserOrderDetail(int $orderId)
+    {
+        // 关联查询
+        $with = [
+            'goods' => ['image', 'goods', 'refund'],
+            'address', 'express'
+        ];
+        // 查询订单记录
+        $order = static::getDetail($orderId, $with);
+        // 该订单是否允许申请售后
+        $order['isAllowRefund'] = static::isAllowRefund($order);
+        return $order;
+    }
+
+    /**
+     * 获取用户订单详情(仅订单记录)
+     * @param int $orderId
+     * @param array $with
+     * @return Order|array|null
+     * @throws BaseException
+     */
+    public static function getDetail(int $orderId, array $with = [])
+    {
+        // 查询订单记录
+        $order = static::detail([
+            'order_id' => $orderId,
+            'user_id' => UserService::getCurrentLoginUserId(),
+        ], $with);
+        empty($order) && throwError('订单不存在');
+        return $order;
+    }
+
+    /**
+     * 获取当前用户待处理的订单数量
+     * @return array
+     * @throws BaseException
+     */
+    public function getTodoCounts(): array
+    {
+        return [
+            'payment' => $this->getCount('payment'),    // 待付款的订单
+            'delivery' => $this->getCount('delivery'),  // 待发货的订单
+            'received' => $this->getCount('received'),  // 待收货的订单
+            'refund' => OrderRefundModel::getCountByUnderway(),  // 进行中的售后单
+        ];
+    }
+
+    // 返回提示信息
+    public function getMessage(): string
+    {
+        return $this->message;
+    }
+
+    /**
+     * 当前订单是否允许申请售后
+     * @param Order $order
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private static function isAllowRefund(self $order): bool
+    {
+        // 必须是已发货的订单
+        if ($order['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
+            return false;
+        }
+        // 允许申请售后期限(天)
+        $refundDays = SettingModel::getItem(SettingEnum::TRADE)['order']['refund_days'];
+        // 不允许售后
+        if ($refundDays == 0) {
+            return false;
+        }
+        // 当前时间超出允许申请售后期限
+        if (
+            $order['receipt_status'] == ReceiptStatusEnum::RECEIVED
+            && time() > ($order->getData('receipt_time') + ((int)$refundDays * 86400))
+        ) {
+            return false;
+        }
+        return true;
+    }
+}

+ 855 - 0
app/index/service/order/Checkout.php

@@ -0,0 +1,855 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\index\service\order;
+
+use app\api\model\Order as OrderModel;
+
+use app\index\model\User as UserModel;
+use app\api\model\Goods as GoodsModel;
+use app\api\model\Setting as SettingModel;
+use app\api\model\UserCoupon as UserCouponModel;
+
+use app\index\service\User as UserService;
+use app\api\service\Payment as PaymentService;
+use app\api\service\user\Grade as UserGradeService;
+use app\api\service\coupon\GoodsDeduct as GoodsDeductService;
+use app\api\service\points\GoodsDeduct as PointsDeductService;
+use app\api\service\order\source\checkout\Factory as CheckoutFactory;
+
+use app\common\enum\order\PayStatus as PayStatusEnum;
+use app\common\enum\Setting as SettingEnum;
+use app\common\enum\order\PayType as OrderPayTypeEnum;
+use app\common\enum\order\OrderStatus as OrderStatusEnum;
+use app\common\enum\order\OrderSource as OrderSourceEnum;
+use app\common\enum\order\DeliveryType as DeliveryTypeEnum;
+use app\common\service\BaseService;
+use app\common\service\delivery\Express as ExpressService;
+use app\common\service\goods\source\Factory as StockFactory;
+use app\common\library\helper;
+use cores\exception\BaseException;
+
+/**
+ * 订单结算台服务类
+ * Class Checkout
+ * @package app\api\service\order
+ */
+class Checkout extends BaseService
+{
+    /* $model OrderModel 订单模型 */
+    public $model;
+
+    /* @var UserModel $user 当前用户信息 */
+    private $user;
+
+    // 订单结算商品列表
+    private $goodsList = [];
+
+    /**
+     * 订单结算api参数
+     * @var array
+     */
+    private $param = [
+        'delivery' => null,     // 配送方式
+        'couponId' => 0,        // 用户的优惠券ID
+        'isUsePoints' => 0,     // 是否使用积分抵扣
+        'remark' => '',         // 买家留言
+        'payType' => OrderPayTypeEnum::BALANCE,  // 支付方式
+    ];
+
+    /**
+     * 订单结算的规则
+     * @var array
+     */
+    private $checkoutRule = [
+        'isUserGrade' => true,    // 会员等级折扣
+        'isCoupon' => true,        // 优惠券抵扣
+        'isUsePoints' => true,        // 是否使用积分抵扣
+    ];
+
+    /**
+     * 订单来源
+     * @var array
+     */
+    private $orderSource = [
+        'source' => OrderSourceEnum::MAIN,
+        'source_id' => 0,
+    ];
+
+    /**
+     * 订单结算数据
+     * @var array
+     */
+    private $orderData = [];
+
+    /**
+     * 构造函数
+     * Checkout constructor.
+     * @throws BaseException
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        $this->user = UserService::getCurrentLoginUser();
+        $this->model = new OrderModel;
+        $this->storeId = $this->getStoreId();
+    }
+
+    /**
+     * 设置结算台请求的参数
+     * @param $param
+     * @return array
+     */
+    public function setParam($param): array
+    {
+        $this->param = array_merge($this->param, $param);
+        return $this->getParam();
+    }
+
+    /**
+     * 获取结算台请求的参数
+     * @return array
+     */
+    public function getParam(): array
+    {
+        return $this->param;
+    }
+
+    /**
+     * 订单结算的规则
+     * @param $data
+     * @return $this
+     */
+    public function setCheckoutRule($data): Checkout
+    {
+        $this->checkoutRule = array_merge($this->checkoutRule, $data);
+        return $this;
+    }
+
+    /**
+     * 设置订单来源(普通订单)
+     * @param $data
+     * @return $this
+     */
+    public function setOrderSource($data): Checkout
+    {
+        $this->orderSource = array_merge($this->orderSource, $data);
+        return $this;
+    }
+
+    /**
+     * 订单确认-结算台
+     * @param $goodsList
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function onCheckout($goodsList): array
+    {
+        // 订单确认-立即购买
+        $this->goodsList = $goodsList;
+        return $this->checkout();
+    }
+
+    /**
+     * 订单结算台
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function checkout(): array
+    {
+        // 整理订单数据
+        $this->orderData = $this->getOrderData();
+        // 验证商品状态, 是否允许购买
+        $this->validateGoodsList();
+        // 订单商品总数量
+        $orderTotalNum = (int)helper::getArrayColumnSum($this->goodsList, 'total_num');
+        // 设置订单商品会员折扣价
+        $this->setOrderGoodsGradeMoney();
+        // 设置订单商品总金额(不含优惠折扣)
+        $this->setOrderTotalPrice();
+        // 当前用户可用的优惠券列表
+        $couponList = $this->getUserCouponList((float)$this->orderData['orderTotalPrice']);
+        // 计算优惠券抵扣
+        $this->setOrderCouponMoney($couponList, (int)$this->param['couponId']);
+        // 计算可用积分抵扣
+        $this->setOrderPoints();
+        // 计算订单商品的实际付款金额
+        $this->setOrderGoodsPayPrice();
+        // 设置默认配送方式
+        if (!$this->param['delivery']) {
+            $deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
+            $this->param['delivery'] = current($deliveryType);
+        }
+        // 处理配送方式
+        if ($this->param['delivery'] == DeliveryTypeEnum::EXPRESS) {
+            $this->setOrderExpress();
+        }
+        // 计算订单最终金额
+        $this->setOrderPayPrice();
+        // 计算订单积分赠送数量
+        $this->setOrderPointsBonus();
+        // 返回订单数据
+        return array_merge([
+            'goodsList' => $this->goodsList,   // 商品信息
+            'orderTotalNum' => $orderTotalNum,        // 商品总数量
+            'couponList' => array_values($couponList), // 优惠券列表
+            'hasError' => $this->hasError(),
+            'errorMsg' => $this->getError(),
+        ], $this->orderData);
+    }
+
+    /**
+     * 计算订单可用积分抵扣
+     * @return void
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function setOrderPoints(): void
+    {
+        // 设置默认的商品积分抵扣信息
+        $this->setDefaultGoodsPoints();
+        // 积分设置
+        $setting = SettingModel::getItem('points');
+        // 条件:后台开启下单使用积分抵扣
+        if (!$setting['is_shopping_discount'] || !$this->checkoutRule['isUsePoints']) {
+            return;
+        }
+        // 条件:订单金额满足[?]元
+        if (helper::bccomp($setting['discount']['full_order_price'], $this->orderData['orderTotalPrice']) === 1) {
+            return;
+        }
+        // 计算订单商品最多可抵扣的积分数量
+        $this->setOrderGoodsMaxPointsNum();
+        // 订单最多可抵扣的积分总数量
+        $maxPointsNumCount = (int)helper::getArrayColumnSum($this->goodsList, 'max_points_num');
+        // 实际可抵扣的积分数量
+        $actualPointsNum = min($maxPointsNumCount, $this->user['points']);
+        if ($actualPointsNum < 1) {
+            return;
+        }
+        // 计算订单商品实际抵扣的积分数量和金额
+        $GoodsDeduct = new PointsDeductService($this->goodsList);
+        $GoodsDeduct->setGoodsPoints($maxPointsNumCount, $actualPointsNum);
+        // 积分抵扣总金额
+        $orderPointsMoney = helper::getArrayColumnSum($this->goodsList, 'points_money');
+        $this->orderData['pointsMoney'] = helper::number2($orderPointsMoney);
+        // 积分抵扣总数量
+        $this->orderData['pointsNum'] = $actualPointsNum;
+        // 允许积分抵扣
+        $this->orderData['isAllowPoints'] = true;
+    }
+
+    /**
+     * 计算订单商品最多可抵扣的积分数量
+     * @return void
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function setOrderGoodsMaxPointsNum(): void
+    {
+        // 积分设置
+        $setting = SettingModel::getItem('points');
+        foreach ($this->goodsList as &$goods) {
+            // 商品不允许积分抵扣
+            if (!$goods['is_points_discount']) continue;
+            // 积分抵扣比例
+            $deductionRatio = helper::bcdiv($setting['discount']['max_money_ratio'], 100);
+            // 最多可抵扣的金额
+            $totalPayPrice = helper::bcsub($goods['total_price'], $goods['coupon_money']);
+            $maxPointsMoney = helper::bcmul($totalPayPrice, $deductionRatio);
+            // 最多可抵扣的积分数量
+            $goods['max_points_num'] = helper::bcdiv($maxPointsMoney, $setting['discount']['discount_ratio'], 0);
+        }
+    }
+
+    /**
+     * 设置默认的商品积分抵扣信息
+     * @return void
+     */
+    private function setDefaultGoodsPoints(): void
+    {
+        foreach ($this->goodsList as &$goods) {
+            // 最多可抵扣的积分数量
+            $goods['max_points_num'] = 0;
+            // 实际抵扣的积分数量
+            $goods['pointsNum'] = 0;
+            // 实际抵扣的金额
+            $goods['points_money'] = 0.00;
+        }
+    }
+
+    /**
+     * 整理订单数据(结算台初始化)
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getOrderData(): array
+    {
+        // 系统支持的配送方式 (后台设置)
+        $deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
+        return [
+            // 当前配送类型
+            'delivery' => $this->param['delivery'] > 0 ? $this->param['delivery'] : $deliveryType[0],
+            // 默认地址
+            'address' => $this->user['address_default'],
+            // 是否存在收货地址
+            'existAddress' => $this->user['address_id'] > 0,
+            // 配送费用
+            'expressPrice' => 0.00,
+            // 当前用户收货城市是否存在配送规则中
+            'isIntraRegion' => true,
+            // 是否允许使用积分抵扣
+            'isAllowPoints' => false,
+            // 是否使用积分抵扣
+            'isUsePoints' => $this->param['isUsePoints'],
+            // 积分抵扣金额
+            'pointsMoney' => 0.00,
+            // 赠送的积分数量
+            'pointsBonus' => 0,
+            // 支付方式
+            'payType' => $this->param['payType'],
+            // 系统设置 TODO: 废弃
+            'setting' => $this->getSetting(),
+        ];
+    }
+
+    /**
+     * 获取订单页面中使用到的系统设置
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getSetting(): array
+    {
+        // 系统支持的配送方式 (后台设置)
+        $deliveryType = SettingModel::getItem(SettingEnum::DELIVERY)['delivery_type'];
+        // 积分设置
+        $pointsSetting = SettingModel::getItem(SettingEnum::POINTS);
+        return [
+            'deliveryType' => $deliveryType,                     // 支持的配送方式
+            'points_name' => $pointsSetting['points_name'],      // 积分名称
+            'points_describe' => $pointsSetting['describe'],     // 积分说明
+        ];
+    }
+
+    // 获取订单结算时的个人信息
+    public function getPersonal(): array
+    {
+        return [
+            'user_id' => $this->user['user_id'],
+            'balance' => $this->user['balance'],
+            'points' => $this->user['points'],
+            'address_id' => $this->user['address_id'],
+        ];
+    }
+
+    /**
+     * 当前用户可用的优惠券列表
+     * @param float $orderTotalPrice 总金额
+     * @return array|mixed
+     * @throws \think\db\exception\DbException
+     */
+    private function getUserCouponList(float $orderTotalPrice)
+    {
+        // 是否开启优惠券折扣
+        if (!$this->checkoutRule['isCoupon']) {
+            return [];
+        }
+        // 整理当前订单所有商品ID集
+        $orderGoodsIds = helper::getArrayColumn($this->goodsList, 'goods_id');
+        // 当前用户可用的优惠券列表
+        $couponList = UserCouponModel::getUserCouponList($this->user['user_id'], $orderTotalPrice);
+        // 判断当前优惠券是否满足订单使用条件 (优惠券适用范围)
+        return UserCouponModel::couponListApplyRange($couponList, $orderGoodsIds);
+
+    }
+
+    /**
+     * 验证订单商品的状态
+     * @return void
+     */
+    private function validateGoodsList(): void
+    {
+        $Checkout = CheckoutFactory::getFactory(
+            $this->user,
+            $this->goodsList,
+            $this->orderSource['source']
+        );
+        $status = $Checkout->validateGoodsList();
+        $status == false && $this->setError($Checkout->getError());
+    }
+
+    /**
+     * 设置订单的商品总金额(不含优惠折扣)
+     */
+    private function setOrderTotalPrice()
+    {
+        // 订单商品的总金额(不含优惠券折扣)
+        $this->orderData['orderTotalPrice'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_price'));
+    }
+
+    /**
+     * 设置订单的实际支付金额(含配送费)
+     */
+    private function setOrderPayPrice()
+    {
+        // 订单金额(含优惠折扣)
+        $this->orderData['orderPrice'] = helper::number2(helper::getArrayColumnSum($this->goodsList, 'total_pay_price'));
+        // 订单实付款金额(订单金额 + 运费)
+        $this->orderData['orderPayPrice'] = helper::number2(helper::bcadd($this->orderData['orderPrice'], $this->orderData['expressPrice']));
+    }
+
+    /**
+     * 计算订单积分赠送数量
+     * @return void
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function setOrderPointsBonus(): void
+    {
+        // 初始化商品积分赠送数量
+        foreach ($this->goodsList as &$goods) {
+            $goods['points_bonus'] = 0;
+        }
+        // 积分设置
+        $setting = SettingModel::getItem('points');
+        // 条件:后台开启开启购物送积分
+        if (!$setting['is_shopping_gift']) {
+            return;
+        }
+        // 设置商品积分赠送数量
+        foreach ($this->goodsList as &$goods) {
+            // 积分赠送比例
+            $ratio = helper::bcdiv($setting['gift_ratio'], 100);
+            // 计算抵扣积分数量
+            $goods['points_bonus'] = !$goods['is_points_gift'] ? 0 : helper::bcmul($goods['total_pay_price'], $ratio, 0);
+        }
+        //  订单积分赠送数量
+        $this->orderData['pointsBonus'] = (int)helper::getArrayColumnSum($this->goodsList, 'points_bonus');
+    }
+
+    /**
+     * 计算订单商品的实际付款金额
+     * @return void
+     */
+    private function setOrderGoodsPayPrice(): void
+    {
+        // 商品总价 - 优惠抵扣
+        foreach ($this->goodsList as &$goods) {
+            // 减去优惠券抵扣金额
+            $value = helper::bcsub($goods['total_price'], $goods['coupon_money']);
+            // 减去积分抵扣金额
+            if ($this->orderData['isAllowPoints'] && $this->orderData['isUsePoints']) {
+                $value = helper::bcsub($value, $goods['points_money']);
+            }
+            $goods['total_pay_price'] = helper::number2($value);
+        }
+    }
+
+    /**
+     * 设置订单商品会员折扣价
+     * @return void
+     * @throws BaseException
+     */
+    private function setOrderGoodsGradeMoney(): void
+    {
+        // 设置默认数据
+        helper::setDataAttribute($this->goodsList, [
+            // 标记参与会员折扣
+            'is_user_grade' => false,
+            // 会员等级抵扣的金额
+            'grade_ratio' => 0,
+            // 会员折扣的商品单价
+            'grade_goods_price' => 0.00,
+            // 会员折扣的总额差
+            'grade_total_money' => 0.00,
+        ], true);
+
+        // 是否开启会员等级折扣
+        if (!$this->checkoutRule['isUserGrade']) {
+            return;
+        }
+        // 获取当前登录用户的会员等级信息
+        $gradeInfo = UserGradeService::getCurrentGradeInfo();
+        // 判断商品是否参与会员折扣
+        if (empty($gradeInfo)) {
+            return;
+        }
+        // 计算抵扣金额
+        foreach ($this->goodsList as &$goods) {
+            // 判断商品是否参与会员折扣
+            if (!$goods['is_enable_grade']) {
+                continue;
+            }
+            // 折扣比例
+            $discountRatio = $gradeInfo['equity']['discount'];
+            // 商品单独设置了会员折扣
+            if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$this->user['grade_id']])) {
+                $discountRatio = $goods['alone_grade_equity'][$gradeInfo['grade_id']];
+            }
+            if (empty($discountRatio)) {
+                continue;
+            }
+            // 会员折扣后的商品总金额
+            $gradeTotalPrice = UserGradeService::getDiscountPrice($goods['total_price'], $discountRatio);
+            helper::setDataAttribute($goods, [
+                'is_user_grade' => true,
+                'grade_ratio' => $discountRatio,
+                'grade_goods_price' => UserGradeService::getDiscountPrice($goods['goods_price'], $discountRatio),
+                'grade_total_money' => helper::bcsub($goods['total_price'], $gradeTotalPrice),
+                'total_price' => $gradeTotalPrice,
+            ], false);
+        }
+    }
+
+    /**
+     * 设置订单优惠券抵扣信息
+     * @param array $couponList 当前用户可用的优惠券列表
+     * @param int $userCouponId 当前选择的优惠券ID
+     * @return void
+     * @throws BaseException
+     */
+    private function setOrderCouponMoney(array $couponList, int $userCouponId): void
+    {
+        // 设置默认数据:订单信息
+        helper::setDataAttribute($this->orderData, [
+            'couponId' => 0,       // 用户的优惠券ID
+            'couponMoney' => 0,    // 优惠券抵扣金额
+        ], false);
+        // 设置默认数据:订单商品列表
+        helper::setDataAttribute($this->goodsList, [
+            'coupon_money' => 0,    // 优惠券抵扣金额
+        ], true);
+        // 验证选择的优惠券ID是否合法
+        if (!$this->verifyOrderCouponId($userCouponId, $couponList)) {
+            return;
+        }
+        // 获取优惠券信息
+        $couponInfo = $this->getCouponInfo($userCouponId, $couponList);
+        // 计算订单商品优惠券抵扣金额
+        $goodsListTemp = helper::getArrayColumns($this->goodsList, ['goods_id', 'goods_sku_id', 'total_price']);
+        $CouponMoney = new GoodsDeductService;
+        $rangeGoodsList = $CouponMoney->setGoodsList($goodsListTemp)
+            ->setCouponInfo($couponInfo)
+            ->setGoodsCouponMoney()
+            ->getRangeGoodsList();
+        // 分配订单商品优惠券抵扣金额
+        foreach ($this->goodsList as &$goods) {
+            $goodsKey = "{$goods['goods_id']}-{$goods['goods_sku_id']}";
+            if (isset($rangeGoodsList[$goodsKey])) {
+                $goods['coupon_money'] = helper::bcdiv($rangeGoodsList[$goodsKey]['coupon_money'], 100);
+            }
+        }
+
+        // 记录订单优惠券信息
+        $this->orderData['couponId'] = $userCouponId;
+        $this->orderData['couponMoney'] = helper::number2(helper::bcdiv($CouponMoney->getActualReducedMoney(), 100));
+    }
+
+    /**
+     * 验证用户选择的优惠券ID是否合法
+     * @param int $userCouponId
+     * @param $couponList
+     * @return bool
+     * @throws BaseException
+     */
+    private function verifyOrderCouponId(int $userCouponId, $couponList): bool
+    {
+        // 是否开启优惠券折扣
+        if (!$this->checkoutRule['isCoupon']) {
+            return false;
+        }
+        // 如果没有可用的优惠券,直接返回
+        if ($userCouponId <= 0 || empty($couponList)) {
+            return false;
+        }
+        // 判断优惠券是否存在
+        $couponInfo = $this->getCouponInfo($userCouponId, $couponList);
+        if (!$couponInfo) {
+            throwError('未找到优惠券信息');
+        }
+        // 判断优惠券适用范围是否合法
+        if (!$couponInfo['is_apply']) {
+            throwError($couponInfo['not_apply_info']);
+        }
+        return true;
+    }
+
+    /**
+     * 查找指定的优惠券信息
+     * @param int $userCouponId 优惠券ID
+     * @param array $couponList 优惠券列表
+     * @return false|mixed
+     */
+    private function getCouponInfo(int $userCouponId, array $couponList)
+    {
+        return helper::getArrayItemByColumn($couponList, 'user_coupon_id', $userCouponId);
+    }
+
+    /**
+     * 订单配送-快递配送
+     * @return void
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function setOrderExpress(): void
+    {
+        // 设置默认数据:配送费用
+        helper::setDataAttribute($this->goodsList, [
+            'expressPrice' => 0,
+        ], true);
+        // 当前用户收货城市id
+        $cityId = $this->user['address_default'] ? (int)$this->user['address_default']['city_id'] : 0;
+        // 初始化配送服务类
+        $ExpressService = new ExpressService($cityId, $this->goodsList);
+        // 验证商品是否在配送范围
+        $isIntraRegion = $ExpressService->isIntraRegion();
+        if ($cityId > 0 && $isIntraRegion == false) {
+            $notInRuleGoodsName = $ExpressService->getNotInRuleGoodsName();
+            $this->setError("很抱歉,您的收货地址不在商品 [{$notInRuleGoodsName}] 的配送范围内");
+        }
+        // 订单总运费金额
+        $this->orderData['isIntraRegion'] = $isIntraRegion;
+        $this->orderData['expressPrice'] = $ExpressService->getDeliveryFee();
+    }
+
+    /**
+     * 创建新订单
+     * @param array $order 订单信息
+     * @return bool
+     */
+    public function createOrder(array $order): bool
+    {
+        // 表单验证
+        if (!$this->validateOrderForm($order)) {
+            return false;
+        }
+        // 创建新的订单
+        $status = $this->model->transaction(function () use ($order) {
+            // 创建订单事件
+            return $this->createOrderEvent($order);
+        });
+        // 余额支付标记订单已支付
+        if ($status && $order['payType'] == OrderPayTypeEnum::BALANCE) {
+            return $this->model->onPaymentByBalance($this->model['order_no']);
+        }
+        return $status;
+    }
+
+    /**
+     * 创建订单事件
+     * @param $order
+     * @return bool
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function createOrderEvent($order): bool
+    {
+        // 新增订单记录
+        $status = $this->add($order, $this->param['remark']);
+        if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
+            // 记录收货地址
+            $this->saveOrderAddress($order['address']);
+        }
+        // 保存订单商品信息
+        $this->saveOrderGoods($order);
+        // 更新商品库存 (针对下单减库存的商品)
+        $this->updateGoodsStockNum($order);
+        // 设置优惠券使用状态
+        $order['couponId'] > 0 && UserCouponModel::setIsUse((int)$order['couponId']);
+        // 积分抵扣情况下扣除用户积分
+        if ($order['isAllowPoints'] && $order['isUsePoints'] && $order['pointsNum'] > 0) {
+            $describe = "用户消费:{$this->model['order_no']}";
+            UserModel::setIncPoints($this->user['user_id'], -$order['pointsNum'], $describe);
+        }
+        // 获取订单详情
+        $detail = OrderModel::getUserOrderDetail((int)$this->model['order_id']);
+        return $status;
+    }
+
+    /**
+     * 构建支付请求的参数
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function onOrderPayment(): array
+    {
+        return PaymentService::orderPayment($this->model, $this->param['payType']);
+    }
+
+    /**
+     * 表单验证 (订单提交)
+     * @param array $order 订单信息
+     * @return bool
+     */
+    private function validateOrderForm(array $order): bool
+    {
+        if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
+            if (empty($order['address'])) {
+                $this->error = '您还没有选择配送地址';
+                return false;
+            }
+        }
+        // 余额支付时判断用户余额是否足够
+        if ($order['payType'] == OrderPayTypeEnum::BALANCE) {
+            if ($this->user['balance'] < $order['orderPayPrice']) {
+                $this->error = '您的余额不足,无法使用余额支付';
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 当前订单是否存在和使用积分抵扣
+     * @param $order
+     * @return bool
+     */
+    private function isExistPointsDeduction($order): bool
+    {
+        return $order['isAllowPoints'] && $order['isUsePoints'];
+    }
+
+    /**
+     * 新增订单记录
+     * @param $order
+     * @param string $remark
+     * @return bool|false
+     */
+    private function add($order, string $remark = ''): bool
+    {
+        // 当前订单是否存在和使用积分抵扣
+        $isExistPointsDeduction = $this->isExistPointsDeduction($order);
+        // 订单数据
+        $data = [
+            'user_id' => $this->user['user_id'],
+            'order_no' => $this->model->orderNo(),
+            'total_price' => $order['orderTotalPrice'],
+            'order_price' => $order['orderPrice'],
+            'coupon_id' => $order['couponId'],
+            'coupon_money' => $order['couponMoney'],
+            'points_money' => $isExistPointsDeduction ? $order['pointsMoney'] : 0.00,
+            'points_num' => $isExistPointsDeduction ? $order['pointsNum'] : 0,
+            'pay_price' => $order['orderPayPrice'],
+            'delivery_type' => $order['delivery'],
+            'pay_type' => $order['payType'],
+            'pay_status' => PayStatusEnum::PENDING,
+            'buyer_remark' => trim($remark),
+            'order_source' => $this->orderSource['source'],
+            'order_source_id' => $this->orderSource['source_id'],
+            'points_bonus' => $order['pointsBonus'],
+            'order_status' => OrderStatusEnum::NORMAL,
+            'platform' => getPlatform(),
+            'store_id' => $this->storeId,
+        ];
+        if ($order['delivery'] == DeliveryTypeEnum::EXPRESS) {
+            $data['express_price'] = $order['expressPrice'];
+        }
+        // 保存订单记录
+        return $this->model->save($data);
+    }
+
+    /**
+     * 保存订单商品信息
+     * @param $order
+     * @return void
+     */
+    private function saveOrderGoods($order): void
+    {
+        // 当前订单是否存在和使用积分抵扣
+        $isExistPointsDeduction = $this->isExistPointsDeduction($order);
+        // 订单商品列表
+        $goodsList = [];
+        foreach ($order['goodsList'] as $goods) {
+            /* @var GoodsModel $goods */
+            $item = [
+                'user_id' => $this->user['user_id'],
+                'store_id' => $this->storeId,
+                'goods_id' => $goods['goods_id'],
+                'goods_name' => $goods['goods_name'],
+                'goods_no' => $goods['goods_no'] ?: '',
+                'image_id' => (int)current($goods['goods_images'])['file_id'],
+                'deduct_stock_type' => $goods['deduct_stock_type'],
+                'spec_type' => $goods['spec_type'],
+                'goods_sku_id' => $goods['skuInfo']['goods_sku_id'],
+                'goods_props' => $goods['skuInfo']['goods_props'] ?: '',
+                'content' => $goods['content'] ?? '',
+                'goods_sku_no' => $goods['skuInfo']['goods_sku_no'] ?: '',
+                'goods_price' => $goods['skuInfo']['goods_price'],
+                'line_price' => $goods['skuInfo']['line_price'],
+                'goods_weight' => $goods['skuInfo']['goods_weight'],
+                'is_user_grade' => (int)$goods['is_user_grade'],
+                'grade_ratio' => $goods['grade_ratio'],
+                'grade_goods_price' => $goods['grade_goods_price'],
+                'grade_total_money' => $goods['grade_total_money'],
+                'coupon_money' => $goods['coupon_money'],
+                'points_money' => $isExistPointsDeduction ? $goods['points_money'] : 0.00,
+                'points_num' => $isExistPointsDeduction ? $goods['points_num'] : 0,
+                'points_bonus' => $goods['points_bonus'],
+                'total_num' => $goods['total_num'],
+                'total_price' => $goods['total_price'],
+                'total_pay_price' => $goods['total_pay_price']
+            ];
+            // 记录订单商品来源id
+            $item['goods_source_id'] = isset($goods['goods_source_id']) ? $goods['goods_source_id'] : 0;
+            $goodsList[] = $item;
+        }
+        $this->model->goods()->saveAll($goodsList) !== false;
+    }
+
+    /**
+     * 更新商品库存 (针对下单减库存的商品)
+     * @param $order
+     * @return void
+     */
+    private function updateGoodsStockNum($order): void
+    {
+        StockFactory::getFactory($this->model['order_source'])->updateGoodsStock($order['goodsList']);
+    }
+
+    /**
+     * 记录收货地址
+     * @param $address
+     * @return void
+     */
+    private function saveOrderAddress($address): void
+    {
+        $this->model->address()->save([
+            'user_id' => $this->user['user_id'],
+            'store_id' => $this->storeId,
+            'name' => $address['name'],
+            'phone' => $address['phone'],
+            'province_id' => $address['province_id'],
+            'city_id' => $address['city_id'],
+            'region_id' => $address['region_id'],
+            'detail' => $address['detail'],
+        ]);
+    }
+}

+ 156 - 0
app/index/service/order/PaySuccess.php

@@ -0,0 +1,156 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\index\service\order;
+
+use think\facade\Event;
+use app\common\service\BaseService;
+use app\api\model\User as UserModel;
+use app\api\model\Order as OrderModel;
+use app\api\model\user\BalanceLog as BalanceLogModel;
+use app\common\service\goods\source\Factory as StockFactory;
+use app\common\enum\OrderType as OrderTypeEnum;
+use app\common\enum\order\PayStatus as PayStatusEnum;
+use app\common\enum\order\PayType as OrderPayTypeEnum;
+use app\common\enum\user\balanceLog\Scene as SceneEnum;
+
+/**
+ * 订单支付成功服务类
+ * Class PaySuccess
+ * @package app\api\service\order
+ */
+class PaySuccess extends BaseService
+{
+    // 订单模型
+    public $model;
+
+    // 当前用户信息
+    private $user;
+
+    /**
+     * 构造函数
+     * PaySuccess constructor.
+     * @param $orderNo
+     */
+    public function __construct($orderNo)
+    {
+        parent::__construct();
+        // 实例化订单模型
+        $this->model = OrderModel::getPayDetail($orderNo);
+        // 获取用户信息
+        $this->user = UserModel::detail($this->model['user_id']);
+    }
+
+    /**
+     * 获取订单详情
+     * @return OrderModel|null
+     */
+    public function getOrderInfo()
+    {
+        return $this->model;
+    }
+
+    /**
+     * 订单支付成功业务处理
+     * @param $payType
+     * @param array $payData
+     * @return bool
+     */
+    public function onPaySuccess($payType, $payData = [])
+    {
+        if (empty($this->model)) {
+            $this->error = '未找到该订单信息';
+            return false;
+        }
+        // 更新付款状态
+        $status = $this->updatePayStatus($payType, $payData);
+        // 订单支付成功事件
+        if ($status == true) {
+            Event::trigger('OrderPaySuccess', ['order' => $this->model, 'orderType' => OrderTypeEnum::ORDER]);
+        }
+        return $status;
+    }
+
+    /**
+     * 更新付款状态
+     * @param $payType
+     * @param array $payData
+     * @return bool
+     */
+    private function updatePayStatus($payType, $payData = [])
+    {
+        // 验证余额支付时用户余额是否满足
+        if ($payType == OrderPayTypeEnum::BALANCE) {
+            if ($this->user['balance'] < $this->model['pay_price']) {
+                $this->error = '用户余额不足,无法使用余额支付';
+                return false;
+            }
+        }
+        // 事务处理
+        $this->model->transaction(function () use ($payType, $payData) {
+            // 更新订单状态
+            $this->updateOrderInfo($payType, $payData);
+            // 累积用户总消费金额
+            UserModel::setIncPayMoney($this->user['user_id'], (float)$this->model['pay_price']);
+            // 记录订单支付信息
+            $this->updatePayInfo($payType);
+        });
+        return true;
+    }
+
+    /**
+     * 更新订单记录
+     * @param int $payType
+     * @param array $payData
+     * @return false|int
+     * @throws \Exception
+     */
+    private function updateOrderInfo(int $payType, array $payData)
+    {
+        // 更新商品库存、销量
+        StockFactory::getFactory($this->model['order_source'])->updateStockSales($this->model['goods']);
+        // 整理订单信息
+        $order = [
+            'pay_type' => $payType,
+            'pay_status' => PayStatusEnum::SUCCESS,
+            'pay_time' => time()
+        ];
+        if ($payType == OrderPayTypeEnum::WECHAT) {
+            $order['transaction_id'] = $payData['transaction_id'];
+        }
+        // 更新订单状态
+        return $this->model->save($order);
+    }
+
+    /**
+     * 记录订单支付信息
+     * @param int $payType
+     */
+    private function updatePayInfo(int $payType)
+    {
+        // 余额支付
+        if ($payType == OrderPayTypeEnum::BALANCE) {
+            // 更新用户余额
+            UserModel::setDecBalance((int)$this->user['user_id'], (float)$this->model['pay_price']);
+            // 新增余额变动记录
+            BalanceLogModel::add(SceneEnum::CONSUME, [
+                'user_id' => (int)$this->user['user_id'],
+                'money' => -$this->model['pay_price'],
+            ], ['order_no' => $this->model['order_no']]);
+        }
+        // 微信支付
+        if ($payType == OrderPayTypeEnum::WECHAT) {
+
+        }
+    }
+
+}

+ 65 - 0
app/index/validate/order/Checkout.php

@@ -0,0 +1,65 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\index\validate\order;
+
+use think\Validate;
+
+/**
+ * 验证类:订单提交
+ * Class Checkout
+ * @package app\api\validate\order
+ */
+class Checkout extends Validate
+{
+    /**
+     * 验证规则
+     * @var array
+     */
+    protected $rule = [
+
+        // 商品id
+        'goodsId' => [
+            'require',
+            'number',
+            'gt' => 0
+        ],
+
+        // 购买数量
+        'goodsNum' => [
+            'require',
+            'number',
+            'gt' => 0
+        ],
+
+        // 商品sku_id
+        'goodsSkuId' => [
+            'require',
+        ],
+
+//        // 购物车id集
+//        'cartIds' => [
+//            'require',
+//        ],
+
+    ];
+
+    /**
+     * 验证场景
+     * @var array
+     */
+    protected $scene = [
+        'buyNow' => ['goodsId', 'goodsNum', 'goodsSkuId'],
+//        'cart' => ['cartIds'],
+    ];
+
+}

+ 46 - 0
app/index/validate/passport/EmailCaptcha.php

@@ -0,0 +1,46 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2024 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\index\validate\passport;
+
+use think\Validate;
+
+/**
+ * 验证类:发送邮件验证码
+ * Class SmsCaptcha
+ * @package app\api\validate\passport
+ */
+class EmailCaptcha extends Validate
+{
+    /**
+     * 验证规则
+     * @var array
+     */
+    protected $rule = [
+        // 图形验证码 (用户输入)
+        //'captchaCode' => ['require'],
+        // 图形验证码 (key)
+        //'captchaKey' => ['require'],
+        // 用户手机号
+        'email' => ['require'],
+    ];
+
+    /**
+     * 验证提示
+     * @var string[]
+     */
+    protected $message  =   [
+        'captchaCode.require' => '图形验证码code不能为空',
+        'captchaKey.require' => '图形验证码key不能为空',
+        'email.require' => '邮箱不能为空',
+    ];
+}

+ 43 - 0
app/index/validate/passport/Login.php

@@ -0,0 +1,43 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\index\validate\passport;
+
+use think\Validate;
+
+/**
+ * 验证类:发送短信验证码
+ * Class Login
+ * @package app\api\validate\passport
+ */
+class Login extends Validate
+{
+    /**
+     * 验证规则
+     * @var array
+     */
+    protected $rule = [
+        // 短信验证码 (用户输入)
+        'smsCode' => ['require'],
+        // 用户手机号
+        'mobile' => ['require'],
+    ];
+
+    /**
+     * 验证提示
+     * @var string[]
+     */
+    protected $message  =   [
+        'smsCode.require' => '短信验证码不能为空',
+        'mobile.require' => '手机号不能为空',
+    ];
+}

+ 46 - 0
app/index/validate/passport/SmsCaptcha.php

@@ -0,0 +1,46 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\index\validate\passport;
+
+use think\Validate;
+
+/**
+ * 验证类:发送短信验证码
+ * Class SmsCaptcha
+ * @package app\api\validate\passport
+ */
+class SmsCaptcha extends Validate
+{
+    /**
+     * 验证规则
+     * @var array
+     */
+    protected $rule = [
+        // 图形验证码 (用户输入)
+        'captchaCode' => ['require'],
+        // 图形验证码 (key)
+        'captchaKey' => ['require'],
+        // 用户手机号
+        'mobile' => ['require'],
+    ];
+
+    /**
+     * 验证提示
+     * @var string[]
+     */
+    protected $message  =   [
+        'captchaCode.require' => '图形验证码code不能为空',
+        'captchaKey.require' => '图形验证码key不能为空',
+        'mobile.require' => '手机号不能为空',
+    ];
+}

+ 12 - 9
app/index/view/cart/shoppingCart.html

@@ -119,7 +119,8 @@
                 <div class="productList">
                     {foreach $list as $cart}
                     <div class="productItem" data-id="{$cart['id']}" goods-id="{$cart['goods']['goods_id']}"
-                         goods-sku-id="{$cart['goods']['skuInfo']['goods_sku_id']}" data-goods-num="{$cart['goods_num']}">
+                         goods-sku-id="{$cart['goods']['skuInfo']['goods_sku_id']}"
+                         data-goods-num="{$cart['goods_num']}">
                         <div class="productImgBox">
                             <img class="lazy" src="/assets/index/asstes/icon/transparent.png"
                                  data-original="{$cart['goods']['goods_image']}" alt="提示信息seo"
@@ -281,6 +282,7 @@
             toastElement.text(message);
             toastElement.stop(true, true).fadeIn(200).delay(2000).fadeOut(200);
         }
+
         // const VAPES_TOKEN = "vapes_token"
         //
         // //未登录处理
@@ -338,10 +340,10 @@
             let goodsNum = parseInt(_this.closest(".productItem").attr("data-goods-num"));
             console.log(productId, goodsId, goodsSkuId)
             //商品增加
-            if (type === "add" || type=== "reduce") {
+            if (type === "add" || type === "reduce") {
                 if (type === "add") {
                     goodsNum += 1
-                }else {
+                } else {
                     goodsNum -= 1
                 }
                 console.log("商品增加")
@@ -352,6 +354,7 @@
                         "Content-Type": "application/json",
                         "storeId": "10001",
                         "platform": "H5",
+                        "Accept": "*/*"
                         // "Access-Token": vapesToken
                     },
                     dataType: "json",
@@ -359,9 +362,9 @@
                         if (obj.status === 200 || obj.status === '200') {
                             showToast("Successful")
                             window.location.href = '../cart/shoppingCart';
-                        }else if (obj.status === 401 || obj.status === '401'){
+                        } else if (obj.status === 401 || obj.status === '401') {
                             window.location.replace('../passport/logIn.html')
-                        }else {
+                        } else {
                             showToast(obj.message)
                         }
                         //window.location.replace( '/index/cart/shoppingCart.html')
@@ -396,9 +399,9 @@
                         if (obj.status === 200 || obj.status === '200') {
                             //showToast("Successful")
                             window.location.href = '../cart/shoppingCart';
-                        }else if (obj.status === 401 || obj.status === '401'){
+                        } else if (obj.status === 401 || obj.status === '401') {
                             window.location.replace('../passport/logIn.html')
-                        }else {
+                        } else {
                             showToast(obj.message)
                         }
                         return;
@@ -428,9 +431,9 @@
             const {status, message, data} = res || {}
             if (status === 200 || status === "200") {
                 window.open(data.payment.approval_link, "_blank");
-            }else if (status === 401 || status === "401"){
+            } else if (status === 401 || status === "401") {
                 window.location.replace('../passport/logIn.html')
-            }else {
+            } else {
                 showToast(message)
             }
         });