wesmiler 3 лет назад
Родитель
Сommit
56408c0d7a
100 измененных файлов с 7890 добавлено и 0 удалено
  1. 218 0
      app/admin/controller/Controller.php
  2. 60 0
      app/admin/controller/Passport.php
  3. 97 0
      app/admin/controller/Store.php
  4. 60 0
      app/admin/controller/admin/User.php
  5. 40 0
      app/admin/controller/setting/Cache.php
  6. 236 0
      app/admin/controller/setting/Science.php
  7. 90 0
      app/admin/controller/store/Api.php
  8. 124 0
      app/admin/controller/store/Menu.php
  9. 48 0
      app/admin/model/Store.php
  10. 25 0
      app/admin/model/UploadFile.php
  11. 110 0
      app/admin/model/admin/User.php
  12. 129 0
      app/admin/model/store/Api.php
  13. 116 0
      app/admin/model/store/Menu.php
  14. 64 0
      app/admin/model/store/MenuApi.php
  15. 61 0
      app/admin/model/store/User.php
  16. 93 0
      app/admin/service/Cache.php
  17. 120 0
      app/admin/service/admin/User.php
  18. 25 0
      app/admin/service/store/User.php
  19. 29 0
      app/api/common.php
  20. 31 0
      app/api/config/captcha.php
  21. 124 0
      app/api/controller/Address.php
  22. 48 0
      app/api/controller/Article.php
  23. 49 0
      app/api/controller/Captcha.php
  24. 118 0
      app/api/controller/Cart.php
  25. 38 0
      app/api/controller/Category.php
  26. 203 0
      app/api/controller/Checkout.php
  27. 76 0
      app/api/controller/Comment.php
  28. 160 0
      app/api/controller/Controller.php
  29. 39 0
      app/api/controller/Coupon.php
  30. 37 0
      app/api/controller/Express.php
  31. 84 0
      app/api/controller/Goods.php
  32. 36 0
      app/api/controller/Help.php
  33. 16 0
      app/api/controller/Index.php
  34. 53 0
      app/api/controller/MyCoupon.php
  35. 37 0
      app/api/controller/Notify.php
  36. 159 0
      app/api/controller/Order.php
  37. 40 0
      app/api/controller/Page.php
  38. 97 0
      app/api/controller/Passport.php
  39. 58 0
      app/api/controller/Recharge.php
  40. 96 0
      app/api/controller/Refund.php
  41. 50 0
      app/api/controller/Region.php
  42. 37 0
      app/api/controller/Setting.php
  43. 77 0
      app/api/controller/Upload.php
  44. 78 0
      app/api/controller/User.php
  45. 39 0
      app/api/controller/article/Category.php
  46. 38 0
      app/api/controller/balance/Log.php
  47. 40 0
      app/api/controller/goods/Service.php
  48. 83 0
      app/api/controller/order/Comment.php
  49. 37 0
      app/api/controller/points/Log.php
  50. 39 0
      app/api/controller/recharge/Order.php
  51. 39 0
      app/api/controller/recharge/Plan.php
  52. 20 0
      app/api/event.php
  53. 104 0
      app/api/listener/order/PaySuccess.php
  54. 97 0
      app/api/model/Article.php
  55. 166 0
      app/api/model/Cart.php
  56. 47 0
      app/api/model/Category.php
  57. 274 0
      app/api/model/Comment.php
  58. 42 0
      app/api/model/CommentImage.php
  59. 86 0
      app/api/model/Coupon.php
  60. 34 0
      app/api/model/Delivery.php
  61. 34 0
      app/api/model/DeliveryRule.php
  62. 36 0
      app/api/model/Express.php
  63. 265 0
      app/api/model/Goods.php
  64. 33 0
      app/api/model/GoodsImage.php
  65. 42 0
      app/api/model/GoodsSku.php
  66. 24 0
      app/api/model/GoodsSpecRel.php
  67. 32 0
      app/api/model/Help.php
  68. 398 0
      app/api/model/Order.php
  69. 33 0
      app/api/model/OrderAddress.php
  70. 50 0
      app/api/model/OrderGoods.php
  71. 234 0
      app/api/model/OrderRefund.php
  72. 33 0
      app/api/model/OrderRefundAddress.php
  73. 25 0
      app/api/model/OrderRefundImage.php
  74. 161 0
      app/api/model/Page.php
  75. 25 0
      app/api/model/Region.php
  76. 33 0
      app/api/model/Setting.php
  77. 24 0
      app/api/model/Spec.php
  78. 24 0
      app/api/model/SpecValue.php
  79. 55 0
      app/api/model/Store.php
  80. 56 0
      app/api/model/UploadFile.php
  81. 112 0
      app/api/model/User.php
  82. 182 0
      app/api/model/UserAddress.php
  83. 206 0
      app/api/model/UserCoupon.php
  84. 44 0
      app/api/model/UserOauth.php
  85. 36 0
      app/api/model/article/Category.php
  86. 54 0
      app/api/model/goods/Service.php
  87. 24 0
      app/api/model/goods/ServiceRel.php
  88. 35 0
      app/api/model/h5/Setting.php
  89. 251 0
      app/api/model/recharge/Order.php
  90. 42 0
      app/api/model/recharge/OrderPlan.php
  91. 67 0
      app/api/model/recharge/Plan.php
  92. 49 0
      app/api/model/user/BalanceLog.php
  93. 25 0
      app/api/model/user/Grade.php
  94. 25 0
      app/api/model/user/GradeLog.php
  95. 41 0
      app/api/model/user/PointsLog.php
  96. 25 0
      app/api/model/wxapp/Setting.php
  97. 164 0
      app/api/service/Cart.php
  98. 25 0
      app/api/service/Goods.php
  99. 95 0
      app/api/service/Payment.php
  100. 0 0
      app/api/service/Setting.php

+ 218 - 0
app/admin/controller/Controller.php

@@ -0,0 +1,218 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\admin\controller;
+
+use cores\BaseController;
+use cores\exception\BaseException;
+use app\admin\service\admin\User as AdminUserService;
+use think\response\Json;
+
+/**
+ * 超管后台控制器基类
+ * Class Controller
+ * @package app\admin\controller
+ */
+class Controller extends BaseController
+{
+    // 商家登录信息
+    protected $admin;
+
+    // 当前控制器名称
+    protected $controller = '';
+
+    // 当前方法名称
+    protected $action = '';
+
+    // 当前路由uri
+    protected $routeUri = '';
+
+    // 当前路由:分组名称
+    protected $group = '';
+
+    // 登录验证白名单
+    protected $allowAllAction = [
+        // 登录页面
+        'passport/login',
+    ];
+
+    // 无需全局layout
+    protected $notLayoutAction = [];
+
+    /**
+     * 强制验证当前访问的控制器方法method
+     * 例: [ 'login' => 'POST' ]
+     * @var array
+     */
+    protected $methodRules = [];
+
+    /**
+     * 后台初始化
+     * @throws \Exception
+     */
+    public function initialize()
+    {
+        // 设置管理员登录信息
+        $this->setAdminInfo();
+        // 当前路由信息
+        $this->getRouteInfo();
+        // 验证登录
+        $this->checkLogin();
+        // 强制验证当前访问的控制器方法method
+        $this->checkMethodRules();
+    }
+
+    /**
+     * 设置管理员登录信息
+     */
+    private function setAdminInfo()
+    {
+        $this->admin = AdminUserService::getLoginInfo();
+    }
+
+    /**
+     * 解析当前路由参数 (分组名称、控制器名称、方法名)
+     */
+    protected function getRouteInfo()
+    {
+        // 控制器名称
+        $this->controller = uncamelize($this->request->controller());
+        // 方法名称
+        $this->action = $this->request->action();
+        // 控制器分组 (用于定义所属模块)
+        $group = strstr($this->controller, '.', true);
+        $this->group = $group !== false ? $group : $this->controller;
+        // 当前uri
+        $this->routeUri = "{$this->controller}/$this->action";
+    }
+
+    /**
+     * 后台菜单配置
+     * @return array
+     */
+    private function menus(): array
+    {
+        // 获取后台菜单内容 [app/admin/config/menus.php]
+        $menus = \think\facade\Config::get('menus');
+        foreach ($menus as $group => &$first) {
+            $first['active'] = $group === $this->group;
+            // 遍历:二级菜单
+            if (isset($first['submenu'])) {
+                foreach ($first['submenu'] as $secondKey => &$second) {
+                    // 二级菜单所有uri
+                    $secondUris = $second['uris'] ?? [$second['index']];
+                    // 二级菜单:active
+                    !isset($second['active']) && $second['active'] = in_array($this->routeUri, $secondUris);
+                }
+            }
+        }
+        return $menus;
+    }
+
+    /**
+     * 验证登录状态
+     * @return void
+     * @throws BaseException
+     */
+    private function checkLogin(): void
+    {
+        // 验证当前请求是否在白名单
+        if (in_array($this->routeUri, $this->allowAllAction)) {
+            return;
+        }
+        // 验证登录状态
+        if (empty($this->admin) || (int)$this->admin['is_login'] !== 1) {
+            throwError('请先登录后再访问', config('status.not_logged'));
+        }
+    }
+
+    /**
+     * 返回封装后的 API 数据到客户端
+     * @param int|null $status 状态码
+     * @param string $message
+     * @param array $data
+     * @return Json
+     */
+    protected function renderJson(int $status = null, string $message = '', array $data = []): Json
+    {
+        return json(compact('status', 'message', 'data'));
+    }
+
+    /**
+     * 返回操作成功json
+     * @param array|string $data
+     * @param string $message
+     * @return Json
+     */
+    protected function renderSuccess($data = [], string $message = 'success'): Json
+    {
+        if (is_string($data)) {
+            $message = $data;
+            $data = [];
+        }
+        return $this->renderJson(config('status.success'), $message, $data);
+    }
+
+    /**
+     * 返回操作失败json
+     * @param string $message
+     * @param array $data
+     * @return Json
+     */
+    protected function renderError(string $message = 'error', array $data = []): Json
+    {
+        return $this->renderJson(config('status.error'), $message, $data);
+    }
+
+    /**
+     * 获取post数据 (数组)
+     * @param $key
+     * @return mixed
+     */
+    protected function postData($key = null)
+    {
+        return $this->request->post(empty($key) ? '' : "{$key}/a");
+    }
+
+    /**
+     * 获取post数据 (数组)
+     * @param string $key
+     * @return mixed
+     */
+    protected function postForm(string $key = 'form')
+    {
+        return $this->postData($key);
+    }
+
+    /**
+     * 强制验证当前访问的控制器方法method
+     * @throws BaseException
+     */
+    private function checkMethodRules(): void
+    {
+        if (!isset($this->methodRules[$this->action])) {
+            return;
+        }
+        $methodRule = $this->methodRules[$this->action];
+        $currentMethod = $this->request->method();
+        if (empty($methodRule)) {
+            return;
+        }
+        if (is_array($methodRule) && in_array($currentMethod, $methodRule)) {
+            return;
+        }
+        if (is_string($methodRule) && $methodRule == $currentMethod) {
+            return;
+        }
+        throwError('illegal request method');
+    }
+}

+ 60 - 0
app/admin/controller/Passport.php

@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\controller;
+
+use app\admin\model\admin\User as UserModel;
+
+/**
+ * 超管后台认证
+ * Class Passport
+ * @package app\store\controller
+ */
+class Passport extends Controller
+{
+    /**
+     * 强制验证当前访问的控制器方法method
+     * @var array
+     */
+    protected $methodRules = [
+        'login' => 'POST',
+    ];
+
+    /**
+     * 超管后台登录
+     * @return array|string
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \Exception
+     */
+    public function login()
+    {
+        // 超管后台用户登录
+        $model = new UserModel;
+        if (($userInfo = $model->login($this->postData())) === false) {
+            return $this->renderError($model->getError() ?: '登录失败');
+        }
+        return $this->renderSuccess([
+            'userId' => $userInfo['admin_user_id'],
+            'token' => $model->getToken()
+        ], '登录成功');
+    }
+
+    /**
+     * 退出登录
+     */
+    public function logout()
+    {
+        return $this->renderSuccess('操作成功');
+    }
+
+}

+ 97 - 0
app/admin/controller/Store.php

@@ -0,0 +1,97 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\admin\controller;
+
+use app\admin\model\Store as StoreModel;
+
+/**
+ * 小程序商城管理
+ * Class Store
+ * @package app\admin\controller
+ */
+class Store extends Controller
+{
+    /**
+     * 强制验证当前访问的控制器方法method
+     * @var array
+     */
+    protected $methodRules = [
+        'index' => 'GET',
+        'recycle' => 'GET',
+        'add' => 'POST',
+        'move' => 'POST',
+        'delete' => 'POST',
+    ];
+
+    /**
+     * 小程序商城列表
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    public function index()
+    {
+        // 商城列表
+        $model = new StoreModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 回收站列表
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    public function recycle()
+    {
+        // 商城列表
+        $model = new StoreModel;
+        $list = $model->getList(true);
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 回收小程序
+     * @param int $storeId
+     * @return array
+     */
+    public function recovery(int $storeId)
+    {
+        // 小程序详情
+        $model = StoreModel::detail($storeId);
+        if (!$model->recycle()) {
+            return $this->renderError($model->getError() ?: '操作失败');
+        }
+        return $this->renderSuccess('操作成功');
+    }
+
+    /**
+     * 移出回收站
+     * @param int $storeId
+     * @return array
+     */
+    public function move(int $storeId)
+    {
+        // 小程序详情
+        $model = StoreModel::detail($storeId);
+        if (!$model->recycle(false)) {
+            return $this->renderError($model->getError() ?: '操作失败');
+        }
+        return $this->renderSuccess('操作成功');
+    }
+
+    public function add()
+    {
+        return $this->renderError('很抱歉,免费版暂不支持多开商城');
+    }
+
+}

+ 60 - 0
app/admin/controller/admin/User.php

@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\controller\admin;
+
+use app\admin\controller\Controller;
+use app\admin\model\admin\User as AdminUserModel;
+
+/**
+ * 超管后台管理员控制器
+ * Class User
+ * @package app\store\controller
+ */
+class User extends Controller
+{
+    /**
+     * 强制验证当前访问的控制器方法method
+     * 例: [ 'login' => 'POST' ]
+     * @var array
+     */
+    protected $methodRules = [
+        'detail' => 'GET',
+        'renew' => 'POST',
+    ];
+
+    /**
+     * 获取当前用户信息
+     * @return array
+     */
+    public function detail()
+    {
+        $userInfo = AdminUserModel::detail($this->admin['user']['admin_user_id']);
+        return $this->renderSuccess(['userInfo' => $userInfo]);
+    }
+
+    /**
+     * 更新当前管理员信息
+     * @return array|string
+     * @throws \Exception
+     */
+    public function renew()
+    {
+        // 获取当前用户信息
+        $model = AdminUserModel::detail($this->admin['user']['admin_user_id']);
+        // 更新用户信息
+        if ($model->renew($this->postForm())) {
+            return $this->renderSuccess('更新成功');
+        }
+        return $this->renderError($model->getError() ?: '更新失败');
+    }
+}

+ 40 - 0
app/admin/controller/setting/Cache.php

@@ -0,0 +1,40 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\controller\setting;
+
+use app\admin\controller\Controller;
+use app\admin\service\Cache as CacheService;
+
+/**
+ * 清理缓存
+ * Class Index
+ * @package app\admin\controller
+ */
+class Cache extends Controller
+{
+    /**
+     * 清理缓存
+     * @return array|string
+     * @throws \Exception
+     */
+    public function clear()
+    {
+        // 清理缓存
+        $CacheService = new CacheService;
+        if (!$CacheService->rmCache($this->postForm())) {
+            return $this->renderError($CacheService->getError() ?: '操作失败');
+        }
+        return $this->renderSuccess('操作成功');
+    }
+
+}

+ 236 - 0
app/admin/controller/setting/Science.php

