瀏覽代碼

index models

541469799@qq.com 1 年之前
父節點
當前提交
c8e23c5491
共有 35 個文件被更改,包括 1906 次插入20 次删除
  1. 1 0
      app/index/controller/Checkout.php
  2. 166 0
      app/index/model/Cart.php
  3. 87 0
      app/index/model/Coupon.php
  4. 33 0
      app/index/model/GoodsImage.php
  5. 42 0
      app/index/model/GoodsSku.php
  6. 24 0
      app/index/model/GoodsSpecRel.php
  7. 3 3
      app/index/model/Order.php
  8. 235 0
      app/index/model/OrderRefund.php
  9. 25 0
      app/index/model/OrderRefundImage.php
  10. 56 0
      app/index/model/UploadFile.php
  11. 2 2
      app/index/model/User.php
  12. 2 2
      app/index/model/UserAddress.php
  13. 2 2
      app/index/model/UserCoupon.php
  14. 44 0
      app/index/model/UserOauth.php
  15. 49 0
      app/index/model/user/BalanceLog.php
  16. 25 0
      app/index/model/user/Grade.php
  17. 25 0
      app/index/model/user/GradeLog.php
  18. 41 0
      app/index/model/user/PointsLog.php
  19. 25 0
      app/index/model/wxapp/Setting.php
  20. 2 2
      app/index/service/Cart.php
  21. 103 0
      app/index/service/Payment.php
  22. 1 1
      app/index/service/User.php
  23. 211 0
      app/index/service/coupon/GoodsDeduct.php
  24. 5 5
      app/index/service/order/Checkout.php
  25. 3 3
      app/index/service/order/PaySuccess.php
  26. 54 0
      app/index/service/order/source/Basics.php
  27. 44 0
      app/index/service/order/source/Factory.php
  28. 85 0
      app/index/service/order/source/Main.php
  29. 50 0
      app/index/service/order/source/checkout/Basics.php
  30. 42 0
      app/index/service/order/source/checkout/Factory.php
  31. 42 0
      app/index/service/order/source/checkout/Main.php
  32. 104 0
      app/index/service/points/GoodsDeduct.php
  33. 109 0
      app/index/service/user/Avatar.php
  34. 61 0
      app/index/service/user/Grade.php
  35. 103 0
      app/index/service/user/Oauth.php

+ 1 - 0
app/index/controller/Checkout.php

@@ -151,6 +151,7 @@ class Checkout extends Controller
         //$cartIds = $this->getCartIds();//不需要接口传过来
         $CartModel = new CartService;
         $cartIds = $CartModel->getCartIds();
+        \think\facade\Log::info('cartsId:'.json_encode($cartIds));
         // 商品结算信息
         // 购物车商品列表
         $goodsList = $CartModel->getOrderGoodsList($cartIds);

+ 166 - 0
app/index/model/Cart.php

@@ -0,0 +1,166 @@
+<?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\index\model\Goods as GoodsModel;
+use app\index\model\GoodsSku as GoodsSkuModel;
+use app\index\service\User as UserService;
+use app\common\model\Cart as CartModel;
+use app\common\enum\goods\Status as GoodsStatusEnum;
+use app\common\exception\BaseException;
+
+/**
+ * 购物车管理
+ * Class Cart
+ * @package app\api\model
+ */
+class Cart extends CartModel
+{
+    /**
+     * 加入购物车
+     * @param int $goodsId 商品ID
+     * @param string $goodsSkuId 商品sku唯一标识
+     * @param int $goodsNum 商品数量
+     * @return bool
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function add(int $goodsId, string $goodsSkuId, int $goodsNum)
+    {
+        // 判断是否已存在购物车记录
+        $detail = $this->getInfo($goodsId, $goodsSkuId, false);
+        // 如果已存在购物车记录, 则累计商品数量
+        !empty($detail) && $goodsNum += $detail['goods_num'];
+        // 验证商品的状态
+        $this->checkGoodsStatus($goodsId, $goodsSkuId, $goodsNum);
+        // 获取当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 实例化模型
+        $model = $detail ?: (new static);
+        return $model->save([
+            'goods_id' => $goodsId,
+            'goods_sku_id' => $goodsSkuId,
+            'goods_num' => $goodsNum,
+            'user_id' => $userId,
+            'store_id' => self::$storeId,
+        ]);
+    }
+
+    /**
+     * 更新购物车记录
+     * @param int $goodsId 商品ID
+     * @param string $goodsSkuId 商品sku唯一标识
+     * @param int $goodsNum 商品数量
+     * @return bool
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function sUpdate(int $goodsId, string $goodsSkuId, int $goodsNum)
+    {
+        // 验证商品的状态
+        $this->checkGoodsStatus($goodsId, $goodsSkuId, $goodsNum);
+        // 获取购物车记录
+        $model = $this->getInfo($goodsId, $goodsSkuId, true);
+        // 更新记录
+        return $model->save(['goods_num' => $goodsNum]);
+    }
+
+    /**
+     * 验证商品的状态
+     * @param int $goodsId 商品ID
+     * @param string $goodsSkuId 商品sku唯一标识
+     * @param int $goodsNum 商品数量
+     * @return bool
+     * @throws BaseException
+     */
+    private function checkGoodsStatus(int $goodsId, string $goodsSkuId, int $goodsNum)
+    {
+        // 获取商品详情
+        $goods = GoodsModel::detail($goodsId);
+        // 商品不存在
+        if (empty($goods) || $goods['is_delete']) {
+            throwError('很抱歉, 商品信息不存在');
+        }
+        // 商品已下架
+        if ($goods['status'] == GoodsStatusEnum::OFF_SALE) {
+            throwError('很抱歉, 该商品已经下架');
+        }
+        // 获取SKU信息
+        $skuInfo = GoodsSkuModel::detail($goodsId, $goodsSkuId);
+        if ($skuInfo['stock_num'] < $goodsNum) {
+            throwError('很抱歉, 该商品库存数量不足');
+        }
+        return true;
+    }
+
+    /**
+     * 获取购物车记录
+     * @param int $goodsId 商品ID
+     * @param string $goodsSkuId 商品sku唯一标识
+     * @param bool $isForce
+     * @return static|bool
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getInfo(int $goodsId, string $goodsSkuId, bool $isForce = true)
+    {
+        // 获取当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 获取购物车记录
+        $model = static::detail($userId, $goodsId, $goodsSkuId);
+        if (empty($model)) {
+            $isForce && throwError('购物车中没有该记录');
+            return false;
+        }
+        return $model;
+    }
+
+    /**
+     * 删除购物车中指定记录
+     * @param array $cartIds 购物车ID集, 如果为空删除所有
+     * @return bool
+     * @throws BaseException
+     */
+    public function clear(array $cartIds = [])
+    {
+        // 获取当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 设置更新条件
+        $where = [['user_id', '=', $userId]];
+        // 购物车ID集
+        !empty($cartIds) && $where[] = ['id', 'in', $cartIds];
+        // 更新记录
+        return $this->updateBase(['is_delete' => 1], $where);
+    }
+
+    /**
+     * 获取当前用户购物车商品总数量
+     * @return float
+     * @throws BaseException
+     */
+    public function getCartTotal()
+    {
+        if (!UserService::isLogin()) return 0;
+        $userId = UserService::getCurrentLoginUserId();
+        return $this->where('user_id', '=', $userId)
+            ->where('is_delete', '=', 0)
+            ->sum('goods_num');
+    }
+
+}