@@ -0,0 +1,236 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\admin\controller\setting;
+
+use think\response\Json;
+use app\admin\controller\Controller;
+use app\common\library\helper;
+
+/**
+ * 环境检测
+ * Class Science
+ * @package app\admin\controller\setting
+ */
+class Science extends Controller
+{
+    /**
+     * 环境检测
+     * @return Json
+     */
+    public function info(): Json
+    {
+        return $this->renderSuccess([
+            'scienceInfo' => [
+                'server' => $this->server(),        // 服务器信息
+                'phpinfo' => $this->phpinfo(),      // PHP环境要求
+                'writeable' => $this->writeable(),  // 目录权限监测
+            ]
+        ]);
+    }
+
+    /**
+     * 服务器信息
+     * @return array
+     */
+    private function server(): array
+    {
+        return [
+            [
+                'name' => '服务器操作系统',
+                'key' => 'system',
+                'value' => PHP_OS,
+                'status' => PHP_SHLIB_SUFFIX === 'dll' ? 'warning' : 'normal',
+                'remark' => '建议使用 Linux 系统以提升程序性能'
+            ],
+            [
+                'name' => 'Web服务器环境',
+                'key' => 'webserver',
+                'value' => $this->request->server('SERVER_SOFTWARE'),
+                'status' => PHP_SAPI === 'isapi' ? 'warning' : 'normal',
+                'remark' => '建议使用 Apache 或 Nginx 以提升程序性能'
+            ],
+            [
+                'name' => 'PHP版本',
+                'key' => 'php',
+                'value' => PHP_VERSION,
+                'status' => version_compare(PHP_VERSION, '7.2.0') === -1 ? 'danger' : 'normal',
+                'remark' => 'PHP版本必须为 7.2.0 以上'
+            ],
+            [
+                'name' => 'PHP运行位数',
+                'key' => 'system',
+                'value' => (PHP_INT_SIZE === 4 ? '32' : '64') . '位',
+                'status' => PHP_INT_SIZE === 4 ? 'warning' : 'normal',
+                'remark' => '建议使用 64位 PHP以提升程序性能'
+            ],
+//            [
+//                'name' => '文件上传功能',
+//                'key' => 'file_uploads',
+//                'value' => !ini_get('file_uploads') ? '关闭' : '开启',
+//                'status' => !ini_get('file_uploads') ? 'danger' : 'normal',
+//                'remark' => '文件上传必须开启 file_uploads'
+//            ],
+            [
+                'name' => '文件上传最大值',
+                'key' => 'upload_max_filesize',
+                'value' => ini_get('upload_max_filesize'),
+                'status' => $this->compareBytes(ini_get('upload_max_filesize'), '10m') ? 'danger' : 'normal',
+                'remark' => '不能小于10MB;请修改php.ini中upload_max_filesize'
+            ],
+            [
+                'name' => 'POST数据最大值',
+                'key' => 'post_max_size',
+                'value' => ini_get('post_max_size'),
+                'status' => $this->compareBytes(ini_get('post_max_size'), '12m') ? 'danger' : 'normal',
+                'remark' => '不能小于12MB;请修改php.ini中post_max_size'
+            ],
+            [
+                'name' => '程序运行目录',
+                'key' => 'web_path',
+                'value' => str_replace('\\', '/', web_path()),
+                'status' => 'normal',
+                'remark' => ''
+            ],
+        ];
+    }
+
+    /**
+     * PHP环境要求
+     * get_loaded_extensions()
+     * @return array
+     */
+    private function phpinfo(): array
+    {
+        return [
+            [
+                'name' => 'PHP版本',
+                'key' => 'php_version',
+                'value' => '7.2.0及以上',
+                'status' => version_compare(PHP_VERSION, '7.2.0') === -1 ? 'danger' : 'normal',
+                'remark' => 'PHP版本必须为 7.2.0及以上'
+            ],
+            [
+                'name' => 'Mysqlnd',
+                'key' => 'mysqlnd',
+                'value' => '支持',
+                'status' => extension_loaded('mysqlnd') ? 'normal' : 'danger',
+                'remark' => '您的PHP环境不支持mysqlnd, 系统无法正常运行'
+            ],
+            [
+                'name' => 'CURL',
+                'key' => 'curl',
+                'value' => '支持',
+                'status' => extension_loaded('curl') && function_exists('curl_init') ? 'normal' : 'danger',
+                'remark' => '您的PHP环境不支持CURL, 系统无法正常运行'
+            ],
+            [
+                'name' => 'OpenSSL',
+                'key' => 'openssl',
+                'value' => '支持',
+                'status' => extension_loaded('openssl') ? 'normal' : 'danger',
+                'remark' => '没有启用OpenSSL, 将无法访问微信平台的接口'
+            ],
+            [
+                'name' => 'PDO',
+                'key' => 'pdo',
+                'value' => '支持',
+                'status' => extension_loaded('PDO') && extension_loaded('pdo_mysql') ? 'normal' : 'danger',
+                'remark' => '您的PHP环境不支持PDO, 系统无法正常运行'
+            ],
+            [
+                'name' => 'GD',
+                'key' => 'gd',
+                'value' => '支持',
+                'status' => extension_loaded('gd') ? 'normal' : 'danger',
+                'remark' => '您的PHP环境不支持GD, 系统无法正常生成图片'
+            ],
+            [
+                'name' => 'BCMath',
+                'key' => 'bcmath',
+                'value' => '支持',
+                'status' => extension_loaded('bcmath') ? 'normal' : 'danger',
+                'remark' => '您的PHP环境不支持BCMath, 系统无法正常运行'
+            ],
+            [
+                'name' => 'mbstring',
+                'key' => 'mbstring',
+                'value' => '支持',
+                'status' => extension_loaded('mbstring') ? 'normal' : 'danger',
+                'remark' => '您的PHP环境不支持mbstring, 系统无法正常运行'
+            ],
+            [
+                'name' => 'SimpleXML',
+                'key' => 'simplexML',
+                'value' => '支持',
+                'status' => extension_loaded('SimpleXML') ? 'normal' : 'danger',
+                'remark' => '您的PHP环境不支持SimpleXML, 系统无法解析xml 无法使用微信支付'
+            ],
+        ];
+    }
+
+    /**
+     * 目录权限监测
+     */
+    private function writeable(): array
+    {
+        $paths = [
+            'uploads' => realpath(web_path()) . '/uploads/',
+            'temp' => realpath(web_path()) . '/temp/',
+            'wxpay_log' => realpath(base_path()) . '/common/library/wechat/logs/',
+            'wxpay_cert' => realpath(base_path()) . '/common/library/wechat/cert/',
+        ];
+        return [
+            [
+                'name' => '文件上传目录',
+                'key' => 'uploads',
+                'value' => str_replace('\\', '/', $paths['uploads']),
+                'status' => helper::checkWriteable($paths['uploads']) ? 'normal' : 'danger',
+                'remark' => '目录不可写,系统将无法正常上传文件'
+            ],
+            [
+                'name' => '临时文件目录',
+                'key' => 'temp',
+                'value' => str_replace('\\', '/', $paths['temp']),
+                'status' => helper::checkWriteable($paths['temp']) ? 'normal' : 'danger',
+                'remark' => '目录不可写,系统将无法正常写入文件'
+            ],
+//            [
+//                'name' => '微信支付日志目录',
+//                'key' => 'wxpay_log',
+//                'value' => str_replace('\\', '/', $paths['wxpay_log']),
+//                'status' => helper::checkWriteable($paths['wxpay_log']) ? 'normal' : 'danger',
+//                'remark' => '目录不可写,系统将无法正常写入文件'
+//            ],
+            [
+                'name' => '微信支付证书目录',
+                'key' => 'wxpay_cert',
+                'value' => str_replace('\\', '/', $paths['wxpay_cert']),
+                'status' => helper::checkWriteable($paths['wxpay_cert']) ? 'normal' : 'danger',
+                'remark' => '目录不可写,系统将无法正常写入文件'
+            ],
+        ];
+    }
+
+    /**
+     * 比较数据大小
+     * @param string $size1
+     * @param string $size2
+     * @return bool
+     */
+    private function compareBytes(string $size1, string $size2): bool
+    {
+        $size1 = helper::convertToBytes($size1);
+        $size2 = helper::convertToBytes($size2);
+        return $size1 < $size2;
+    }
+}

+ 90 - 0
app/admin/controller/store/Api.php

@@ -0,0 +1,90 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\controller\store;
+
+use app\admin\controller\Controller;
+use app\admin\model\store\Api as ApiModel;
+
+/**
+ * 商家后台API权限控制器
+ * Class Api
+ * @package app\store\controller
+ */
+class Api extends Controller
+{
+    /**
+     * 权限列表
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function index()
+    {
+        $model = new ApiModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 添加权限
+     * @return array
+     */
+    public function add()
+    {
+        // 新增记录
+        $model = new ApiModel;
+        if ($model->add($this->postForm())) {
+            return $this->renderSuccess('添加成功');
+        }
+        return $this->renderError($model->getError() ?: '添加失败');
+    }
+
+    /**
+     * 更新权限
+     * @param $apiId
+     * @return array|string
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function edit(int $apiId)
+    {
+        // 权限详情
+        $model = ApiModel::detail($apiId);
+        // 更新记录
+        if ($model->edit($this->postForm())) {
+            return $this->renderSuccess('更新成功');
+        }
+        return $this->renderError($model->getError() ?: '更新失败');
+    }
+
+    /**
+     * 删除权限
+     * @param $apiId
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function delete(int $apiId)
+    {
+        // 权限详情
+        $model = ApiModel::detail($apiId);
+        if (!$model->remove()) {
+            return $this->renderError($model->getError() ?: '删除失败');
+        }
+        return $this->renderSuccess('删除成功');
+    }
+
+}

+ 124 - 0
app/admin/controller/store/Menu.php

@@ -0,0 +1,124 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\controller\store;
+
+use app\admin\controller\Controller;
+use app\admin\model\store\Menu as MenuModel;
+
+/**
+ * 商家后台菜单控制器
+ * Class Menu
+ * @package app\store\controller
+ */
+class Menu extends Controller
+{
+    /**
+     * 菜单列表
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function index()
+    {
+        $model = new MenuModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 菜单详情
+     * @param int $menuId
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function info(int $menuId)
+    {
+        // 菜单详情
+        $model = MenuModel::detail($menuId);
+        return $this->renderSuccess(['info' => $model]);
+    }
+
+    /**
+     * 添加菜单
+     * @return array
+     */
+    public function add()
+    {
+        // 新增记录
+        $model = new MenuModel;
+        if ($model->add($this->postForm())) {
+            return $this->renderSuccess('添加成功');
+        }
+        return $this->renderError($model->getError() ?: '添加失败');
+    }
+
+    /**
+     * 更新菜单
+     * @param $menuId
+     * @return array|string
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function edit(int $menuId)
+    {
+        // 菜单详情
+        $model = MenuModel::detail($menuId);
+        // 更新记录
+        if ($model->edit($this->postForm())) {
+            return $this->renderSuccess('更新成功');
+        }
+        return $this->renderError($model->getError() ?: '更新失败');
+    }
+
+    /**
+     * 设置菜单绑定的Api
+     * @param int $menuId
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function setApis(int $menuId)
+    {
+        // 菜单详情
+        $model = MenuModel::detail($menuId);
+        // 更新记录
+        if ($model->setApis($this->postForm())) {
+            return $this->renderSuccess('操作成功');
+        }
+        return $this->renderError($model->getError() ?: '操作失败');
+    }
+
+    /**
+     * 删除菜单
+     * @param $menuId
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function delete(int $menuId)
+    {
+        // 菜单详情
+        $model = MenuModel::detail($menuId);
+        if (!$model->remove()) {
+            return $this->renderError($model->getError() ?: '删除失败');
+        }
+        return $this->renderSuccess('删除成功');
+    }
+
+}

+ 48 - 0
app/admin/model/Store.php

@@ -0,0 +1,48 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\admin\model;
+
+use app\common\model\Store as StoreModel;
+
+/**
+ * 商家记录表模型
+ * Class Store
+ * @package app\admin\model
+ */
+class Store extends StoreModel
+{
+    /**
+     * 获取列表数据
+     * @param bool $isRecycle
+     * @return \think\Paginator
+     * @throws \think\db\exception\DbException
+     */
+    public function getList(bool $isRecycle = false): \think\Paginator
+    {
+        return $this->where('is_recycle', '=', (int)$isRecycle)
+            ->where('is_delete', '=', 0)
+            ->order(['create_time' => 'desc'])
+            ->paginate(15);
+    }
+
+    /**
+     * 移入移出回收站
+     * @param bool $isRecycle
+     * @return false|int
+     */
+    public function recycle($isRecycle = true)
+    {
+        return $this->save(['is_recycle' => (int)$isRecycle]);
+    }
+
+}

+ 25 - 0
app/admin/model/UploadFile.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\admin\model;
+
+use app\common\model\UploadFile as UploadFileModel;
+
+/**
+ * 文件库模型
+ * Class UploadFile
+ * @package app\admin\model
+ */
+class UploadFile extends UploadFileModel
+{
+
+}

+ 110 - 0
app/admin/model/admin/User.php

@@ -0,0 +1,110 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\admin\model\admin;
+
+use app\common\model\admin\User as UserModel;
+use app\admin\service\admin\User as AdminUserService;
+
+/**
+ * 超管后台用户模型
+ * Class User
+ * @package app\admin\model\admin
+ */
+class User extends UserModel
+{
+    /**
+     * 隐藏的字段
+     * @var array
+     */
+    protected $hidden = [
+        'password',
+    ];
+
+    // 用户登录token
+    private $token;
+
+    /**
+     * 超管后台用户登录
+     * @param array $data
+     * @return array|bool|null|\think\Model
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function login(array $data)
+    {
+        // 验证用户名密码是否正确
+        if (!$user = $this->getUserInfoByLogin($data)) {
+            $this->error = '登录失败, 用户名或密码错误';
+            return false;
+        }
+        // 记录登录状态, 并记录token
+        $this->token = AdminUserService::login($user->toArray());
+        return $user;
+    }
+
+    /**
+     * 返回生成的token
+     * @return mixed
+     */
+    public function getToken()
+    {
+        return $this->token;
+    }
+
+    /**
+     * 获取登录的用户信息
+     * @param $data
+     * @return array|false|\think\Model
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getUserInfoByLogin(array $data)
+    {
+        // 用户信息
+        $useInfo = static::withoutGlobalScope()
+            ->where(['user_name' => $data['username']])
+            ->find();
+        if (empty($useInfo)) return false;
+        // 验证密码是否正确
+        if (!password_verify($data['password'], $useInfo['password'])) {
+            return false;
+        }
+        return $useInfo;
+    }
+
+    /**
+     * 更新当前管理员信息
+     * @param $data
+     * @return bool
+     */
+    public function renew(array $data)
+    {
+        if ($data['password'] !== $data['password_confirm']) {
+            $this->error = '确认密码不正确';
+            return false;
+        }
+        // 更新管理员信息
+        if ($this->save([
+                'user_name' => $data['user_name'],
+                'password' => encryption_hash($data['password']),
+            ]) === false) {
+            return false;
+        }
+        // 更新登录信息
+        AdminUserService::update($this->toArray());
+        return true;
+    }
+
+}

+ 129 - 0
app/admin/model/store/Api.php

@@ -0,0 +1,129 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\admin\model\store;
+
+use app\common\model\store\Api as ApiModel;
+
+/**
+ * 商家后台API权限模型
+ * Class Api
+ * @package app\admin\model\store
+ */
+class Api extends ApiModel
+{
+    /**
+     * 获取权限列表
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getList()
+    {
+        $all = static::getAll();
+        return $this->getTreeData($all);
+    }
+
+    /**
+     * 新增记录
+     * @param $data
+     * @return false|int
+     */
+    public function add(array $data)
+    {
+        return $this->allowField(['name', 'parent_id', 'url', 'sort'])->save($data);
+    }
+
+    /**
+     * 更新记录
+     * @param $data
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function edit($data)
+    {
+        // 判断上级角色是否为当前子级
+        if ($data['parent_id'] > 0) {
+            // 获取所有上级id集
+            $parentIds = $this->getTopApiIds($data['parent_id']);
+            if (in_array($this['api_id'], $parentIds)) {
+                $this->error = '上级权限不允许设置为当前子权限';
+                return false;
+            }
+        }
+        return $this->allowField(['name', 'parent_id', 'url', 'sort'])->save($data) !== false;
+    }
+
+    /**
+     * 删除权限
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \Exception
+     */
+    public function remove()
+    {
+        // 判断是否存在下级权限
+        if (self::detail(['parent_id' => $this['api_id']])) {
+            $this->error = '当前权限下存在子权限,请先删除';
+            return false;
+        }
+        return $this->delete();
+    }
+
+    /**
+     * 获取所有上级id集
+     * @param int $apiId
+     * @param null $all
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getTopApiIds(int $apiId, $all = null)
+    {
+        static $ids = [];
+        is_null($all) && $all = $this->getAll();
+        foreach ($all as $item) {
+            if ($item['api_id'] == $apiId && $item['parent_id'] > 0) {
+                $ids[] = $item['parent_id'];
+                $this->getTopApiIds($item['parent_id'], $all);
+            }
+        }
+        return $ids;
+    }
+
+    /**
+     * 获取树状列表
+     * @param array $list
+     * @param int $parentId
+     * @return array
+     */
+    private function getTreeData(array &$list, int $parentId = 0)
+    {
+        $data = [];
+        foreach ($list as $key => $item) {
+            if ($item['parent_id'] == $parentId) {
+                $children = $this->getTreeData($list, (int)$item['api_id']);
+                !empty($children) && $item['children'] = $children;
+                $data[] = $item;
+                unset($list[$key]);
+            }
+        }
+        return $data;
+    }
+
+}

+ 116 - 0
app/admin/model/store/Menu.php

@@ -0,0 +1,116 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\model\store;
+
+use app\common\model\store\Menu as MenuModel;
+use app\admin\model\store\MenuApi as MenuApiModel;
+
+/**
+ * 商家后台菜单模型
+ * Class Menu
+ * @package app\admin\model\store
+ */
+class Menu extends MenuModel
+{
+    /**
+     * 新增记录
+     * @param $data
+     * @return false|int
+     */
+    public function add(array $data)
+    {
+        return $this->save($data);
+    }
+
+    /**
+     * 更新记录
+     * @param array $data
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function edit(array $data)
+    {
+        // 判断上级角色是否为当前子级
+        if (isset($data['parent_id']) && $data['parent_id'] > 0) {
+            // 获取所有上级id集
+            $parentIds = $this->getTopMenuIds($data['parent_id']);
+            if (in_array($this['menu_id'], $parentIds)) {
+                $this->error = '上级菜单不允许设置为当前子菜单';
+                return false;
+            }
+        }
+        // 如果模块是操作, 不允许修改上级菜单id
+        if ($this['module'] == 20 && isset($data['parent_id'])) {
+            unset($data['parent_id']);
+        }
+        return $this->save($data) !== false;
+    }
+
+    /**
+     * 设置菜单的API权限
+     * @param array $data
+     * @return bool|int
+     */
+    public function setApis(array $data)
+    {
+        if (!isset($data['apiIds']) || empty($data['apiIds'])) {
+            $this->error = 'API权限不能为空';
+            return false;
+        }
+        // 根据菜单id批量更新API关联记录
+        return (new MenuApiModel)->updateByMenuId($this['menu_id'], $data['apiIds']);
+    }
+
+    /**
+     * 删除菜单
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \Exception
+     */
+    public function remove()
+    {
+        // 判断是否存在下级菜单
+        if (self::detail(['parent_id' => $this['menu_id']])) {
+            $this->error = '当前菜单下存在子菜单或操作,请先删除';
+            return false;
+        }
+        return $this->delete();
+    }
+
+    /**
+     * 获取所有上级id集
+     * @param int $menuId
+     * @param null $menuList
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getTopMenuIds(int $menuId, $menuList = null)
+    {
+        static $ids = [];
+        is_null($menuList) && $menuList = $this->getAll();
+        foreach ($menuList as $item) {
+            if ($item['menu_id'] == $menuId && $item['parent_id'] > 0) {
+                $ids[] = $item['parent_id'];
+                $this->getTopMenuIds($item['parent_id'], $menuList);
+            }
+        }
+        return $ids;
+    }
+
+}

+ 64 - 0
app/admin/model/store/MenuApi.php

@@ -0,0 +1,64 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\model\store;
+
+use app\common\model\store\MenuApi as MenuApiModel;
+
+/**
+ * 商家后台用户角色与菜单权限关系表模型
+ * Class MenuApi
+ * @package app\admin\model\store
+ */
+class MenuApi extends MenuApiModel
+{
+    /**
+     * 根据菜单id批量更新记录
+     * @param int $menuId
+     * @param array $apiIds
+     * @return int
+     */
+    public function updateByMenuId(int $menuId, array $apiIds)
+    {
+        return $this->transaction(function () use ($menuId, $apiIds) {
+            $this->removeByMenuId($menuId);
+            return $this->insertByMenuId($menuId, $apiIds);
+        });
+    }
+
+    /**
+     * 根据菜单id批量删除记录
+     * @param int $menuId
+     * @return bool
+     * @throws \Exception
+     */
+    private function removeByMenuId(int $menuId)
+    {
+        return $this->where('menu_id', '=', $menuId)->delete();
+    }
+
+    /**
+     * 根据菜单id批量新增记录
+     * @param int $menuId
+     * @param array $apiIds
+     * @return bool
+     */
+    private function insertByMenuId(int $menuId, array $apiIds)
+    {
+        $data = [];
+        foreach ($apiIds as $api) {
+            $data[] = ['menu_id' => $menuId, 'api_id' => $api];
+        }
+        return (bool)$this->addAll($data);
+    }
+
+}

+ 61 - 0
app/admin/model/store/User.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\admin\model\store;
+
+use app\common\exception\BaseException;
+use app\common\model\store\User as StoreUserModel;
+
+/**
+ * 商家用户模型
+ * Class StoreUser
+ * @package app\admin\model
+ */
+class User extends StoreUserModel
+{
+    /**
+     * 商家用户登录
+     * @param int $storeId
+     * @throws BaseException
+     * @throws \think\Exception
+     */
+    public function login(int $storeId)
+    {
+        // 获取该商户管理员用户信息
+        $userInfo = $this->getSuperStoreUser($storeId);
+        if (empty($userInfo)) {
+            throwError('超级管理员用户信息不存在');
+        }
+    }
+
+    /**
+     * 获取该商户管理员用户信息
+     * @param int $storeId
+     * @return static|null
+     */
+    private function getSuperStoreUser(int $storeId)
+    {
+        return static::detail(['store_id' => $storeId, 'is_super' => 1], ['wxapp']);
+    }
+
+    /**
+     * 删除小程序下的商家用户
+     * @param $storeId
+     * @return false|int
+     */
+    public static function setDelete(int $storeId)
+    {
+        static::update(['is_delete' => '1'], ['store_id' => $storeId]);
+        return true;
+    }
+
+}

+ 93 - 0
app/admin/service/Cache.php

@@ -0,0 +1,93 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\service;
+
+use app\common\service\BaseService;
+use think\facade\Cache as CacheDriver;
+
+/**
+ * 清理缓存
+ * Class Cache
+ */
+class Cache extends BaseService
+{
+    // 缓存驱动句柄
+    /** @var $CacheDriver \think\Cache */
+    private $cache;
+
+    /**
+     * 构造方法
+     * Cache constructor.
+     */
+    public function initialize()
+    {
+        // 实例化缓存驱动
+        $this->cache = CacheDriver::instance();
+    }
+
+    /**
+     * 删除缓存
+     * @param $data
+     * @return bool
+     */
+    public function rmCache($data)
+    {
+        // 数据缓存
+        if (in_array('data', $data['item'])) {
+            // 强制模式
+            $isForce = isset($data['isForce']) ? (bool)$data['isForce'] : false;
+            // 清除缓存
+            $isForce ? $this->cache->clear() : $this->cache->tag('cache')->clear();
+        }
+        // 临时文件
+        if (in_array('temp', $data['item'])) {
+            $paths = [
+                'temp' => web_path() . 'temp/',
+                'runtime' => runtime_root_path() . 'image/'
+            ];
+            foreach ($paths as $path) {
+                $this->deleteFolder($path);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * 递归删除指定目录下所有文件
+     * @param $path
+     * @return bool
+     */
+    private function deleteFolder($path)
+    {
+        if (!is_dir($path))
+            return false;
+        // 扫描一个文件夹内的所有文件夹和文件
+        foreach (scandir($path) as $val) {
+            // 排除目录中的.和..
+            if (!in_array($val, ['.', '..', '.gitignore'])) {
+                // 如果是目录则递归子目录,继续操作
+                if (is_dir($path . $val)) {
+                    // 子目录中操作删除文件夹和文件
+                    $this->deleteFolder($path . $val . '/');
+                    // 目录清空后删除空文件夹
+                    rmdir($path . $val . '/');
+                } else {
+                    // 如果是文件直接删除
+                    unlink($path . $val);
+                }
+            }
+        }
+        return true;
+    }
+
+}

+ 120 - 0
app/admin/service/admin/User.php

@@ -0,0 +1,120 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\admin\service\admin;
+
+use think\facade\Cache;
+use app\common\service\BaseService;
+
+/**
+ * 超管用户服务类
+ * Class User
+ */
+class User extends BaseService
+{
+    // 用于生成token的自定义盐
+    const TOKEN_SALT = '_admin_user_salt_';
+
+    /**
+     * 获取登录的信息
+     * @return mixed
+     */
+    public static function getLoginInfo()
+    {
+        if (($token = self::getToken()) !== false) {
+            return Cache::get($token);
+        }
+        return false;
+    }
+
+    /**
+     * 记录登录信息
+     * @param array $userInfo
+     * @return string
+     */
+    public static function login(array $userInfo)
+    {
+        // 生成token
+        $token = self::makeToken((int)$userInfo['admin_user_id']);
+        // 记录缓存, 7天
+        Cache::set($token, [
+            'user' => [
+                'admin_user_id' => (int)$userInfo['admin_user_id'],
+                'user_name' => $userInfo['user_name'],
+            ],
+            'is_login' => true,
+        ], 86400 * 7);
+        return $token;
+    }
+
+    /**
+     * 清空登录状态
+     * @return bool
+     */
+    public static function logout()
+    {
+        Cache::delete(self::getToken());
+        return true;
+    }
+
+    /**
+     * 更新登录信息
+     * @param array $userInfo
+     * @return mixed
+     */
+    public static function update(array $userInfo)
+    {
+        return Cache::set(self::getToken(), [
+            'user' => [
+                'admin_user_id' => $userInfo['admin_user_id'],
+                'user_name' => $userInfo['user_name'],
+            ],
+            'is_login' => true,
+        ], 86400 * 7);
+    }
+
+    /**
+     * 生成用户认证的token
+     * @param int $userId
+     * @return string
+     */
+    private static function makeToken(int $userId)
+    {
+        // 生成一个不会重复的随机字符串
+        $guid = get_guid_v4();
+        // 当前时间戳 (精确到毫秒)
+        $timeStamp = microtime(true);
+        // 自定义一个盐
+        $salt = self::TOKEN_SALT;
+        return md5("{$timeStamp}_{$userId}_{$guid}_{$salt}");
+    }
+
+    /**
+     * 获取用户认证Token
+     * @return bool|string
+     */
+    private static function getToken()
+    {
+        // 获取请求中的token
+        $token = request()->header('Access-Token');
+        // 调试模式下可通过param
+        if (empty($token) && is_debug()) {
+            $token = request()->param('Access-Token');
+        }
+        // 不存在token报错
+        if (empty($token)) {
+            return false;
+        }
+        return $token;
+    }
+
+}

+ 25 - 0
app/admin/service/store/User.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\admin\service\store;
+
+use app\common\service\store\User as Basics;
+use app\admin\model\store\User as StoreUserModel;
+
+/**
+ * 超管用户服务类
+ * Class User
+ */
+class User extends Basics
+{
+
+}

+ 29 - 0
app/api/common.php

@@ -0,0 +1,29 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+// 应用公共函数库文件
+
+/**
+ * 获取当前访问的渠道(微信小程序、H5、APP等)
+ * @return string|null
+ */
+function getPlatform()
+{
+    static $value = null;
+    // 从header中获取 channel
+    empty($value) && $value = request()->header('platform');
+    // 调试模式下可通过param中获取
+    if (is_debug() && empty($value)) {
+        $value = request()->param('platform');
+    }
+    return $value;
+}

+ 31 - 0
app/api/config/captcha.php

@@ -0,0 +1,31 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+return [
+    // 验证码图片宽度
+    'imageW' => 0,
+    // 验证码图片高度
+    'imageH' => 0,
+    // 验证码位数
+    'length' => 4,
+    // 是否画混淆曲线
+    'useCurve' => false,
+    // 是否添加杂点
+    'useNoise' => true,
+    // 验证码字体大小(px)
+    'fontSize' => 26,
+    // 验证码字符集合
+    // 复杂版:2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY
+    'codeSet' => '23456ACEFHJKLMNPRTUVWXY',
+    // 验证码可重复验证的次数
+    'checkTimes' => 5,
+];

+ 124 - 0
app/api/controller/Address.php

@@ -0,0 +1,124 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\service\User as UserService;
+use app\api\model\UserAddress as UserAddressModel;
+use app\common\exception\BaseException;
+
+/**
+ * 收货地址管理
+ * Class Address
+ * @package app\api\controller
+ */
+class Address extends Controller
+{
+    /**
+     * 收货地址列表
+     * @return array|\think\response\Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function list()
+    {
+        // 获取收货地址列表
+        $model = new UserAddressModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 获取当前用户默认收货地址
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function defaultId()
+    {
+        $useInfo = UserService::getCurrentLoginUser(true);
+        return $this->renderSuccess(['defaultId' => $useInfo['address_id']]);
+    }
+
+    /**
+     * 收货地址详情
+     * @param int $addressId 地址ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function detail(int $addressId)
+    {
+        $detail = UserAddressModel::detail($addressId);
+        return $this->renderSuccess(compact('detail'));
+    }
+
+    /**
+     * 添加收货地址
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function add()
+    {
+        $model = new UserAddressModel;
+        if ($model->add($this->postForm())) {
+            return $this->renderSuccess([], '添加成功');
+        }
+        return $this->renderError($model->getError() ?: '添加失败');
+    }
+
+    /**
+     * 编辑收货地址
+     * @param int $addressId 地址ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function edit(int $addressId)
+    {
+        $model = UserAddressModel::detail($addressId);
+        if ($model->edit($this->postForm())) {
+            return $this->renderSuccess([], '更新成功');
+        }
+        return $this->renderError($model->getError() ?: '更新失败');
+    }
+
+    /**
+     * 设为默认地址
+     * @param int $addressId 地址ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function setDefault(int $addressId)
+    {
+        $model = UserAddressModel::detail($addressId);
+        if ($model->setDefault((int)$model['address_id'])) {
+            return $this->renderSuccess([], '设置成功');
+        }
+        return $this->renderError($model->getError() ?: '设置失败');
+    }
+
+    /**
+     * 删除收货地址
+     * @param int $addressId 地址ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function remove(int $addressId)
+    {
+        $model = UserAddressModel::detail($addressId);
+        if ($model->remove()) {
+            return $this->renderSuccess([], '删除成功');
+        }
+        return $this->renderError($model->getError() ?: '删除失败');
+    }
+
+}

+ 48 - 0
app/api/controller/Article.php

@@ -0,0 +1,48 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\Article as ArticleModel;
+
+/**
+ * 文章控制器
+ * Class Article
+ * @package app\api\controller
+ */
+class Article extends Controller
+{
+    /**
+     * 文章列表
+     * @param int $categoryId
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    public function list(int $categoryId = 0)
+    {
+        $model = new ArticleModel;
+        $list = $model->getList($categoryId);
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 文章详情
+     * @param int $articleId
+     * @return array|\think\response\Json
+     * @throws \app\common\exception\BaseException
+     */
+    public function detail(int $articleId)
+    {
+        $detail = ArticleModel::getDetail($articleId);
+        return $this->renderSuccess(compact('detail'));
+    }
+}

+ 49 - 0
app/api/controller/Captcha.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\api\controller;
+
+use think\response\Json;
+use app\api\service\passport\{Captcha as CaptchaService, SmsCaptcha as SmsCaptchaService};
+use cores\exception\BaseException;
+
+/**
+ * 验证码管理
+ * Class Cart
+ * @package app\api\controller
+ */
+class Captcha extends Controller
+{
+    /**
+     * 图形验证码
+     * @return Json
+     */
+    public function image(): Json
+    {
+        $CaptchaService = new CaptchaService;
+        return $this->renderSuccess($CaptchaService->create());
+    }
+
+    /**
+     * 发送短信验证码
+     * @return Json
+     * @throws BaseException
+     */
+    public function sendSmsCaptcha(): Json
+    {
+        $SmsCaptchaService = new SmsCaptchaService;
+        if ($SmsCaptchaService->handle($this->postForm())) {
+            return $this->renderSuccess('发送成功,请注意查收');
+        }
+        return $this->renderError($SmsCaptchaService->getError() ?: '短信发送失败');
+    }
+}

+ 118 - 0
app/api/controller/Cart.php

@@ -0,0 +1,118 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\Cart as CartModel;
+use app\api\service\Cart as CartService;
+use app\common\exception\BaseException;
+use think\response\Json;
+
+/**
+ * 购物车管理
+ * Class Cart
+ * @package app\api\controller
+ */
+class Cart extends Controller
+{
+    /**
+     * 购物车商品列表
+     * @return Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws \cores\exception\BaseException
+     */
+    public function list(): Json
+    {
+        // 购物车商品列表
+        $service = new CartService;
+        $list = $service->getList();
+        // 购物车商品总数量
+        $cartTotal = (new CartModel)->getCartTotal();
+        return $this->renderSuccess(compact('cartTotal', 'list'));
+    }
+
+    /**
+     * 购物车商品总数量
+     * @return Json
+     * @throws BaseException
+     */
+    public function total(): Json
+    {
+        $model = new CartModel;
+        $cartTotal = $model->getCartTotal();
+        return $this->renderSuccess(compact('cartTotal'));
+    }
+
+    /**
+     * 加入购物车
+     * @param int $goodsId 商品ID
+     * @param string $goodsSkuId 商品sku索引
+     * @param int $goodsNum 商品数量
+     * @return Json
+     * @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): Json
+    {
+        $model = new CartModel;
+        if (!$model->add($goodsId, $goodsSkuId, $goodsNum)) {
+            return $this->renderError($model->getError() ?: '加入购物车失败');
+        }
+        // 购物车商品总数量
+        $cartTotal = $model->getCartTotal();
+        return $this->renderSuccess(compact('cartTotal'), '加入购物车成功');
+    }
+
+    /**
+     * 更新购物车商品数量
+     * @param int $goodsId 商品ID
+     * @param string $goodsSkuId 商品sku索引
+     * @param int $goodsNum 商品数量
+     * @return Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function update(int $goodsId, string $goodsSkuId, int $goodsNum): Json
+    {
+        $model = new CartModel;
+        if (!$model->sUpdate($goodsId, $goodsSkuId, $goodsNum)) {
+            return $this->renderError($model->getError() ?: '更新失败');
+        }
+        // 购物车商品总数量
+        $cartTotal = $model->getCartTotal();
+        return $this->renderSuccess(compact('cartTotal'), '更新成功');
+    }
+
+    /**
+     * 删除购物车中指定记录
+     * @param array $cartIds 购物车ID集, 如果为空删除所有
+     * @return Json
+     * @throws BaseException
+     */
+    public function clear(array $cartIds = []): Json
+    {
+        $model = new CartModel;
+        if (!$model->clear($cartIds)) {
+            return $this->renderError($model->getError() ?: '操作失败');
+        }
+        // 购物车商品总数量
+        $cartTotal = $model->getCartTotal();
+        return $this->renderSuccess(compact('cartTotal'), '操作成功');
+    }
+}

+ 38 - 0
app/api/controller/Category.php

@@ -0,0 +1,38 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\Category as CategoryModel;
+
+/**
+ * 商品分类控制器
+ * Class Category
+ * @package app\api\controller
+ */
+class Category extends Controller
+{
+    /**
+     * 分类列表
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function list()
+    {
+        $model = new CategoryModel;
+        $list = $model->getList($this->request->param());
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 203 - 0
app/api/controller/Checkout.php

@@ -0,0 +1,203 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\api\model\Order as OrderModel;
+use app\api\service\User as UserService;
+use app\api\service\Cart as CartService;
+use app\api\service\order\Checkout as CheckoutService;
+use app\api\validate\order\Checkout as CheckoutValidate;
+use cores\exception\BaseException;
+use think\response\Json;
+
+/**
+ * 订单结算控制器
+ * Class Checkout
+ * @package app\api\controller
+ */
+class Checkout extends Controller
+{
+    // 结算台验证器
+    /* @var CheckoutValidate $validate */
+    private $validate;
+
+    /**
+     * 结算台订单信息
+     * @param string $mode
+     * @return Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function order(string $mode = 'buyNow'): Json
+    {
+        if ($mode === 'buyNow') {
+            return $this->buyNow();
+        } elseif ($mode === 'cart') {
+            return $this->cart();
+        }
+        return $this->renderError('结算模式不合法');
+    }
+
+    /**
+     * 订单提交
+     * @param string $mode
+     * @return Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function submit(string $mode = 'buyNow'): Json
+    {
+        return $this->order($mode);
+    }
+
+    /**
+     * 订单确认-立即购买
+     * @return Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function buyNow(): Json
+    {
+        // 实例化结算台服务
+        $Checkout = new CheckoutService;
+        // 订单结算api参数
+        $params = $Checkout->setParam($this->getParam([
+            'goodsId' => 0,
+            'goodsSkuId' => '',
+            'goodsNum' => 0,
+        ]));
+        // 表单验证
+        if (!$this->getValidate()->scene('buyNow')->check($params)) {
+            return $this->renderError($this->getValidate()->getError(), ['isCreated' => false]);
+        }
+        // 立即购买:获取订单商品列表
+        $model = new OrderModel;
+        $goodsList = $model->getOrderGoodsListByNow(
+            (int)$params['goodsId'],
+            (string)$params['goodsSkuId'],
+            (int)$params['goodsNum']
+        );
+        // 获取订单确认信息
+        $orderInfo = $Checkout->onCheckout($goodsList);
+        if ($this->request->isGet()) {
+            return $this->renderSuccess([
+                'order' => $orderInfo,
+                'personal' => $Checkout->getPersonal(),
+                'setting' => $Checkout->getSetting(),
+            ]);
+        }
+        // 验证订单是否存在错误
+        if ($Checkout->hasError()) {
+            return $this->renderError($Checkout->getError(), ['is_created' => false]);
+        }
+        // 创建订单
+        if (!$Checkout->createOrder($orderInfo)) {
+            return $this->renderError($Checkout->getError() ?: '订单创建失败', ['is_created' => false]);
+        }
+        // 构建微信支付请求
+        $payment = $model->onOrderPayment($Checkout->model, $params['payType']);
+        // 返回结算信息
+        return $this->renderSuccess([
+            'orderId' => $Checkout->model['order_id'],   // 订单id
+            'payType' => $params['payType'],            // 支付方式
+            'payment' => $payment                         // 微信支付参数
+        ]);
+    }
+
+    /**
+     * 订单确认-购物车结算
+     * @return Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function cart(): Json
+    {
+        // 实例化结算台服务
+        $Checkout = new CheckoutService;
+        // 订单结算api参数
+        $params = $Checkout->setParam($this->getParam());
+        // 购物车ID集
+        $cartIds = $this->getCartIds();
+        // 商品结算信息
+        $CartModel = new CartService;
+        // 购物车商品列表
+        $goodsList = $CartModel->getOrderGoodsList($cartIds);
+        // 获取订单结算信息
+        $orderInfo = $Checkout->onCheckout($goodsList);
+        if ($this->request->isGet()) {
+            return $this->renderSuccess([
+                'order' => $orderInfo,
+                'personal' => $Checkout->getPersonal(),
+                'setting' => $Checkout->getSetting(),
+            ]);
+        }
+        // 验证订单是否存在错误
+        if ($Checkout->hasError()) {
+            return $this->renderError($Checkout->getError(), ['is_created' => false]);
+        }
+        // 创建订单
+        if (!$Checkout->createOrder($orderInfo)) {
+            return $this->renderError($Checkout->getError() ?: '订单创建失败');
+        }
+        // 移出购物车中已下单的商品
+        $CartModel->clear($cartIds);
+        // 构建微信支付请求
+        $payment = $Checkout->onOrderPayment();
+        // 返回状态
+        return $this->renderSuccess([
+            'orderId' => $Checkout->model['order_id'],   // 订单id
+            'payType' => $params['payType'],  // 支付方式
+            'payment' => $payment               // 微信支付参数
+        ]);
+    }
+
+    /**
+     * 获取结算台验证器
+     * @return CheckoutValidate
+     */
+    private function getValidate(): CheckoutValidate
+    {
+        if (!$this->validate) {
+            $this->validate = new CheckoutValidate;
+        }
+        return $this->validate;
+    }
+
+    /**
+     * 获取购物车ID集
+     * @return false|string[]
+     */
+    private function getCartIds()
+    {
+        $cartIds = $this->request->param('cartIds');
+        return explode(',', $cartIds);
+    }
+
+    /**
+     * 订单结算提交的参数
+     * @param array $define
+     * @return array
+     */
+    private function getParam(array $define = []): array
+    {
+        return array_merge($define, $this->request->param());
+    }
+}

+ 76 - 0
app/api/controller/Comment.php

@@ -0,0 +1,76 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\Comment as CommentModel;
+
+/**
+ * 商品评价控制器
+ * Class Comment
+ * @package app\api\controller
+ */
+class Comment extends Controller
+{
+    /**
+     * 商品评价列表
+     * @param int $goodsId 商品ID
+     * @param int|null $scoreType 评价评分
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function list(int $goodsId, int $scoreType = null)
+    {
+        // 评价列表
+        $model = new CommentModel;
+        $list = $model->getCommentList($goodsId, $scoreType);
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 商品评分总数
+     * @param int $goodsId
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function total(int $goodsId)
+    {
+        // 指定评分总数
+        $model = new CommentModel;
+        $total = $model->getTotal($goodsId);
+        return $this->renderSuccess(compact('total'));
+    }
+
+    /**
+     * 商品评价列表 (限制数量, 用于商品详情页展示)
+     * @param int $goodsId
+     * @param int $limit
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function listRows(int $goodsId, int $limit = 5)
+    {
+        // 评价列表
+        $model = new CommentModel;
+        $list = $model->listRows($goodsId, $limit);
+        // 评价总数量
+        $total = $model->rowsTotal($goodsId);
+        return $this->renderSuccess(compact('list', 'total'));
+    }
+
+}

+ 160 - 0
app/api/controller/Controller.php

@@ -0,0 +1,160 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use think\response\Json;
+use cores\BaseController;
+use app\api\model\User as UserModel;
+use app\api\model\Store as StoreModel;
+use app\api\service\User as UserService;
+use cores\exception\BaseException;
+
+/**
+ * API控制器基类
+ * Class Controller
+ * @package app\store\controller
+ */
+class Controller extends BaseController
+{
+    // 当前商城ID
+    protected $storeId;
+
+    /**
+     * API基类初始化
+     * @throws BaseException
+     */
+    public function initialize()
+    {
+        // 当前商城id
+        $this->storeId = $this->getStoreId();
+        // 验证当前商城状态
+        $this->checkStore();
+        // 验证当前客户端状态
+        $this->checkClient();
+    }
+
+    /**
+     * 获取当前商城ID
+     * @return int|null
+     * @throws BaseException
+     */
+    protected function getStoreId(): ?int
+    {
+        $storeId = getStoreId();    // app/api/common.php
+        empty($storeId) && throwError('缺少必要的参数:storeId');
+        return $storeId;
+    }
+
+    /**
+     * 验证当前商城状态
+     * @return void
+     * @throws BaseException
+     */
+    private function checkStore(): void
+    {
+        // 获取当前商城信息
+        $store = StoreModel::detail($this->storeId);
+        if (empty($store)) {
+            throwError('当前商城信息不存在');
+        }
+        if ($store['is_recycle'] || $store['is_delete']) {
+            throwError('当前商城已删除');
+        }
+    }
+
+    /**
+     * 验证当前客户端是否允许访问
+     * @throws BaseException
+     */
+    private function checkClient()
+    {
+        $client = getPlatform();
+        $settingClass = [
+            'H5' => [\app\api\model\h5\Setting::class, 'checkStatus', 'H5端']
+        ];
+        if (!isset($settingClass[$client])) {
+            return;
+        }
+        $status = call_user_func([$settingClass[$client][0], $settingClass[$client][1]]);
+        $status === false && throwError('很抱歉,当前' . $settingClass[$client][2] . '端暂不支持访问');
+    }
+
+    /**
+     * 获取当前用户信息
+     * @param bool $isForce 强制验证登录
+     * @return UserModel|bool|null
+     * @throws BaseException
+     */
+    protected function getLoginUser(bool $isForce = true)
+    {
+        return UserService::getCurrentLoginUser($isForce);
+    }
+
+    /**
+     * 返回封装后的 API 数据到客户端
+     * @param int|null $status 状态码
+     * @param string $message
+     * @param array $data
+     * @return Json
+     */
+    protected function renderJson(int $status = null, string $message = '', array $data = []): Json
+    {
+        return json(compact('status', 'message', 'data'));
+    }
+
+    /**
+     * 返回操作成功json
+     * @param array|string $data
+     * @param string $message
+     * @return Json
+     */
+    protected function renderSuccess($data = [], string $message = 'success'): Json
+    {
+        if (is_string($data)) {
+            $message = $data;
+            $data = [];
+        }
+        return $this->renderJson(config('status.success'), $message, $data);
+    }
+
+    /**
+     * 返回操作失败json
+     * @param string $message
+     * @param array $data
+     * @return Json
+     */
+    protected function renderError(string $message = 'error', array $data = []): Json
+    {
+        return $this->renderJson(config('status.error'), $message, $data);
+    }
+
+    /**
+     * 获取post数据 (数组)
+     * @param $key
+     * @return mixed
+     */
+    protected function postData($key = null)
+    {
+        return $this->request->post(is_null($key) ? '' : $key . '/a');
+    }
+
+    /**
+     * 获取post数据 (数组)
+     * @param string $key
+     * @return mixed
+     */
+    protected function postForm(string $key = 'form')
+    {
+        return $this->postData($key);
+    }
+}

+ 39 - 0
app/api/controller/Coupon.php

@@ -0,0 +1,39 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use think\response\Json;
+use app\api\model\Coupon as CouponModel;
+
+/**
+ * 优惠券中心
+ * Class Coupon
+ * @package app\api\controller
+ */
+class Coupon extends Controller
+{
+    /**
+     * 优惠券列表
+     * @return Json
+     * @throws \cores\exception\BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function list(): Json
+    {
+        $model = new CouponModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+}

+ 37 - 0
app/api/controller/Express.php

@@ -0,0 +1,37 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\Express as ExpressModel;
+
+/**
+ * 物流公司管理
+ * Class Express
+ * @package app\api\controller
+ */
+class Express extends Controller
+{
+    /**
+     * 物流公司列表
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function list()
+    {
+        $model = new ExpressModel;
+        $list = $model->getAll();
+        return $this->renderSuccess(compact('list'));
+    }
+}

+ 84 - 0
app/api/controller/Goods.php

@@ -0,0 +1,84 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\api\model\Goods as GoodsModel;
+
+/**
+ * 商品控制器
+ * Class Goods
+ * @package app\api\controller
+ */
+class Goods extends Controller
+{
+    /**
+     * 商品列表
+     * @return \think\response\Json
+     * @throws \think\db\exception\DbException
+     */
+    public function list(): \think\response\Json
+    {
+        // 获取列表数据
+        $model = new GoodsModel;
+        $list = $model->getList($this->request->param());
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 获取商品详情
+     * @param int $goodsId
+     * @return \think\response\Json
+     * @throws \cores\exception\BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function detail(int $goodsId): \think\response\Json
+    {
+        // 商品详情
+        $model = new GoodsModel;
+        $goodsInfo = $model->getDetails($goodsId);
+        return $this->renderSuccess(['detail' => $goodsInfo]);
+    }
+
+    /**
+     * 获取商品详情(基础信息)
+     * @param int $goodsId
+     * @param bool $verifyStatus
+     * @return \think\response\Json
+     * @throws \cores\exception\BaseException
+     */
+    public function basic(int $goodsId, bool $verifyStatus = true): \think\response\Json
+    {
+        // 获取商品详情
+        $model = new GoodsModel;
+        $detail = $model->getBasic($goodsId, $verifyStatus);
+        return $this->renderSuccess(compact('detail'));
+    }
+
+    /**
+     * 获取商品规格数据
+     * @param int $goodsId 商品ID
+     * @return \think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function specData(int $goodsId): \think\response\Json
+    {
+        // 获取商品详情
+        $model = new GoodsModel;
+        $specData = $model->getSpecData($goodsId);
+        return $this->renderSuccess(compact('specData'));
+    }
+}

+ 36 - 0
app/api/controller/Help.php

@@ -0,0 +1,36 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\Help as HelpModel;
+
+/**
+ * 帮助中心
+ * Class help
+ * @package app\store\controller\wxapp
+ */
+class Help extends Controller
+{
+    /**
+     * 获取列表记录
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    public function list()
+    {
+        $model = new HelpModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 16 - 0
app/api/controller/Index.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace app\api\controller;
+
+/**
+ * 默认控制器
+ * Class Index
+ * @package app\api\controller
+ */
+class Index
+{
+    public function index()
+    {
+        echo '当前访问的index.php,请将index.html设为默认站点入口';
+    }
+}

+ 53 - 0
app/api/controller/MyCoupon.php

@@ -0,0 +1,53 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\UserCoupon as UserCouponModel;
+use app\api\service\User as UserService;
+
+/**
+ * 用户优惠券
+ * Class Coupon
+ * @package app\api\controller
+ */
+class MyCoupon extends Controller
+{
+    /**
+     * 用户优惠券列表
+     * @return \think\response\Json
+     * @throws \cores\exception\BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function list(): \think\response\Json
+    {
+        $userId = UserService::getCurrentLoginUserId();
+        $model = new UserCouponModel;
+        $list = $model->getList($userId, $this->request->param());
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 领取优惠券
+     * @param int $couponId
+     * @return \think\response\Json
+     * @throws \cores\exception\BaseException
+     */
+    public function receive(int $couponId): \think\response\Json
+    {
+        $model = new UserCouponModel;
+        if ($model->receive($couponId)) {
+            return $this->renderSuccess([], '领取成功');
+        }
+        return $this->renderError($model->getError() ?: '领取失败');
+    }
+}

+ 37 - 0
app/api/controller/Notify.php

@@ -0,0 +1,37 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\common\library\wechat\WxPay;
+use app\common\exception\BaseException;
+
+/**
+ * 支付成功异步通知接口
+ * Class Notify
+ * @package app\api\controller
+ */
+class Notify
+{
+    /**
+     * 支付成功异步通知(微信小程序-微信支付)
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function wxpay()
+    {
+        // 微信支付组件:验证异步通知
+        $WxPay = new WxPay();
+        $WxPay->notify();
+    }
+}

+ 159 - 0
app/api/controller/Order.php

@@ -0,0 +1,159 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\api\model\Order as OrderModel;
+use app\api\model\Setting as SettingModel;
+use app\store\model\Express as ExpressModel;
+use app\common\enum\order\PayType as OrderPayTypeEnum;
+use cores\exception\BaseException;
+use think\response\Json;
+
+/**
+ * 我的订单控制器
+ * Class Order
+ * @package app\api\controller
+ */
+class Order extends Controller
+{
+    /**
+     * 获取当前用户待处理的订单数量
+     * @return Json
+     * @throws BaseException
+     */
+    public function todoCounts(): Json
+    {
+        $model = new OrderModel;
+        $counts = $model->getTodoCounts();
+        return $this->renderSuccess(compact('counts'));
+    }
+
+    /**
+     * 我的订单列表
+     * @param string $dataType 订单类型 (all全部 payment待付款 received待发货 deliver待收货 comment待评价)
+     * @return Json
+     * @throws \think\db\exception\DbException
+     * @throws BaseException
+     */
+    public function list(string $dataType): Json
+    {
+        $model = new OrderModel;
+        $list = $model->getList($dataType);
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 订单详情信息
+     * @param int $orderId 订单ID
+     * @return Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws BaseException
+     */
+    public function detail(int $orderId): Json
+    {
+        // 订单详情
+        $model = OrderModel::getUserOrderDetail($orderId);
+        return $this->renderSuccess([
+            'order' => $model,  // 订单详情
+            'setting' => [
+                // 积分名称
+                'points_name' => SettingModel::getPointsName(),
+            ],
+        ]);
+    }
+
+    /**
+     * 获取物流信息
+     * @param int $orderId 订单ID
+     * @return Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws BaseException
+     */
+    public function express(int $orderId): Json
+    {
+        // 订单信息
+        $order = OrderModel::getDetail($orderId);
+        if (!$order['express_no']) {
+            return $this->renderError('没有物流信息');
+        }
+        // 获取物流信息
+        $model = ExpressModel::detail($order['express_id']);
+        $express = $model->dynamic($model['express_name'], $model['kuaidi100_code'], $order['express_no']);
+        if ($express === false) {
+            return $this->renderError($model->getError());
+        }
+        return $this->renderSuccess(compact('express'));
+    }
+
+    /**
+     * 取消订单
+     * @param int $orderId
+     * @return Json
+     * @throws BaseException
+     */
+    public function cancel(int $orderId): Json
+    {
+        $model = OrderModel::getDetail($orderId);
+        if ($model->cancel()) {
+            return $this->renderSuccess('订单取消成功');
+        }
+        return $this->renderError($model->getError() ?: '订单取消失败');
+    }
+
+    /**
+     * 确认收货
+     * @param int $orderId
+     * @return Json
+     * @throws BaseException
+     */
+    public function receipt(int $orderId): Json
+    {
+        $model = OrderModel::getDetail($orderId);
+        if ($model->receipt()) {
+            return $this->renderSuccess('确认收货成功');
+        }
+        return $this->renderError($model->getError());
+    }
+
+    /**
+     * 立即支付
+     * @param int $orderId 订单ID
+     * @param int $payType 支付方式
+     * @return Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws BaseException
+     */
+    public function pay(int $orderId, int $payType = OrderPayTypeEnum::WECHAT): Json
+    {
+        // 获取订单详情
+        $model = OrderModel::getUserOrderDetail($orderId);
+        // 订单支付事件
+        if (!$model->onPay($payType)) {
+            return $this->renderError($model->getError() ?: '订单支付失败');
+        }
+        // 构建微信支付请求
+        $payment = $model->onOrderPayment($model, $payType);
+        // 支付状态提醒
+        return $this->renderSuccess([
+            'order_id' => $model['order_id'],   // 订单id
+            'pay_type' => $payType,             // 支付方式
+            'payment' => $payment               // 微信支付参数
+        ]);
+    }
+}

+ 40 - 0
app/api/controller/Page.php

@@ -0,0 +1,40 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\Page as PageModel;
+
+/**
+ * 页面控制器
+ * Class Index
+ * @package app\api\controller
+ */
+class Page extends Controller
+{
+    /**
+     * 页面数据
+     * @param int|null $pageId 页面ID, 不传的话默认为首页
+     * @return array|\think\response\Json
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function detail(int $pageId = null)
+    {
+        // 页面数据
+        $model = new PageModel;
+        $pageData = $model->getPageData($pageId);
+        return $this->renderSuccess(compact('pageData'));
+    }
+}

+ 97 - 0
app/api/controller/Passport.php

@@ -0,0 +1,97 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\api\service\passport\Login as LoginService;
+
+/**
+ * 用户认证模块
+ * Class Passport
+ * @package app\api\controller
+ */
+class Passport extends Controller
+{
+    /**
+     * 登录接口 (需提交手机号、短信验证码、第三方用户信息)
+     * @return array|\think\response\Json
+     * @throws \app\common\exception\BaseException
+     * @throws \think\Exception
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function login()
+    {
+        // 执行登录
+        $LoginService = new LoginService;
+        if (!$LoginService->login($this->postForm())) {
+            return $this->renderError($LoginService->getError());
+        }
+        // 用户信息
+        $userInfo = $LoginService->getUserInfo();
+        return $this->renderSuccess([
+            'userId' => (int)$userInfo['user_id'],
+            'token' => $LoginService->getToken((int)$userInfo['user_id'])
+        ], '登录成功');
+    }
+
+    /**
+     * 微信小程序快捷登录 (需提交wx.login接口返回的code、微信用户公开信息)
+     * 业务流程:判断openid是否存在 -> 存在:  更新用户登录信息 -> 返回userId和token
+     *                          -> 不存在: 返回false, 跳转到注册页面
+     * @return array|\think\response\Json
+     * @throws \app\common\exception\BaseException
+     * @throws \think\Exception
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function loginMpWx()
+    {
+        // 微信小程序一键登录
+        $LoginService = new LoginService;
+        if (!$LoginService->loginMpWx($this->postForm())) {
+            return $this->renderError($LoginService->getError());
+        }
+        // 获取登录成功后的用户信息
+        $userInfo = $LoginService->getUserInfo();
+        return $this->renderSuccess([
+            'userId' => (int)$userInfo['user_id'],
+            'token' => $LoginService->getToken((int)$userInfo['user_id'])
+        ], '登录成功');
+    }
+
+    /**
+     * 快捷登录: 微信小程序授权手机号登录
+     * @return array|\think\response\Json
+     * @throws \app\common\exception\BaseException
+     * @throws \think\Exception
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function loginMpWxMobile()
+    {
+        // 微信小程序一键登录
+        $LoginService = new LoginService;
+        if (!$LoginService->loginMpWxMobile($this->postForm())) {
+            return $this->renderError($LoginService->getError());
+        }
+        // 获取登录成功后的用户信息
+        $userInfo = $LoginService->getUserInfo();
+        return $this->renderSuccess([
+            'userId' => (int)$userInfo['user_id'],
+            'token' => $LoginService->getToken((int)$userInfo['user_id'])
+        ], '登录成功');
+    }
+}

+ 58 - 0
app/api/controller/Recharge.php

@@ -0,0 +1,58 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\api\model\recharge\Order as OrderModel;
+use app\api\service\Payment as PaymentService;
+use app\common\enum\OrderType as OrderTypeEnum;
+use app\common\exception\BaseException;
+
+/**
+ * 用户充值管理
+ * Class Recharge
+ * @package app\api\controller
+ */
+class Recharge extends Controller
+{
+    /**
+     * 确认充值
+     * @param int|null $planId 方案ID
+     * @param float|string|null $customMoney 自定义金额
+     * @return array|\think\response\Json
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function submit(int $planId = null, $customMoney = null)
+    {
+        if (getPlatform() !== 'MP-WEIXIN') {
+            return $this->renderError('很抱歉,余额充值暂时仅支持微信小程序端');
+        }
+        // 生成充值订单
+        $model = new OrderModel;
+        if (!$model->createOrder($planId, (float)$customMoney)) {
+            return $this->renderError($model->getError() ?: '充值失败');
+        }
+        // 构建微信支付
+        $payment = PaymentService::wechat(
+            $model['order_id'],
+            $model['order_no'],
+            $model['pay_price'],
+            OrderTypeEnum::RECHARGE
+        );
+        // 充值状态提醒
+        $message = ['success' => '充值成功', 'error' => '订单未支付'];
+        return $this->renderSuccess(compact('payment', 'message'));
+    }
+}

+ 96 - 0
app/api/controller/Refund.php

@@ -0,0 +1,96 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\api\model\OrderRefund as OrderRefundModel;
+use app\common\exception\BaseException;
+
+/**
+ * 订单售后服务
+ * Class service
+ * @package app\api\controller\user\order
+ */
+class Refund extends Controller
+{
+    /**
+     * 售后单列表
+     * @param int $state
+     * @return array|\think\response\Json
+     * @throws BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function list(int $state = -1)
+    {
+        $model = new OrderRefundModel;
+        $list = $model->getList($state);
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 订单商品详情
+     * @param int $orderGoodsId 订单商品ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function goods(int $orderGoodsId)
+    {
+        $model = new OrderRefundModel;
+        $goods = $model->getRefundGoods($orderGoodsId);
+        return $this->renderSuccess(compact('goods'));
+    }
+
+    /**
+     * 申请售后
+     * @param int $orderGoodsId 订单商品ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function apply(int $orderGoodsId)
+    {
+        // 新增售后单记录
+        $model = new OrderRefundModel;
+        if ($model->apply($orderGoodsId, $this->postForm())) {
+            return $this->renderSuccess([], '提交成功');
+        }
+        return $this->renderError($model->getError() ?: '提交失败');
+    }
+
+    /**
+     * 售后单详情
+     * @param int $orderRefundId 售后单ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function detail(int $orderRefundId)
+    {
+        $detail = OrderRefundModel::getDetail($orderRefundId, true);
+        return $this->renderSuccess(compact('detail'));
+    }
+
+    /**
+     * 用户发货
+     * @param int $orderRefundId 售后单ID
+     * @return array|\think\response\Json
+     * @throws BaseException
+     */
+    public function delivery(int $orderRefundId)
+    {
+        // 售后单详情
+        $model = OrderRefundModel::getDetail($orderRefundId, false);
+        if ($model->delivery($this->postForm())) {
+            return $this->renderSuccess([], '操作成功');
+        }
+        return $this->renderError($model->getError() ?: '提交失败');
+    }
+
+}

+ 50 - 0
app/api/controller/Region.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\api\controller;
+
+use app\api\model\Region as RegionModel;
+
+/**
+ * 地区管理
+ * Class Region
+ * @package app\api\controller
+ */
+class Region extends Controller
+{
+    /**
+     * 获取所有地区
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function all()
+    {
+        $list = RegionModel::getCacheAll();
+        return $this->renderSuccess(compact('list'));
+    }
+
+    /**
+     * 获取所有地区(树状)
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function tree()
+    {
+        $list = RegionModel::getCacheTree();
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 37 - 0
app/api/controller/Setting.php

@@ -0,0 +1,37 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\service\Setting as SettingService;
+
+/**
+ * 商城设置控制器
+ * Class Setting
+ * @package app\store\controller
+ */
+class Setting extends Controller
+{
+    /**
+     * 商城公共设置(仅展示可公开的信息)
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function data()
+    {
+        $service = new SettingService;
+        $setting = $service->getPublic();
+        return $this->renderSuccess(compact('setting'));
+    }
+}

+ 77 - 0
app/api/controller/Upload.php

@@ -0,0 +1,77 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\controller;
+
+use app\api\model\Setting as SettingModel;
+use app\api\model\UploadFile as UploadFileModel;
+use app\api\service\User as UserService;
+use app\common\enum\Setting as SettingEnum;
+use app\common\enum\file\FileType as FileTypeEnum;
+use app\common\library\storage\Driver as StorageDriver;
+use app\common\exception\BaseException;
+
+/**
+ * 文件库管理
+ * Class Upload
+ * @package app\api\controller
+ */
+class Upload extends Controller
+{
+    // 当前商城的上传设置
+    private $config;
+
+    /**
+     * 构造方法
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function initialize()
+    {
+        parent::initialize();
+        // 验证登录
+        UserService::isLogin(true);
+        // 存储配置信息
+        $this->config = SettingModel::getItem(SettingEnum::STORAGE);
+    }
+
+    /**
+     * 图片上传接口
+     * @return array|\think\response\Json
+     * @throws BaseException
+     * @throws \think\Exception
+     */
+    public function image()
+    {
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 实例化存储驱动
+        $storage = new StorageDriver($this->config);
+        // 设置上传文件的信息
+        $storage->setUploadFile('file')
+            ->setRootDirName((string)$this->getStoreId())
+            ->setValidationScene('image');
+        // 执行文件上传
+        if (!$storage->upload()) {
+            return $this->renderError('图片上传失败:' . $storage->getError());
+        }
+        // 文件信息
+        $fileInfo = $storage->getSaveFileInfo();
+        // 添加文件库记录
+        $model = new UploadFileModel;
+        $model->add($fileInfo, FileTypeEnum::IMAGE, $userId);
+        // 图片上传成功
+        return $this->renderSuccess(['fileInfo' => $model->toArray()], '图片上传成功');
+    }
+}

+ 78 - 0
app/api/controller/User.php

@@ -0,0 +1,78 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller;
+
+use app\api\model\User as UserModel;
+use app\common\exception\BaseException;
+use app\api\model\UserCoupon as UserCouponModel;
+use app\api\service\User as UserService;
+use think\response\Json;
+
+/**
+ * 用户管理
+ * Class User
+ * @package app\api
+ */
+class User extends Controller
+{
+    /**
+     * 当前用户详情
+     * @return Json
+     * @throws BaseException
+     */
+    public function info(): Json
+    {
+        // 当前用户信息
+        $userInfo = UserService::getCurrentLoginUser(true);
+        // 获取用户头像
+        $userInfo['avatar'];
+        // 获取会员等级
+        $userInfo['grade'];
+        return $this->renderSuccess(compact('userInfo'));
+    }
+
+    /**
+     * 账户资产
+     * @return Json
+     * @throws BaseException
+     */
+    public function assets(): Json
+    {
+        // 当前用户信息
+        $userInfo = UserService::getCurrentLoginUser(true);
+        // 用户优惠券模型
+        $model = new UserCouponModel;
+        // 返回数据
+        return $this->renderSuccess([
+            'assets' => [
+                'balance' => $userInfo['balance'],  // 账户余额
+                'points' => $userInfo['points'],    // 会员积分
+                'coupon' => $model->getCount($userInfo['user_id']),    // 优惠券数量(可用)
+            ]
+        ]);
+    }
+
+    /**
+     * 手机号绑定
+     * @return Json
+     * @throws \cores\exception\BaseException
+     */
+    public function bindMobile(): Json
+    {
+        $model = new UserModel;
+        if (!$model->bindMobile($this->postForm())) {
+            return $this->renderSuccess($model->getError() ?: '操作失败');
+        }
+        return $this->renderSuccess('恭喜您,手机号绑定成功');
+    }
+}

+ 39 - 0
app/api/controller/article/Category.php

@@ -0,0 +1,39 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller\article;
+
+use app\api\controller\Controller;
+use app\api\model\article\Category as CategoryModel;
+
+/**
+ * 文章分类
+ * Class Category
+ * @package app\api\controller\article
+ */
+class Category extends Controller
+{
+    /**
+     * 文章分类列表
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function list()
+    {
+        $model = new CategoryModel;
+        $list = $model->getShowList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 38 - 0
app/api/controller/balance/Log.php

@@ -0,0 +1,38 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller\balance;
+
+use app\api\controller\Controller;
+use app\api\model\user\BalanceLog as BalanceLogModel;
+
+/**
+ * 余额账单明细
+ * Class Log
+ * @package app\api\controller\balance
+ */
+class Log extends Controller
+{
+    /**
+     * 余额账单明细列表
+     * @return array
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function list()
+    {
+        $model = new BalanceLogModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 40 - 0
app/api/controller/goods/Service.php

@@ -0,0 +1,40 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller\goods;
+
+use app\api\controller\Controller;
+use app\api\model\goods\Service as ServiceModel;
+
+/**
+ * 商品服务与承诺管理
+ * Class Service
+ * @package app\store\controller\goods
+ */
+class Service extends Controller
+{
+    /**
+     * 获取指定商品的服务与承诺
+     * @param int $goodsId
+     * @return array|\think\response\Json
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function list(int $goodsId)
+    {
+        $model = new ServiceModel;
+        $list = $model->getListByGoods($goodsId);
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 83 - 0
app/api/controller/order/Comment.php

@@ -0,0 +1,83 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller\order;
+
+use app\api\controller\Controller;
+use app\api\model\Order as OrderModel;
+use app\api\model\Comment as CommentModel;
+use app\api\model\OrderGoods as OrderGoodsModel;
+
+/**
+ * 订单评价管理
+ * Class Comment
+ * @package app\api\controller\order
+ */
+class Comment extends Controller
+{
+    /**
+     * 待评价订单商品列表
+     * @param int $orderId
+     * @return array
+     * @throws \Exception
+     * @throws \app\common\exception\BaseException
+     * @throws \think\exception\DbException
+     */
+    public function list(int $orderId)
+    {
+        // 订单信息
+        $orderInfo = OrderModel::getDetail($orderId);
+        // 验证订单是否已完成
+        $model = new CommentModel;
+        if (!$model->checkOrderAllowComment($orderInfo)) {
+            return $this->renderError($model->getError());
+        }
+        // 待评价商品列表
+        $goodsList = OrderGoodsModel::getNotCommentGoodsList($orderId);
+        if ($goodsList->isEmpty()) {
+            return $this->renderError('该订单没有可评价的商品');
+        }
+        return $this->renderSuccess(compact('goodsList'));
+    }
+
+    /**
+     * 创建商品评价
+     * @param int $orderId
+     * @return array|\think\response\Json
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function submit(int $orderId)
+    {
+        // 订单信息
+        $orderInfo = OrderModel::getDetail($orderId);
+        // 验证订单是否已完成
+        $model = new CommentModel;
+        if (!$model->checkOrderAllowComment($orderInfo)) {
+            return $this->renderError($model->getError());
+        }
+        // 待评价商品列表
+        $goodsList = OrderGoodsModel::getNotCommentGoodsList($orderId);
+        if ($goodsList->isEmpty()) {
+            return $this->renderError('该订单没有可评价的商品');
+        }
+        // 提交商品评价
+        $model = new CommentModel;
+        if ($model->increased($orderInfo, $goodsList, $this->postForm())) {
+            return $this->renderSuccess([], '评价发表成功');
+        }
+        return $this->renderError($model->getError() ?: '评价发表失败');
+    }
+
+}

+ 37 - 0
app/api/controller/points/Log.php

@@ -0,0 +1,37 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller\points;
+
+use app\api\controller\Controller;
+use app\api\model\user\PointsLog as PointsLogModel;
+
+/**
+ * 积分明细
+ * Class Log
+ * @package app\api\controller\balance
+ */
+class Log extends Controller
+{
+    /**
+     * 积分明细列表
+     * @return array
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function list()
+    {
+        $model = new PointsLogModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+}

+ 39 - 0
app/api/controller/recharge/Order.php

@@ -0,0 +1,39 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller\recharge;
+
+use app\api\controller\Controller;
+use app\api\model\recharge\Order as OrderModel;
+use app\common\exception\BaseException;
+
+/**
+ * 充值记录管理
+ * Class Order
+ * @package app\api\controller\recharge
+ */
+class Order extends Controller
+{
+    /**
+     * 我的充值记录列表
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function list()
+    {
+        $model = new OrderModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 39 - 0
app/api/controller/recharge/Plan.php

@@ -0,0 +1,39 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\controller\recharge;
+
+use app\api\controller\Controller;
+use app\api\model\recharge\Plan as PlanModel;
+use app\common\exception\BaseException;
+
+/**
+ * 充值套餐管理
+ * Class Plan
+ * @package app\api\controller\recharge
+ */
+class Plan extends Controller
+{
+    /**
+     * 充值套餐列表
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function list()
+    {
+        $model = new PlanModel;
+        $list = $model->getList();
+        return $this->renderSuccess(compact('list'));
+    }
+
+}

+ 20 - 0
app/api/event.php

@@ -0,0 +1,20 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+// 应用行为扩展定义文件
+return [
+    'listen' => [
+        'OrderPaySuccess' => [
+            \app\api\listener\order\PaySuccess::class
+        ]
+    ]
+];

+ 104 - 0
app/api/listener/order/PaySuccess.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\api\listener\order;
+
+use app\common\service\Message as MessageService;
+use app\common\enum\OrderType as OrderTypeEnum;
+use app\common\enum\order\OrderSource as OrderSourceEnum;
+use app\common\exception\BaseException;
+
+/**
+ * 订单支付成功后扩展类
+ * Class PaySuccess
+ * @package app\api\behavior\order
+ */
+class PaySuccess
+{
+    // 订单信息
+    private $order;
+
+    // 订单类型
+    private $orderType;
+
+    // 当前商城ID
+    private $storeId;
+
+    /**
+     * 订单来源回调业务映射类
+     * @var array
+     */
+    protected $sourceCallbackClass = [
+        OrderSourceEnum::MAIN => \app\api\service\main\order\PaySuccess::class,
+    ];
+
+    /**
+     * 执行句柄
+     * @param array $params
+     * @return bool
+     * @throws BaseException
+     */
+    public function handle(array $params)
+    {
+        // 解构赋值: 订单模型、订单类型
+        ['order' => $order, 'orderType' => $orderType] = $params;
+        // 设置当前类的属性
+        $this->setAttribute($order, $orderType);
+        // 订单公共事件
+        $this->onCommonEvent();
+        // 订单来源回调业务
+        $this->onSourceCallback();
+        return true;
+    }
+
+    /**
+     * 设置当前类的属性
+     * @param $order
+     * @param int $orderType
+     */
+    private function setAttribute($order, $orderType = OrderTypeEnum::ORDER)
+    {
+        $this->order = $order;
+        $this->storeId = $this->order['store_id'];
+        $this->orderType = $orderType;
+    }
+
+    /**
+     * 订单公共业务
+     * @throws BaseException
+     */
+    private function onCommonEvent()
+    {
+        // 发送消息通知
+        MessageService::send('order.payment', [
+            'order' => $this->order,
+            'order_type' => $this->orderType,
+        ], $this->storeId);
+    }
+
+    /**
+     * 订单来源回调业务
+     * @return bool
+     */
+    private function onSourceCallback()
+    {
+        if (!isset($this->order['order_source'])) {
+            return false;
+        }
+        if (!isset($this->sourceCallbackClass[$this->order['order_source']])) {
+            return false;
+        }
+        $class = $this->sourceCallbackClass[$this->order['order_source']];
+        return !is_null($class) ? (new $class)->onPaySuccess($this->order) : false;
+    }
+
+}

+ 97 - 0
app/api/model/Article.php

@@ -0,0 +1,97 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use app\common\exception\BaseException;
+use app\common\model\Article as ArticleModel;
+
+/**
+ * 商品评价模型
+ * Class Article
+ * @package app\api\model
+ */
+class Article extends ArticleModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'is_delete',
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+    /**
+     * 获取器:文章详情HTML实体转换回普通字符
+     * @param $value
+     * @return string
+     */
+    public function getArticleContentAttr($value)
+    {
+        return htmlspecialchars_decode($value);
+    }
+
+    /**
+     * 获取文章详情并累计阅读次数
+     * @param int $articleId 文章ID
+     * @return static|null
+     * @throws BaseException
+     */
+    public static function getDetail(int $articleId)
+    {
+        // 获取文章详情
+        $detail = parent::detail($articleId, ['image']);
+        if (empty($detail) || $detail['is_delete']) {
+            throwError('很抱歉,当前文章不存在');
+        }
+        // 累积文章实际阅读数
+        static::setIncActualViews($articleId);
+        return $detail;
+    }
+
+    /**
+     * 累积文章实际阅读数
+     * @param int $articleId 文章ID
+     * @param int $num 递增的数量
+     * @return mixed
+     */
+    private static function setIncActualViews(int $articleId, int $num = 1)
+    {
+        return (new static)->setInc($articleId, 'actual_views', $num);
+    }
+
+    /**
+     * 获取文章列表
+     * @param int $categoryId
+     * @param int $limit
+     * @return \think\Paginator
+     * @throws \think\db\exception\DbException
+     */
+    public function getList(int $categoryId = 0, int $limit = 15)
+    {
+        // 检索查询条件
+        $filter = [];
+        $categoryId > 0 && $filter[] = ['category_id', '=', $categoryId];
+        // 获取列表数据
+        return $this->withoutField(['content'])
+            ->with(['image', 'category'])
+            ->where($filter)
+            ->where('status', '=', 1)
+            ->where('is_delete', '=', 0)
+            ->order(['sort' => 'asc', 'create_time' => 'desc'])
+            ->paginate($limit);
+    }
+
+}

+ 166 - 0
app/api/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\api\model;
+
+use app\api\model\Goods as GoodsModel;
+use app\api\model\GoodsSku as GoodsSkuModel;
+use app\api\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');
+    }
+
+}

+ 47 - 0
app/api/model/Category.php

@@ -0,0 +1,47 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model;
+
+use app\common\model\Category as CategoryModel;
+
+/**
+ * 商品分类模型
+ * Class Category
+ * @package app\common\model
+ */
+class Category extends CategoryModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+    /**
+     * 获取列表记录
+     * @param array $param
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getList(array $param = [])
+    {
+        return parent::getList(array_merge($param, ['status' => 1]));
+    }
+
+}

+ 274 - 0
app/api/model/Comment.php

@@ -0,0 +1,274 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model;
+
+use app\api\model\Order as OrderModel;
+use app\common\model\Comment as CommentModel;
+use app\api\model\OrderGoods as OrderGoodsModel;
+use app\api\service\User as UserService;
+use app\common\library\helper;
+use app\common\exception\BaseException;
+
+/**
+ * 商品评价模型
+ * Class Comment
+ * @package app\api\model
+ */
+class Comment extends CommentModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'status',
+        'sort',
+        'order_id',
+        'goods_id',
+        'order_goods_id',
+        'is_delete',
+        'update_time'
+    ];
+
+    /**
+     * 获取指定商品评价列表
+     * @param int $goodsId 商品ID
+     * @param int|null $scoreType 评分 (10好评 20中评 30差评)
+     * @return \think\Paginator
+     * @throws \think\db\exception\DbException
+     */
+    public function getCommentList(int $goodsId, int $scoreType = null)
+    {
+        $filter = $this->getFilter($goodsId, $scoreType);
+        return $this->with(['user.avatar', 'orderGoods', 'images.file'])
+            ->where($filter)
+            ->order(['sort' => 'asc', 'create_time' => 'desc'])
+            ->paginate(15);
+    }
+
+    /**
+     * 获取指定商品评价列表 (限制数量, 不分页)
+     * @param int $goodsId 商品ID
+     * @param int $limit 限制的数量
+     * @return \think\Collection
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function listRows(int $goodsId, int $limit = 5)
+    {
+        $filter = $this->getFilter($goodsId);
+        return $this->with(['user.avatar'])
+            ->where($filter)
+            ->order(['sort' => 'asc', $this->getPk()])
+            ->limit($limit)
+            ->select();
+    }
+
+    /**
+     * 获取指定商品评价总数量
+     * @param int $goodsId
+     * @return int
+     */
+    public function rowsTotal(int $goodsId)
+    {
+        $filter = $this->getFilter($goodsId);
+        return $this->where($filter)->count();
+    }
+
+    /**
+     * 获取查询条件
+     * @param int $goodsId 商品ID
+     * @param int|null $scoreType 评分 (10好评 20中评 30差评)
+     * @return array[]
+     */
+    private function getFilter(int $goodsId, int $scoreType = null)
+    {
+        // 筛选条件
+        $filter = [
+            ['goods_id', '=', $goodsId],
+            ['status', '=', 1],
+            ['is_delete', '=', 0],
+        ];
+        // 评分
+        $scoreType > 0 && $filter[] = ['score', '=', $scoreType];
+        return $filter;
+    }
+
+    /**
+     * 获取指定评分总数
+     * @param int $goodsId
+     * @return array|null|\think\Model
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getTotal(int $goodsId)
+    {
+        return $this->field([
+            'count(comment_id) AS `all`',
+            'count(score = 10 OR NULL) AS `praise`',
+            'count(score = 20 OR NULL) AS `review`',
+            'count(score = 30 OR NULL) AS `negative`',
+        ])->where([
+            'goods_id' => $goodsId,
+            'is_delete' => 0,
+            'status' => 1
+        ])->find();
+    }
+
+    /**
+     * 验证订单是否允许评价
+     * @param OrderModel $order
+     * @return boolean
+     */
+    public function checkOrderAllowComment(OrderModel $order)
+    {
+        // 验证订单是否已完成
+        if ($order['order_status'] != 30) {
+            $this->error = '该订单未完成,无法评价';
+            return false;
+        }
+        // 验证订单是否已评价
+        if ($order['is_comment'] == 1) {
+            $this->error = '该订单已完成评价';
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 根据已完成订单商品 添加评价
+     * @param OrderModel $order
+     * @param $goodsList
+     * @param array $data
+     * @return boolean
+     * @throws BaseException
+     */
+    public function increased(OrderModel $order, $goodsList, array $data)
+    {
+        // 生成 formData
+        $formData = $this->formatFormData($data);
+        // 生成评价数据
+        $data = $this->createCommentData($order['order_id'], $goodsList, $formData);
+        if (empty($data)) {
+            $this->error = '没有输入评价内容';
+            return false;
+        }
+        return $this->transaction(function () use ($order, $goodsList, $formData, $data) {
+            // 记录评价内容
+            $result = $this->addAll($data);
+            // 记录评价图片`
+            $this->saveAllImages($result, $formData);
+            // 更新订单评价状态
+            $isComment = count($goodsList) === count($data);
+            $this->updateOrderIsComment($order, $isComment, $result);
+            return true;
+        });
+    }
+
+    /**
+     * 更新订单评价状态
+     * @param OrderModel $order
+     * @param $isComment
+     * @param $commentList
+     * @return array|false
+     * @throws \Exception
+     */
+    private function updateOrderIsComment(OrderModel $order, $isComment, $commentList)
+    {
+        // 更新订单商品
+        $orderGoodsData = [];
+        foreach ($commentList as $comment) {
+            $orderGoodsData[] = [
+                'where' => [
+                    'order_goods_id' => $comment['order_goods_id'],
+                ],
+                'data' => [
+                    'is_comment' => 1
+                ]
+            ];
+        }
+        // 更新订单
+        $isComment && $order->save(['is_comment' => 1]);
+        return (new OrderGoods)->updateAll($orderGoodsData);
+    }
+
+    /**
+     * 生成评价数据
+     * @param int $orderId
+     * @param $goodsList
+     * @param array $formData
+     * @return array
+     * @throws BaseException
+     */
+    private function createCommentData(int $orderId, $goodsList, array $formData)
+    {
+        $data = [];
+        foreach ($goodsList as $goods) {
+            if (!isset($formData[$goods['order_goods_id']])) {
+                throwError('提交的数据不合法');
+            }
+            $commentItem = $formData[$goods['order_goods_id']];
+            $commentItem['content'] = trim($commentItem['content']);
+            !empty($commentItem['content']) && $data[$goods['order_goods_id']] = [
+                'score' => $commentItem['score'],
+                'content' => $commentItem['content'],
+                'is_picture' => !empty($commentItem['uploaded']),
+                'sort' => 100,
+                'status' => 1,
+                'user_id' => UserService::getCurrentLoginUserId(),
+                'order_id' => $orderId,
+                'goods_id' => $commentItem['goods_id'],
+                'order_goods_id' => $commentItem['order_goods_id'],
+                'store_id' => self::$storeId
+            ];
+        }
+        return $data;
+    }
+
+    /**
+     * 格式化 formData
+     * @param array $data
+     * @return array
+     */
+    private function formatFormData(array $data)
+    {
+        return helper::arrayColumn2Key($data, 'order_goods_id');
+    }
+
+    /**
+     * 记录评价图片
+     * @param $commentList
+     * @param $formData
+     * @return bool
+     */
+    private function saveAllImages($commentList, $formData)
+    {
+        // 生成评价图片数据
+        $imageData = [];
+        foreach ($commentList as $comment) {
+            $item = $formData[$comment['order_goods_id']];
+            foreach ($item['uploaded'] as $imageId) {
+                $imageData[] = [
+                    'comment_id' => $comment['comment_id'],
+                    'image_id' => $imageId,
+                    'store_id' => self::$storeId
+                ];
+            }
+        }
+        $model = new CommentImage;
+        return !empty($imageData) && $model->addAll($imageData) !== false;
+    }
+
+}

+ 42 - 0
app/api/model/CommentImage.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\api\model;
+
+use app\common\model\CommentImage as CommentImageModel;
+
+/**
+ * 商品图片模型
+ * Class GoodsImage
+ * @package app\api\model
+ */
+class CommentImage extends CommentImageModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+    ];
+
+    /**
+     * 关联文件库
+     * @return \think\model\relation\BelongsTo
+     */
+    public function file()
+    {
+        return parent::file()->bind(['image_url' => 'preview_url']);
+    }
+
+}

+ 86 - 0
app/api/model/Coupon.php

@@ -0,0 +1,86 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use app\api\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;
+    }
+}

+ 34 - 0
app/api/model/Delivery.php

@@ -0,0 +1,34 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model;
+
+use app\common\model\Delivery as DeliveryModel;
+
+/**
+ * 配送模板模型
+ * Class Delivery
+ * @package app\api\model
+ */
+class Delivery extends DeliveryModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+}

+ 34 - 0
app/api/model/DeliveryRule.php

@@ -0,0 +1,34 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model;
+
+use app\common\model\DeliveryRule as DeliveryRuleModel;
+
+/**
+ * 配送模板区域及运费模型
+ * Class DeliveryRule
+ * @package app\api\model
+ */
+class DeliveryRule extends DeliveryRuleModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+}

+ 36 - 0
app/api/model/Express.php

@@ -0,0 +1,36 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model;
+
+use app\common\model\Express as ExpressModel;
+
+/**
+ * 物流公司模型
+ * Class Express
+ * @package app\api\model
+ */
+class Express extends ExpressModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'kuaidi100_code',
+        'sort',
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+}

+ 265 - 0
app/api/model/Goods.php

@@ -0,0 +1,265 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use app\api\service\Goods as GoodsService;
+use app\api\service\user\Grade as UserGradeService;
+use app\api\model\GoodsSku as GoodsSkuModel;
+use app\api\model\GoodsSpecRel as GoodsSpecRelModel;
+use app\common\model\Goods as GoodsModel;
+use app\common\enum\goods\Status as GoodsStatusEnum;
+use cores\exception\BaseException;
+
+/**
+ * 商品模型
+ * Class Goods
+ * @package app\api\model
+ */
+class Goods extends GoodsModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    public $hidden = [
+        'images',
+        'delivery',
+        'deduct_stock_type',
+        'sales_initial',
+        'sales_actual',
+        'sort',
+        'is_delete',
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+    // 是否设置会员折扣价
+    private $isGoodsGradeMoney = true;
+
+    /**
+     * 商品详情:HTML实体转换回普通字符
+     * @param $value
+     * @return string
+     */
+    public function getContentAttr($value): string
+    {
+        return htmlspecialchars_decode((string)$value);
+    }
+
+    /**
+     * 是否设置会员折扣价
+     * @param bool $value
+     * @return $this
+     */
+    public function isGoodsGradeMoney(bool $value): Goods
+    {
+        $this->isGoodsGradeMoney = $value;
+        return $this;
+    }
+
+    /**
+     * 获取商品列表
+     * @param array $param 查询条件
+     * @param int $listRows 分页数量
+     * @return mixed|\think\model\Collection|\think\Paginator
+     * @throws \think\db\exception\DbException
+     */
+    public function getList(array $param = [], int $listRows = 15)
+    {
+        // 整理查询参数
+        $params = array_merge($param, ['status' => GoodsStatusEnum::ON_SALE]);
+        // 获取商品列表
+        $list = parent::getList($params, $listRows);
+        if ($list->isEmpty()) {
+            return $list;
+        }
+        // 隐藏冗余的字段
+        $list->hidden(array_merge($this->hidden, ['content', 'goods_images', 'images']));
+        // 整理列表数据并返回
+        return $this->setGoodsListDataFromApi($list);
+    }
+
+    /**
+     * 获取商品详情 (详细数据用于页面展示)
+     * @param int $goodsId 商品ID
+     * @param bool $verifyStatus 是否验证商品状态(上架)
+     * @return mixed
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getDetails(int $goodsId, bool $verifyStatus = true)
+    {
+        // 关联查询(商品图片、sku列表)
+        $with = ['images.file', 'skuList.image', 'video', 'videoCover'];
+        // 获取商品记录
+        $goodsInfo = $this->getGoodsMain($goodsId, $with, $verifyStatus);
+        // 商品规格列表
+        $goodsInfo['specList'] = GoodsSpecRelModel::getSpecList($goodsInfo['goods_id']);
+        return $goodsInfo->hidden(array_merge($this->hidden, ['images']));
+    }
+
+    /**
+     * 获取商品详情 (仅包含主商品信息和商品图片)
+     * @param int $goodsId 商品ID
+     * @param bool $verifyStatus 是否验证商品状态(上架)
+     * @return mixed
+     * @throws BaseException
+     */
+    public function getBasic(int $goodsId, bool $verifyStatus = true)
+    {
+        // 关联查询(商品图片)
+        $with = ['images.file'];
+        // 获取商品记录
+        return $this->getGoodsMain($goodsId, $with, $verifyStatus);
+    }
+
+    /**
+     * 获取商品规格数据
+     * @param int $goodsId
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getSpecData(int $goodsId): array
+    {
+        $data = [];
+        // 商品SKU列表
+        $data['skuList'] = GoodsSkuModel::getSkuList($goodsId);
+        // 商品规格列表
+        $data['specList'] = GoodsSpecRelModel::getSpecList($goodsId);
+        return $data;
+    }
+
+    /**
+     * 获取商品主体信息
+     * @param int $goodsId 商品ID
+     * @param array $with 关联查询
+     * @param bool $verifyStatus 是否验证商品状态(上架)
+     * @return mixed
+     * @throws BaseException
+     */
+    private function getGoodsMain(int $goodsId, array $with = [], bool $verifyStatus = true)
+    {
+        // 获取商品记录
+        $goodsInfo = static::detail($goodsId, $with);
+        // 判断商品是否存在
+        if (empty($goodsInfo) || $goodsInfo['is_delete']) {
+            throwError('很抱歉,商品信息不存在');
+        }
+        // 判断商品状态(上架)
+        if ($verifyStatus && $goodsInfo['status'] == GoodsStatusEnum::OFF_SALE) {
+            throwError('很抱歉,当前商品已下架');
+        }
+        // 整理商品数据并返回
+        return $this->setGoodsDataFromApi($goodsInfo);
+    }
+
+    /**
+     * 根据商品id集获取商品列表
+     * @param array $goodsIds
+     * @return mixed
+     */
+    public function getListByIdsFromApi(array $goodsIds)
+    {
+        // 获取商品列表
+        $data = $this->getListByIds($goodsIds, GoodsStatusEnum::ON_SALE);
+        // 整理列表数据并返回
+        return $this->setGoodsListDataFromApi($data);
+    }
+
+    /**
+     * 获取商品指定的sku信息并且设置商品的会员价
+     * @param mixed $goodsInfo 商品信息
+     * @param string $goodsSkuId 商品SKUID
+     * @param bool $isGoodsGradeMoney 是否设置会员折扣价
+     * @return \app\common\model\GoodsSku|array|null
+     * @throws BaseException
+     */
+    public static function getSkuInfo($goodsInfo, string $goodsSkuId, bool $isGoodsGradeMoney = true)
+    {
+        $goodsInfo['skuInfo'] = GoodsService::getSkuInfo($goodsInfo['goods_id'], $goodsSkuId);
+        $isGoodsGradeMoney && (new static)->setGoodsGradeMoney($goodsInfo);
+        return $goodsInfo['skuInfo'];
+    }
+
+    /**
+     * 设置商品展示的数据 api模块
+     * @param $data
+     * @return mixed
+     */
+    private function setGoodsListDataFromApi($data)
+    {
+        return $this->setGoodsListData($data, function ($goods) {
+            // 整理商品数据 api模块
+            $this->setGoodsDataFromApi($goods);
+        });
+    }
+
+    /**
+     * 整理商品数据 api模块
+     * @param $goodsInfo
+     * @return mixed
+     */
+    private function setGoodsDataFromApi($goodsInfo)
+    {
+        return $this->setGoodsData($goodsInfo, function ($goods) {
+            // 计算并设置商品会员价
+            $this->isGoodsGradeMoney && $this->setGoodsGradeMoney($goods);
+        });
+    }
+
+    /**
+     * 设置商品的会员价
+     * @param Goods $goods
+     * @throws BaseException
+     */
+    private function setGoodsGradeMoney(self $goods)
+    {
+        // 设置当前商品是否使用会员等级折扣价
+        $goods['is_user_grade'] = false;
+        // 获取当前登录用户的会员等级信息
+        $gradeInfo = UserGradeService::getCurrentGradeInfo();
+        // 判断商品是否参与会员折扣
+        if (empty($gradeInfo) || !$goods['is_enable_grade']) {
+            return;
+        }
+        // 默认的折扣比例
+        $discountRatio = $gradeInfo['equity']['discount'];
+        // 商品单独设置了会员折扣
+        if ($goods['is_alone_grade'] && isset($goods['alone_grade_equity'][$gradeInfo['grade_id']])) {
+            $discountRatio = $goods['alone_grade_equity'][$gradeInfo['grade_id']];
+        }
+        if (empty($discountRatio)) {
+            return;
+        }
+        // 标记参与会员折扣
+        $goods['is_user_grade'] = true;
+        // 会员折扣价: 商品基础价格
+        $goods['goods_price_min'] = UserGradeService::getDiscountPrice($goods['goods_price_min'], $discountRatio);
+        $goods['goods_price_max'] = UserGradeService::getDiscountPrice($goods['goods_price_max'], $discountRatio);
+        // 会员折扣价: 商品sku列表
+        if ($goods->getRelation('skuList')) {
+            foreach ($goods['skuList'] as &$skuItem) {
+                $skuItem['goods_price'] = UserGradeService::getDiscountPrice($skuItem['goods_price'], $discountRatio);
+            }
+        }
+        // 会员折扣价: 已选择的商品sku(用于购物车)
+        if ($goods->getAttr('skuInfo')) {
+            $goods['skuInfo']['goods_price'] = UserGradeService::getDiscountPrice($goods['skuInfo']['goods_price'], $discountRatio);
+        }
+    }
+}

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

+ 32 - 0
app/api/model/Help.php

@@ -0,0 +1,32 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model;
+
+use app\common\model\Help as HelpModel;
+
+/**
+ * 模型类:帮助中心
+ * Class Help
+ * @package app\api\model
+ */
+class Help extends HelpModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'create_time',
+        'update_time',
+    ];
+}

+ 398 - 0
app/api/model/Order.php

@@ -0,0 +1,398 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+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);
+        }
+        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
+        );
+    }
+
+    /**
+     * 立即购买:获取订单商品列表
+     * @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;
+    }
+}

+ 33 - 0
app/api/model/OrderAddress.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\api\model;
+
+use app\common\model\OrderAddress as OrderAddressModel;
+
+/**
+ * 订单收货地址模型
+ * Class OrderAddress
+ * @package app\api\model
+ */
+class OrderAddress extends OrderAddressModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+    ];
+
+}

+ 50 - 0
app/api/model/OrderGoods.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\api\model;
+
+use app\common\model\OrderGoods as OrderGoodsModel;
+
+/**
+ * 订单商品模型
+ * Class OrderGoods
+ * @package app\api\model
+ */
+class OrderGoods extends OrderGoodsModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'content',
+        'store_id',
+        'create_time',
+    ];
+
+    /**
+     * 获取未评价的商品
+     * @param int $orderId 订单ID
+     * @return \think\Collection
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function getNotCommentGoodsList(int $orderId)
+    {
+        return (new static)->with(['image'])
+            ->where('order_id', '=', $orderId)
+            ->where('is_comment', '=', 0)
+            ->select();
+    }
+
+}

+ 234 - 0
app/api/model/OrderRefund.php

@@ -0,0 +1,234 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use app\api\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;
+    }
+}

+ 33 - 0
app/api/model/OrderRefundAddress.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\api\model;
+
+use app\common\model\OrderRefundAddress as OrderRefundAddressModel;
+
+/**
+ * 售后单退货地址模型
+ * Class OrderRefundAddress
+ * @package app\api\model
+ */
+class OrderRefundAddress extends OrderRefundAddressModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time'
+    ];
+
+}

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

+ 161 - 0
app/api/model/Page.php

@@ -0,0 +1,161 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use app\api\model\Goods as GoodsModel;
+use app\api\model\Coupon as CouponModel;
+use app\common\model\Page as PageModel;
+use app\common\library\helper;
+use cores\exception\BaseException;
+
+/**
+ * 页面模型
+ * Class Page
+ * @package app\api\model
+ */
+class Page extends PageModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+    /**
+     * DIY页面详情
+     * @param int|null $pageId 页面ID
+     * @return array
+     * @throws \app\common\exception\BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getPageData(int $pageId = null)
+    {
+        // 页面详情
+        $detail = $pageId > 0 ? parent::detail($pageId) : parent::getHomePage();
+        if (empty($detail)) {
+            throwError('很抱歉,未找到该页面');
+        }
+        // 页面diy元素
+        $pageData = $detail['page_data'];
+        // 获取动态数据
+        foreach ($pageData['items'] as &$item) {
+            // 移出默认数据
+            if (array_key_exists('defaultData', $item)) {
+                unset($item['defaultData']);
+            }
+            if ($item['type'] === 'window') {
+                $item['data'] = array_values($item['data']);
+            } else if ($item['type'] === 'goods') {
+                $item['data'] = $this->getGoodsList($item);
+            } else if ($item['type'] === 'coupon') {
+                $item['data'] = $this->getCouponList($item);
+            } else if ($item['type'] === 'article') {
+                $item['data'] = $this->getArticleList($item);
+            } else if ($item['type'] === 'special') {
+                $item['data'] = $this->getSpecialList($item);
+            }
+        }
+        return $pageData;
+    }
+
+    /**
+     * 商品组件:获取商品列表
+     * @param $item
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    private function getGoodsList($item)
+    {
+        // 获取商品数据
+        $model = new GoodsModel;
+        if ($item['params']['source'] === 'choice') {
+            // 数据来源:手动
+            $goodsIds = helper::getArrayColumn($item['data'], 'goods_id');
+            if (empty($goodsIds)) return [];
+            $goodsList = $model->getListByIdsFromApi($goodsIds);
+        } else {
+            // 数据来源:自动
+            $goodsList = $model->getList([
+                'status' => 10,
+                'categoryId' => $item['params']['auto']['category'],
+                'sortType' => $item['params']['auto']['goodsSort'],
+            ], $item['params']['auto']['showNum']);
+        }
+        if ($goodsList->isEmpty()) return [];
+        // 格式化商品列表
+        $data = [];
+        foreach ($goodsList as $goods) {
+            $data[] = [
+                'goods_id' => $goods['goods_id'],
+                'goods_name' => $goods['goods_name'],
+                'selling_point' => $goods['selling_point'],
+                'goods_image' => $goods['goods_images'][0]['preview_url'],
+                'goods_price_min' => $goods['goods_price_min'],
+                'goods_price_max' => $goods['goods_price_max'],
+                'line_price_min' => $goods['line_price_min'],
+                'line_price_max' => $goods['line_price_max'],
+                'goods_sales' => $goods['goods_sales'],
+            ];
+        }
+        return $data;
+    }
+
+    /**
+     * 优惠券组件:获取优惠券列表
+     * @param $item
+     * @return array|mixed
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getCouponList($item)
+    {
+        // 获取优惠券数据
+        return (new CouponModel)->getList($item['params']['limit'], true);
+    }
+
+    /**
+     * 文章组件:获取文章列表
+     * @param $item
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    private function getArticleList($item)
+    {
+        // 获取文章数据
+        $model = new Article;
+        $articleList = $model->getList($item['params']['auto']['category'], $item['params']['auto']['showNum']);
+        return $articleList->isEmpty() ? [] : $articleList->toArray()['data'];
+    }
+
+    /**
+     * 头条快报:获取头条列表
+     * @param $item
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    private function getSpecialList($item)
+    {
+        // 获取头条数据
+        $model = new Article;
+        $articleList = $model->getList($item['params']['auto']['category'], $item['params']['auto']['showNum']);
+        return $articleList->isEmpty() ? [] : $articleList->toArray()['data'];
+    }
+}

+ 25 - 0
app/api/model/Region.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\api\model;
+
+use app\common\model\Region as RegionModel;
+
+/**
+ * 地区模型
+ * Class Region
+ * @package app\api\model
+ */
+class Region extends RegionModel
+{
+
+}

+ 33 - 0
app/api/model/Setting.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\api\model;
+
+use app\common\model\store\Setting as SettingModel;
+
+/**
+ * 系统设置模型
+ * Class Setting
+ * @package app\api\model
+ */
+class Setting extends SettingModel
+{
+    /**
+     * 获取积分名称
+     * @return string
+     */
+    public static function getPointsName()
+    {
+        return static::getItem('points')['points_name'];
+    }
+
+}

+ 24 - 0
app/api/model/Spec.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\api\model;
+
+use app\common\model\Spec as SpecModel;
+
+/**
+ * 规格/属性(组)模型
+ * Class Spec
+ * @package app\api\model
+ */
+class Spec extends SpecModel
+{
+}

+ 24 - 0
app/api/model/SpecValue.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\api\model;
+
+use app\common\model\SpecValue as SpecValueModel;
+
+/**
+ * 规格/属性(值)模型
+ * Class SpecValue
+ * @package app\api\model
+ */
+class SpecValue extends SpecValueModel
+{
+}

+ 55 - 0
app/api/model/Store.php

@@ -0,0 +1,55 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use app\common\model\Store as StoreModel;
+use think\model\relation\HasOne;
+
+/**
+ * 商家记录表模型
+ * Class Store
+ * @package app\store\model
+ */
+class Store extends StoreModel
+{
+    /**
+     * 隐藏的字段
+     * @var string[]
+     */
+    protected $hidden = [
+        'sort',
+        'is_recycle',
+        'is_delete',
+        'create_time',
+        'update_time'
+    ];
+
+    /**
+     * 关联logo图片
+     * @return HasOne
+     */
+    public function logoImage(): HasOne
+    {
+        return $this->hasOne('UploadFile', 'file_id', 'logo_image_id')
+            ->bind(['image_url' => 'preview_url']);
+    }
+
+    /**
+     * 获取当前商城的基本信息
+     * @return Store|array|null
+     */
+    public static function getInfo()
+    {
+        return static::detail(static::$storeId);
+    }
+}

+ 56 - 0
app/api/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\api\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
+        ]);
+    }
+
+}

+ 112 - 0
app/api/model/User.php

@@ -0,0 +1,112 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use think\facade\Cache;
+use app\api\service\User as UserService;
+use app\api\model\UserOauth as UserOauthModel;
+use app\common\model\User as UserModel;
+use cores\exception\BaseException;
+use yiovo\captcha\facade\CaptchaApi;
+
+/**
+ * 用户模型类
+ * Class User
+ * @package app\api\model
+ */
+class User extends UserModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'open_id',
+        'is_delete',
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+    /**
+     * 获取器:隐藏手机号中间四位
+     * @param string $value
+     * @return string
+     */
+    public function getMobileAttr(string $value): string
+    {
+        return strlen($value) === 11 ? hide_mobile($value) : $value;
+    }
+
+    /**
+     * 获取用户信息
+     * @param string $token
+     * @return User|array|false|null
+     * @throws BaseException
+     */
+    public static function getUserByToken(string $token)
+    {
+        // 检查登录态是否存在
+        if (!Cache::has($token)) {
+            return false;
+        }
+        // 用户的ID
+        $userId = (int)Cache::get($token)['user']['user_id'];
+        // 用户基本信息
+        $userInfo = self::detail($userId);
+        if (empty($userInfo) || $userInfo['is_delete']) {
+            throwError('很抱歉,用户信息不存在或已删除', config('status.not_logged'));
+        }
+        // 获取用户关联的第三方用户信息(当前客户端)
+        try {
+            $userInfo['currentOauth'] = UserOauthModel::getOauth($userId, getPlatform());
+        } catch (\Throwable $e) {
+            throwError($e->getMessage());
+        }
+        return $userInfo;
+    }
+
+    /**
+     * 绑定手机号(当前登录用户)
+     * @param array $data
+     * @return bool
+     * @throws BaseException
+     */
+    public function bindMobile(array $data): bool
+    {
+        // 当前登录的用户信息
+        $userInfo = UserService::getCurrentLoginUser(true);
+        // 验证绑定的手机号
+        $this->checkBindMobile($data);
+        // 更新手机号记录
+        return $userInfo->save(['mobile' => $data['mobile']]);
+    }
+
+    /**
+     * 验证绑定的手机号
+     * @param array $data
+     * @return void
+     * @throws BaseException
+     */
+    private function checkBindMobile(array $data): void
+    {
+        // 验证短信验证码是否匹配
+        if (!CaptchaApi::checkSms($data['smsCode'], $data['mobile'])) {
+            throwError('短信验证码不正确');
+        }
+        // 判断手机号是否已存在
+        if (static::checkExistByMobile($data['mobile'])) {
+            throwError('很抱歉,该手机号已绑定其他账户');
+        }
+    }
+}

+ 182 - 0
app/api/model/UserAddress.php

@@ -0,0 +1,182 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model;
+
+use app\api\model\User as UserModel;
+use app\api\service\User as UserService;
+use app\common\model\UserAddress as UserAddressModel;
+use app\common\exception\BaseException;
+
+/**
+ * 用户收货地址模型
+ * Class UserAddress
+ * @package app\common\model
+ */
+class UserAddress extends UserAddressModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'is_delete',
+        'store_id',
+        'create_time',
+        'update_time'
+    ];
+
+//    /**
+//     * 地区名称
+//     * @param $value
+//     * @param $data
+//     * @return array
+//     */
+//    public function getRegionAttr($value, $data)
+//    {
+//        return array_values(parent::getRegionAttr($value, $data));
+//    }
+
+    /**
+     * 获取收货地址列表
+     * @return \think\Collection
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @throws BaseException
+     */
+    public function getList()
+    {
+        $userId = UserService::getCurrentLoginUserId();
+        return $this->where('user_id', '=', $userId)
+            ->where('is_delete', '=', 0)
+            ->select();
+    }
+
+    /**
+     * 新增收货地址
+     * @param array $data
+     * @return mixed
+     * @throws BaseException
+     */
+    public function add(array $data)
+    {
+        // 当前用户信息
+        $user = UserService::getCurrentLoginUser(true);
+        // 省市区ID
+        list($data['province_id'], $data['city_id'], $data['region_id']) = $this->getRegionId($data);
+        // 添加收货地址
+        return $this->transaction(function () use ($user, $data) {
+            $this->save([
+                'name' => $data['name'],
+                'phone' => $data['phone'],
+                'province_id' => $data['province_id'],
+                'city_id' => $data['city_id'],
+                'region_id' => $data['region_id'],
+                'detail' => $data['detail'],
+                'user_id' => $user['user_id'],
+                'store_id' => self::$storeId
+            ]);
+            // 设为默认收货地址
+            !$user['address_id'] && $this->setDefault((int)$this['address_id']);
+            return true;
+        });
+    }
+
+    /**
+     * 格式化用户上传的省市区数据
+     * @param array $data
+     * @return array
+     * @throws BaseException
+     */
+    private function getRegionId(array $data)
+    {
+        if (!isset($data['region'])) {
+            throwError('省市区不能为空');
+        }
+        if (count($data['region']) != 3) {
+            throwError('省市区数据不合法');
+        }
+        return array_map(function ($item) {
+            return $item['value'];
+        }, $data['region']);
+    }
+
+    /**
+     * 编辑收货地址
+     * @param array $data
+     * @return bool
+     * @throws BaseException
+     */
+    public function edit(array $data)
+    {
+        // 省市区ID
+        list($data['province_id'], $data['city_id'], $data['region_id']) = $this->getRegionId($data);
+        // 更新收货地址
+        return $this->save([
+                'name' => $data['name'],
+                'phone' => $data['phone'],
+                'province_id' => $data['province_id'],
+                'city_id' => $data['city_id'],
+                'region_id' => $data['region_id'],
+                'detail' => $data['detail']
+            ]) !== false;
+    }
+
+    /**
+     * 设为默认收货地址
+     * @param int $addressIid
+     * @return bool
+     * @throws BaseException
+     */
+    public function setDefault(int $addressIid)
+    {
+        // 设为默认地址
+        $userId = UserService::getCurrentLoginUserId();
+        return UserModel::updateBase(['address_id' => $addressIid], ['user_id' => $userId]);
+    }
+
+    /**
+     * 删除收货地址
+     * @return bool
+     * @throws BaseException
+     */
+    public function remove()
+    {
+        // 查询当前是否为默认地址
+        $user = UserService::getCurrentLoginUser(true);
+        // 清空默认地址
+        if ($user['address_id'] == $this['address_id']) {
+            UserModel::updateBase(['address_id' => 0], ['user_id' => $this['user_id']]);
+        }
+        // 标记为已删除
+        return $this->save(['is_delete' => 1]);
+    }
+
+    /**
+     * 收货地址详情
+     * @param int $addressId
+     * @return UserAddress|array|null
+     * @throws BaseException
+     */
+    public static function detail(int $addressId)
+    {
+        $userId = UserService::getCurrentLoginUserId();
+        $detail = self::get(['user_id' => $userId, 'address_id' => $addressId]);
+        if (empty($detail)) {
+            throwError('未找到该收货地址');
+            return false;
+        }
+        return $detail;
+    }
+
+}