+ 87 - 0
app/index/model/Coupon.php

@@ -0,0 +1,87 @@
+<?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\index\model\UserCoupon;
+use app\index\service\User as UserService;
+use app\common\model\Coupon as CouponModel;
+use cores\exception\BaseException;
+
+/**
+ * 优惠券模型
+ * Class Coupon
+ * @package app\api\model
+ */
+class Coupon extends CouponModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'receive_num',
+        'is_delete',
+        'create_time',
+        'update_time',
+    ];
+
+    /**
+     * 获取优惠券列表
+     * @param int|null $limit 获取的数量
+     * @param bool $onlyReceive 只显示可领取
+     * @return mixed
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getList(int $limit = null, bool $onlyReceive = false)
+    {
+        // 查询构造器
+        $query = $this->getNewQuery();
+        // 只显示可领取(未过期,未发完)的优惠券
+        if ($onlyReceive) {
+            $query->where('IF ( `total_num` > - 1, `receive_num` < `total_num`, 1 = 1 )')
+                ->where('IF ( `expire_type` = 20, (`end_time` + 86400) >= ' . time() . ', 1 = 1 )');
+        }
+        // 查询数量
+        $limit > 0 && $query->limit($limit);
+        // 优惠券列表
+        $couponList = $query->where('status', '=', 1)
+            ->where('is_delete', '=', 0)
+            ->order(['sort', 'create_time' => 'desc'])
+            ->select();
+        // 获取用户已领取的优惠券
+        return $this->setIsReceive($couponList);
+    }
+
+    /**
+     * 获取用户已领取的优惠券
+     * @param mixed $couponList
+     * @return mixed
+     * @throws BaseException
+     */
+    private function setIsReceive($couponList)
+    {
+        // 获取用户已领取的优惠券
+        $userInfo = UserService::getCurrentLoginUser();
+        if ($userInfo !== false) {
+            $UserCouponModel = new UserCoupon;
+            $userCouponIds = $UserCouponModel->getUserCouponIds($userInfo['user_id']);
+            foreach ($couponList as $key => $item) {
+                $couponList[$key]['is_receive'] = in_array($item['coupon_id'], $userCouponIds);
+            }
+        }
+        return $couponList;
+    }
+}

+ 33 - 0
app/index/model/GoodsImage.php

@@ -0,0 +1,33 @@
+<?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\model\GoodsImage as GoodsImageModel;
+
+/**
+ * 商品图片模型
+ * Class GoodsImage
+ * @package app\api\model
+ */
+class GoodsImage extends GoodsImageModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+    ];
+
+}

+ 42 - 0
app/index/model/GoodsSku.php

@@ -0,0 +1,42 @@
+<?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\model\GoodsSku as GoodsSkuModel;
+
+/**
+ * 商品规格模型
+ * Class GoodsSku
+ * @package app\api\model
+ */
+class GoodsSku extends GoodsSkuModel
+{
+    /**
+     * 关联模型:规格图片
+     * @return \think\model\relation\HasOne
+     */
+    public function image(): \think\model\relation\HasOne
+    {
+        return parent::image()->bind(['image_url' => 'preview_url']);
+    }
+
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+}

+ 24 - 0
app/index/model/GoodsSpecRel.php

@@ -0,0 +1,24 @@
+<?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\model\GoodsSpecRel as GoodsSpecRelModel;
+
+/**
+ * 商品规格关系模型
+ * Class GoodsSpecRel
+ * @package app\api\model
+ */
+class GoodsSpecRel extends GoodsSpecRelModel
+{
+}

+ 3 - 3
app/index/model/Order.php

@@ -13,9 +13,9 @@ 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\index\model\{Goods as GoodsModel, OrderRefund as OrderRefundModel, Setting as SettingModel};
+use app\index\service\{User as UserService, Payment as PaymentService};
+use app\index\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\{

+ 235 - 0
app/index/model/OrderRefund.php

@@ -0,0 +1,235 @@
+<?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\index\model\OrderRefundImage;
+use app\index\service\User as UserService;
+use app\api\model\OrderGoods as OrderGoodsModel;
+use app\common\model\OrderRefund as OrderRefundModel;
+use app\common\enum\order\refund\RefundType as RefundTypeEnum;
+use app\common\enum\order\refund\AuditStatus as AuditStatusEnum;
+use app\common\enum\order\refund\RefundStatus as RefundStatusEnum;
+use cores\exception\BaseException;
+
+/**
+ * 售后单模型
+ * Class OrderRefund
+ * @package app\api\model
+ */
+class OrderRefund extends OrderRefundModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'update_time'
+    ];
+
+    /**
+     * 追加字段
+     * @var array
+     */
+    protected $append = [
+        'state_text',   // 售后单状态文字描述
+    ];
+
+    /**
+     * 售后单状态文字描述
+     * @param $value
+     * @param $data
+     * @return string
+     */
+    public function getStateTextAttr($value, $data): string
+    {
+        // 已完成
+        if ($data['status'] == RefundStatusEnum::COMPLETED) {
+            $text = [RefundTypeEnum::RETURN => '已同意退货并已退款', RefundTypeEnum::EXCHANGE => '已同意换货'];
+            return $text[$data['type']];
+        }
+        // 已取消
+        if ($data['status'] == RefundStatusEnum::CANCELLED) {
+            return '已取消';
+        }
+        // 已拒绝
+        if ($data['status'] == RefundStatusEnum::REJECTED) {
+            // return '已拒绝';
+            return $data['type'] == RefundTypeEnum::RETURN ? '已拒绝退货退款' : '已拒绝换货';
+        }
+        // 进行中
+        if ($data['status'] == RefundStatusEnum::NORMAL) {
+            if ($data['audit_status'] == AuditStatusEnum::WAIT) {
+                return '等待审核中';
+            }
+            if ($data['type'] == RefundTypeEnum::RETURN) {
+                return $data['is_user_send'] ? '已发货,待平台确认' : '已同意退货,请及时发货';
+            }
+        }
+        return $value;
+    }
+
+    /**
+     * 获取用户售后单列表
+     * @param int $state 售后单状态 -1为全部
+     * @return \think\Paginator
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DbException
+     * @throws BaseException
+     */
+    public function getList(int $state = -1): \think\Paginator
+    {
+        // 检索查询条件
+        $filter = [];
+        // 售后单状态
+        $state > -1 && $filter[] = ['status', '=', $state];
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 查询列表记录
+        return $this->with(['orderGoods.image'])
+            ->where($filter)
+            ->where('user_id', '=', $userId)
+            ->order(['create_time' => 'desc'])
+            ->paginate(15);
+    }
+
+    /**
+     * 获取当前用户的售后单详情
+     * @param int $orderRefundId 售后单ID
+     * @param bool $isWith 是否关联
+     * @return static|null
+     * @throws BaseException
+     */
+    public static function getDetail(int $orderRefundId, bool $isWith = false): ?OrderRefund
+    {
+        // 关联查询
+        $with = $isWith ? ['orderGoods' => ['image'], 'images.file', 'address', 'express'] : [];
+        // 获取记录
+        $detail = static::detail([
+            'user_id' => UserService::getCurrentLoginUserId(),
+            'order_refund_id' => $orderRefundId
+        ], $with);
+        if (empty($detail)) throwError('未找到该售后单');
+        return $detail;
+    }
+
+    /**
+     * 获取当前用户的售后单数量(进行中的)
+     * @return int
+     * @throws BaseException
+     */
+    public static function getCountByUnderway(): int
+    {
+        $userId = UserService::getCurrentLoginUserId();
+        return (new static)->where('user_id', '=', $userId)
+            ->where('status', '=', 0)
+            ->count();
+    }
+
+    /**
+     * 订单商品详情
+     * @param int $orderGoodsId 订单商品ID
+     * @return \app\common\model\OrderGoods|null
+     * @throws BaseException
+     */
+    public function getRefundGoods(int $orderGoodsId)
+    {
+        $goods = OrderGoodsModel::detail($orderGoodsId);
+        if (isset($goods['refund']) && !empty($goods['refund'])) {
+            throwError('当前商品已申请售后');
+        }
+        return $goods;
+    }
+
+    /**
+     * 用户发货
+     * @param $data
+     * @return false|int
+     */
+    public function delivery(array $data)
+    {
+        if (
+            $this['type'] != RefundTypeEnum::RETURN
+            || $this['audit_status'] != AuditStatusEnum::REVIEWED
+            || $this['is_user_send'] != 0
+        ) {
+            $this->error = '当前售后单不合法,不允许该操作';
+            return false;
+        }
+        if ($data['expressId'] <= 0) {
+            $this->error = '请选择物流公司';
+            return false;
+        }
+        if (empty($data['expressNo'])) {
+            $this->error = '请填写物流单号';
+            return false;
+        }
+        return $this->save([
+            'is_user_send' => 1,
+            'send_time' => time(),
+            'express_id' => (int)$data['expressId'],
+            'express_no' => $data['expressNo'],
+        ]);
+    }
+
+    /**
+     * 新增售后单记录
+     * @param int $orderGoodsId 订单商品ID
+     * @param array $data 用户提交的表单数据
+     * @return mixed
+     * @throws BaseException
+     */
+    public function apply(int $orderGoodsId, array $data)
+    {
+        // 订单商品详情
+        $goods = $this->getRefundGoods($orderGoodsId);
+        return $this->transaction(function () use ($orderGoodsId, $data, $goods) {
+            // 新增售后单记录
+            $this->save([
+                'order_goods_id' => $orderGoodsId,
+                'order_id' => $goods['order_id'],
+                'user_id' => UserService::getCurrentLoginUserId(),
+                'type' => $data['type'],
+                'apply_desc' => $data['content'],
+                'audit_status' => AuditStatusEnum::WAIT,
+                'status' => 0,
+                'store_id' => self::$storeId
+            ]);
+            // 记录凭证图片关系
+            if (isset($data['images']) && !empty($data['images'])) {
+                $this->saveImages((int)$this['order_refund_id'], $data['images']);
+            }
+            return true;
+        });
+    }
+
+    /**
+     * 记录售后单图片
+     * @param int $orderRefundId 售后单ID
+     * @param array $images 图片列表
+     * @return bool
+     */
+    private function saveImages(int $orderRefundId, array $images)
+    {
+        // 生成评价图片数据
+        $data = [];
+        foreach ($images as $imageId) {
+            $data[] = [
+                'order_refund_id' => $orderRefundId,
+                'image_id' => $imageId,
+                'store_id' => self::$storeId
+            ];
+        }
+        return !empty($data) && (new OrderRefundImage)->addAll($data) !== false;
+    }
+}

+ 25 - 0
app/index/model/OrderRefundImage.php

@@ -0,0 +1,25 @@
+<?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\model\OrderRefundImage as OrderRefundImageModel;
+
+/**
+ * 售后单图片模型
+ * Class OrderRefundImage
+ * @package app\api\model
+ */
+class OrderRefundImage extends OrderRefundImageModel
+{
+
+}

+ 56 - 0
app/index/model/UploadFile.php

@@ -0,0 +1,56 @@
+<?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\model\UploadFile as UploadFileModel;
+
+/**
+ * 文件库模型
+ * Class UploadFile
+ * @package app\api\model
+ */
+class UploadFile extends UploadFileModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+    ];
+
+    /**
+     * 添加新记录
+     * @param array $data 文件信息
+     * @param int $fileType 文件类型
+     * @param int $userId 用户ID
+     * @return bool
+     */
+    public function add(array $data, int $fileType, int $userId)
+    {
+        return $this->save([
+            'channel' => 20,
+            'storage' => $data['storage'],
+            'domain' => $data['domain'],
+            'file_name' => $data['file_name'],
+            'file_path' => $data['file_path'],
+            'file_size' => $data['file_size'],
+            'file_ext' => $data['file_ext'],
+            'file_type' => $fileType,
+            'uploader_id' => $userId,
+            'store_id' => self::$storeId
+        ]);
+    }
+
+}

+ 2 - 2
app/index/model/User.php