+ 206 - 0
app/api/model/UserCoupon.php

@@ -0,0 +1,206 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model;
+
+use app\api\service\User as UserService;
+use app\api\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;
+use app\common\library\helper;
+use cores\exception\BaseException;
+
+/**
+ * 用户优惠券模型
+ * Class UserCoupon
+ * @package app\api\model
+ */
+class UserCoupon extends UserCouponModel
+{
+    /**
+     * 获取用户优惠券列表
+     * @param int $userId
+     * @param array $param
+     * @return \think\Paginator
+     * @throws \think\db\exception\DbException
+     */
+    public function getList(int $userId, array $param): \think\Paginator
+    {
+        $filter = $this->getFilter($param);
+        return $this->where($filter)
+            ->where('user_id', '=', $userId)
+            ->paginate();
+    }
+
+    /**
+     * 检索查询条件
+     * @param array $param
+     * @return array
+     */
+    private function getFilter(array $param = []): array
+    {
+        // 设置默认查询参数
+        $params = $this->setQueryDefaultValue($param, [
+            'dataType' => 'all',     // all:全部 isUsable:可用的 isExpire:已过期 isUse:已使用
+            'amount' => null,        // 订单消费金额
+        ]);
+        // 检索列表类型
+        $filter = [];
+        // 可用的优惠券
+        if ($params['dataType'] === 'isUsable') {
+            $filter[] = ['is_use', '=', 0];
+            $filter[] = ['is_expire', '=', 0];
+            $filter[] = ['start_time', '<=', time()];
+            $filter[] = ['end_time', '>', time()];
+        }
+        // 未使用的优惠券
+        if ($params['dataType'] === 'isUnused') {
+            $filter[] = ['is_use', '=', 0];
+            $filter[] = ['is_expire', '=', 0];
+            $filter[] = ['end_time', '>', time()];
+        }
+        // 已过期的优惠券
+        if ($params['dataType'] === 'isExpire') {
+            $filter[] = ['is_expire', '=', 1];
+        }
+        // 已使用的优惠券
+        if ($params['dataType'] === 'isUse') {
+            $filter[] = ['is_use', '=', 1];
+        }
+        // 订单消费金额
+        $params['amount'] > 0 && $filter[] = ['min_price', '<=', $params['amount']];
+        return $filter;
+    }
+
+    /**
+     * 获取用户优惠券总数量(可用)
+     * @param int $userId
+     * @return int
+     */
+    public function getCount(int $userId): int
+    {
+        return $this->where('user_id', '=', $userId)
+            ->where('is_use', '=', 0)
+            ->where('is_expire', '=', 0)
+            ->where('end_time', '>', time())
+            ->count();
+    }
+
+    /**
+     * 获取用户优惠券ID集
+     * @param int $userId
+     * @return array
+     */
+    public function getUserCouponIds(int $userId): array
+    {
+        return $this->where('user_id', '=', $userId)->column('coupon_id');
+    }
+
+    /**
+     * 领取优惠券
+     * @param int $couponId 优惠券ID
+     * @return bool
+     * @throws BaseException
+     */
+    public function receive(int $couponId): bool
+    {
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId(true);
+        // 获取优惠券信息
+        $couponInfo = Coupon::detail($couponId);
+        // 验证优惠券是否可领取
+        if (!$this->checkReceive($userId, $couponInfo)) {
+            return false;
+        }
+        // 添加领取记录
+        return $this->add($userId, $couponInfo);
+    }
+
+    /**
+     * 验证优惠券是否可领取
+     * @param int $userId 当前用户ID
+     * @param CouponModel $couponInfo 优惠券详情
+     * @return bool
+     */
+    private function checkReceive(int $userId, CouponModel $couponInfo): bool
+    {
+        if (empty($couponInfo)) {
+            $this->error = '当前优惠券不存在';
+            return false;
+        }
+        // 验证优惠券状态是否可领取
+        $model = new CouponModel;
+        if (!$model->checkReceive($couponInfo)) {
+            $this->error = $model->getError() ?: '优惠券状态不可领取';
+            return false;
+        }
+        // 验证当前用户是否已领取
+        if (static::checktUserCoupon($couponInfo['coupon_id'], $userId)) {
+            $this->error = '当前用户已领取该优惠券';
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 订单结算优惠券列表
+     * @param int $userId 用户id
+     * @param float $orderPayPrice 订单商品总金额
+     * @return array
+     * @throws \think\db\exception\DbException
+     */
+    public static function getUserCouponList(int $userId, float $orderPayPrice): array
+    {
+        // 获取用户可用的优惠券列表
+        $list = (new static)->getList($userId, ['dataType' => 'isUsable', 'amount' => $orderPayPrice]);
+        $data = $list->isEmpty() ? [] : $list->toArray()['data'];
+        foreach ($data as &$item) {
+            // 计算最大能折扣的金额
+            if ($item['coupon_type'] == CouponTypeEnum::DISCOUNT) {
+                $reducePrice = helper::bcmul($orderPayPrice, $item['discount'] / 10);
+                $item['reduced_price'] = helper::bcsub($orderPayPrice, $reducePrice);
+            } else {
+                $item['reduced_price'] = $item['reduce_price'];
+            }
+        }
+        // 根据折扣金额排序并返回
+        return !empty($data) ? array_sort($data, 'reduced_price', true) : [];
+    }
+
+    /**
+     * 判断当前优惠券是否满足订单使用条件
+     * @param array $couponList
+     * @param array $orderGoodsIds 订单商品ID集
+     * @return array
+     */
+    public static function couponListApplyRange(array $couponList, array $orderGoodsIds): array
+    {
+        // 名词解释(is_apply):允许用于抵扣当前订单
+        foreach ($couponList as &$item) {
+            if ($item['apply_range'] == ApplyRangeEnum::ALL) {
+                // 1. 全部商品
+                $item['is_apply'] = true;
+            } elseif ($item['apply_range'] == ApplyRangeEnum::SOME) {
+                // 2. 指定商品, 判断订单商品是否存在可用
+                $applyGoodsIds = array_intersect($item['apply_range_config']['applyGoodsIds'], $orderGoodsIds);
+                $item['is_apply'] = !empty($applyGoodsIds);
+            } elseif ($item['apply_range'] == ApplyRangeEnum::EXCLUDE) {
+                // 2. 排除商品, 判断订单商品是否全部都在排除行列
+                $excludedGoodsIds = array_intersect($item['apply_range_config']['excludedGoodsIds'], $orderGoodsIds);
+                $item['is_apply'] = count($excludedGoodsIds) != count($orderGoodsIds);
+            }
+            !$item['is_apply'] && $item['not_apply_info'] = '该优惠券不支持当前商品';
+        }
+        return $couponList;
+    }
+}

+ 44 - 0
app/api/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\api\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);
+    }
+}