@@ -13,8 +13,8 @@ declare (strict_types=1);
 namespace app\index\model;
 
 use think\facade\Cache;
-use app\api\service\User as UserService;
-use app\api\model\UserOauth as UserOauthModel;
+use app\index\service\User as UserService;
+use app\index\model\UserOauth as UserOauthModel;
 use app\common\model\User as UserModel;
 use cores\exception\BaseException;
 use yiovo\captcha\facade\CaptchaApi;

+ 2 - 2
app/index/model/UserAddress.php

@@ -12,8 +12,8 @@ declare (strict_types = 1);
 
 namespace app\index\model;
 
-use app\api\model\User as UserModel;
-use app\api\service\User as UserService;
+use app\index\model\User as UserModel;
+use app\index\service\User as UserService;
 use app\common\model\UserAddress as UserAddressModel;
 use app\common\exception\BaseException;
 

+ 2 - 2
app/index/model/UserCoupon.php

@@ -12,8 +12,8 @@ declare (strict_types=1);
 
 namespace app\index\model;
 
-use app\api\service\User as UserService;
-use app\api\model\Coupon as CouponModel;
+use app\index\service\User as UserService;
+use app\index\model\Coupon as CouponModel;
 use app\common\model\UserCoupon as UserCouponModel;
 use app\common\enum\coupon\CouponType as CouponTypeEnum;
 use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;

+ 44 - 0
app/index/model/UserOauth.php

@@ -0,0 +1,44 @@
+<?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\model\UserOauth as UserOauthModel;
+
+/**
+ * 模型类:第三方用户信息
+ * Class UserOauth
+ * @package app\api\model
+ */
+class UserOauth extends UserOauthModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'is_delete',
+        'create_time',
+        'update_time',
+    ];
+
+    /**
+     * 新增数据
+     * @param array $data
+     * @return bool
+     */
+    public function add(array $data)
+    {
+        return $this->save($data);
+    }
+}

+ 49 - 0
app/index/model/user/BalanceLog.php

@@ -0,0 +1,49 @@
+<?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\user;
+
+use app\index\service\User as UserService;
+use app\common\model\user\BalanceLog as BalanceLogModel;
+
+/**
+ * 用户余额变动明细模型
+ * Class BalanceLog
+ * @package app\api\model\user
+ */
+class BalanceLog extends BalanceLogModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+    ];
+
+    /**
+     * 获取账单明细列表
+     * @return \think\Paginator
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function getList()
+    {
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 获取列表数据
+        return $this->where('user_id', '=', $userId)
+            ->order(['create_time' => 'desc'])
+            ->paginate(15);
+    }
+
+}

+ 25 - 0
app/index/model/user/Grade.php

@@ -0,0 +1,25 @@
+<?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\user;
+
+use app\common\model\user\Grade as GradeModel;
+
+/**
+ * 用户会员等级模型
+ * Class Grade
+ * @package app\api\model\user
+ */
+class Grade extends GradeModel
+{
+
+}

+ 25 - 0
app/index/model/user/GradeLog.php

@@ -0,0 +1,25 @@
+<?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\user;
+
+use app\common\model\user\GradeLog as GradeLogModel;
+
+/**
+ * 用户会员等级变更记录模型
+ * Class GradeLog
+ * @package app\api\model\user
+ */
+class GradeLog extends GradeLogModel
+{
+
+}

+ 41 - 0
app/index/model/user/PointsLog.php