+ 36 - 0
app/api/model/article/Category.php

@@ -0,0 +1,36 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model\article;
+
+use app\common\model\article\Category as CategoryModel;
+
+/**
+ * 文章分类模型
+ * Class Category
+ * @package app\api\model\article
+ */
+class Category extends CategoryModel
+{
+    /**
+     * 获取分类列表(未隐藏的)
+     * @return \think\Collection
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getShowList()
+    {
+        $where = ['status', '=', 1];
+        return $this->getList([$where]);
+    }
+}

+ 54 - 0
app/api/model/goods/Service.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\api\model\goods;
+
+use app\common\model\goods\Service as ServiceModel;
+use app\api\model\goods\ServiceRel as ServiceRelModel;
+
+/**
+ * 商品服务与承诺模型
+ * Class Service
+ */
+class Service extends ServiceModel
+{
+    // 隐藏的字段
+    protected $hidden = [
+        'is_default',
+        'status',
+        'sort',
+        'is_delete',
+        'store_id',
+        'update_time',
+    ];
+
+    /**
+     * 获取指定商品的服务与承诺
+     * @param int $goodsId
+     * @return \think\Collection
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getListByGoods(int $goodsId)
+    {
+        // 获取指定商品的服务承诺id集
+        $serviceIds = ServiceRelModel::getServiceIds($goodsId);
+        // 获取服务与承诺列表
+        return $this->where('service_id', 'in', $serviceIds)
+            ->where('status', '=', 1)
+            ->where('is_delete', '=', 0)
+            ->order(['sort', $this->getPk()])
+            ->select();
+    }
+
+}

+ 24 - 0
app/api/model/goods/ServiceRel.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\api\model\goods;
+
+use app\common\model\goods\ServiceRel as ServiceRelModel;
+
+/**
+ * 商品服务与承诺模型
+ * Class ServiceRel
+ */
+class ServiceRel extends ServiceRelModel
+{
+
+}

+ 35 - 0
app/api/model/h5/Setting.php

@@ -0,0 +1,35 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model\h5;
+
+use app\common\model\h5\Setting as SettingModel;
+
+/**
+ * H5设置模型
+ * Class Setting
+ * @package app\api\model\h5
+ */
+class Setting extends SettingModel
+{
+    /**
+     * 验证当前是否允许访问
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public static function checkStatus(): bool
+    {
+        return (bool)static::getItem('basic', static::$storeId)['enabled'];
+    }
+}

+ 251 - 0
app/api/model/recharge/Order.php

@@ -0,0 +1,251 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\model\recharge;
+
+use app\api\model\Setting as SettingModel;
+use app\api\model\recharge\Plan as PlanModel;
+use app\api\model\recharge\OrderPlan as OrderPlanModel;
+use app\api\service\User as UserService;
+use app\common\library\helper;
+use app\common\model\recharge\Order as OrderModel;
+use app\common\service\Order as OrderService;
+use app\common\enum\recharge\order\PayStatus as PayStatusEnum;
+use app\common\enum\recharge\order\RechargeType as RechargeTypeEnum;
+use app\common\exception\BaseException;
+
+/**
+ * 用户充值订单模型
+ * Class Order
+ * @package app\api\model\recharge
+ */
+class Order extends OrderModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'transaction_id',
+        'store_id',
+        'create_time',
+        'update_time',
+    ];
+
+    /**
+     * 获取订单列表
+     * @return \think\Paginator
+     * @throws BaseException
+     * @throws \think\db\exception\DbException
+     */
+    public function getList()
+    {
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 获取列表数据
+        return $this->where('user_id', '=', $userId)
+            ->where('pay_status', '=', PayStatusEnum::SUCCESS)
+            ->order(['create_time' => 'desc'])
+            ->paginate(15);
+    }
+
+    /**
+     * 获取订单详情(待付款状态)
+     * @param $orderNo
+     * @return array|null|static
+     */
+    public static function getPayDetail(string $orderNo)
+    {
+        return self::detail(['order_no' => $orderNo, 'pay_status' => PayStatusEnum::PENDING]);
+    }
+
+    /**
+     * 创建充值订单
+     * @param int|null $planId
+     * @param float $customMoney
+     * @return bool|int
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function createOrder(?int $planId = null, float $customMoney = 0.00)
+    {
+        // 确定充值方式
+        $rechargeType = $planId > 0 ? RechargeTypeEnum::PLAN : RechargeTypeEnum::CUSTOM;
+        // 验证用户输入
+        if (!$this->validateForm($rechargeType, $planId, $customMoney)) {
+            $this->error = $this->error ?: '数据验证错误';
+            return false;
+        }
+        // 获取订单数据
+        $data = $this->getOrderData($rechargeType, $planId, $customMoney);
+        // 记录订单信息
+        return $this->saveOrder($data);
+    }
+
+    /**
+     * 保存订单记录
+     * @param $data
+     * @return bool|false|int
+     */
+    private function saveOrder(array $data)
+    {
+        // 写入订单记录
+        $this->save($data['order']);
+        // 记录订单套餐快照
+        if (!empty($data['plan'])) {
+            $PlanModel = new OrderPlanModel;
+            return $PlanModel->add($this['order_id'], $data['plan']);
+        }
+        return true;
+    }
+
+    /**
+     * 生成充值订单
+     * @param int $rechargeType 充值方式
+     * @param int $planId 方案ID
+     * @param float $customMoney 自定义金额
+     * @return array|array[]|bool
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getOrderData(int $rechargeType, int $planId, float $customMoney)
+    {
+        // 订单信息
+        $data = [
+            'order' => [
+                'user_id' => UserService::getCurrentLoginUserId(),
+                'order_no' => 'RC' . OrderService::createOrderNo(),
+                'recharge_type' => $rechargeType,
+                'gift_money' => 0.00,
+                'store_id' => self::$storeId,
+            ],
+            'plan' => []    // 订单套餐快照
+        ];
+        // 自定义金额充值
+        if ($rechargeType == RechargeTypeEnum::CUSTOM) {
+            $data = $this->createDataByCustom($data, $customMoney);
+        }
+        // 套餐充值
+        if ($rechargeType == RechargeTypeEnum::PLAN) {
+            $data = $this->createDataByPlan($data, $planId);
+        }
+        // 实际到账金额
+        $data['order']['actual_money'] = helper::bcadd($data['order']['pay_price'], $data['order']['gift_money']);
+        return $data;
+    }
+
+    /**
+     * 创建套餐充值订单数据
+     * @param array $order
+     * @param int $planId
+     * @return array
+     * @throws BaseException
+     */
+    private function createDataByPlan(array $order, int $planId)
+    {
+        // 获取套餐详情
+        $planInfo = PlanModel::detail($planId);
+        if (empty($planInfo)) {
+            throwError('充值套餐不存在');
+        }
+        $order['plan'] = $planInfo;
+        $order['order']['plan_id'] = $planInfo['plan_id'];
+        $order['order']['gift_money'] = $planInfo['gift_money'];
+        $order['order']['pay_price'] = $planInfo['money'];
+        return $order;
+    }
+
+    /**
+     * 创建自定义充值订单数据
+     * @param array $order
+     * @param float $customMoney
+     * @return array|bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function createDataByCustom(array $order, float $customMoney)
+    {
+        // 用户支付金额
+        $order['order']['pay_price'] = $customMoney;
+        // 充值设置
+        $setting = SettingModel::getItem('recharge');
+        if ($setting['is_custom'] == false) {
+            return true;
+        }
+        // 根据自定义充值金额匹配满足的套餐
+        if ($setting['is_match_plan'] == true) {
+            $matchPlanInfo = (new PlanModel)->getMatchPlan($customMoney);
+            if (!empty($matchPlanInfo)) {
+                $order['plan'] = $matchPlanInfo;
+                $order['order']['plan_id'] = $matchPlanInfo['plan_id'];
+                $order['order']['gift_money'] = $matchPlanInfo['gift_money'];
+            }
+        }
+        return $order;
+    }
+
+    /**
+     * 表单验证
+     * @param int $rechargeType
+     * @param int $planId
+     * @param float $customMoney
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function validateForm(int $rechargeType, int $planId, float $customMoney)
+    {
+        if (empty($planId) && $customMoney <= 0) {
+            $this->error = '请选择充值套餐或输入充值金额';
+            return false;
+        }
+        // 验证自定义的金额
+        if ($rechargeType == RechargeTypeEnum::CUSTOM && !$this->validateFormCustom($customMoney)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 表单验证 [自定义充值]
+     * @param float $customMoney 充值金额
+     * @return bool
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function validateFormCustom(float $customMoney)
+    {
+        // 充值设置
+        $setting = SettingModel::getItem('recharge');
+        if ($setting['is_custom'] == false) {
+            $this->error = '很抱歉,当前不允许充值自定义金额';
+            return false;
+        }
+        if ($customMoney <= 0) {
+            $this->error = '请输入正确的充值金额';
+            return false;
+        }
+        // 验证最低充值金额
+        if (helper::bccomp($customMoney, $setting['lowest_money']) === -1) {
+            $this->error = "很抱歉,当前最低充值金额不能低于{$setting['lowest_money']}元";
+            return false;
+        }
+        return true;
+    }
+}

+ 42 - 0
app/api/model/recharge/OrderPlan.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\api\model\recharge;
+
+use app\common\model\recharge\OrderPlan as OrderPlanModel;
+
+/**
+ * 用户充值订单套餐快照模型
+ * Class OrderPlan
+ * @package app\api\model\recharge
+ */
+class OrderPlan extends OrderPlanModel
+{
+    /**
+     * 新增记录
+     * @param $orderId
+     * @param $data
+     * @return false|int
+     */
+    public function add($orderId, $data)
+    {
+        return $this->save([
+            'order_id' => $orderId,
+            'plan_id' => $data['plan_id'],
+            'plan_name' => $data['plan_name'],
+            'money' => $data['money'],
+            'gift_money' => $data['gift_money'],
+            'store_id' => self::$storeId
+        ]);
+    }
+
+}

+ 67 - 0
app/api/model/recharge/Plan.php

@@ -0,0 +1,67 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types = 1);
+
+namespace app\api\model\recharge;
+
+use app\common\model\recharge\Plan as PlanModel;
+
+/**
+ * 用户充值订单模型
+ * Class Plan
+ * @package app\api\model\recharge
+ */
+class Plan extends PlanModel
+{
+    /**
+     * 隐藏字段
+     * @var array
+     */
+    protected $hidden = [
+        'sort',
+        'is_delete',
+        'store_id',
+        'create_time',
+        'update_time',
+    ];
+
+    /**
+     * 获取可用的充值套餐列表
+     * @return \think\Collection
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getList()
+    {
+        // 获取列表数据
+        return $this->where('is_delete', '=', 0)
+            ->order(['sort' => 'asc', 'money' => 'desc', 'create_time' => 'desc'])
+            ->select();
+    }
+
+    /**
+     * 根据自定义充值金额匹配满足的套餐
+     * @param $payPrice
+     * @return array|\think\Model|null
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getMatchPlan($payPrice)
+    {
+        return (new static)->where('money', '<=', $payPrice)
+            ->where('is_delete', '=', 0)
+            ->order(['money' => 'desc'])
+            ->find();
+    }
+
+}

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

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

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

+ 164 - 0
app/api/service/Cart.php

@@ -0,0 +1,164 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\service;
+
+use app\api\model\Cart as CartModel;
+use app\api\model\Goods as GoodsModel;
+use app\api\service\User as UserService;
+use cores\exception\BaseException;
+use app\common\library\helper;
+use app\common\service\BaseService;
+
+/**
+ * 服务类: 购物车
+ * Class Cart
+ * @package app\api\service
+ */
+class Cart extends BaseService
+{
+    /**
+     * 购物车商品列表(用于购物车页面)
+     * @param array $cartIds 购物车记录ID集
+     * @param bool $isGoodsGradeMoney 是否设置商品会员价
+     * @return array|\think\Collection
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getList(array $cartIds = [], bool $isGoodsGradeMoney = true)
+    {
+        // 购物车列表
+        $cartList = $this->getCartList($cartIds);
+        // 整理商品ID集
+        $goodsIds = helper::getArrayColumn($cartList, 'goods_id');
+        if (empty($goodsIds)) return [];
+        // 获取商品列表
+        $goodsList = $this->getGoodsListByIds($goodsIds, $isGoodsGradeMoney);
+        // 整理购物车商品列表
+        foreach ($cartList as $cartIdx => $item) {
+            // 查找商品, 商品不存在则删除该购物车记录
+            $result = $this->findGoods($goodsList, $item, $isGoodsGradeMoney);
+            if ($result !== false) {
+                $cartList[$cartIdx]['goods'] = $result;
+            } else {
+                $this->clear([$item['id']]);
+                unset($cartList[$cartIdx]);
+            }
+        }
+        return $cartList;
+    }
+
+    /**
+     * 获取购物车商品列表(用于订单结算台)
+     * @param array $cartIds 购物车记录ID集
+     * @return array
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function getOrderGoodsList(array $cartIds = []): array
+    {
+        // 购物车列表
+        $cartList = $this->getList($cartIds, false);
+        // 订单商品列表
+        $goodsList = [];
+        foreach ($cartList as $item) {
+            // 商品记录
+            $goods = $item['goods'];
+            // 商品单价
+            $goods['goods_price'] = $goods['skuInfo']['goods_price'];
+            // 商品购买数量
+            $goods['total_num'] = $item['goods_num'];
+            // 商品SKU索引
+            $goods['goods_sku_id'] = $item['goods_sku_id'];
+            // 商品购买总金额
+            $goods['total_price'] = helper::bcmul($goods['goods_price'], $item['goods_num']);
+            $goodsList[] = $goods;
+        }
+        return $goodsList;
+    }
+
+    /**
+     * 检索查询商品
+     * @param mixed $goodsList 商品列表
+     * @param CartModel $item 购物车记录
+     * @param bool $isGoodsGradeMoney 是否设置商品会员价
+     * @return false|mixed
+     * @throws BaseException
+     */
+    private function findGoods($goodsList, CartModel $item, bool $isGoodsGradeMoney = true)
+    {
+        // 查找商品记录
+        $goodsInfo = helper::getArrayItemByColumn($goodsList, 'goods_id', $item['goods_id']);
+        if (empty($goodsInfo)) {
+            return false;
+        }
+        // 获取当前选择的商品SKU信息
+        $goodsInfo['skuInfo'] = GoodsModel::getSkuInfo($goodsInfo, $item['goods_sku_id'], $isGoodsGradeMoney);
+        // 这里需要用到clone, 因对象是引用传递 后面的值会覆盖前面的
+        return clone $goodsInfo;
+    }
+
+    /**
+     * 删除购物车中指定记录
+     * @param array $cartIds
+     * @return bool
+     * @throws BaseException
+     */
+    public function clear(array $cartIds = []): bool
+    {
+        $model = new CartModel;
+        return $model->clear($cartIds);
+    }
+
+    /**
+     * 根据商品ID集获取商品列表
+     * @param array $goodsIds
+     * @param bool $isGoodsGradeMoney 是否设置会员折扣价
+     * @return mixed
+     */
+    private function getGoodsListByIds(array $goodsIds, bool $isGoodsGradeMoney = true)
+    {
+        $model = new GoodsModel;
+        return $model->isGoodsGradeMoney($isGoodsGradeMoney)->getListByIdsFromApi($goodsIds);
+    }
+
+    /**
+     * 获取当前用户的购物车记录
+     * @param array $cartIds 购物车记录ID集
+     * @return \think\Collection
+     * @throws BaseException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    private function getCartList(array $cartIds = []): \think\Collection
+    {
+        // 当前用户ID
+        $userId = UserService::getCurrentLoginUserId();
+        // 购物车记录模型
+        $model = new CartModel;
+        // 检索查询条件
+        $filter = [];
+        if (!empty($cartIds)) {
+            $filter[] = ['id', 'in', $cartIds];
+        }
+        // 查询列表记录
+        return $model->where($filter)
+            ->where('user_id', '=', $userId)
+            ->where('is_delete', '=', 0)
+            ->select();
+    }
+}

+ 25 - 0
app/api/service/Goods.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\api\service;
+
+use app\common\service\Goods as GoodsService;
+
+/**
+ * 商品服务类
+ * Class Goods
+ * @package app\api\service
+ */
+class Goods extends GoodsService
+{
+
+}

+ 95 - 0
app/api/service/Payment.php

@@ -0,0 +1,95 @@
+<?php
+// +----------------------------------------------------------------------
+// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2017~2021 https://www.yiovo.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
+// +----------------------------------------------------------------------
+// | Author: 萤火科技 <admin@yiovo.com>
+// +----------------------------------------------------------------------
+declare (strict_types=1);
+
+namespace app\api\service;
+
+use app\api\service\User as UserService;
+use app\api\model\wxapp\Setting as WxappSettingModel;
+use app\common\enum\OrderType as OrderTypeEnum;
+use app\common\enum\order\PayType as OrderPayTypeEnum;
+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
+            );
+        }
+        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);
+    }
+}

+ 0 - 0
app/api/service/Setting.php


Некоторые файлы не были показаны из-за большого количества измененных файлов