@@ -0,0 +1,41 @@
+<?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\user;
+
+use app\index\service\User as UserService;
+use app\common\model\user\PointsLog as PointsLogModel;
+
+/**
+ * 用户余额变动明细模型
+ * Class PointsLog
+ * @package app\api\model\user
+ */
+class PointsLog extends PointsLogModel
+{
+    /**
+     * 获取日志明细列表
+     * @return \think\Paginator
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function getList()
+    {
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 获取列表数据
+        return $this->where('user_id', '=', $userId)
+            ->order(['create_time' => 'desc'])
+            ->paginate(15);
+    }
+
+}

+ 25 - 0
app/index/model/wxapp/Setting.php

@@ -0,0 +1,25 @@
+<?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\wxapp;
+
+use app\common\model\wxapp\Setting as SettingModel;
+
+/**
+ * 微信小程序设置模型
+ * Class Setting
+ * @package app\api\model\wxapp
+ */
+class Setting extends SettingModel
+{
+
+}

+ 2 - 2
app/index/service/Cart.php

@@ -12,8 +12,8 @@ declare (strict_types=1);
 
 namespace app\index\service;
 
-use app\api\model\Cart as CartModel;
-use app\api\model\Goods as GoodsModel;
+use app\index\model\Cart as CartModel;
+use app\index\model\Goods as GoodsModel;
 use app\index\service\User as UserService;
 use cores\exception\BaseException;
 use app\common\library\helper;

+ 103 - 0
app/index/service/Payment.php

@@ -0,0 +1,103 @@
+<?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;
+
+use app\index\service\User as UserService;
+use app\index\model\wxapp\Setting as WxappSettingModel;
+use app\common\enum\OrderType as OrderTypeEnum;
+use app\common\enum\order\PayType as OrderPayTypeEnum;
+use app\common\library\paypal\PayPal;
+use app\common\service\BaseService;
+use app\common\library\wechat\WxPay;
+use app\common\exception\BaseException;
+
+/**
+ * 订单支付服务类
+ * Class Payment
+ * @package app\api\service
+ */
+class Payment extends BaseService
+{
+    /**
+     * 构建订单支付参数
+     * @param $order
+     * @param $payType
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function orderPayment($order, $payType): array
+    {
+        if ($payType == OrderPayTypeEnum::WECHAT) {
+            return self::wechat(
+                $order['order_id'],
+                $order['order_no'],
+                $order['pay_price'],
+                OrderTypeEnum::ORDER
+            );
+        }
+
+        if ($payType == OrderPayTypeEnum::PAYPAL) {
+            $conf = config('paypal');
+            $pp = new PayPal($conf);
+            return $pp->unify($order['order_no'], $order['pay_price']);
+        }
+        //todo stripe
+        return [];
+    }
+
+    /**
+     * 构建微信支付
+     * @param $orderId
+     * @param $orderNo
+     * @param $payPrice
+     * @param int $orderType
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function wechat(
+        $orderId,
+        $orderNo,
+        $payPrice,
+        int $orderType = OrderTypeEnum::ORDER
+    ): array
+    {
+        // 获取当前用户信息
+        $userInfo = UserService::getCurrentLoginUser(true);
+        // 获取第三方用户信息(微信)
+        $oauth = UserService::getOauth($userInfo['user_id'], 'MP-WEIXIN');
+        empty($oauth) && throwError('没有找到第三方用户信息oauth');
+        // 统一下单API
+        $WxPay = new WxPay(static::getWxConfig());
+        return $WxPay->unifiedorder($orderNo, $oauth['oauth_id'], $payPrice, $orderType);
+    }
+
+    /**
+     * 获取微信支付配置
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private static function getWxConfig(): array
+    {
+        $storeId = getStoreId();
+        return WxappSettingModel::getWxappConfig($storeId);
+    }
+}

+ 1 - 1
app/index/service/User.php

@@ -13,7 +13,7 @@ declare (strict_types = 1);
 namespace app\index\service;
 
 use app\index\model\User as UserModel;
-use app\api\model\UserOauth as UserOauthModel;
+use app\index\model\UserOauth as UserOauthModel;
 use app\common\service\BaseService;
 use cores\exception\BaseException;
 use think\facade\Session;

+ 211 - 0
app/index/service/coupon/GoodsDeduct.php

@@ -0,0 +1,211 @@
+<?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\coupon;
+
+use app\common\enum\coupon\ApplyRange as ApplyRangeEnum;
+use app\common\enum\coupon\CouponType as CouponTypeEnum;
+use app\common\service\BaseService;
+use app\common\library\helper;
+use cores\exception\BaseException;
+
+/**
+ * 订单优惠券折扣服务类
+ * Class GoodsDeduct
+ * @package app\api\service\coupon
+ */
+class GoodsDeduct extends BaseService
+{
+    // 实际抵扣金额
+    private $actualReducedMoney;
+
+    // 订单商品列表
+    private $goodsList = [];
+
+    // 优惠券信息
+    private $couponInfo = [];
+
+    // 实际参与优惠券折扣的商品记录
+    private $rangeGoodsList = [];
+
+    // 设置订单商品列表
+    public function setGoodsList(iterable $goodsList): GoodsDeduct
+    {
+        $this->goodsList = $goodsList;
+        return $this;
+    }
+
+    // 设置优惠券信息
+    public function setCouponInfo(array $couponInfo): GoodsDeduct
+    {
+        $this->couponInfo = $couponInfo;
+        return $this;
+    }
+
+    /**
+     * 计算优惠券抵扣金额
+     * @return GoodsDeduct
+     * @throws BaseException
+     */
+    public function setGoodsCouponMoney(): GoodsDeduct
+    {
+        // 验证当前类属性
+        $this->checkAttribute();
+        // 设置实际参与优惠券折扣的商品记录
+        $this->setRangeGoodsList();
+        // 计算实际抵扣的金额
+        $this->setActualReducedMoney();
+        // 实际抵扣金额为0
+        if ($this->actualReducedMoney > 0) {
+            // 计算商品的价格权重
+            $this->setGoodsListWeight();
+            // 计算商品优惠券抵扣金额
+            $this->setGoodsListCouponMoney();
+            // 总抵扣金额 (已分配的)
+            $assignedCouponMoney = helper::getArrayColumnSum($this->rangeGoodsList, 'coupon_money');
+            // 分配剩余的抵扣金额
+            $this->setGoodsListCouponMoneyFill($assignedCouponMoney);
+            $this->setGoodsListCouponMoneyDiff($assignedCouponMoney);
+        }
+        return $this;
+    }
+
+    // 获取实际参与优惠券折扣的商品记录
+    public function getRangeGoodsList(): array
+    {
+        return $this->rangeGoodsList;
+    }
+
+    /**
+     * 设置实际参与优惠券折扣的商品记录
+     * @return void
+     */
+    private function setRangeGoodsList()
+    {
+        $this->rangeGoodsList = [];
+        foreach ($this->goodsList as $goods) {
+            $goods['total_price'] *= 100;
+            $goodsKey = "{$goods['goods_id']}-{$goods['goods_sku_id']}";
+            switch ($this->couponInfo['apply_range']) {
+                case ApplyRangeEnum::ALL:
+                    $this->rangeGoodsList[$goodsKey] = $goods;
+                    break;
+                case ApplyRangeEnum::SOME:
+                    if (in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['applyGoodsIds'])) {
+                        $this->rangeGoodsList[$goodsKey] = $goods;
+                    }
+                    break;
+                case ApplyRangeEnum::EXCLUDE:
+                    if (!in_array($goods['goods_id'], $this->couponInfo['apply_range_config']['excludedGoodsIds'])) {
+                        $this->rangeGoodsList[$goodsKey] = $goods;
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    /**
+     * 验证当前类属性
+     * @throws BaseException
+     */
+    private function checkAttribute()
+    {
+        if (empty($this->goodsList) || empty($this->couponInfo)) {
+            throwError('goodsList or couponInfo not found.');
+        }
+    }
+
+    public function getActualReducedMoney()
+    {
+        return $this->actualReducedMoney;
+    }
+
+    /**
+     * 计算实际抵扣的金额
+     */
+    private function setActualReducedMoney()
+    {
+        // 获取当前订单商品总额
+        $orderTotalPrice = $this->getOrderTotalPrice();
+        // 计算最大抵扣金额
+        $reducedMoney = 0;
+        foreach ($this->rangeGoodsList as $goods) {
+            $reducedMoney += $goods['total_price'];
+        }
+        // 计算打折金额
+        if ($this->couponInfo['coupon_type'] == CouponTypeEnum::DISCOUNT) {
+            $reducePrice = $reducedMoney - ($reducedMoney * ($this->couponInfo['discount'] / 10));
+        } else {
+            $reducePrice = $this->couponInfo['reduce_price'] * 100;
+        }
+        // 优惠券最大允许抵扣到一分钱,所以此处判断抵扣金额大于等于订单金额时,减去一分钱
+        $this->actualReducedMoney = ($reducePrice >= $orderTotalPrice) ? $orderTotalPrice - 1 : $reducePrice;
+    }
+
+    /**
+     * 获取当前订单商品总额
+     * @return float|int
+     */
+    private function getOrderTotalPrice()
+    {
+        $orderTotalPrice = 0;
+        foreach ($this->goodsList as $goods) {
+            $orderTotalPrice += ($goods['total_price'] * 100);
+        }
+        return $orderTotalPrice;
+    }
+
+    /**
+     * 计算商品抵扣的权重(占比)
+     */
+    private function setGoodsListWeight()
+    {
+        $orderTotalPrice = helper::getArrayColumnSum($this->rangeGoodsList, 'total_price');
+        foreach ($this->rangeGoodsList as &$goods) {
+            $goods['weight'] = round($goods['total_price'] / $orderTotalPrice, 6);
+        }
+        array_sort($this->rangeGoodsList, 'weight', true);
+    }
+
+    /**
+     * 计算商品抵扣的金额
+     */
+    private function setGoodsListCouponMoney(): void
+    {
+        foreach ($this->rangeGoodsList as &$goods) {
+            $goods['coupon_money'] = helper::bcmul($this->actualReducedMoney, $goods['weight'], 0);
+        }
+    }
+
+    private function setGoodsListCouponMoneyFill($assignedCouponMoney): void
+    {
+        if ($assignedCouponMoney === 0) {
+            $temReducedMoney = $this->actualReducedMoney;
+            foreach ($this->rangeGoodsList as &$goods) {
+                if ($temReducedMoney === 0) break;
+                $goods['coupon_money'] = 1;
+                $temReducedMoney--;
+            }
+        }
+    }
+
+    private function setGoodsListCouponMoneyDiff($assignedCouponMoney): void
+    {
+        $tempDiff = $this->actualReducedMoney - $assignedCouponMoney;
+        foreach ($this->rangeGoodsList as &$goods) {
+            if ($tempDiff < 1) break;
+            $goods['coupon_money']++ && $tempDiff--;
+        }
+    }
+}

+ 5 - 5
app/index/service/order/Checkout.php

@@ -20,11 +20,11 @@ use app\index\model\Setting as SettingModel;
 use app\index\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\index\service\Payment as PaymentService;
+use app\index\service\user\Grade as UserGradeService;
+use app\index\service\coupon\GoodsDeduct as GoodsDeductService;
+use app\index\service\points\GoodsDeduct as PointsDeductService;
+use app\index\service\order\source\checkout\Factory as CheckoutFactory;
 
 use app\common\enum\order\PayStatus as PayStatusEnum;
 use app\common\enum\Setting as SettingEnum;

+ 3 - 3
app/index/service/order/PaySuccess.php

@@ -14,9 +14,9 @@ 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\index\model\User as UserModel;
+use app\index\model\Order as OrderModel;
+use app\index\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;

+ 54 - 0
app/index/service/order/source/Basics.php

@@ -0,0 +1,54 @@
+<?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\source;
+
+use app\common\service\BaseService;
+use app\common\enum\order\OrderStatus as OrderStatusEnum;
+use app\common\enum\order\PayStatus as OrderPayStatusEnum;
+
+abstract class Basics extends BaseService
+{
+    /**
+     * 判断订单是否允许付款
+     * @param $order
+     * @return mixed
+     */
+    abstract public function checkOrderStatusOnPay($order);
+
+    /**
+     * 判断商品状态、库存 (未付款订单)
+     * @param $goodsList
+     * @return mixed
+     */
+    abstract protected function checkGoodsStatusOnPay($goodsList);
+
+    /**
+     * 判断订单状态(公共)
+     * @param $order
+     * @return bool
+     */
+    protected function checkOrderStatusOnPayCommon($order)
+    {
+
+        // 判断订单状态
+        if (
+            $order['order_status'] != OrderStatusEnum::NORMAL
+            || $order['pay_status'] != OrderPayStatusEnum::PENDING
+        ) {
+            $this->error = '很抱歉,当前订单不合法,无法支付';
+            return false;
+        }
+        return true;
+    }
+
+}

+ 44 - 0
app/index/service/order/source/Factory.php

@@ -0,0 +1,44 @@
+<?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\source;
+
+use app\common\service\BaseService;
+use app\common\enum\order\OrderSource as OrderSourceEnum;
+
+/**
+ * 商品库存工厂类
+ * Class Factory
+ * @package app\common\service\stock
+ */
+class Factory extends BaseService
+{
+    // 订单来源的结算台服务类
+    private static $class = [
+        OrderSourceEnum::MAIN => 'Main',
+    ];
+
+    /**
+     * 根据订单来源获取商品库存类
+     * @param int $orderSource
+     * @return mixed
+     */
+    public static function getFactory($orderSource = OrderSourceEnum::MAIN)
+    {
+        static $classObj = [];
+        if (!isset($classObj[$orderSource])) {
+            $className = __NAMESPACE__ . '\\' . static::$class[$orderSource];
+            $classObj[$orderSource] = new $className();
+        }
+        return $classObj[$orderSource];
+    }
+}

+ 85 - 0
app/index/service/order/source/Main.php

@@ -0,0 +1,85 @@
+<?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\source;
+
+use app\index\model\GoodsSku as GoodsSkuModel;
+
+/**
+ * 订单来源-普通订单扩展类
+ * Class Main
+ * @package app\api\service\order\source
+ */
+class Main extends Basics
+{
+    /**
+     * 判断订单是否允许付款
+     * @param $order
+     * @return bool
+     */
+    public function checkOrderStatusOnPay($order)
+    {
+        // 判断订单状态
+        if (!$this->checkOrderStatusOnPayCommon($order)) {
+            return false;
+        }
+        // 判断商品状态、库存
+        if (!$this->checkGoodsStatusOnPay($order['goods'])) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 判断商品状态、库存 (未付款订单)
+     * @param $goodsList
+     * @return bool
+     */
+    protected function checkGoodsStatusOnPay($goodsList)
+    {
+        foreach ($goodsList as $goods) {
+            // 判断商品是否下架
+            if (
+                empty($goods['goods'])
+                || $goods['goods']['status'] != 10
+            ) {
+                $this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架";
+                return false;
+            }
+            // 获取商品的sku信息
+            $goodsSku = $this->getOrderGoodsSku($goods['goods_id'], $goods['goods_sku_id']);
+            // sku已不存在
+            if (empty($goodsSku)) {
+                $this->error = "很抱歉,商品 [{$goods['goods_name']}] sku已不存在,请重新下单";
+                return false;
+            }
+            // 付款减库存
+            if ($goods['deduct_stock_type'] == 20 && $goods['total_num'] > $goodsSku['stock_num']) {
+                $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足";
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 获取指定的商品sku信息
+     * @param $goodsId
+     * @param $goodsSkuId
+     * @return GoodsSkuModel|array|null
+     */
+    private function getOrderGoodsSku($goodsId, $goodsSkuId)
+    {
+        return GoodsSkuModel::detail($goodsId, $goodsSkuId);
+    }
+
+}

+ 50 - 0
app/index/service/order/source/checkout/Basics.php

@@ -0,0 +1,50 @@
+<?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\source\checkout;
+
+use app\common\service\BaseService;
+use app\index\model\User as UserModel;
+
+/**
+ * 订单结算台扩展基类
+ * Class Basics
+ * @package app\api\service\order\source\checkout
+ */
+abstract class Basics extends BaseService
+{
+    /* @var UserModel $user 当前用户信息 */
+    protected $user;
+
+    // 订单结算商品列表
+    protected $goodsList = [];
+
+    /**
+     * 构造方法
+     * Checkout constructor.
+     * @param UserModel $user
+     * @param array $goodsList
+     */
+    public function __construct($user, $goodsList)
+    {
+        parent::__construct();
+        $this->user = $user;
+        $this->goodsList = $goodsList;
+    }
+
+    /**
+     * 验证商品列表
+     * @return mixed
+     */
+    abstract public function validateGoodsList();
+
+}

+ 42 - 0
app/index/service/order/source/checkout/Factory.php

@@ -0,0 +1,42 @@
+<?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\source\checkout;
+
+use app\common\service\BaseService;
+use app\common\enum\order\OrderSource as OrderSourceEnum;
+
+/**
+ * 订单结算台扩展工厂类
+ * Class Factory
+ * @package app\common\service\stock
+ */
+class Factory extends BaseService
+{
+    // 订单来源的结算台服务类
+    private static $class = [
+        OrderSourceEnum::MAIN => Main::class,
+    ];
+
+    /**
+     * 根据订单来源获取商品库存类
+     * @param $user
+     * @param $goodsList
+     * @param int $orderSource
+     * @return mixed
+     */
+    public static function getFactory($user, $goodsList, $orderSource = OrderSourceEnum::MAIN)
+    {
+        return new static::$class[$orderSource]($user, $goodsList);
+    }
+
+}

+ 42 - 0
app/index/service/order/source/checkout/Main.php

@@ -0,0 +1,42 @@
+<?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\source\checkout;
+
+/**
+ * 订单结算台-普通商品扩展类
+ * Class Main
+ * @package app\api\service\order\source\checkout
+ */
+class Main extends Basics
+{
+    /**
+     * 验证商品列表
+     * @return bool
+     */
+    public function validateGoodsList()
+    {
+        foreach ($this->goodsList as $goods) {
+            // 判断商品是否下架
+            if ($goods['status'] != 10) {
+                $this->error = "很抱歉,商品 [{$goods['goods_name']}] 已下架";
+                return false;
+            }
+            // 判断商品库存
+            if ($goods['total_num'] > $goods['skuInfo']['stock_num']) {
+                $this->error = "很抱歉,商品 [{$goods['goods_name']}] 库存不足";
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 104 - 0
app/index/service/points/GoodsDeduct.php

@@ -0,0 +1,104 @@
+<?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\points;
+
+use app\common\library\helper;
+use app\index\model\Setting as SettingModel;
+use app\common\service\BaseService;
+
+/**
+ * 订单积分抵扣服务类
+ * Class GoodsDeduct
+ * @package app\api\service\points
+ */
+class GoodsDeduct extends BaseService
+{
+    private $goodsList;
+
+    /**
+     * 构造方法
+     * GoodsDeduct constructor.
+     * @param $goodsList
+     */
+    public function __construct($goodsList)
+    {
+        parent::__construct();
+        $this->goodsList = $goodsList;
+    }
+
+    public function setGoodsPoints($maxPointsNumCount, $actualPointsNum)
+    {
+        // 计算实际积分抵扣数量
+        $this->setGoodsListPointsNum($maxPointsNumCount, $actualPointsNum);
+        // 总抵扣数量
+        $totalPointsNum = (int)helper::getArrayColumnSum($this->goodsList, 'points_num');
+        // 填充余数
+        $this->setGoodsListPointsNumFill($actualPointsNum, $totalPointsNum);
+        $this->setGoodsListPointsNumDiff($actualPointsNum, $totalPointsNum);
+        // 计算实际积分抵扣金额
+        $this->setGoodsListPointsMoney();
+        return true;
+    }
+
+    /**
+     * 计算实际积分抵扣数量
+     * @param $maxPointsNumCount
+     * @param $actualPointsNum
+     */
+    private function setGoodsListPointsNum($maxPointsNumCount, $actualPointsNum)
+    {
+        foreach ($this->goodsList as &$goods) {
+            if (!$goods['is_points_discount']) continue;
+            $goods['points_num'] = floor($goods['max_points_num'] / $maxPointsNumCount * $actualPointsNum);
+        }
+    }
+
+    /**
+     * 计算实际积分抵扣金额
+     */
+    private function setGoodsListPointsMoney()
+    {
+        $setting = SettingModel::getItem('points');
+        foreach ($this->goodsList as &$goods) {
+            if (!$goods['is_points_discount']) continue;
+            $goods['points_money'] = helper::bcmul($goods['points_num'], $setting['discount']['discount_ratio']);
+        }
+    }
+
+    private function setGoodsListPointsNumFill($actualPointsNum, $totalPointsNum)
+    {
+        if ($totalPointsNum === 0) {
+            $temReducedMoney = $actualPointsNum;
+            foreach ($this->goodsList as &$goods) {
+                if (!$goods['is_points_discount']) continue;
+                if ($temReducedMoney === 0) break;
+                $goods['points_num'] = 1;
+                $temReducedMoney--;
+            }
+        }
+        return true;
+    }
+
+    private function setGoodsListPointsNumDiff($actualPointsNum, $totalPointsNum)
+    {
+        $tempDiff = $actualPointsNum - $totalPointsNum;
+        foreach ($this->goodsList as &$goods) {
+            if (!$goods['is_points_discount']) continue;
+            if ($tempDiff < 1) break;
+            $goods['points_num'] = $goods['points_num'] + 1;
+            $tempDiff--;
+        }
+        return true;
+    }
+
+}

+ 109 - 0
app/index/service/user/Avatar.php

@@ -0,0 +1,109 @@
+<?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\user;
+
+use app\common\service\BaseService;
+use app\index\model\Setting as SettingModel;
+use app\index\model\UploadFile as UploadFileModel;
+use app\common\enum\file\FileType as FileTypeEnum;
+use app\common\library\{Download, storage\Driver as Storage};
+use cores\exception\BaseException;
+
+/**
+ * 服务类: 用户头像
+ * Class Avatar
+ * @package app\api\service\user
+ */
+class Avatar extends BaseService
+{
+    // 存储配置
+    private $config;
+
+    /**
+     * 构造函数
+     * Avatar constructor.
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function __construct()
+    {
+        parent::__construct();
+        // 存储配置
+        $this->config = SettingModel::getItem('storage');
+    }
+
+    /**
+     * 下载第三方头像并返回文件记录ID
+     * @param string $avatarUrl
+     * @return int
+     * @throws BaseException
+     * @throws \think\Exception
+     */
+    public function party(string $avatarUrl): int
+    {
+        // 下载网络图片
+        $filePath = $this->download($avatarUrl);
+        // 上传到本地
+        $fileInfo = $this->upload($filePath);
+        // 新增文件记录
+        return $this->record($fileInfo);
+    }
+
+    /**
+     * 图片上传接口
+     * @param string $filePath
+     * @return false|mixed
+     * @throws \think\Exception
+     */
+    public function upload(string $filePath = '')
+    {
+        // 实例化存储驱动
+        $storage = new Storage($this->config);
+        // 设置上传文件的信息
+        $storage->setUploadFileByReal($filePath)
+            ->setRootDirName((string)$this->storeId)
+            ->setValidationScene('image')
+            ->upload();
+        // 执行文件上传
+        if (!$storage->upload()) {
+            throwError('图片上传失败:' . $storage->getError());
+        }
+        // 文件信息
+        return $storage->getSaveFileInfo();
+    }
+
+    /**
+     * 添加文件库记录
+     * @param array $fileInfo
+     * @return int
+     */
+    private function record(array $fileInfo): int
+    {
+        $model = new UploadFileModel;
+        $model->add($fileInfo, FileTypeEnum::IMAGE, 0);
+        return (int)$model['file_id'];
+    }
+
+    /**
+     * 下载网络图片
+     * @param string $avatarUrl
+     * @return string
+     * @throws BaseException
+     */
+    private function download(string $avatarUrl): string
+    {
+        $Download = new Download;
+        return $Download->saveTempImage($this->storeId, $avatarUrl, 'avatar');
+    }
+}

+ 61 - 0
app/index/service/user/Grade.php

@@ -0,0 +1,61 @@
+<?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\user;
+
+use app\common\library\helper;
+use app\common\service\BaseService;
+use app\index\service\User as UserService;
+use app\index\model\user\Grade as UserGradeModel;
+use cores\exception\BaseException;
+
+/**
+ * 服务类: 会员等级
+ * Class Grade
+ * @package app\api\service\user
+ */
+class Grade extends BaseService
+{
+    /**
+     * 获取当前登录用户的会员等级信息
+     * @param bool $isForce 是否强制验证登录, 如果未登录将抛错
+     * @throws BaseException
+     */
+    public static function getCurrentGradeInfo(bool $isForce = false)
+    {
+        // 当前登录的用户信息
+        $userInfo = UserService::getCurrentLoginUser($isForce);
+        if (empty($userInfo) || empty($userInfo['grade_id'])) {
+            return false;
+        }
+        // 获取会员等级信息
+        $gradeInfo = UserGradeModel::detail($userInfo['grade_id']);
+        if (empty($gradeInfo) || $gradeInfo['is_delete'] || empty($gradeInfo['status'])) {
+            return false;
+        }
+        return $gradeInfo;
+    }
+
+    /**
+     * 获取和计算折扣后的价格
+     * @param $originalPrice
+     * @param $discountRatio
+     * @return string
+     */
+    public static function getDiscountPrice($originalPrice, $discountRatio): string
+    {
+        // 使用高精度方法计算等级折扣; 因bcmach不支持四舍五入, 所以精确计算到3位数, 使用round四舍五入
+        $discountPrice = helper::bcmul($originalPrice, $discountRatio / 10, 3);
+        $discountPrice = round((float)$discountPrice, 2);
+        return helper::number2($discountPrice, true);
+    }
+}

+ 103 - 0
app/index/service/user/Oauth.php

@@ -0,0 +1,103 @@
+<?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\user;
+
+use app\index\model\UserOauth as UserOauthModel;
+use app\index\model\wxapp\Setting as WxappSettingModel;
+use app\common\service\BaseService;
+use app\common\library\wechat\WxUser;
+use app\common\library\wechat\ErrorCode;
+use app\common\library\wechat\WXBizDataCrypt;
+use cores\exception\BaseException;
+
+/**
+ * 服务类: 第三方用户服务类
+ * Class Avatar
+ * @package app\api\service\user
+ */
+class Oauth extends BaseService
+{
+    /**
+     * 微信小程序通过code获取session (openid session_key unionid)
+     * @param string $code
+     * @return array|false
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function wxCode2Session(string $code)
+    {
+        // 获取当前小程序信息
+        $wxConfig = static::getMpWxConfig();
+        // 微信登录 (获取session_key)
+        $WxUser = new WxUser($wxConfig['app_id'], $wxConfig['app_secret']);
+        $result = $WxUser->jscode2session($code);
+        !$result && throwError($WxUser->getError());
+        return $result;
+    }
+
+    /**
+     * 解密微信的加密数据encryptedData
+     * @param string $sessionKey
+     * @param string $encryptedData
+     * @param string $iv
+     * @return mixed
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \cores\exception\BaseException
+     */
+    public static function wxDecryptData(string $sessionKey, string $encryptedData, string $iv)
+    {
+        // 获取当前小程序信息
+        $wxConfig = static::getMpWxConfig();
+        // 微信数据解密
+        $WXBizDataCrypt = new WXBizDataCrypt($wxConfig['app_id'], $sessionKey);
+        $content = null;
+        $code = $WXBizDataCrypt->decryptData($encryptedData, $iv, $content);
+        if ($code !== ErrorCode::$OK) {
+            throwError('微信数据 encryptedData 解密失败');
+        }
+        return $content;
+    }
+
+    /**
+     * 获取微信小程序配置项
+     * @return array
+     * @throws \cores\exception\BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private static function getMpWxConfig(): array
+    {
+        $wxConfig = WxappSettingModel::getWxappConfig();
+        if (empty($wxConfig['app_id']) || empty($wxConfig['app_secret'])) {
+            throwError('请到后台小程序设置填写AppID和AppSecret参数');
+        }
+        return $wxConfig;
+    }
+
+    /**
+     * 根据openid获取用户ID
+     * @param string $oauthId 第三方用户唯一标识 (openid)
+     * @param string $oauthType 第三方登陆类型
+     * @return mixed
+     */
+    public static function getUserIdByOauthId(string $oauthId, string $oauthType)
+    {
+        return UserOauthModel::getUserIdByOauthId($oauthId, $oauthType);
+    }
+}