zxhxx 5 месяцев назад
Родитель
Сommit
54f72291b6
100 измененных файлов с 13802 добавлено и 0 удалено
  1. 1 0
      .example.env
  2. 11 0
      .gitignore
  3. 22 0
      .phpstorm.meta.php
  4. 42 0
      .travis.yml
  5. 3 0
      .version
  6. 53 0
      Dockerfile
  7. 32 0
      LICENSE.txt
  8. 1 0
      app/.htaccess
  9. 34 0
      app/AppService.php
  10. 86 0
      app/ExceptionHandle.php
  11. 68 0
      app/PATH_README.md
  12. 243 0
      app/Request.php
  13. 57 0
      app/command/ClearCacheAttachment.php
  14. 76 0
      app/command/ClearMerchantData.php
  15. 50 0
      app/command/ClearRedundancy.php
  16. 58 0
      app/command/FormatMenuPath.php
  17. 193 0
      app/command/VersionUpdate.php
  18. 45 0
      app/command/changeHotTop.php
  19. 60 0
      app/command/clearCache.php
  20. 237 0
      app/command/createTool.php
  21. 63 0
      app/command/crmebCommand.php
  22. 72 0
      app/command/resetImagePath.php
  23. 59 0
      app/command/resetPassword.php
  24. 161 0
      app/command/updateAuth.php
  25. 73 0
      app/command/updateCityArea.php
  26. 58 0
      app/command/updateSpu.php
  27. 1386 0
      app/common.php
  28. 532 0
      app/common/dao/BaseDao.php
  29. 150 0
      app/common/dao/article/ArticleCategoryDao.php
  30. 32 0
      app/common/dao/article/ArticleContentDao.php
  31. 210 0
      app/common/dao/article/ArticleDao.php
  32. 28 0
      app/common/dao/community/CommunityCategoryDao.php
  33. 227 0
      app/common/dao/community/CommunityDao.php
  34. 77 0
      app/common/dao/community/CommunityReplyDao.php
  35. 60 0
      app/common/dao/community/CommunityTopicDao.php
  36. 23 0
      app/common/dao/delivery/DeliveryOrderDao.php
  37. 23 0
      app/common/dao/delivery/DeliveryServiceDao.php
  38. 23 0
      app/common/dao/delivery/DeliveryStationDao.php
  39. 46 0
      app/common/dao/openapi/OpenAuthDao.php
  40. 87 0
      app/common/dao/store/CityAreaDao.php
  41. 78 0
      app/common/dao/store/ExcelDao.php
  42. 25 0
      app/common/dao/store/GuaranteeDao.php
  43. 41 0
      app/common/dao/store/GuaranteeTemplateDao.php
  44. 54 0
      app/common/dao/store/GuaranteeValueDao.php
  45. 50 0
      app/common/dao/store/PriceRuleDao.php
  46. 72 0
      app/common/dao/store/StoreActivityDao.php
  47. 45 0
      app/common/dao/store/StoreActivityRelatedDao.php
  48. 168 0
      app/common/dao/store/StoreAttrTemplateDao.php
  49. 118 0
      app/common/dao/store/StoreBrandCategoryDao.php
  50. 120 0
      app/common/dao/store/StoreBrandDao.php
  51. 253 0
      app/common/dao/store/StoreCategoryDao.php
  52. 26 0
      app/common/dao/store/StorePrinterDao.php
  53. 260 0
      app/common/dao/store/StoreSeckillActiveDao.php
  54. 112 0
      app/common/dao/store/StoreSeckillTimeDao.php
  55. 79 0
      app/common/dao/store/broadcast/BroadcastAssistantDao.php
  56. 175 0
      app/common/dao/store/broadcast/BroadcastGoodsDao.php
  57. 177 0
      app/common/dao/store/broadcast/BroadcastRoomDao.php
  58. 118 0
      app/common/dao/store/broadcast/BroadcastRoomGoodsDao.php
  59. 399 0
      app/common/dao/store/coupon/StoreCouponDao.php
  60. 56 0
      app/common/dao/store/coupon/StoreCouponIssueUserDao.php
  61. 103 0
      app/common/dao/store/coupon/StoreCouponProductDao.php
  62. 74 0
      app/common/dao/store/coupon/StoreCouponSendDao.php
  63. 191 0
      app/common/dao/store/coupon/StoreCouponUserDao.php
  64. 83 0
      app/common/dao/store/order/MerchantReconciliationDao.php
  65. 54 0
      app/common/dao/store/order/MerchantReconciliationOrderDao.php
  66. 105 0
      app/common/dao/store/order/PresellOrderDao.php
  67. 317 0
      app/common/dao/store/order/StoreCartDao.php
  68. 66 0
      app/common/dao/store/order/StoreCartPriceDao.php
  69. 148 0
      app/common/dao/store/order/StoreGroupOrderDao.php
  70. 33 0
      app/common/dao/store/order/StoreImportDao.php
  71. 33 0
      app/common/dao/store/order/StoreImportDeliveryDao.php
  72. 1248 0
      app/common/dao/store/order/StoreOrderDao.php
  73. 296 0
      app/common/dao/store/order/StoreOrderProductDao.php
  74. 114 0
      app/common/dao/store/order/StoreOrderProfitsharingDao.php
  75. 151 0
      app/common/dao/store/order/StoreOrderReceiptDao.php
  76. 101 0
      app/common/dao/store/order/StoreOrderStatusDao.php
  77. 257 0
      app/common/dao/store/order/StoreRefundOrderDao.php
  78. 77 0
      app/common/dao/store/order/StoreRefundProductDao.php
  79. 44 0
      app/common/dao/store/order/StoreRefundStatusDao.php
  80. 42 0
      app/common/dao/store/parameter/ParameterDao.php
  81. 42 0
      app/common/dao/store/parameter/ParameterProductDao.php
  82. 41 0
      app/common/dao/store/parameter/ParameterTemplateDao.php
  83. 43 0
      app/common/dao/store/parameter/ParameterValueDao.php
  84. 124 0
      app/common/dao/store/product/CdkeyLibraryDao.php
  85. 181 0
      app/common/dao/store/product/ProductAssistDao.php
  86. 106 0
      app/common/dao/store/product/ProductAssistSetDao.php
  87. 84 0
      app/common/dao/store/product/ProductAssistSkuDao.php
  88. 48 0
      app/common/dao/store/product/ProductAssistUserDao.php
  89. 78 0
      app/common/dao/store/product/ProductAttrDao.php
  90. 466 0
      app/common/dao/store/product/ProductAttrValueDao.php
  91. 26 0
      app/common/dao/store/product/ProductAttrValueReservationDao.php
  92. 70 0
      app/common/dao/store/product/ProductCateDao.php
  93. 94 0
      app/common/dao/store/product/ProductCdkeyDao.php
  94. 48 0
      app/common/dao/store/product/ProductContentDao.php
  95. 71 0
      app/common/dao/store/product/ProductCopyDao.php
  96. 1105 0
      app/common/dao/store/product/ProductDao.php
  97. 83 0
      app/common/dao/store/product/ProductGroupBuyingDao.php
  98. 212 0
      app/common/dao/store/product/ProductGroupDao.php
  99. 75 0
      app/common/dao/store/product/ProductGroupSkuDao.php
  100. 80 0
      app/common/dao/store/product/ProductGroupUserDao.php

+ 1 - 0
.example.env

@@ -0,0 +1 @@
+APP_KEY = '#APP_KEY#'
APP_DEBUG = false
INSTALLED = true
QUEUE_NAME = default

[APP]
DEFAULT_TIMEZONE = Asia/Shanghai

[DATABASE]
TYPE = mysql
HOSTNAME = '#DB_HOST#'
HOSTPORT = '#DB_PORT#'
USERNAME = '#DB_USER#'
PASSWORD = '#DB_PWD#'
DATABASE = '#DB_NAME#'
PREFIX   = '#DB_PREFIX#'
CHARSET  = utf8
DEBUG    = true

[LANG]
default_lang = zh-cn


[REDIS]
REDIS_HOSTNAME = '#RB_HOST#'
PORT           = '#RB_PORT#'
REDIS_PASSWORD = '#RB_PWD#'
SELECT         = '#RB_SELECT#'

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+/.idea
+/.vscode
+*.log
+.env
+.constant
+.DS_Store
+install/install.lock
+public/static/download/*
+public/phpExcel/*
+app/controller/api/Test.php
+public/protocol.html

+ 22 - 0
.phpstorm.meta.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace PHPSTORM_META {
+
+    use think\Container;
+    use function \app;
+
+    override(
+        \app(),
+        map([
+            'json' => \crmeb\services\ApiResponseService::class
+        ])
+    );
+
+    override(
+        \think\Container::make(),
+        map([
+            ''=>'@'
+        ])
+    );
+
+}

+ 42 - 0
.travis.yml

@@ -0,0 +1,42 @@
+sudo: false
+
+language: php
+
+branches:
+  only:
+    - stable
+
+cache:
+  directories:
+    - $HOME/.composer/cache
+
+before_install:
+  - composer self-update
+
+install:
+  - composer install --no-dev --no-interaction --ignore-platform-reqs
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
+  - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
+  - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
+  - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
+  - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
+
+script:
+  - php think unit
+
+deploy:
+  provider: releases
+  api_key:
+    secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
+  file:
+    - ThinkPHP_Core.zip
+    - ThinkPHP_Full.zip
+  skip_cleanup: true
+  on:
+    tags: true

+ 3 - 0
.version

@@ -0,0 +1,3 @@
+version=CRMEB-MER-v3.2
+version_code=10
+code=3.2

+ 53 - 0
Dockerfile

@@ -0,0 +1,53 @@
+# 使用基础镜像
+FROM leekay0218/crmeb-mer
+
+## 复制代码
+## 在本地调试注释掉,使用映射把文件映射进去
+#ADD ./ /var/www
+
+# 设置工作目录
+WORKDIR /var/www
+
+# 设置时区为上海
+ENV TZ=Asia/Shanghai
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
+    echo $TZ > /etc/timezone && \
+    echo '[PHP]\ndate.timezone = "'$TZ'"\n' > /usr/local/etc/php/conf.d/tzone.ini
+
+# 创建 mer_s.conf 配置文件
+RUN echo "[program:mer_s]" > /etc/supervisor/conf.d/mer_s.conf && \
+    echo "command=php think swoole restart" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "directory=/var/www/" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "autorestart=true" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "startsecs=3" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "startretries=3" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "stdout_logfile=/var/log/supervisor/mer_s.out.log" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "stderr_logfile=/var/log/supervisor/mer_s.err.log" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "stdout_logfile_maxbytes=2MB" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "stderr_logfile_maxbytes=2MB" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "user=root" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "priority=999" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "numprocs=1" >> /etc/supervisor/conf.d/mer_s.conf && \
+    echo "process_name=%(program_name)s_%(process_num)02d" >> /etc/supervisor/conf.d/mer_s.conf
+
+# 创建 mer_q.conf 配置文件
+RUN echo "[program:mer_q]" > /etc/supervisor/conf.d/mer_q.conf && \
+    echo "command=php think queue:work --tries 2" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "directory=/var/www/" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "autorestart=true" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "startsecs=3" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "startretries=3" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "stdout_logfile=/var/log/supervisor/mer_q.out.log" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "stderr_logfile=/var/log/supervisor/mer_q.err.log" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "stdout_logfile_maxbytes=2MB" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "stderr_logfile_maxbytes=2MB" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "user=root" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "priority=999" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "numprocs=1" >> /etc/supervisor/conf.d/mer_q.conf && \
+    echo "process_name=%(program_name)s_%(process_num)02d" >> /etc/supervisor/conf.d/mer_q.conf
+
+# 设置入口命令
+ENTRYPOINT ["/entrypoint.sh"]
+
+# 创建日志文件
+RUN touch /var/www/service.err.log /var/www/service.out.log

+ 32 - 0
LICENSE.txt

@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件: 
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。 
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.

+ 1 - 0
app/.htaccess

@@ -0,0 +1 @@
+deny from all

+ 34 - 0
app/AppService.php

@@ -0,0 +1,34 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types = 1);
+
+namespace app;
+
+use think\Service;
+
+/**
+ * 应用服务类
+ */
+class AppService extends Service
+{
+
+    public function register()
+    {
+        defined('TOP_PRECISION') || define('TOP_PRECISION', 2);
+        // 服务注册
+    }
+
+    public function boot()
+    {
+        // 服务启动
+    }
+}

+ 86 - 0
app/ExceptionHandle.php

@@ -0,0 +1,86 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app;
+
+use think\db\exception\DataNotFoundException;
+use think\db\exception\ModelNotFoundException;
+use think\db\exception\PDOException;
+use think\exception\ErrorException;
+use think\exception\Handle;
+use think\exception\HttpException;
+use think\exception\HttpResponseException;
+use think\exception\ValidateException;
+use think\Response;
+use Throwable;
+
+/**
+ * 应用异常处理类
+ */
+class ExceptionHandle extends Handle
+{
+    /**
+     * 不需要记录信息(日志)的异常类列表
+     * @var array
+     */
+    protected $ignoreReport = [
+        HttpException::class,
+        HttpResponseException::class,
+        ModelNotFoundException::class,
+        DataNotFoundException::class,
+        ValidateException::class,
+    ];
+
+    /**
+     * 记录异常信息(包括日志或者其它方式记录)
+     *
+     * @access public
+     * @param Throwable $exception
+     * @return void
+     */
+    public function report(Throwable $exception): void
+    {
+        // 使用内置的方式记录异常日志
+        parent::report($exception);
+    }
+
+    /**
+     * Render an exception into an HTTP response.
+     *
+     * @access public
+     * @param \think\Request $request
+     * @param Throwable $e
+     * @return Response
+     */
+    public function render($request, Throwable $e): Response
+    {
+        // 添加自定义异常处理机制
+        $this->report($e);
+        // 其他错误交给系统处理
+        if ($e instanceof ValidateException)
+            return app('json')->fail($e->getMessage());
+        else if ($e instanceof DataNotFoundException)
+            return app('json')->fail(isDebug() ? $e->getMessage() : '数据不存在');
+        else if ($e instanceof ModelNotFoundException)
+            return app('json')->fail(isDebug() ? $e->getMessage() : '数据不存在');
+        else if ($e instanceof PDOException)
+            return app('json')->fail(isDebug() ? $e->getMessage() : '数据库操作失败', isDebug() ? $e->getData() : []);
+        else if ($e instanceof ErrorException)
+            return app('json')->fail(isDebug() ? $e->getMessage() : '系统错误', isDebug() ? $e->getData() : []);
+        else if ($e instanceof \PDOException)
+            return app('json')->fail(isDebug() ? $e->getMessage() : '数据库连接失败');
+        else if ($e instanceof \EasyWeChat\Core\Exceptions\HttpException)
+            return app('json')->fail($e->getMessage());
+
+        return parent::render($request, $e);
+    }
+}

+ 68 - 0
app/PATH_README.md

@@ -0,0 +1,68 @@
+```
+├── command  #自定义命令行工具
+├── common   #服务和model等层级模块
+│   ├── dao  #数据库操作层
+│   │   ├── article     #文章管理
+│   │   ├── community   #社区
+│   │   ├── delivery    #同城配送
+│   │   ├── openapi     #开放接口
+│   │   ├── store       #商城商品等模块
+│   │   ├── system      #商城系统配置等模块
+│   │   ├── user        #用户模块
+│   │   └── wechat      #微信相关
+│   ├── middleware  #中间件
+│   ├── model  #model层
+│   │   ├── article     #文章
+│   │   ├── community   #社区
+│   │   ├── delivery    #同城配送
+│   │   ├── openapi     #开放接口
+│   │   ├── store       #商品等模块
+│   │   ├── system      #系统配置相关
+│   │   ├── user        #用户管理
+│   │   └── wechat      #微信相关
+│   └── repositories    #服务层
+│       ├── article     #文章管理
+│       ├── community   #社区
+│       ├── delivery    #同城配送
+│       ├── openapi     #开放接口
+│       ├── store       #商品等
+│       ├── system      #系统配置
+│       ├── user        #用户
+│       └── wechat      #微信相关
+├── controller  #控制器层
+│   ├── admin      #平台后台模块控制器
+│   │   ├── article     #文章
+│   │   ├── community   #社区
+│   │   ├── delivery    #同城配送
+│   │   ├── order       #订单/退款单等
+│   │   ├── parameter   #商品参数
+│   │   ├── points      #积分商城
+│   │   ├── store       #商品及活动商品
+│   │   ├── system      #系统配置等
+│   │   ├── user        #用户
+│   │   └── wechat      #微信相关
+│   ├── api         #移动端控制器   
+│   │   ├── article     #文章
+│   │   ├── community   #社区
+│   │   ├── server      #客服
+│   │   ├── store       #商品相关
+│   │   └── user        #用户
+│   ├── merchant    #商户后台模块
+│   │   ├── store       #商品相关
+│   │   ├── system      #系统配置等
+│   │   └── user        #用户
+│   ├── openapi     #开放接口模块
+│   │   └── store   
+│   ├── pc          #PC端专属接口 (只有购买PC端才会有此模块)
+│   │   └── store
+│   └── service     #客服
+├── validate    #验证器
+│   ├── admin 
+│   ├── api
+│   └── merchant
+├── view    #view页面层
+│   ├── install #安装页面
+│   └── mobile  #手机模拟
+└── webscoket   #长链接
+    └── handler #客服聊天长链接处理,消息等
+```

+ 243 - 0
app/Request.php

@@ -0,0 +1,243 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app;
+
+use crmeb\traits\Macro;
+use think\File;
+use think\file\UploadedFile;
+
+class Request extends \think\Request
+{
+    use Macro;
+
+    protected $cache = [];
+    protected $except = [
+        'content',
+        'sys_integral_rule',
+        'sys_intention_agree',
+        'sys_product_presell_agree',
+        'wechat_menus',
+        'sys_receipt_agree',
+        'sys_extension_agree',
+        'sys_merchant_type',
+        'sys_brokerage',
+        'sys_user_agree',
+        'sys_userr_privacy',
+        'sys_member',
+        'sys_about_us',
+        'sys_certificate',
+        'the_cancellation_msg',
+        'the_cancellation_prompt',
+        'platform_rule',
+        'sys_coupon_agree',
+        'sys_svip',
+    ];
+
+    public function __construct()
+    {
+        parent::__construct();
+        $this->filter[] = function ($val) {
+            return is_string($val) ? trim($val) : $val;
+        };
+    }
+
+    public function ip(): string
+    {
+        return $this->header('remote-host') ?? parent::ip();
+    }
+
+    public function isApp()
+    {
+        return $this->header('Form-type') === 'app';
+    }
+
+    /**
+     * @param $db
+     * @param $key
+     * @return bool
+     * @author xaboy
+     * @day 2020/10/22
+     */
+    public function hasCache($db, $key)
+    {
+        return isset($this->cache[$db][$key]);
+    }
+
+    /**
+     * @param $db
+     * @param $key
+     * @return array|mixed|string
+     * @author xaboy
+     * @day 2020/10/22
+     */
+    public function getCache($db, $key)
+    {
+        if (is_array($key)) {
+            $data = [];
+            foreach ($key as $v) {
+                $data[$v] = $this->getCache($db, $v);
+            }
+            return $data;
+        }
+        return $this->cache[$db][$key] ?? '';
+    }
+
+    /**
+     * @param $db
+     * @param $key
+     * @param null $value
+     * @author xaboy
+     * @day 2020/10/22
+     */
+    public function setCache($db, $key, $value = null)
+    {
+        if (!isset($this->cache[$db])) $this->cache[$db] = [];
+        if (is_array($key)) {
+            foreach ($key as $k => $v) {
+                $this->setCache($db, $k, $v);
+            }
+            return;
+        }
+        $this->cache[$db][$key] = $value;
+    }
+
+    public function clearCache()
+    {
+        $this->cache = [];
+    }
+
+    public function params(array $names, $filter = '')
+    {
+        $data = [];
+        $flag = false;
+        if ($filter === true) {
+            $filter = '';
+            $flag = true;
+        }
+        foreach ($names as $name) {
+            if (!is_array($name))
+                $data[$name] = $this->param($name, '', $filter);
+            else
+                $data[$name[0]] = $this->param($name[0], $name[1], $filter);
+        }
+        if ($data) {
+            $data = $this->filterArrayValues($data);
+        }
+        return $flag ? array_values($data) : $data;
+    }
+
+    /**
+     * 过滤接数组中的字符串
+     * @param $str
+     * @param bool $filter
+     * @return array|mixed|string|string[]
+     */
+    public function filterArrayValues($array)
+    {
+        $result = [];
+        foreach ($array as $key => $value) {
+            if (is_array($value)) {
+                // 如果值是数组,递归调用 filterArrayValues
+                $result[$key] = $this->filterArrayValues($value);
+            } else {
+                if (in_array($key, $this->except) || is_int($value) || is_null($value)) {
+                    $result[$key] = $value;
+                } else {
+                    // 如果值是字符串,过滤特殊字符
+                    $result[$key] = filter_str($value);
+                }
+            }
+        }
+        return $result;
+    }
+
+    public function merId()
+    {
+        return intval($this->hasMacro('merchantId') ? $this->merchantId() : 0);
+    }
+
+    public function merAdminId()
+    {
+        return intval($this->hasMacro('adminId') ? $this->adminId() : 0);
+    }
+
+    public function setOriginFile($name, $array)
+    {
+        $this->file[$name] = $array;
+    }
+
+    public function getOriginFile($name)
+    {
+        return $this->file[$name] ?? null;
+    }
+
+
+    protected function dealUploadFile(array $files, string $name): array
+    {
+        $array = [];
+        foreach ($files as $key => $file) {
+            if (is_array($file['name'])) {
+                $item = [];
+                $keys = array_keys($file);
+                $count = count($file['name']);
+
+                for ($i = 0; $i < $count; $i++) {
+                    if ($file['error'][$i] > 0) {
+                        if ($name == $key) {
+                            $this->throwUploadFileError($file['error'][$i]);
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    $temp['key'] = $key;
+
+                    foreach ($keys as $_key) {
+                        $temp[$_key] = $file[$_key][$i];
+
+                        $name = explode('.',$temp['name']);
+                        $num = count($name);
+                        $suffix = strtolower($name[$num - 1]);
+                        array_pop($name);
+                        $temp['name'] = implode('.',$name).'.'.$suffix;
+                    }
+
+                    $item[] = new UploadedFile($temp['tmp_name'], $temp['name'], $temp['type'], $temp['error']);
+                }
+
+                $array[$key] = $item;
+            } else {
+                if ($file instanceof File) {
+                    $array[$key] = $file;
+                } else {
+                    if ($file['error'] > 0) {
+                        if ($key == $name) {
+                            $this->throwUploadFileError($file['error']);
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    $name = explode('.',$file['name']);
+                    $num = count($name);
+                    $suffix = strtolower($name[$num - 1]);
+                    array_pop($name);
+                    $file['name'] = implode('.',$name).'.'.$suffix;
+
+                    $array[$key] = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error']);
+                }
+            }
+        }
+        return $array;
+    }
+}

+ 57 - 0
app/command/ClearCacheAttachment.php

@@ -0,0 +1,57 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use app\common\repositories\system\attachment\AttachmentRepository;
+use app\common\repositories\system\auth\MenuRepository;
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+
+/**
+ * Class FormatMenuPath
+ * @package app\command
+ * @author xaboy
+ * @day 2020/8/26
+ */
+class ClearCacheAttachment extends Command
+{
+    /**
+     * @author xaboy
+     * @day 2020/9/21
+     */
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('clear:attachment')
+            ->setDescription('clear cache attachment');
+    }
+
+
+    /**
+     * 清楚昨日海报
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @author xaboy
+     * @day 2020/9/21
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $output->writeln('开始清理');
+        app()->make(AttachmentRepository::class)->clearCache();
+        $output->writeln('开始完毕');
+    }
+
+}

+ 76 - 0
app/command/ClearMerchantData.php

@@ -0,0 +1,76 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\command;
+
+
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+use think\facade\Db;
+
+class ClearMerchantData extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('clear:merchant')
+            ->setDescription('清空数据(除系统配置以外的所有数据)');
+    }
+
+    /**
+     * 清除商城数据
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @throws \think\db\exception\DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $flag = $output->confirm($input, '清空数据前务必做好数据库的备份,防止数据被误删 !!!', false);
+        if (!$flag) return;
+        $tables = Db::query('SHOW TABLES FROM ' . env('database.database', ''));
+        $pre = env('database.prefix', '');
+        $bakTables = [
+            $pre . 'page_link',
+            $pre . 'page_category',
+            $pre . 'diy',
+            $pre . 'city_area',
+            $pre . 'express',
+            $pre . 'system_admin',
+            $pre . 'system_city',
+            $pre . 'system_config',
+            $pre . 'system_config_classify',
+            $pre . 'system_config_value',
+            $pre . 'system_group',
+            $pre . 'system_group_data',
+            $pre . 'system_menu',
+            $pre . 'system_role',
+            $pre . 'template_message',
+            $pre . 'system_notice_config',
+            $pre . 'cache',
+        ];
+
+        foreach ($tables as $table) {
+            $name = array_values($table)[0];
+            if (!in_array($name, $bakTables)) {
+                Db::table($name)->delete(true);
+            }
+        }
+        Db::table( $pre . 'cache')->whereNotIn('key','copyright_context,copyright_image,copyright_status')->delete(true);
+        $output->info('删除成功');
+    }
+
+}

+ 50 - 0
app/command/ClearRedundancy.php

@@ -0,0 +1,50 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\command;
+
+
+use app\common\repositories\system\merchant\MerchantRepository;
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+use think\facade\Log;
+
+class ClearRedundancy extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('clear:redundancy')
+            ->setDescription('已删除的商户的商品相关数据');
+    }
+
+    /**
+     * 清除回收站商品
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        try{
+            app()->make(MerchantRepository::class)->clearRedundancy();
+        }catch (\Exception $exception){
+            Log::info('清除冗余错误:'.$exception->getMessage());
+        }
+        $output->info('执行成功');
+    }
+
+}

+ 58 - 0
app/command/FormatMenuPath.php

@@ -0,0 +1,58 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use app\common\repositories\system\auth\MenuRepository;
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+
+/**
+ * Class FormatMenuPath
+ * @package app\command
+ * @author xaboy
+ * @day 2020/8/26
+ */
+class FormatMenuPath extends Command
+{
+    /**
+     * @author xaboy
+     * @day 2020/8/26
+     */
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('menu:format')
+            ->setDescription('the format menu command');
+    }
+
+
+    /**
+     * 修复菜单路径
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @author xaboy
+     * @day 2020/8/26
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $output->writeln('开启修复');
+        $menuRepository = app()->make(MenuRepository::class);
+        $menuRepository->formatPath(0);
+        $menuRepository->formatPath(1);
+        $output->writeln('执行成功');
+    }
+
+}

+ 193 - 0
app/command/VersionUpdate.php

@@ -0,0 +1,193 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\command;
+
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Option;
+use think\console\Output;
+use think\Exception;
+use think\facade\Db;
+
+class VersionUpdate extends Command
+{
+    protected function configure()
+    {
+        $this->setName('version:update')
+            ->setDescription('crmeb_merchant 版本更新命令')
+            ->addOption('package', 'p', Option::VALUE_REQUIRED, '指定更新包的路径');
+    }
+
+    /**
+     * 版本升级
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $flag = $output->confirm($input, '更新之前请务必做好数据库和代码的备份,防止数据或代码在更新中被覆盖 !!!', false);
+        if (!$flag) return;
+        $flag = $output->confirm($input, '请确保swoole服务和队列服务已关闭,防止意外报错', false);
+        if (!$flag) return;
+
+        $version = get_crmeb_version_code();
+        ini_set('memory_limit', '-1');
+        set_time_limit(0);
+
+        $packagePath = $input->getOption('package') ?: 'auto_update.zip';
+        $updateFilePath = app()->getRootPath() . ltrim($packagePath, '/= ');
+        $updatePath = dirname($updateFilePath);
+        $unzipPath = $updatePath . '/_update_runtime_' . str_replace('.', '_', $version);
+        if (!is_file($updateFilePath)) {
+            $output->warning($updateFilePath . ' 更新包不存在');
+            return;
+        }
+        $zip = new \ZipArchive();
+        if ($zip->open($updateFilePath) === true) {
+            $zip->extractTo($unzipPath);
+            $zip->close();
+        } else {
+            $output->warning($updateFilePath . ' 更新包打开失败');
+            return;
+        }
+        $unlink = function () use ($unzipPath) {
+            @unlink($unzipPath . '/update.sql');
+            @unlink($unzipPath . '/update.zip');
+            @unlink($unzipPath . '/AutoUpdate.php');
+            @unlink($unzipPath . '/.env');
+            @unlink($unzipPath . '/.config');
+            @rmdir($unzipPath);
+        };
+        if (!is_file($unzipPath . '/.env') && !is_file($unzipPath . '/.config')) {
+            $output->warning('文件不完整');
+            $unlink();
+            return;
+        }
+        if (is_file($unzipPath . '/.env')) {
+            $env = parse_ini_file($unzipPath . '/.env', true) ?: [];
+        }
+        if (is_file($unzipPath . '/.config')) {
+            $env = parse_ini_file($unzipPath . '/.config', true) ?: [];
+        }
+        if (($env['NAME'] ?? '') !== 'CRMEB-MERCHANT') {
+            $output->warning('(1)版本号对比失败,请检查当前版本号(.version/更新包)是否被修改');
+            $unlink();
+        }
+        if (($env['TYPE'] ?? '') !== 'MODEL') {
+            if (!($env['OLD_VERSION'] ?? '')) {
+                $output->warning('(2)版本号对比失败,请检查当前版本号(.version/更新包)是否被修改');
+                $unlink();
+            }
+            if ($env['OLD_VERSION'] !== $version) {
+                if (($env['UPDATE_VERSION']?? '') == $version) {
+                    $r = $output->confirm($input, '当前版本号和升级包版本号一致,是否重新覆盖操作???', false);
+                    if (!$r) return;
+                } else {
+                    $output->warning('(2)版本号对比失败,请检查当前版本号(.version/更新包)是否被修改');
+                    $unlink();
+                }
+            }
+        }
+        $r = true;
+        $confirm = [];
+        if (isset($env['confirm'])) {
+            $confirm = is_array($env['confirm']) ? $env['confirm'] : [$env['confirm']];
+        }
+        foreach ($confirm as $item) {
+            if (!$output->confirm($input, $item, false)) {
+                $unlink();
+                return;
+            }
+        }
+        $installHost = systemConfig('site_url');
+        if (substr($installHost, 0, 5) == 'https'){
+            $_url = str_replace('//' ,'\\\/\\\/', $installHost);
+        } else {
+            $_url = str_replace('http://' ,'http:\\\/\\\/', $installHost);
+        }
+
+        if (is_file($unzipPath . '/update.sql')) {
+            $str = preg_replace('/--.*/i', '', file_get_contents($unzipPath . '/update.sql'));
+            $str = preg_replace('/\/\*.*\*\/(\;)?/i', '', $str);
+            $sqlList = explode(";\n", $str);
+        } else {
+            $sqlList = [];
+        }
+        $autoUpdateData = null;
+        if (is_file($unzipPath . '/AutoUpdate.php')) {
+            try {
+                require_once $unzipPath . '/AutoUpdate.php';
+                $autoUpdateData = new \crmeb\update\AutoUpdate($input, $output);
+            } catch (\Throwable $e) {}
+        }
+
+        if ($autoUpdateData) $autoUpdateData->autoUpdateStart();
+        $output->info('开始更新');
+        $pre = env('database.prefix');
+        try {
+            Db::transaction(function () use ($pre, $output, $unzipPath, $sqlList, $autoUpdateData, $installHost, $_url, $r) {
+                if ($autoUpdateData) $autoUpdateData->autoUpdateBefore();
+                $count = count($sqlList);
+                if ($count && $autoUpdateData) {
+                    $autoUpdateData->autoSqlBefore();
+                }
+                foreach ($sqlList as $idx => $sql) {
+                    $sql = trim($sql, " \xEF\xBB\xBF\r\n");
+                    if (!$sql) continue;
+                    if ($pre && $pre !== 'eb_') {
+                        $sql = str_replace('eb_', $pre, $sql);
+                    }
+                    $sql = str_replace('https://mer1.crmeb.net', $installHost , $sql);
+                    $sql = str_replace('https:\\\/\\\/mer1.crmeb.net', $_url , $sql);
+                    try{
+                        Db::query($sql . ';');
+                    }catch (\Exception $e) {
+                        if (!$r) throw new Exception($e->getMessage());
+                    }
+                    if (!($idx % 50)) {
+                        $output->info("导入中($idx/$count)");
+                    }
+                }
+                if ($count) {
+                    if ($autoUpdateData) $autoUpdateData->autoSqlAfter();
+                    $output->info('数据库更新成功');
+                }
+                $zip = new \ZipArchive();
+                if ($zip->open($unzipPath . '/update.zip') === true) {
+                    if ($autoUpdateData) $autoUpdateData->autoCopyBefore();
+                    $zip->extractTo(app()->getRootPath());
+                    $zip->close();
+                    if ($autoUpdateData) $autoUpdateData->autoCopyAfter();
+                } else {
+                    throw new Exception('更新文件覆盖失败');
+                }
+            });
+            if ($autoUpdateData) $autoUpdateData->autoUpdateAfter();
+        } catch (\Throwable $e) {
+            $output->warning('更新失败:' . $e->getMessage());
+            $unlink();
+            if ($autoUpdateData) $autoUpdateData->autoUpdateFail($e);
+            return;
+        }
+        $unlink();
+        if ($autoUpdateData) $autoUpdateData->autoUpdateEnd();
+        $output->info('版本更新成功, 请重启swoole服务和队列服务');
+        update_crmeb_compiled();
+    }
+
+}

+ 45 - 0
app/command/changeHotTop.php

@@ -0,0 +1,45 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use crmeb\jobs\SyncProductTopJob;
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+use think\facade\Queue;
+
+class changeHotTop extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('change:hotTop')
+            ->setDescription('清楚缓存:php think change:hotTop');
+    }
+
+    /**
+     * 队列执行
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @author Qinii
+     * @day 4/24/22
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        Queue::push(SyncProductTopJob::class,[]);
+        $output->writeln('开始执行队列');
+    }
+
+}

+ 60 - 0
app/command/clearCache.php

@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\Output;
+use think\facade\Cache;
+
+class clearCache extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('clearCache')
+            ->addArgument('cacheType',Argument::OPTIONAL, 'php think menu [1] / [2]')
+            ->setDescription('清楚缓存:php think clearCache 1');
+    }
+
+    /**
+     * 清楚缓存执行
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @author Qinii
+     * @day 4/24/22
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $type = $input->getArgument('cacheType');
+        $tag = ['sys_login_freeze','mer_login_freeze'];
+        $msg = '';
+        switch ($type) {
+            case 0:
+                $msg = '平台登录限制';
+                $tag = 'sys_login_freeze';
+                break;
+            case 1:
+                $msg = '商户登录限制';
+                $tag = 'mer_login_freeze';
+                break;
+        }
+        Cache::tag($tag)->clear();
+        $output->writeln('清楚缓存'.$msg);
+    }
+
+
+}

+ 237 - 0
app/command/createTool.php

@@ -0,0 +1,237 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use think\Exception;
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+use think\console\input\Option;
+
+class createTool extends Command
+{
+    public $config;
+    public $log = '创建记录:'.PHP_EOL;
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('createTool')
+            ->addArgument('table', Option::VALUE_REQUIRED, '表名称')
+            ->addOption('path', 'p',Option::VALUE_REQUIRED, '创建路径,不填则创建在最外层')
+            ->addOption('controller',  'c',Option::VALUE_OPTIONAL,"可选参数,需要创建的控制器:admin,mer,api,pc,ser")
+            ->addOption('key', 'k',Option::VALUE_REQUIRED, '主键名称')
+            ->setDescription('创建一个新的数据表全部模块:model,dao,repository,controller');
+    }
+
+    protected function execute(Input $input, Output $output)
+    {
+        $options['table'] = $input->getArgument('table');
+        if (!preg_match('/^[a-z_]+$/', $options['table'])){
+            $output->error('表名称格式不正确:仅包含小写字母和下划线');
+            $output->error('例: php think createTool table_name -p system/merchant -c admin -k id');
+            return;
+        }
+        $options['path'] = $input->getOption('path');
+        $options['controller'] = null;
+        if ($controller = $input->getOption('controller')) {
+            $options['controller'] = explode(',',$controller);
+        }
+        $options['key'] = $input->getOption('key');
+        $output->writeln('开始执行');
+        $output->writeln('');
+        $this->create($options);
+        $output->writeln($this->log);
+        $output->writeln('执行完成');
+    }
+
+    public function create($config)
+    {
+        $table = $config['table'];
+        $prefix = env('database.prefix', '');
+        $len = strlen($prefix);
+        $_prefix = substr($table,0,$len);
+        if ($prefix == $_prefix) {
+            $table = substr($table,$len);
+        }
+        $config['table'] = $table;
+        $class_name = str_replace(' ', '', ucwords(str_replace('_', ' ', $table)));
+        $config['class_name'] = $class_name;
+        $path = explode('/', $config['path']);
+        $config['namespace_path']  = implode('\\',$path);
+        $this->config = $config;
+
+        ////生成model
+        $this->createModel();
+
+        //生成dao
+        $this->createDao();
+
+        //生成repository
+        $this->createRepository();
+
+        if ($this->config['controller']) {
+            foreach ($this->config['controller'] as $c) {
+                $this->createController($c);
+            }
+        }
+    }
+
+    public function createModel()
+    {
+        $file_path = app_path().'common/model/'.$this->config['path'];
+        $file_name = $this->config['class_name'].'.php';
+        $id = $this->config['key'] ?: $this->config['table'].'_id';
+        $content = $this->getStart();
+        $content .= "namespace app\common\model\\{$this->config['namespace_path']};
+        
+use app\common\model\BaseModel;
+
+class {$this->config['class_name']} extends BaseModel
+{
+    public static function tablePk(): ?string
+    {
+        return '{$id}';
+    }
+    
+    public static function tableName(): string
+    {
+        return '{$this->config['table']}';
+    }
+}
+";
+        $this->createFile($file_path, $file_name,$content);
+    }
+
+    public function createDao()
+    {
+        $file_path = app_path().'common/dao/'.$this->config['path'];
+        $file_name = $this->config['class_name'].'Dao.php';
+        $content = $this->getStart();
+        $content .= "namespace app\common\dao\\{$this->config['namespace_path']};
+
+use app\common\dao\BaseDao;
+use app\common\model\\{$this->config['namespace_path']}\\{$this->config['class_name']};
+
+class {$this->config['class_name']}Dao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return {$this->config['class_name']}::class;
+    }
+}";
+        $this->createFile($file_path, $file_name, $content);
+    }
+
+    public function createRepository()
+    {
+        $file_path = app_path().'common/repositories/'.$this->config['path'];
+        $file_name = $this->config['class_name'].'Repository.php';
+
+        $content = $this->getStart();
+        $content .= "namespace app\common\\repositories\\{$this->config['namespace_path']};
+
+use app\common\\repositories\BaseRepository;
+use app\common\\dao\\{$this->config['namespace_path']}\\{$this->config['class_name']}Dao;
+
+class {$this->config['class_name']}Repository extends BaseRepository
+{
+    public function __construct({$this->config['class_name']}Dao \$dao)
+    {
+        \$this->dao = \$dao;
+    }
+  
+ }";
+        $this->createFile($file_path, $file_name, $content);
+    }
+
+    public function createController($controller)
+    {
+        switch ($controller) {
+            case "admin":
+                $contr = 'admin';
+                break;
+            case "mer":
+                $contr = 'merchant';
+                break;
+            case "api":
+                $contr = 'api';
+                break;
+            case "pc":
+                $contr = 'pc';
+                break;
+            case "ser":
+                $contr = 'service';
+                break;
+            default:
+                throw new Exception('控制器类型错误');
+        }
+        $file_path = app_path().'controller/'.$contr.'/'.$this->config['path'];
+        $file_name = $this->config['class_name'].'.php';
+
+        $content = $this->getStart();
+        $content .= "namespace app\controller\\{$contr}\\{$this->config['namespace_path']};
+
+use think\App;
+use crmeb\basic\BaseController;
+use app\common\\repositories\\{$this->config['namespace_path']}\\{$this->config['class_name']}Repository;
+
+class {$this->config['class_name']} extends BaseController
+{
+    protected \$repository;
+
+    public function __construct(App \$app, {$this->config['class_name']}Repository \$repository)
+    {
+        parent::__construct(\$app);
+        \$this->repository = \$repository;
+    }
+}";
+        $this->createFile($file_path, $file_name, $content);
+    }
+
+    public function createFile($path,$name, $content)
+    {
+        if (!is_dir($path)) {
+            mkdir($path, 0777, true);
+        }
+        $file_path = $path.'/'.$name;
+        if (file_exists($file_path)) {
+            throw new \Exception('文件已存在:'.$file_path);
+        }
+        try{
+            file_put_contents($file_path, $content);
+            $this->log .= $file_path. PHP_EOL;
+            return [true, '创建成功'];
+        }catch (\Exception $exception) {
+            throw new Exception($exception->getMessage());
+        }
+    }
+
+    public function getStart()
+    {
+        $time = date('Y',time());
+        $content = '<?php'.PHP_EOL.PHP_EOL;
+        $content .= "// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~$time https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+".PHP_EOL;
+        return $content;
+    }
+}

+ 63 - 0
app/command/crmebCommand.php

@@ -0,0 +1,63 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\command;
+
+
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Option;
+use think\console\Output;
+use think\Exception;
+use think\facade\Db;
+
+class crmebCommand extends Command
+{
+    protected function configure()
+    {
+        $this->setName('cm_cli')
+            ->setDescription('crmeb_merchant命令集');
+    }
+
+    /**
+     *  获取所有可使用的命令
+     * @param Input $input
+     * @param Output $output
+     * @return void
+     * @author Qinii
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $context = '-------------------------------------------------------------------------------------------------------------- ' . PHP_EOL;
+        $context .= $this->get_msage('php think menu','自动同步路由权限');
+        $context .= $this->get_msage('php think spu','将所有商品加入到spu表');
+        $context .= $this->get_msage('php think clear:attachment','清除缓存素材,(图片信息)');
+        $context .= $this->get_msage('php think version:update','版本更新(代码升级/pc代码安装升级)');
+        $context .= $this->get_msage('php think clear:merchant','清除所有[除配置相关之外]的数据');
+        $context .= $this->get_msage('php think clear:redundancy','清除所有已删除的商户的商品相关数据');
+        $context .= $this->get_msage('php think reset:password','重置平台管理员的密码:php think reset:password admin --pwd 123456');
+        $context .= $this->get_msage('php think reset:imagePath','修改图片地址前缀: php think reset:imagePath http://old.com  http://new.com');
+        $context .= $this->get_msage('php think clear:cache','清除登录限制');
+        $context .= $this->get_msage('php think change:hotTop','更新热卖榜单');
+        $context .= $this->get_msage('php think update:city','更新城市数据:将需要导入的文件 addres.txt 文件放到项目目录下');
+        $context .= $this->get_msage('php think createTool','根据数据表创建新的模块');
+        $output->info('多商户可使用的命令:'.PHP_EOL.$context);
+    }
+
+    public function get_msage($name,$value)
+    {
+        $context = "\e[34m" . str_pad($name, 30, ' ', STR_PAD_RIGHT) .'|    '. "\e[32m" . $value . "\e[0m \n";
+        $context .= '-------------------------------------------------------------------------------------------------------------- '. PHP_EOL;
+        return $context;
+    }
+
+}

+ 72 - 0
app/command/resetImagePath.php

@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+namespace app\command;
+
+use app\common\model\article\Article;
+use app\common\model\article\ArticleCategory;
+use app\common\model\community\Community;
+use app\common\model\community\CommunityTopic;
+use app\common\model\store\broadcast\BroadcastGoods;
+use app\common\model\store\broadcast\BroadcastRoom;
+use app\common\model\store\Guarantee;
+use app\common\model\store\product\Product;
+use app\common\model\store\product\ProductAssistUser;
+use app\common\model\store\product\ProductAttrValue;
+use app\common\model\store\product\ProductGroupUser;
+use app\common\model\store\product\ProductReply;
+use app\common\model\store\product\Spu;
+use app\common\model\store\service\StoreService;
+use app\common\model\store\StoreCategory;
+use app\common\model\store\StoreSeckillTime;
+use app\common\model\system\attachment\Attachment;
+use app\common\model\system\financial\Financial;
+use app\common\model\system\merchant\Merchant;
+use app\common\model\system\merchant\MerchantIntention;
+use app\common\model\user\MemberInterests;
+use app\common\model\user\User;
+use app\common\model\user\UserBrokerage;
+use crmeb\services\ImageHostService;
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\facade\Log;
+
+class resetImagePath extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('reset:imagePath')
+            ->addArgument('origin', Argument::OPTIONAL, 'path:http:/crmeb.com')
+            ->addArgument('replace', Argument::OPTIONAL, 'path:http:/crmeb.com')
+            ->setDescription('php think reset:imagePath http://old.com  http://new.com');
+    }
+
+    /**
+     * 重置图片路径
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $origin = $input->getArgument('origin');
+        $replace = $input->getArgument('replace');
+        $output->writeln('开始执行');
+        $service = ImageHostService::getInstance();
+        $res = $service->execute($origin,$replace);
+        if ($res) {
+            $output->info('执行完成');
+        } else {
+            $output->warning('执行过程中存在错误,请在runtime/log中查看具体错误信息');
+        }
+    }
+}
+

+ 59 - 0
app/command/resetPassword.php

@@ -0,0 +1,59 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use app\common\repositories\system\admin\AdminRepository;
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\Output;
+use think\console\input\Option;
+
+class resetPassword extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('reset:password')
+            ->addArgument('root', Argument::OPTIONAL, 'root : admin')
+            ->addOption('pwd', null, Option::VALUE_REQUIRED, 'pwd : 123456')
+            ->setDescription('php think reset:password admin --pwd 123456');
+    }
+
+    /**
+     * 重置管理员密码
+     * @Author:Qinii
+     * @Date: 2020/5/15
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $account = $input->getArgument('root');
+        if ($input->hasOption('pwd')){
+            $pwd = $input->getOption('pwd');
+        }
+        $make = app()->make(AdminRepository::class);
+        $accountData = $make->accountByAdmin($account);
+        if(!$accountData) {
+            $output->warning('管理员账号不存在');
+        }else{
+            $pwd_ = $make->passwordEncode($pwd);
+            $accountData->pwd = $pwd_;
+            $accountData->save();
+            $output->info('账号:'.$account.';密码已重置:'.$pwd);
+        }
+    }
+}

+ 161 - 0
app/command/updateAuth.php

@@ -0,0 +1,161 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use Swoole\Coroutine\MySQL\Exception;
+use think\console\Command;
+use think\console\Input;
+use think\console\input\Argument;
+use think\console\input\Option;
+use think\console\Output;
+use think\event\RouteLoaded;
+use think\facade\Route;
+use app\common\repositories\system\auth\MenuRepository;
+
+class updateAuth extends Command
+{
+    protected $k = [];
+    protected $kv =[];
+
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('setAuth')
+            ->addArgument('prompt',Argument::OPTIONAL, 'php think menu [s] / [e]')
+            ->setDescription('使用方法: `php think menu` , 可选传参数 s 只显示成功信息');
+    }
+
+    /**
+     * 更新路由
+     * @Author:Qinii
+     * @Date: 2020/5/15
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $prompt = $input->getArgument('prompt');
+        $output->writeln('开始执行');
+        $output->writeln('<----------------');
+        $output->writeln('开始平台权限');
+        $sys = $this->routeList('sys',$prompt);
+
+        $output->writeln('开始商户权限');
+
+        $mer = $this->routeList('mer',$prompt);
+        $output->writeln('<----------------');
+        $output->writeln('平台执行成功,合计: '. $sys .'条 , 商户执行成功,合计: '. $mer .'条');
+    }
+
+
+    /**
+     * 路由列表
+     * @Author:Qinii
+     * @Date: 2020/5/15
+     * @param string|null $dir
+     * @return mixed
+     */
+    public function routeList($type, $prompt)
+    {
+        $this->k = [];
+        $this->kv = [];
+        $this->app->route->setTestMode(true);
+        $this->app->route->clear();
+        $path = $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR;
+
+        if ($type == 'sys')
+            include $path . 'admin.php';
+//            include $path . 'admin/config.php';
+        if ($type == 'mer')
+            include $path . 'merchant.php';
+        //触发路由载入完成事件
+        $this->app->event->trigger(RouteLoaded::class);
+        $routeList = $this->app->route->getRuleList();
+        $resp = [];
+
+        foreach ($routeList as $k => $item) {
+            if ($item['option'] && isset($item['option']['_auth']) && $item['option']['_auth']) {
+                if (!(strpos($item['name'], '/') !== false) && !(strpos($item['name'], '@') !== false)) {
+                    if (isset($item['option']['_init'])) {
+                        $route = (new $item['option']['_init'][0]())->create($item, $item['option']['_init'][1]);
+                    } else {
+                        $route = [$item];
+                    }
+                    if ($route) {
+                        foreach ($route as $one) {
+                            if (!isset($one['option']['_name'])) $one['option']['_name'] = $one['name'];
+                            $this->menu($one['option']['_path'] ?? '', $one['option'], $resp);
+                        }
+                    }
+                }
+            }
+        }
+        return app()->make(MenuRepository::class)->commandMenu($type, $resp, $prompt);
+    }
+
+
+    /**
+     * 菜单权限
+     * @param $_path
+     * @param $data
+     * @param array $resp
+     * @return array
+     * @author Qinii
+     * @day 3/18/22
+     */
+    protected function menu($_path, $data, &$resp = [], $isAppend = 0)
+    {
+        $check = true;
+            if ($_path && is_array($data)) {
+                $v = [
+                    'route'     => $data['_name'],
+                    'menu_name' => $data['_alias'] ?? '权限',
+                    'params'    => $data['_params'] ?? '',
+                ];
+                if (!isset($data['_repeat']) || (isset($data['_repeat']) && !$data['_repeat'])){
+                    $check = $this->checkRepeat($v['route'], $v['menu_name']);
+                    $this->k[] = $v['route'];
+                    $this->kv[$v['route']] = $v['menu_name'];
+                }
+
+                if (!$check) {
+                    throw new Exception( "路由名重复 < " . $v['route']. ' >' . '「'. $v['menu_name']. ' 」');
+                }
+                if ($isAppend) {
+                    $_path = 'append_'.$_path;
+                }
+
+                $resp[$_path][$data['_name']] = $v;
+
+                if (isset($data['_append']) && !empty($data['_append'])) {
+                    foreach ($data['_append'] as $datum) {
+                        $datum['_repeat'] = true;
+                        $this->menu($datum['_path'] ?? $data['_path'], $datum, $resp, 1);
+                    }
+                }
+            }
+        return $resp;
+    }
+
+    protected function checkRepeat($key, $value)
+    {
+        if (in_array($key, $this->k)) {
+            if ($value != $this->kv[$key]) {
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 73 - 0
app/command/updateCityArea.php

@@ -0,0 +1,73 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use app\common\repositories\store\CityAreaRepository;
+use app\common\repositories\store\product\SpuRepository;
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+use think\console\input\Option;
+
+class updateCityArea extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('update:city')
+            ->setDescription('更新数据地址信息,城市数据');
+    }
+
+    /**
+     * 执行城市数据更新
+     * @Author:Qinii
+     * @Date: 2020/5/15
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $output->writeln('开始执行');
+        $this->updateData();
+        $this->changSnum();
+        $output->writeln('执行完成');
+    }
+
+    /**
+     * 如果需要重新导入数据,将需要导入的文件 addres.txt 文件放到项目目录下 和 public平级
+     * @return bool
+     * @author Qinii
+     * @day 2024/1/19
+     */
+    public function updateData()
+    {
+        $fiel = root_path().'addres.txt';
+        if (file_exists($fiel)) {
+            app()->make(CityAreaRepository::class)->updateCityForTxt($fiel);
+        }
+        return true;
+    }
+
+    /**
+     *  统计每个地址的子集数量
+     * @author Qinii
+     * @day 2024/1/19
+     */
+    public function changSnum()
+    {
+        app()->make(CityAreaRepository::class)->sumChildren();
+    }
+
+}

+ 58 - 0
app/command/updateSpu.php

@@ -0,0 +1,58 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+declare (strict_types=1);
+
+namespace app\command;
+
+use app\common\repositories\store\product\SpuRepository;
+use think\console\Command;
+use think\console\Input;
+use think\console\Output;
+use think\console\input\Option;
+
+class updateSpu extends Command
+{
+    protected function configure()
+    {
+        // 指令配置
+        $this->setName('spu')
+            ->addOption('productType', null, Option::VALUE_REQUIRED, 'product type :0,1,2,3')
+            ->setDescription('the update spu command');
+    }
+
+    /**
+     * 更新商品spu
+     * @Author:Qinii
+     * @Date: 2020/5/15
+     * @param Input $input
+     * @param Output $output
+     * @return int|void|null
+     */
+    protected function execute(Input $input, Output $output)
+    {
+        $prodcutType = [];
+        if ($input->hasOption('productType')){
+            $tyep = $input->getOption('productType');
+            if(in_array($tyep,[0,1,2,3,4,20])) $prodcutType = [$tyep];
+        }
+        $output->writeln('开始执行');
+        $this->checkAndUpdateSpu($prodcutType);
+        $output->writeln('执行完成');
+    }
+
+    public function checkAndUpdateSpu($prodcutType)
+    {
+        //TODO  如果数据量过大运行时间过长请设置脚本运行时间
+//        set_time_limit(0);
+        app()->make(SpuRepository::class)->updateSpu($prodcutType);
+    }
+}

+ 1386 - 0
app/common.php

@@ -0,0 +1,1386 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+// 应用公共文件
+
+use app\common\repositories\system\config\ConfigValueRepository;
+use app\common\repositories\system\groupData\GroupDataRepository;
+use app\common\repositories\system\StorageRepository;
+use crmeb\services\UploadService;
+use Swoole\Lock;
+use think\db\BaseQuery;
+
+if (!function_exists('go')) {
+    function go(): bool
+    {
+        return \Swoole\Coroutine::create(...func_get_args());
+    }
+}
+
+if (!function_exists('isDebug')) {
+    function isDebug(): bool
+    {
+        return !!env('APP_DEBUG');
+    }
+}
+
+if (!function_exists('formToData')) {
+    function formToData($form): array
+    {
+        $rule = $form->formRule();
+        $action = $form->getAction();
+        $method = $form->getMethod();
+        $title = $form->getTitle();
+        $config = (object)$form->formConfig();
+        $admin = config('admin.api_admin_prefix');
+        $merchant = config('admin.api_merchant_prefix');
+        $api = $action;
+        if (strpos($api, '/' . $admin) === 0) {
+            $api = substr($api, strlen($admin) + 1);
+        } else if (strpos($api, '/' . $merchant) === 0) {
+            $api = substr($api, strlen($merchant) + 1);
+        }
+        $api = str_replace('.html', '', $api);
+        return compact('rule', 'action', 'method', 'title', 'config', 'api');
+    }
+}
+
+if (!function_exists('getDistance')) {
+
+    function getDistance($lat1, $lng1, $lat2, $lng2)
+    {
+        //将角度转为狐度
+        $radLat1 = deg2rad($lat1); //deg2rad()函数将角度转换为弧度
+        $radLat2 = deg2rad($lat2);
+        $radLng1 = deg2rad($lng1);
+        $radLng2 = deg2rad($lng2);
+        $a = $radLat1 - $radLat2;
+        $b = $radLng1 - $radLng2;
+        $s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2))) * 6371;
+        return round($s, 1);
+    }
+}
+
+/**
+ * 无线级分类处理
+ *
+ * @param array $data 数据源
+ * @param string $idName 主键
+ * @param string $fieldName 父级字段
+ * @param string $childrenKey 子级字段名
+ * @return array
+ * @author 张先生
+ * @date 2020-03-27
+ */
+if (!function_exists('formatCategory')) {
+    function formatCategory(array $data, string $idName = "id", string $fieldName = 'pid', $childrenKey = 'children')
+    {
+        $items = [];
+        foreach ($data as $item) {
+            $items[$item[$idName]] = $item;
+        }
+        $result = array();
+        foreach ($items as $item) {
+            if (isset($items[$item[$fieldName]])) {
+                $items[$item[$fieldName]][$childrenKey][] = &$items[$item[$idName]];
+            } else if ($item[$fieldName] == 0) {
+                $result[] = &$items[$item[$idName]];
+            }
+        }
+        return $result;
+    }
+}
+
+if (!function_exists('formatTreeList')) {
+    function formatTreeList(&$options, $name, $pidName = 'pid', $pid = 0, $level = 0, &$data = []): array
+    {
+        $_options = $options;
+        foreach ($_options as $k => $option) {
+            if ($option[$pidName] == $pid) {
+                $data[] = ['value' => $k, 'label' => str_repeat('|---', $level + 1) . $option[$name]];
+                unset($options[$k]);
+                formatTreeList($options, $name, $pidName, $k, $level + 1, $data);
+            }
+        }
+        return $data;
+    }
+}
+
+if (!function_exists('formatTree')) {
+    function formatTree(&$options, $name, $pidName = 'pid', $pid = 0, $level = 0, $data = []): array
+    {
+        $_options = $options;
+        foreach ($_options as $k => $option) {
+            if ($option[$pidName] == $pid) {
+                $value = ['id' => $k, 'title' => $option[$name]];
+                unset($options[$k]);
+                $value['children'] = formatTree($options, $name, $pidName, $k, $level + 1);
+                $data[] = $value;
+            }
+        }
+        return $data;
+    }
+}
+
+if (!function_exists('formatCascaderData')) {
+    function formatCascaderData(&$options, $name, $baseLevel = 0, $pidName = 'pid', $pid = 0, $level = 0, $data =
+    [], $disabled = []): array
+    {
+        $_options = $options;
+        foreach ($_options as $k => $option) {
+            if ($option[$pidName] == $pid) {
+                $value = ['value' => $k, 'label' => $option[$name],'disabled' => false];
+                if ($disabled) {
+                    $value['disabled'] = in_array($k,$disabled) ? false : true;
+                }
+                unset($options[$k]);
+                $value['children'] = formatCascaderData($options, $name, $baseLevel, $pidName, $k, $level + 1,[],$disabled);
+                if (!count($value['children'])) unset($value['children']);
+                $data[] = $value;
+            }
+        }
+        return $data;
+    }
+}
+
+
+/**
+ * @function toMap 数组重新组装
+ * @param array $data 数据
+ * @param string $field key
+ * @param string $value value default null
+ * @return array
+ * @author 张先生
+ * @date 2020-04-01
+ */
+if (!function_exists('toMap')) {
+    function toMap(array $data, $field = 'id', $value = '')
+    {
+        $result = array();
+
+        if (empty($data)) {
+            return $result;
+        }
+
+        //开始处理数据
+        foreach ($data as $item) {
+            $val = $item;
+            if (!empty($value)) {
+                $val = $item[$value];
+            }
+            $result[$item[$field]] = $val;
+        }
+
+        return $result;
+    }
+}
+
+/**
+ * @function getUniqueListByArray 从数组中获取某个字段的值,重新拼装成新的一维数组
+ * @param array $data 数据
+ * @param string $field key
+ * @return array
+ * @author 张先生
+ * @date 2020-04-01
+ */
+if (!function_exists('getUniqueListByArray')) {
+    function getUniqueListByArray(array $data, $field = 'id')
+    {
+        return array_unique(array_values(array_column($data, $field)));
+    }
+}
+
+
+if (!function_exists('isPhone')) {
+    function isPhone($test)
+    {
+        return !preg_match("/^1[3456789]{1}\d{9}$/", $test);
+    }
+}
+
+if (!function_exists('getMonth')) {
+    /**
+     * 获取本季度 time
+     * @param int|string $time
+     * @param $ceil
+     * @return array
+     */
+    function getMonth($time = '', $ceil = 0)
+    {
+        if ($ceil != 0)
+            $season = ceil(date('n') / 3) - $ceil;
+        else
+            $season = ceil(date('n') / 3);
+        $firstday = date('Y-m-01', mktime(0, 0, 0, ($season - 1) * 3 + 1, 1, date('Y')));
+        $lastday = date('Y-m-t', mktime(0, 0, 0, $season * 3, 1, date('Y')));
+        return array($firstday, $lastday);
+    }
+}
+
+
+if (!function_exists('getModelTime')) {
+    /**
+     * @param BaseQuery $model
+     * @param string $section
+     * @param string $prefix
+     * @param string $field
+     * @return mixed
+     * @author xaboy
+     * @day 2020-04-29
+     */
+    function getModelTime(BaseQuery $model, string $section, $prefix = 'create_time', $field = '-',$time = '')
+    {
+        if (!isset($section)) return $model;
+        switch ($section) {
+            case 'today':
+                $model->whereBetween($prefix, [date('Y-m-d H:i:s', strtotime('today')), date('Y-m-d H:i:s', strtotime('tomorrow -1second'))]);
+                break;
+            case 'week':
+                $model->whereBetween($prefix, [date('Y-m-d H:i:s', strtotime('this week 00:00:00')), date('Y-m-d H:i:s', strtotime('next week 00:00:00 -1second'))]);
+                break;
+            case 'month':
+                $model->whereBetween($prefix, [date('Y-m-d H:i:s', strtotime('first Day of this month 00:00:00')), date('Y-m-d H:i:s', strtotime('first Day of next month 00:00:00 -1second'))]);
+                break;
+            case 'year':
+                $model->whereBetween($prefix, [date('Y-m-d H:i:s', strtotime('this year 1/1')), date('Y-m-d H:i:s', strtotime('next year 1/1 -1second'))]);
+                break;
+            case 'yesterday':
+                $model->whereBetween($prefix, [date('Y-m-d H:i:s', strtotime('yesterday')), date('Y-m-d H:i:s', strtotime('today -1second'))]);
+                break;
+            case 'quarter':
+                list($startTime, $endTime) = getMonth();
+                $model = $model->where($prefix, '>', $startTime);
+                $model = $model->where($prefix, '<', $endTime);
+                break;
+            case 'lately7':
+                $model = $model->where($prefix, 'between', [date('Y-m-d', strtotime("-7 day")), date('Y-m-d H:i:s')]);
+                break;
+            case 'lately30':
+                $model = $model->where($prefix, 'between', [date('Y-m-d', strtotime("-30 day")), date('Y-m-d H:i:s')]);
+                break;
+            default:
+                if (strstr($section, $field) !== false) {
+                    list($startTime, $endTime) = explode($field, $section);
+                    if (strlen($startTime) == 4) {
+                        $model->whereBetweenTime($prefix, date('Y-m-d H:i:s', strtotime($section)), date('Y-m-d H:i:s', strtotime($section . ' +1day -1second')));
+                    } else {
+                        if ($startTime == $endTime) {
+                            $model = $model->whereBetweenTime($prefix, date('Y-m-d 0:0:0', strtotime($startTime)), date('Y-m-d 23:59:59', strtotime($endTime)));
+                        } else if(strpos($startTime, ':')) {
+                            $model = $model->whereBetweenTime($prefix, $startTime, $endTime);
+                        } else {
+                            $model = $model->whereBetweenTime($prefix, date('Y-m-d H:i:s', strtotime($startTime)), date('Y-m-d H:i:s', strtotime($endTime . ' +1day -1second')));
+                        }
+                    }
+                }
+                break;
+        }
+        return $model;
+    }
+}
+
+if (!function_exists('hasMany')) {
+    function hasMany($collection, $field, $model, $searchKey, $insertKey, $where = [] ,$select = '*')
+    {
+        $ids = [];
+        $link = [];
+
+        if (!$collection) return [];
+        $collection = $collection->toArray();
+        foreach ($collection as $k => $item) {
+            if (is_array($item[$field])) {
+                $link[$k] = array_unique($item[$field]);
+                $ids = array_merge($item[$field], $ids);
+            } else {
+                $link[$k] = array_unique(explode(',', $item[$field]));
+            }
+            $ids = array_merge($link[$k], $ids);
+            if (isset($collection[$k][$insertKey])) unset($collection[$k][$insertKey]);
+        }
+        $ids = array_filter(array_unique($ids));
+        if (!count($ids)) {
+            return $collection;
+        }
+        $many = $model::whereIn($searchKey, array_unique($ids))->where($where)->field($select)->select();
+
+        if (!$many) return $collection;
+        $many = $many->toArray();
+        foreach ($link as $k => $val) {
+            foreach ($many as $item) {
+                if (in_array($item[$searchKey], $val)) {
+
+                    if (!isset($collection[$k][$insertKey])) $collection[$k][$insertKey] = [];
+
+                    $collection[$k][$insertKey][] = $item;
+                }
+            }
+        }
+
+        return $collection;
+    }
+}
+
+if (!function_exists('activeProductSku')) {
+    //格式活动商品SKU
+    function activeProductSku($activeData, $type = null)
+    {
+        $make = app()->make(\app\common\repositories\store\product\ProductRepository::class);
+        $price = 0;
+        $data = [];
+        foreach($activeData as $key => $value) {
+            $maxPrice = 0;
+            $must_price = 0;
+            $attrValue = [];
+            if(is_null($value['product'])) continue;
+            $productSku = $value['productSku'];
+            $productAttr = $value['product']['attr'];
+            $productAttrValue = $value['product']['attrValue'];
+            unset($value['productSku'], $value['product']['attrValue'], $value['product']['attr']);
+            foreach ($productAttrValue as $attr_value) {
+                if (!empty($productSku)){
+                    foreach ($productSku as $sk => $sv) {
+                        if ( $sv['unique'] == $attr_value['unique']) {
+                            if ($type == 'discounts') {
+                                unset($attr_value['ot_price'], $attr_value['price']);
+                                $attr_value['ot_price'] = $sv['price'];
+                                $attr_value['price']    = $sv['active_price'];
+                                $_price = bcsub($sv['price'], $sv['active_price'], 2);
+                                if ($value['type']){
+                                    $must_price = $must_price > $_price ? $must_price : $_price;
+                                } else {
+                                    $maxPrice = $maxPrice > $_price ? $maxPrice : $_price;
+                                }
+                            } else {
+                                $attr_value['productSku'] = $sv;
+                            }
+                            $attrValue[] = $attr_value;
+                        }
+                    }
+                }
+            }
+            $attr = $make->detailAttr($productAttr);
+            if ($type == 'discounts') {
+                $sku = $make->detailAttrValue($attrValue, null);
+                $value['product']['sku'] = $sku;
+
+            } else {
+                $value['product']['attrValue'] = $attrValue;
+            }
+            $value['product']['attr'] = $attr;
+            $price = bcadd($price, bcadd($must_price,$maxPrice,2), 2);
+            if ($value['type'] == 1) {
+                array_unshift($data,$value);
+            }else {
+                $data[] = $value;
+            }
+        }
+        return compact('data', 'price');
+    }
+}
+
+
+if (!function_exists('systemConfig')) {
+    /**
+     * 获取系统配置
+     *
+     * @param string|string[] $key
+     * @return mixed
+     * @author xaboy
+     * @day 2020-05-08
+     */
+    function systemConfig($key)
+    {
+        return merchantConfig(0, $key);
+    }
+}
+
+if (!function_exists('systemConfigNoCache')) {
+    /**
+     * 获取系统配置 不读缓存
+     *
+     * @param string|string[] $key
+     * @return mixed
+     * @author xaboy
+     */
+    function systemConfigNoCache($key)
+    {
+        return merchantConfigNoCache(0, $key);
+    }
+}
+
+if (!function_exists('merchantConfigNoCache')) {
+    /**
+     * 获取商户配置 不读缓存
+     *
+     * @param int $merId
+     * @param string|string[] $key
+     * @return mixed
+     * @author xaboy
+     */
+    function merchantConfigNoCache(int $merId, $key)
+    {
+        $request = app('request');
+        $make = app()->make(ConfigValueRepository::class);
+        if (is_array($key)) {
+            $_key = [];
+            $cacheData = [];
+            foreach ($key as $v) {
+                if ($request->hasCache($merId, $v)) {
+                    $cacheData[$v] = $request->getCache($merId, $v);
+                } else {
+                    $_key[] = $v;
+                }
+            }
+            if (!count($_key)) return $cacheData;
+            $data = $make->more($_key, $merId);
+            $request->setCache($merId, $data);
+            $data += $cacheData;
+        } else {
+            if ($request->hasCache($merId, $key)) {
+                $data = $request->getCache($merId, $key);
+            } else {
+                $data = $make->get($key, $merId);
+                $request->setCache($merId, $key, $data);
+            }
+        }
+        return $data;
+    }
+}
+
+if (!function_exists('merchantConfig')) {
+    /**
+     * 获取商户配置
+     *
+     * @param int $merId
+     * @param string|string[] $key
+     * @return mixed
+     * @author xaboy
+     * @day 2020-05-08
+     */
+    function merchantConfig(int $merId, $key)
+    {
+        return app()->make(ConfigValueRepository::class)->getConfig($merId,$key);
+    }
+}
+
+if (!function_exists('getDatesBetweenTwoDays')) {
+    function getDatesBetweenTwoDays($startDate, $endDate, $md = 'm-d')
+    {
+        $dates = [];
+        if (strtotime($startDate) > strtotime($endDate)) {
+            //如果开始日期大于结束日期,直接return 防止下面的循环出现死循环
+            return $dates;
+        } elseif ($startDate == $endDate) {
+            //开始日期与结束日期是同一天时
+            array_push($dates, date($md, strtotime($startDate)));
+            return $dates;
+        } else {
+            array_push($dates, date($md, strtotime($startDate)));
+            $currentDate = $startDate;
+            do {
+                $nextDate = date('Y-m-d', strtotime($currentDate . ' +1 days'));
+                array_push($dates, date($md, strtotime($currentDate . ' +1 days')));
+                $currentDate = $nextDate;
+            } while ($endDate != $currentDate);
+            return $dates;
+        }
+    }
+}
+
+if (!function_exists('getStartModelTime')) {
+    function getStartModelTime(string $section)
+    {
+        switch ($section) {
+            case 'today':
+            case 'yesterday':
+                return date('Y-m-d', strtotime($section));
+            case 'week':
+                return date('Y-m-d', strtotime('this week'));
+            case 'month':
+                return date('Y-m-d', strtotime('first Day of this month'));
+            case 'year':
+                return date('Y-m-d', strtotime('this year 1/1'));
+            case 'quarter':
+                list($startTime, $endTime) = getMonth();
+                return $startTime;
+            case 'lately7':
+                return date('Y-m-d', strtotime("-7 day"));
+            case 'lately30':
+                return date('Y-m-d', strtotime("-30 day"));
+            default:
+                if (strstr($section, '-') !== false) {
+                    list($startTime, $endTime) = explode('-', $section);
+                    return date('Y-m-d H:i:s', strtotime($startTime));
+                }
+                return date('Y-m-d H:i:s');
+        }
+    }
+}
+
+
+
+if (!function_exists('systemGroupData')) {
+    /**
+     * 获取总后台组合数据配置
+     *
+     * @param string $key
+     * @param int|null $page
+     * @param int|null $limit
+     * @return array
+     * @author xaboy
+     * @day 2020/5/27
+     */
+    function systemGroupData(string $key, ?int $page = null, ?int $limit = 10)
+    {
+        $make = app()->make(GroupDataRepository::class);
+        return $make->groupData($key, 0, $page, $limit);
+    }
+}
+
+if (!function_exists('merchantGroupData')) {
+    /**
+     * 获取商户后台组合数据配置
+     *
+     * @param int $merId
+     * @param string $key
+     * @param int|null $page
+     * @param int|null $limit
+     * @return array
+     * @author xaboy
+     * @day 2020/5/27
+     */
+    function merchantGroupData(int $merId, string $key, ?int $page = null, ?int $limit = 10)
+    {
+        $make = app()->make(GroupDataRepository::class);
+        return $make->groupData($key, $merId, $page, $limit);
+    }
+}
+
+if (!function_exists('filter_emoji')) {
+
+    // 过滤掉emoji表情
+    function filter_emoji($str)
+    {
+        $str = preg_replace_callback(    //执行一个正则表达式搜索并且使用一个回调进行替换
+            '/./u',
+            function (array $match) {
+                return strlen($match[0]) >= 4 ? '' : $match[0];
+            },
+            $str
+        );
+        return $str;
+    }
+}
+
+if (!function_exists('setHttpType')) {
+
+    /**
+     *  修改 https 和 http 移动到common
+     * @param string $url 域名
+     * @param int $type 0 返回https 1 返回 http
+     * @return string
+     */
+    function setHttpType($url, $type = 0)
+    {
+        $domainTop = substr($url, 0, 5);
+        if ($type) {
+            if ($domainTop == 'https') $url = 'http' . substr($url, 5, strlen($url));
+        } else {
+            if ($domainTop != 'https') $url = 'https:' . substr($url, 5, strlen($url));
+        }
+        return $url;
+    }
+}
+
+if (!function_exists('remoteImage')) {
+
+    /**
+     *  获取小程序二维码是否生成
+     * @param $url
+     * @return array
+     */
+    function remoteImage($url)
+    {
+        $curl = curl_init();
+        curl_setopt($curl, CURLOPT_URL, $url);
+        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+        $result = curl_exec($curl);
+        $result = json_decode($result, true);
+        if (is_array($result)) return ['status' => false, 'msg' => $result['errcode'] . '---' . $result['errmsg']];
+        return ['status' => true];
+    }
+}
+
+if (!function_exists('image_to_base64')) {
+    /**
+     * 获取图片转为base64
+     * @param string $avatar
+     * @return bool|string
+     */
+    function image_to_base64($avatar = '', $timeout = 9)
+    {
+        checkSuffix($avatar);
+        try {
+            $url = parse_url($avatar);
+            if (is_file($file = public_path().$url['path'])) {
+                return "data:image/jpeg;base64," . base64_encode(file_get_contents($file, 1));
+            }
+            $url = $url['host'];
+            $header = [
+                'User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0',
+                'Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
+                'Accept-Encoding: gzip, deflate, br',
+                'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
+                'Host:' . $url
+            ];
+            $dir = pathinfo($url);
+            $host = $dir['dirname'];
+            $refer = $host . '/';
+            $curl = curl_init();
+            curl_setopt($curl, CURLOPT_REFERER, $refer);
+            curl_setopt($curl, CURLOPT_URL, $avatar);
+            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+            curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
+            curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeout);
+            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
+            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
+            $data = curl_exec($curl);
+            $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+            curl_close($curl);
+            if ($code == 200) {
+                return "data:image/jpeg;base64," . base64_encode($data);
+            } else {
+                return false;
+            }
+        } catch (Exception $e) {
+            return false;
+        }
+    }
+}
+
+
+if (!function_exists('put_image')) {
+    /**
+     * 获取图片转为base64
+     * @param string $avatar
+     * @return bool|string
+     */
+    function put_image($url, $filename = '')
+    {
+
+        if ($url == '') {
+            return false;
+        }
+        try {
+            if ($filename == '') {
+
+                $ext = pathinfo($url);
+                if ($ext['extension'] != "jpg" && $ext['extension'] != "png" && $ext['extension'] != "jpeg") {
+                    return false;
+                }
+                $filename = time() . "." . $ext['extension'];
+            }
+
+            //文件保存路径
+            ob_start();
+            readfile($url);
+            $img = ob_get_contents();
+            ob_end_clean();
+            $path = 'public/uploads/qrcode';
+            $fp2 = fopen($path . '/' . $filename, 'a');
+            fwrite($fp2, $img);
+            fclose($fp2);
+            return $path . '/' . $filename;
+        } catch (Exception $e) {
+            return false;
+        }
+    }
+}
+
+if (!function_exists('path_to_url')) {
+    /**
+     * 路径转url路径
+     * @param $path
+     * @return string
+     */
+    function path_to_url($path)
+    {
+        return trim(str_replace(DIRECTORY_SEPARATOR, '/', $path), '.');
+    }
+}
+
+if (!function_exists('tidy_url')) {
+    /**
+     * 路径转url路径
+     * @param $url
+     * @param int $http
+     * @param string $site
+     * @return string
+     */
+    function tidy_url($url, $http = null, $site = null)
+    {
+        if (!$site) {
+            $site = systemConfig('site_url');
+        }
+        $url = path_to_url($url);
+        if (strpos($url, 'http') === false)
+            $url = rtrim($site, '/') . '/' . ltrim($url, '/');
+
+        if (is_null($http)) {
+            $http = (parse_url($site)['scheme'] ?? '') == 'https' ? 0 : 1;
+        }
+        $url = set_http_type($url, $http);
+        return $url;
+    }
+}
+
+
+if (!function_exists('curl_file_exist')) {
+    /**
+     * CURL 检测远程文件是否在
+     * @param $url
+     * @return bool
+     */
+    function curl_file_exist($url)
+    {
+        $ch = curl_init();
+        try {
+            curl_setopt($ch, CURLOPT_URL, $url);
+            curl_setopt($ch, CURLOPT_HEADER, 1);
+            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
+            $contents = curl_exec($ch);
+            if (preg_match("/404/", $contents)) return false;
+            if (preg_match("/403/", $contents)) return false;
+            return true;
+        } catch (Exception $e) {
+            return false;
+        }
+    }
+}
+
+
+if (!function_exists('set_http_type')) {
+    /**
+     * 修改 https 和 http
+     * @param string $url 域名
+     * @param int $type 0 返回https 1 返回 http
+     * @return string
+     */
+    function set_http_type($url, $type = 0)
+    {
+        $domainTop = substr($url, 0, 5);
+        if ($type) {
+            if ($domainTop == 'https') $url = 'http' . substr($url, 5, strlen($url));
+        } else {
+            if ($domainTop != 'https') $url = 'https:' . substr($url, 5, strlen($url));
+        }
+        return $url;
+    }
+}
+
+if (!function_exists('setSharePoster')) {
+    /**
+     *  生成分享二维码图片
+     * @param array $config
+     * @param $path
+     * @return array|bool|string
+     * @throws Exception
+     */
+    function setSharePoster($config, $path)
+    {
+        $imageDefault = array(
+            'left' => 0,
+            'top' => 0,
+            'right' => 0,
+            'bottom' => 0,
+            'width' => 100,
+            'height' => 100,
+            'opacity' => 100
+        );
+        $textDefault = array(
+            'text' => '',
+            'left' => 0,
+            'top' => 0,
+            'fontSize' => 32,       //字号
+            'fontColor' => '255,255,255', //字体颜色
+            'angle' => 0,
+        );
+        $background = $config['background']; //海报最底层得背景
+        if (substr($background, 0, 1) === '/') {
+            $background = substr($background, 1);
+        }
+        $backgroundInfo = getimagesize($background);
+        $background = imagecreatefromstring(file_get_contents($background));
+        $backgroundWidth = $backgroundInfo[0];  //背景宽度
+        $backgroundHeight = $backgroundInfo[1];  //背景高度
+        $imageRes = imageCreatetruecolor($backgroundWidth, $backgroundHeight);
+        $color = imagecolorallocate($imageRes, 0, 0, 0);
+        imagefill($imageRes, 0, 0, $color);
+        imagecopyresampled($imageRes, $background, 0, 0, 0, 0, imagesx($background), imagesy($background), imagesx($background), imagesy($background));
+        if (!empty($config['image'])) {
+            foreach ($config['image'] as $key => $val) {
+                $val = array_merge($imageDefault, $val);
+                $info = getimagesize($val['url']);
+                $function = 'imagecreatefrom' . image_type_to_extension($info[2], false);
+                if ($val['stream']) {
+                    $info = getimagesizefromstring($val['url']);
+                    $function = 'imagecreatefromstring';
+                }
+                $res = $function($val['url']);
+                $resWidth = $info[0];
+                $resHeight = $info[1];
+                $canvas = imagecreatetruecolor($val['width'], $val['height']);
+                imagefill($canvas, 0, 0, $color);
+                imagecopyresampled($canvas, $res, 0, 0, 0, 0, $val['width'], $val['height'], $resWidth, $resHeight);
+                $val['left'] = $val['left'] < 0 ? $backgroundWidth - abs($val['left']) - $val['width'] : $val['left'];
+                $val['top'] = $val['top'] < 0 ? $backgroundHeight - abs($val['top']) - $val['height'] : $val['top'];
+                imagecopymerge($imageRes, $canvas, $val['left'], $val['top'], $val['right'], $val['bottom'], $val['width'], $val['height'], $val['opacity']); //左,上,右,下,宽度,高度,透明度
+            }
+        }
+        if (isset($config['text']) && !empty($config['text'])) {
+            foreach ($config['text'] as $key => $val) {
+                $val = array_merge($textDefault, $val);
+                list($R, $G, $B) = explode(',', $val['fontColor']);
+                $fontColor = imagecolorallocate($imageRes, $R, $G, $B);
+                $val['left'] = $val['left'] < 0 ? $backgroundWidth - abs($val['left']) : $val['left'];
+                $val['top'] = $val['top'] < 0 ? $backgroundHeight - abs($val['top']) : $val['top'];
+                imagettftext($imageRes, $val['fontSize'], $val['angle'], $val['left'], $val['top'], $fontColor, $val['fontPath'], $val['text']);
+            }
+        }
+        ob_start();
+        imagejpeg($imageRes);
+        imagedestroy($imageRes);
+        $res = ob_get_contents();
+        ob_end_clean();
+        $key = substr(md5(rand(0, 9999)), 0, 5) . date('YmdHis') . rand(0, 999999) . '.jpg';
+        $uploadType = (int)systemConfig('upload_type') ?: 1;
+        $upload = UploadService::create($uploadType);
+        $res = $upload->to($path)->validate()->stream($res, $key);
+        if ($res === false) {
+            return $upload->getError();
+        } else {
+            $info = $upload->getUploadInfo();
+            $info['image_type'] = $uploadType;
+            return $info;
+        }
+    }
+}
+
+if (!function_exists('getTimes')) {
+    function getTimes()
+    {
+        $dates = [];
+        for ($i = 0; $i <= 24; $i++) {
+            for ($j = 0; $j < 60; $j++) {
+                $dates[] = sprintf('%02.d', $i) . ':' . sprintf('%02.d', $j);
+            }
+        }
+        return $dates;
+    }
+}
+
+if (!function_exists('monday')) {
+    /**
+     * 获取周一
+     *
+     * @param null $time
+     * @return false|string
+     * @author xaboy
+     * @day 2020/6/22
+     */
+    function monday($time = null)
+    {
+        return date('Y-m-d', strtotime('Sunday -6 day', $time ?: time()));
+    }
+}
+
+if (!function_exists('orderLock')) {
+    /**
+     * @param string $name
+     * @return Lock
+     * @author xaboy
+     * @day 2020/8/25
+     */
+    function makeLock($name = 'default'): Lock
+    {
+        return $GLOBALS['_swoole_order_lock'][$name];
+    }
+}
+
+if (!function_exists('get_crmeb_version')) {
+    /**
+     * 获取CRMEB系统版本号
+     * @param string $default
+     * @return string
+     */
+    function get_crmeb_version($default = 'v1.0.0')
+    {
+        try {
+            $version = parse_ini_file(app()->getRootPath() . '.version');
+            return $version['version'] ?? $default;
+        } catch (Throwable $e) {
+            return $default;
+        }
+    }
+}
+
+if (!function_exists('get_crmeb_version_code')) {
+    /**
+     * 获取CRMEB系统版本号
+     * @param string $default
+     * @return string
+     */
+    function get_crmeb_version_code($default = '1.7.2')
+    {
+        try {
+            $version = parse_ini_file(app()->getRootPath() . '.version');
+            return $version['code'] ?? $default;
+        } catch (Throwable $e) {
+            return $default;
+        }
+    }
+}
+
+if (!function_exists('update_crmeb_compiled')) {
+    /**
+     * 获取CRMEB系统版本号
+     * @param string $default
+     * @return string
+     */
+    function update_crmeb_compiled($default = 'v1.0.0')
+    {
+        $compiled = [
+            '7.1' => 'compiled71',
+            '7.2' => 'compiled72',
+            '7.3' => 'compiled73',
+            '7.4' => 'compiled74',
+        ];
+
+        $phpv = @phpversion();
+        $phpvs = substr($phpv, 0, 3);
+        $key = $compiled[$phpvs] ?? '';
+        if (!$key)
+            return false;
+        $root = app()->getRootPath();
+        $compiledPath = $root . 'install/compiled';
+        $file = $root . 'install/compiled/' . $key . '.zip';
+        $toPath = $root . 'crmeb/basic';
+        $toConfigPath = $root . 'config/crmeb.php';
+        $demoImage = $root.'public/uploads'.'images.zip';
+        try {
+            if (is_file($file)) {
+                $zip = new ZipArchive();
+                if ($zip->open($file) === true) {
+                    $zip->extractTo($compiledPath . '/');
+                    $zip->close();
+                }
+                if (is_dir($compiledPath . '/basic')) {
+                    if (is_dir($toPath) || mkdir($toPath, 0777) || is_dir($toPath)) {
+                        foreach (glob($compiledPath . '/basic/*') as $item) {
+                            @rename($item, $toPath . '/' . pathinfo($item, PATHINFO_BASENAME));
+                        }
+                    }
+                    @rmdir($compiledPath . '/basic');
+                }
+                if (is_file($compiledPath . '/crmeb.php')) {
+                    @rename($compiledPath . '/crmeb.php', $toConfigPath);
+                }
+            }
+        } catch (\Exception $exception) {
+            return false;
+        }
+        try{
+            if (is_file($demoImage)) {
+                $zip = new ZipArchive();
+                if ($zip->open($demoImage) === true) {
+                    $zip->extractTo($compiledPath . '/');
+                    $zip->close();
+                }
+            }
+        }catch (\Exception $exception) {
+
+        }
+        return true;
+    }
+}
+if (!function_exists('attr_format')) {
+
+    /**
+     * 格式化属性
+     * @param $arr
+     * @return array
+     */
+    function attr_format($arr): array
+    {
+        $len = count($arr);
+        $title = array_column($arr, 'value');
+        $result = [];
+
+        if ($len > 0) {
+            if ($len > 1) {
+                $result = $arr[0]['detail'];
+                for ($i = 0; $i < $len - 1; $i++) {
+                    $temp = $result;
+                    $result = [];
+                    foreach ($temp as $item) {
+                        foreach ($arr[$i + 1]['detail'] as $datum) {
+                            $result[] = trim($item) . ',' . trim($datum);
+                        }
+                    }
+                }
+            } else {
+                foreach ($arr[0]['detail'] as $item) {
+                    $result[] = trim($item);
+                }
+            }
+        }
+        return [$result, $title];
+    }
+}
+
+if (!function_exists('filter_emoji')) {
+    //过滤掉emoji表情
+    function filter_emoji($str)
+    {
+        $str = preg_replace_callback('/./u', function (array $match) {
+            return strlen($match[0]) >= 4 ? '' : $match[0];
+        }, $str);
+        return $str;
+    }
+}
+
+/*
+ *  腾讯地图转换百度地图 GCJ02 转 BD09
+ * 中国正常GCJ02坐标---->百度地图BD09坐标
+ * 腾讯地图/高德地图用的也是GCJ02坐标
+ * @param double $lat 纬度
+ * @param double $lng 经度
+ */
+if (!function_exists('gcj02ToBd09')) {
+    function gcj02ToBd09($lng, $lat)
+    {
+        $x_pi = 3.14159265358979324 * 3000.0 / 180.0;
+        $x = $lng;
+        $y = $lat;
+        $z = sqrt($x * $x + $y * $y) - 0.00002 * sin($y * $x_pi);
+        $theta = atan2($y, $x) - 0.000003 * cos($x * $x_pi);
+
+        $lng = $z * cos($theta) + 0.0065;
+        $lat = $z * sin($theta) + 0.006;
+        return [$lng,$lat];
+    }
+}
+
+if (!function_exists('lbs_address')) {
+    function lbs_address($region, $address)
+    {
+        $locationOption = new \Joypack\Tencent\Map\Bundle\AddressOption(systemConfig('tx_map_key'));
+        $locationOption->setAddress($address);
+        $locationOption->setRegion($region);
+        $location = new \Joypack\Tencent\Map\Bundle\Address($locationOption);
+        $res = $location->request();
+        if ($res->error) {
+            throw new \think\exception\ValidateException($res->error);
+        }
+        if ($res->status) {
+            throw new \think\exception\ValidateException($res->message);
+        }
+        if (!$res->result) {
+            throw new \think\exception\ValidateException('获取失败');
+        }
+        return $res->result;
+    }
+}
+
+
+if (!function_exists('aj_captcha_check_one')) {
+    /**
+     * 验证滑块1次验证
+     * @param string $token
+     * @param string $pointJson
+     * @return bool
+     */
+    function aj_captcha_check_one(string $captchaType, string $token, string $pointJson)
+    {
+        aj_get_serevice($captchaType)->check($token, $pointJson);
+        return true;
+    }
+}
+
+if (!function_exists('aj_captcha_check_two')) {
+    /**
+     * 验证滑块2次验证
+     * @param string $token
+     * @param string $pointJson
+     * @return bool
+     */
+    function aj_captcha_check_two(string $captchaType, string $captchaVerification )
+    {
+        aj_get_serevice($captchaType)->verificationByEncryptCode($captchaVerification);
+        return true;
+    }
+}
+
+
+if (!function_exists('aj_captcha_create')) {
+    /**
+     * 创建验证码
+     * @return array
+     */
+    function aj_captcha_create(string $captchaType)
+    {
+        return aj_get_serevice($captchaType)->get();
+    }
+}
+
+if (!function_exists('aj_get_serevice')) {
+
+    function aj_get_serevice(string $captchaType)
+    {
+        $config = \think\facade\Config::get('ajcaptcha');
+        switch ($captchaType) {
+            case "clickWord":
+                $service = new \Fastknife\Service\ClickWordCaptchaService($config);
+//                $service = new  \crmeb\services\BlockPuzzleCaptchaService($config);
+                break;
+            case "blockPuzzle":
+                $service = new \Fastknife\Service\BlockPuzzleCaptchaService($config);
+                //$service = new  \crmeb\services\BlockPuzzleCaptchaService($config);
+                break;
+            default:
+                throw new \think\exception\ValidateException('captchaType参数不正确:'.$captchaType);
+        }
+        return $service;
+    }
+}
+
+if (!function_exists('checkSuffix')) {
+    function checkSuffix($data)
+    {
+        $suffix = \think\facade\Config::get('upload.fileExt');
+        if (is_array($data)){
+            foreach ($data as $datum) {
+                if (strpos($datum,'phar://') !== false)
+                    throw new \think\exception\ValidateException('操作失败');
+                $result = pathinfo($datum);
+                if (isset($result['extension']) && !in_array($result['extension'],$suffix)) {
+                    throw new \think\exception\ValidateException('文件后缀不允许');
+                }
+            }
+        } else {
+            if (strpos($data,'phar://') !== false )
+                throw new \think\exception\ValidateException('操作失败');
+            $result = pathinfo($data);
+            if (isset($result['extension']) && !in_array($result['extension'],$suffix)) {
+                throw new \think\exception\ValidateException('文件后缀不允许');
+            }
+        }
+        return ;
+    }
+}
+
+if (!function_exists('strUcwords')) {
+    function strUcwords($str)
+    {
+        return str_replace(' ', '', ucwords(str_replace('_', ' ', $str)));
+    }
+}
+
+
+if(!function_exists('getThumbWaterImage')){
+    /**
+     * 获取缩略图水印文件
+     * @param string $filePath 文件路径
+     * @param string $fileName 文件名称
+     * @param string $type 大中小 ['big', 'mid', 'small'] / all
+     * @param string $rename 重命名字段,也就是将获取的图片以新的字段名添加到列表
+     * @return mixed
+     */
+    function getThumbWaterImage($list,array $fields = ['image'], string $type = 'all', $fileName = '', $rename = '')
+    {
+        $upload_type = systemConfig('upload_type') ?: 1;
+        $image_thumb_status = systemConfig('image_thumb_status');
+        if ($image_thumb_status) {
+            $upload = UploadService::create($upload_type);
+            $domain = app()->make(StorageRepository::class)->domains($upload_type);
+            foreach ($list as &$item){
+                foreach ($fields as $field) {
+                    $f = $rename ? $rename : $field ;
+                    if(is_array($item[$field]) && !empty($item[$field])){
+                        $imageList = $item[$field];
+                        $item[$f] = [];
+                        foreach ($imageList as $filePath){
+                            $host = parse_url($filePath);
+                            if (isset($host['scheme']) && in_array($host['scheme'].'://'.$host['host'], $domain) ) {
+                                $thumbResult = $upload->thumb($filePath,$fileName,$type);
+                                $item[$f][] = $thumbResult[$type] ?: $item[$field];
+                            } else {
+                                $item[$f][] = $filePath;
+                            }
+                        }
+                    }else{
+                        $filePath = $item[$field];
+                        $host = parse_url($filePath);
+                        if (isset($host['scheme']) && in_array($host['scheme'].'://'.$host['host'], $domain)) {
+                            $thumbResult = $upload->thumb($filePath, $fileName, $type);
+                            $item[$f] = $thumbResult[$type] ?: $item[$field];
+                        } else {
+                            $item[$f] = $filePath;
+                        }
+                    }
+                }
+            }
+        }
+        return $list;
+    }
+}
+
+if(!function_exists('get_extension_info')){
+    /**
+     * @param $user 用户信息
+     * @return array
+     */
+    function get_extension_info($user = null)
+    {
+        /**
+         *  是否开启分销
+         *  分销方式
+         *  是否为分销员
+         *  是否内购
+         */
+        $config = systemConfig(['extension_status','promoter_type','extension_self','extension_pop']);
+        $extension_status = $config['extension_status'];
+        $promoter_type = $config['promoter_type'];
+        $isPromoter = 0;//是否分销员
+        $isSelfBuy = 0;//是否内购
+        $promoter_switch = 1;
+        if ($extension_status && $user) {
+            if ($user) {
+                $promoter_switch = $user['promoter_switch'] ?? 0;
+            }
+            if ($promoter_switch) {
+                $isPromoter = ($user['is_promoter'] ?? 0) ? 1 : 0;
+                //如果需要人人分销不设置为每个人都是 is_promoter = 1 ; 就开启这里
+                //if (!$isPromoter) {
+                //    $isPromoter = systemConfig('promoter_type') == 2  ? 1 : 0;
+                //}
+                $isSelfBuy = $isPromoter && systemConfig('extension_self') ? 1 : 0;//是否内购
+            }
+        }
+        // 佣金显示弹框开关
+        //0:全部可见
+        //1:推广员可见
+        //2:非推广员可见
+        //3:关闭
+        $extension_pop = 0;
+        if ($extension_status) {
+            $extension_pop = 1;
+            switch ($config['extension_pop']) {
+                case 1:
+                    $extension_pop = $isPromoter ? 1 : 0;
+                    break;
+                case 2:
+                    $extension_pop = $isPromoter ? 0 : 1;
+                    break;
+                case 3:
+                    $extension_pop = 0;
+                    break;
+            }
+        }
+        return compact('isPromoter','isSelfBuy','extension_status','promoter_type','extension_pop');
+    }
+}
+
+if (!function_exists('filter_str')) {
+    /**
+     * 过滤字符串敏感字符
+     * @param $str
+     * @return array|mixed|string|string[]|null
+     */
+    function filter_str($str)
+    {
+        $rules = [
+            '/\.\./', // 禁用包含 ../ 的参数
+            '/\<\?/', // 禁止 php 脚本出现
+            '/\bor\b.*=.*/i', // 匹配 'or 1=1',防止 SQL 注入(注意边界词 \b 和不区分大小写 i 修饰符)
+            '/(select[\s\S]*?)(from|limit)/i', // 防止 SQL 注入
+            '/(union[\s\S]*?select)/i', // 防止 SQL 注入
+            '/(having|updatexml|extractvalue)/i', // 防止 SQL 注入
+            '/sleep\((\s*)(\d*)(\s*)\)/i', // 防止 SQL 盲注
+            '/benchmark\((.*)\,(.*)\)/i', // 防止 SQL 盲注
+            '/base64_decode\(/i', // 防止 SQL 变种注入
+            '/(?:from\W+information_schema\W)/i', // 注意这里的 (?:...) 是不合法的,应该是 (?:...) 表示非捕获组,但通常我们不需要这个
+            '/(?:current_|user|database|schema|connection_id)\s*\(/i', // 防止 SQL 注入(注意去掉了不必要的 (?:...))
+            '/(?:etc\/\W*passwd)/i', // 防止窥探 Linux 用户信息
+            '/into(\s+)(?:dump|out)file\s*/i', // 禁用 MySQL 导出函数
+            '/group\s+by.+\(/i', // 防止 SQL 注入
+            '/(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\(/i', // 禁用 webshell 相关某些函数
+            '/(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/\//i', // 防止一些协议攻击(注意协议后的三个斜杠)
+            '/\$_(GET|POST|COOKIE|FILES|SESSION|ENV|GLOBALS|SERVER)\[/i', // 禁用一些内置变量,注意 PHP 变量名通常是大写的
+            '/<(iframe|script|body|img|layer|div|meta|style|base|object|input)/i', // 防止 XSS 标签植入
+            '/(onmouseover|onerror|onload|onclick)\=/i', // 防止 XSS 事件植入
+            '/\|\|.*?(?:ls|pwd|whoami|ll|ifconfig|ipconfig|&&|chmod|cd|mkdir|rmdir|cp|mv)/i', // 防止执行 shell(注意去掉了不合适的 ifconfog)
+            '/\sand\s+.*=.*/i' // 匹配 and 1=1
+        ];
+        if (filter_var($str, FILTER_VALIDATE_URL)) {
+            $url = parse_url($str);
+            if (!isset($url['scheme'])) return $str;
+            $host = $url['scheme'] . '://' . $url['host'];
+            $str = $host . preg_replace($rules, '', str_replace($host, '', $str));
+        } else {
+            $str = preg_replace($rules, '', $str);
+        }
+        return $str;
+    }
+}
+if(!function_exists('generateTimeRanges')) {
+    /**
+     * 生成时间段数组
+     *
+     * @param string $startTime
+     * @param string $endTime
+     * @param integer $intervalMinutes
+     * @return array
+     */
+    function generateTimeRanges(string $startTime, string $endTime, int $intervalMinutes = 10) : array
+    {
+        // 验证时间间隔
+        if ($intervalMinutes < 10) {
+            throw new \think\exception\ValidateException('时间间隔必须大于等于10分钟');
+        }
+        // 转换起止时间为时间戳
+        $startTimestamp = strtotime($startTime);
+        $endTimestamp = strtotime($endTime);
+        if ($startTimestamp >= $endTimestamp) {
+            throw new \think\exception\ValidateException('起始时间必须小于结束时间');
+        }
+        // 时间间隔转换为秒
+        $intervalSeconds = $intervalMinutes * 60;
+        // 初始化时间段数组,生成时间段
+        $timeRanges = [];
+        for ($current = $startTimestamp; $current < $endTimestamp; $current += $intervalSeconds) {
+             // 下一个时间段的开始时间
+            $next = $current + $intervalSeconds;
+            // 处理最后一个时间段
+            if ($next > $endTimestamp) {
+                $next = $endTimestamp;
+            }
+            // 添加时间段到数组中
+            $timeRanges[] = [
+                'start' => date('H:i', $current),
+                'end' => date('H:i', $next)
+            ];
+        }
+        return $timeRanges;
+    }
+}

+ 532 - 0
app/common/dao/BaseDao.php

@@ -0,0 +1,532 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao;
+
+
+use app\common\model\BaseModel;
+use Carbon\Carbon;
+use crmeb\services\crud\CrudFormEnum;
+use think\Collection;
+use think\db\Builder;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\db\Query;
+use think\Model;
+use crmeb\services\crud\CrudOperatorEnum;
+
+/**
+ * Class BaseDao
+ * @package app\common\dao
+ * @author xaboy
+ * @day 2020-03-30
+ */
+abstract class BaseDao
+{
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    abstract protected function getModel(): string;
+
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    public function getPk()
+    {
+        return ($this->getModel())::tablePk();
+    }
+
+    /**
+     * @param int $id
+     * @return bool
+     * @author xaboy
+     * @day 2020-03-27
+     */
+    public function exists(int $id)
+    {
+        return $this->fieldExists($this->getPk(), $id);
+    }
+
+    public function merInExists(int $merId, $ids)
+    {
+        $pk = ($this->getModel())::getDB()->where('mer_id',$merId)->where($this->getPk(),'in',$ids)->column($this->getPk());
+        $ids = is_array($ids) ? $ids : explode(',',$ids);
+        sort($ids);
+        sort($pk);
+        return $ids == $pk;
+    }
+
+    /**
+     * @param array $where
+     * @return BaseModel
+     */
+    public function query(array $where):Query
+    {
+        return ($this->getModel())::getInstance()->where($where);
+    }
+
+    /**
+     * @param $field
+     * @param $value
+     * @param int|null $except
+     * @return bool
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    public function fieldExists($field, $value, ?int $except = null): bool
+    {
+        $query = ($this->getModel())::getDB()->where($field, $value);
+        if (!is_null($except)) $query->where($this->getPk(), '<>', $except);
+        return $query->count() > 0;
+    }
+
+    /**
+     * @param array $data
+     * @return self|Model
+     * @author xaboy
+     * @day 2020-03-27
+     */
+    public function create(array $data)
+    {
+        return ($this->getModel())::create($data);
+    }
+
+
+    /**
+     * @param int $id
+     * @param array $data
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020-03-27
+     */
+    public function update(int $id, array $data)
+    {
+        return ($this->getModel())::getDB()->where($this->getPk(), $id)->update($data);
+    }
+
+    /**
+     * @param array $ids
+     * @param array $data
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020/6/8
+     */
+    public function updates(array $ids, array $data)
+    {
+        return ($this->getModel())::getDB()->whereIn($this->getPk(), $ids)->update($data);
+    }
+
+
+    /**
+     * @param int $id
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020-03-27
+     */
+    public function delete(int $id)
+    {
+        return ($this->getModel())::getDB()->where($this->getPk(), $id)->delete();
+    }
+
+
+    /**
+     * @param int $id
+     * @return array|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020-03-27
+     */
+    public function get($id)
+    {
+        return ($this->getModel())::getInstance()->find($id);
+    }
+
+    /**
+     * @param array $where
+     * @param string $field
+     * @return array|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/1
+     */
+    public function getWhere(array $where, string $field = '*', array $with = [])
+    {
+        return ($this->getModel())::getInstance()->where($where)->when($with, function ($query) use ($with) {
+            $query->with($with);
+        })->field($field)->find();
+    }
+
+    /**
+     * @param array $where
+     * @param string $field
+     * @return Collection
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/1
+     */
+    public function selectWhere(array $where, string $field = '*')
+    {
+        return ($this->getModel())::getInstance()->where($where)->field($field)->select();
+    }
+
+    /**
+     * @param int $id
+     * @param array $with
+     * @return array|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020-03-27
+     */
+    public function getWith(int $id, $with = [])
+    {
+        return ($this->getModel())::getInstance()->with($with)->find($id);
+    }
+
+
+    /**
+     * @param array $data
+     * @return int
+     * @author xaboy
+     * @day 2020/6/8
+     */
+    public function insertAll(array $data)
+    {
+        return ($this->getModel())::getDB()->insertAll($data);
+    }
+
+    /**
+     *  通过条件判断是否存在
+     * @param array $where
+     * @author Qinii
+     * @day 2020-06-13
+     */
+    public function getWhereCount(array $where)
+    {
+        return ($this->getModel()::getDB())->where($where)->count();
+    }
+
+    public function existsWhere($where)
+    {
+        return ($this->getModel())::getDB()->where($where)->count() > 0;
+    }
+
+    /**
+     *  查询,如果不存在就创建
+     * @Author:Qinii
+     * @Date: 2020/9/8
+     * @param array $where
+     * @return array|Model|null
+     */
+    public function findOrCreate(array $where)
+    {
+       $res = ($this->getModel()::getDB())->where($where)->find();
+       if(!$res)$res = $this->getModel()::create($where);
+       return $res;
+    }
+
+    /**
+     *  搜索
+     * @param $where
+     * @return BaseModel
+     * @author Qinii
+     * @day 2020-10-16
+     */
+    public function getSearch(array $where)
+    {
+        foreach ($where as $key => $item) {
+                if ($item !== '') {
+                $keyArray[] = $key;
+                $whereArr[$key] = $item;
+            }
+        }
+        if(empty($keyArray)){
+            return ($this->getModel())::getDB();
+        }else{
+            return ($this->getModel())::withSearch($keyArray, $whereArr);
+        }
+    }
+
+    /**
+     *  自增
+     * @param array $id
+     * @param string $field
+     * @param int $num
+     * @return mixed
+     * @author Qinii
+     * @day 1/11/21
+     */
+    public function incField(int $id, string $field , $num = 1)
+    {
+        return ($this->getModel()::getDB())->where($this->getPk(),$id)->inc($field,$num)->update();
+    }
+
+    /**
+     *  自减
+     * @param array $id
+     * @param string $field
+     * @param int $num
+     * @return mixed
+     * @author Qinii
+     * @day 1/11/21
+     */
+    public function decField(int $id, string $field , $num = 1)
+    {
+        return ($this->getModel()::getDB())
+            ->where($this->getPk(),$id)
+            ->where($field, '>=' ,$num)
+            ->dec($field,$num)->update();
+    }
+
+    public function merHas(int $merId, int $id, ?int $isDel = 0)
+    {
+        return ($this->getModel()::getDB())->where($this->getPk(), $id)->where('mer_id', $merId)
+                ->when(!is_null($isDel), function($query) use($isDel) {
+                    $query->where('is_del', $isDel);
+                })->count($this->getPk()) > 0;
+    }
+
+    public function viewSearch(array $viewSearch = [], $query = null, string $defaulAlias = '')
+    {
+        if (is_null($query)) {
+            $query = $this->getModel();
+        }
+        if(empty($viewSearch) || !isset($viewSearch[0]['boolean'])) return $query;
+        $logic = $viewSearch[0]['boolean'] == 'and' ? 'where' : 'whereOr';
+        $query = $query->where(function (Query $query) use ($viewSearch, $logic, $defaulAlias) {
+            foreach ($viewSearch as $search) {
+                if (empty($search['field_name']) || empty($search['operator'])) {
+                    continue;
+                }
+                if (!isset($search['value'])) {
+                    $search['value'] = '';
+                }
+                if (strstr($search['field_name'], '.') !== false) {
+                    $fieldName = $search['field_name'];
+                } else {
+                    $alias     = $item['alias'] ?? $defaulAlias;
+                    $fieldName = ($alias ? $alias . '.' : '') . $search['field_name'];
+                }
+                $query->{$logic}(function(Query  $query) use($search, $fieldName){
+                    switch ($search['operator']) {
+                        case CrudOperatorEnum::OPERATOR_IN :
+                            if (isset($search['form_value'])) {
+                                switch ($search['form_value']) {
+                                    case CrudFormEnum::FORM_INPUT:
+                                    case CrudFormEnum::FORM_TEXTAREA:
+                                        if (is_array($search['value'])) {
+                                            $search['value'] = json_encode($search['value']);
+                                        }
+                                        $query = $query->where($fieldName, 'LIKE', '%' . $search['value'] . '%');
+                                        break;
+                                    case CrudFormEnum::FORM_TAG:
+                                    case CrudFormEnum::FORM_CHECKBOX:
+                                    case CrudFormEnum::FORM_CASCADER_ADDRESS:
+                                        $tags  = is_array($search['value']) ? $search['value'] : explode(',', $search['value']);
+                                        $query = $query->where(function ($query) use ($tags, $fieldName) {
+                                            foreach ($tags as $i => $tag) {
+                                                if ($i) {
+                                                    $query->whereOr($fieldName, 'like', '%/' . $tag . '/%');
+                                                } else {
+                                                    $query->where($fieldName, 'like', '%/' . $tag . '/%');
+                                                }
+                                            }
+                                        });
+                                        break;
+                                    case CrudFormEnum::FORM_CASCADER_RADIO:
+                                        $query = $query->where(function ($query) use ($search, $fieldName) {
+                                            foreach ($search['value'] as $i => $val) {
+                                                $val = implode('/', $val);
+                                                if ($i) {
+                                                    $query->whereOr($fieldName, 'like', '%/' . $val . '/%');
+                                                } else {
+                                                    $query->where($fieldName, 'like', '%/' . $val . '/%');
+                                                }
+                                            }
+                                        });
+                                        break;
+                                    default:
+                                        $query = $query->whereIn($fieldName, is_array($search['value']) ? $search['value'] : explode(',',$search['value']));
+                                        break;
+                                }
+                            } else {
+                                $query = $query->whereIn($fieldName, is_array($search['value']) ? $search['value'] : [$search['value']]);
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_NOT_IN:
+                            if (isset($search['form_value'])) {
+                                switch ($search['form_value']) {
+                                    case CrudFormEnum::FORM_INPUT:
+                                    case CrudFormEnum::FORM_TEXTAREA:
+                                        if (is_array($search['value'])) {
+                                            $search['value'] = json_encode($search['value']);
+                                        }
+                                        $query = $query->whereNot($fieldName, 'LIKE', '%' . $search['value'] . '%');
+                                        break;
+                                    case CrudFormEnum::FORM_CASCADER_RADIO:
+                                        $query = $query->whereNot(function ($query) use ($search, $fieldName) {
+                                            foreach ($search['value'] as $i => $val) {
+                                                $val = implode('/', $val);
+                                                if ($i) {
+                                                    $query->whereOr($fieldName, 'like', '%/' . $val . '/%');
+                                                } else {
+                                                    $query->where($fieldName, 'like', '%/' . $val . '/%');
+                                                }
+                                            }
+                                        });
+                                        break;
+                                    case CrudFormEnum::FORM_TAG:
+                                    case CrudFormEnum::FORM_CHECKBOX:
+                                    case CrudFormEnum::FORM_CASCADER_ADDRESS:
+                                        $tags  = is_array($search['value']) ? $search['value'] : explode(',', $search['value']);
+                                        $query = $query->whereNot(function ($query) use ($tags, $fieldName) {
+                                            foreach ($tags as $i => $tag) {
+                                                if ($i) {
+                                                    $query->whereOr($fieldName, 'like', '%/' . $tag . '/%');
+                                                } else {
+                                                    $query->where($fieldName, 'like', '%/' . $tag . '/%');
+                                                }
+                                            }
+                                        });
+                                        break;
+                                    default:
+                                        $query = $query->whereIn($fieldName, is_array($search['value']) ? $search['value'] : [$search['value']]);
+                                        break;
+                                }
+                            } else {
+                                $query = $query->whereNotIn($fieldName, is_array($search['value']) ? $search['value'] : [$search['value']]);
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_EQ:
+                            switch ($search['form_value']) {
+                                case CrudFormEnum::FORM_DATE_TIME_PICKER:
+                                    $query = $query->whereDay($fieldName,$search['value']);
+                                    break;
+                                default:
+                                    $query = $query->where($fieldName, $search['value']);
+                                    break;
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_GT:
+                            switch ($search['form_value']) {
+                                case CrudFormEnum::FORM_DATE_TIME_PICKER:
+                                    $query = $query->whereTime($fieldName,'>', $search['value'].'23:59:59');
+                                    break;
+                                default:
+                                    $query = $query->where($fieldName,'>', $search['value']);
+                                    break;
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_GT_EQ:
+                            switch ($search['form_value']) {
+                                case CrudFormEnum::FORM_DATE_TIME_PICKER:
+                                    $query = $query->whereTime($fieldName,'>=', $search['value']);
+                                    break;
+                                default:
+                                    $query = $query->where($fieldName, '>=', $search['value']);
+                                    break;
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_LT:
+                            switch ($search['form_value']) {
+                                case CrudFormEnum::FORM_DATE_TIME_PICKER:
+                                    $query = $query->whereTime($fieldName,'<', $search['value']);
+                                    break;
+                                default:
+                                    $query = $query->where($fieldName, '<', $search['value']);
+                                    break;
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_LT_EQ:
+                            switch ($search['form_value']) {
+                                case CrudFormEnum::FORM_DATE_TIME_PICKER:
+                                    $query = $query->whereTime($fieldName,'<=', $search['value']. ' 23:59:59');
+                                    break;
+                                default:
+                                    $query = $query->where($fieldName, '<=', $search['value']);
+                                    break;
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_NOT_EQ:
+                            switch ($search['form_value']) {
+                                case CrudFormEnum::FORM_DATE_TIME_PICKER:
+                                    $query = $query->whereNotBetweenTime($fieldName, $search['value'], $search['value'].' 23:59:59');
+                                    break;
+                                default:
+                                    $query = $query->where($fieldName, '<>', $search['value']);
+                                    break;
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_IS_EMPTY:
+                            $query = $query->whereNull($fieldName);
+                            break;
+                        case CrudOperatorEnum::OPERATOR_NOT_EMPTY:
+                            $query = $query->whereNotNull($fieldName);
+                            break;
+                        case CrudOperatorEnum::OPERATOR_BT:
+                            switch ($search['form_value']) {
+                                case CrudFormEnum::FORM_DATE_TIME_PICKER:
+                                    [$startTime,$endTime] = explode('-', $search['value']);
+                                    $query = $query->whereBetweenTime($fieldName, $startTime,$endTime.' 23:59:59');
+                                    break;
+                                default:
+                                    $query = $query->whereBetween($fieldName, $search['value']);
+                                    break;
+                            }
+                            break;
+                        case CrudOperatorEnum::OPERATOR_N_DAY:
+                            $query = $query->whereTime($fieldName, '<', Carbon::today()->subDays((int)$search['value'])->toDateTimeString());
+                            break;
+                        case CrudOperatorEnum::OPERATOR_LAST_DAY:
+
+                            $query = $query->whereBetweenTime($fieldName, Carbon::today()->subDays((int)$search['value'])->toDateTimeString(), Carbon::today()->toDateTimeString());
+                            break;
+                        case CrudOperatorEnum::OPERATOR_NEXT_DAY:
+                            $query = $query->whereBetweenTime($fieldName, Carbon::today()->toDateTimeString(), Carbon::today()->addDays((int)$search['value'])->toDateTimeString());
+                            break;
+                        case CrudOperatorEnum::OPERATOR_TO_DAY:
+                            $query = $query->whereTime($fieldName, Carbon::today()->toDateString());
+                            break;
+                        case CrudOperatorEnum::OPERATOR_WEEK:
+                            $query = $query->whereWeek($fieldName);
+                            break;
+                        case CrudOperatorEnum::OPERATOR_MONTH:
+                            $query = $query->whereMonth($fieldName);
+                            break;
+                        case CrudOperatorEnum::OPERATOR_QUARTER:
+                            $query = $query->whereBetweenTime($fieldName, Carbon::today()->startOfQuarter()->toDateTimeString(), Carbon::today()->endOfQuarter()->toDateTimeString());
+                            break;
+                    }
+                });
+            }
+        });
+        return $query;
+    }
+
+}

+ 150 - 0
app/common/dao/article/ArticleCategoryDao.php

@@ -0,0 +1,150 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\article;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\article\ArticleCategory;
+use app\common\model\BaseModel;
+use think\Collection;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\Model;
+
+/**
+ * Class ArticleCategoryDao
+ * @package app\common\dao\article
+ * @author xaboy
+ * @day 2020-04-20
+ */
+class ArticleCategoryDao extends BaseDao
+{
+
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    protected function getModel(): string
+    {
+        return ArticleCategory::class;
+    }
+
+    /**
+     * 获取文章分类集合
+     * @param int $mer_id
+     * @return array
+     * @author xaboy
+     * @day 2020-04-20
+     */
+    public function getAllOptions($mer_id = 0)
+    {
+        return ArticleCategory::getDB()->where('mer_id', $mer_id)->order('sort DESC')->column('pid,title', $this->getPk());
+    }
+
+    /**
+     * 获取文章分类
+     * @param int $mer_id
+     * @return Collection
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020-04-20
+     */
+    public function getAll($mer_id = 0,$status = null)
+    {
+        return ArticleCategory::getDB()->where('mer_id', $mer_id)->when($status,function($query)use($status){
+            $query->where('status',$status);
+        })->order('sort DESC')->select();
+    }
+
+    /**
+     * 查询文章分类
+     * @param array $where
+     * @return \think\db\BaseQuery
+     * @author xaboy
+     * @day 2020/9/18
+     */
+    public function search(array $where)
+    {
+        return ArticleCategory::getDB()->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+            $query->where('status', $where['status']);
+        })->when(isset($where['pid']) && $where['pid'] !== '', function ($query) use ($where) {
+            $query->where('pid', $where['pid']);
+        })->order('sort DESC, article_category_id DESC');
+    }
+
+    /**
+     * 查询指定字段的数据是否存在
+     * @param int $merId
+     * @param $field
+     * @param $value
+     * @param null $except
+     * @return bool
+     * @author xaboy
+     * @day 2020-04-20
+     */
+    public function merFieldExists(int $merId, $field, $value, $except = null)
+    {
+        return ($this->getModel())::getDB()->when($except, function ($query, $except) use ($field) {
+                $query->where($field, '<>', $except);
+            })->where('mer_id', $merId)->where($field, $value)->count() > 0;
+    }
+
+    /**
+     * 查询主键是否存在
+     * @param int $merId
+     * @param int $id
+     * @param null $except
+     * @return bool
+     * @author xaboy
+     * @day 2020-04-20
+     */
+    public function merExists(int $merId, int $id, $except = null)
+    {
+        return $this->merFieldExists($merId, $this->getPk(), $id, $except);
+    }
+
+    /**
+     * 文章分类详情
+     * @param int $id
+     * @param int $merId
+     * @return array|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020-04-15
+     */
+    public function get( $id, $merId = 0)
+    {
+        return ($this->getModel())::getDB()->where('mer_id', 0)->find($id);
+    }
+
+    /**
+     * 删除文章分类
+     * @param int $id
+     * @param int $merId
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020-04-20
+     */
+    public function delete(int $id, $merId = 0)
+    {
+        return ($this->getModel())::getDB()->where($this->getPk(), $id)->where('mer_id', $merId)->delete();
+    }
+}

+ 32 - 0
app/common/dao/article/ArticleContentDao.php

@@ -0,0 +1,32 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\article;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+
+class ArticleContentDao extends BaseDao
+{
+
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    protected function getModel(): string
+    {
+        return ArticleContentDao::class;
+    }
+}

+ 210 - 0
app/common/dao/article/ArticleDao.php

@@ -0,0 +1,210 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\article;
+
+use think\Collection;
+use think\db\BaseQuery;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\facade\Db;
+use app\common\dao\BaseDao;
+use app\common\model\article\Article;
+use app\common\model\BaseModel;
+use think\Model;
+
+class ArticleDao extends BaseDao
+{
+
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    protected function getModel(): string
+    {
+        return Article::class;
+    }
+
+    /**
+     * 获取全部文章
+     * @param int $mer_id
+     * @return Collection
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     */
+    public function getAll($mer_id = 0)
+    {
+        return Article::getDB()->with('content')->where('mer_id', $mer_id)->select();
+    }
+
+    /**
+     * 搜索列表
+     * @param $merId
+     * @param array $where
+     * @return BaseQuery
+     * @author Qinii
+     */
+    public function search($merId,array $where)
+    {
+        $query = Article::getDB();
+        if (isset($where['cid']) && $where['cid'] !== '') $query->where('cid', (int)$where['cid']);
+        if (isset($where['title']) && $where['title'] !== '') $query->whereLike('title', "%{$where['title']}%");
+        if (isset($where['status']) && $where['status'] !== '') $query->where('status', $where['status']);
+        if (isset($where['wechat_news_id']) && $where['wechat_news_id'] !== '') $query->where('wechat_news_id', $where['wechat_news_id']);
+
+        if (isset($where['article_id']) && $where['article_id'] !== ''){
+            if (is_array($where['article_id'])) {
+                $query->whereIn('article_id', $where['article_id']);
+            } else {
+                $query->where('article_id', $where['article_id']);
+            }
+
+        }
+
+
+        $query->with(['content','articleCategory']);
+
+        return $query->where('mer_id',$merId)->order('sort DESC,create_time DESC');
+    }
+
+
+
+    /**
+     * 根据 字段名查询
+     * @param int $merId
+     * @param $field
+     * @param $value
+     * @param null $except
+     * @return bool
+     * @author Qinii
+     */
+    public function merFieldExists(int $merId, $field, $value, $except = null)
+    {
+        return ($this->getModel())::getDB()->when($except, function ($query, $except) use ($field) {
+                $query->where($field, '<>', $except);
+            })->where('mer_id', $merId)->where('wechat_news_id',0)->where($field, $value)->count() > 0;
+    }
+
+    /**
+     * @param int $id
+     * @param int $merId
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020-04-20
+     */
+    public function delete(int $id, int $merId = 0)
+    {
+        $result = ($this->getModel())::getDB()->where('mer_id', $merId)
+            ->where($this->getPk(),$id)
+            ->with('content')
+            ->find();
+        return $result->together(['content'])->delete();
+    }
+
+
+    /**
+     * 关联添加
+     * @param array $data
+     * @return BaseDao|Model|void
+     * @author Qinii
+     */
+    public function create(array $data)
+    {
+        Db::transaction(function()use($data){
+            $content = $data['content'];
+            unset($data['content']);
+            $article = $this->getModel()::create($data);
+            $article->content()->save(['content' => $content]);
+        });
+    }
+
+    /**
+     * 关联更新
+     * @param int $id
+     * @param array $data
+     * @return int|void
+     * @author Qinii
+     */
+    public function update(int $id, array $data)
+    {
+        Db::transaction(function()use($id,$data){
+            $content = $data['content'];
+            unset($data['content']);
+
+            $this->getModel()::where($this->getPk(),$id)->update($data);
+
+            $article = $this->getModel()::find($id);
+            $article->content->content = $content;
+            $article->together(['content'])->save();
+        });
+    }
+
+    /**
+     * 根据字段获取 主键值
+     * @param int $vale
+     * @param null $field
+     * @return array
+     * @author Qinii
+     */
+    public function getKey(int $vale,$field = null)
+    {
+        return ($this->getModel())::getDB()->where($field,$vale)->column($this->getPk());
+    }
+
+    /**
+     * 查询微信图文消息
+     * @param $id
+     * @return array|Collection|BaseQuery[]
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function wechatNewIdByData($id)
+    {
+        return ($this->getModel())::getDB()->where('wechat_news_id', $id)->select();
+    }
+
+    /**
+     * 切换状态
+     * @param $id
+     * @param $data
+     * @return int
+     * @throws DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function switchStatus($id, $data)
+    {
+       return ($this->getModel())::getDB()->where($this->getPk(),$id)->update($data);
+    }
+
+    /**
+     * 增加阅读量
+     * @param $id
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function incVisit($id)
+    {
+        $this->getModel()::getDB()->where($this->getPk(),$id)->inc('visit',1);
+    }
+}

+ 28 - 0
app/common/dao/community/CommunityCategoryDao.php

@@ -0,0 +1,28 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\community;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\community\CommunityCategory;
+use crmeb\traits\CategoresDao;
+
+class CommunityCategoryDao extends BaseDao
+{
+    use CategoresDao;
+
+    protected function getModel(): string
+    {
+        return CommunityCategory::class;
+    }
+}

+ 227 - 0
app/common/dao/community/CommunityDao.php

@@ -0,0 +1,227 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\community;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\community\Community;
+use app\common\model\system\Relevance;
+use app\common\repositories\system\RelevanceRepository;
+
+class CommunityDao extends BaseDao
+{
+
+    /**
+     * @return Community
+     *
+     * @date 2023/10/21
+     * @author yyw
+     */
+    protected function getModel(): string
+    {
+        return Community::class;
+    }
+
+    /**
+     * 搜索社区帖子
+     * @param array $where
+     * @return \think\db\BaseQuery
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function search(array $where)
+    {
+        $query = Community::hasWhere('author', function($query) use ($where){
+            $query->when(isset($where['username']) && $where['username'] !==  '', function ($query) use($where) {
+                $query->whereLike('real_name|phone|nickname',"%{$where['username']}%");
+            })
+                ->when(isset($where['phone']) && $where['phone'] !==  '', function ($query) use($where) {
+                    $query->whereLike('phone',"%{$where['phone']}%");
+                })
+                ->when(isset($where['real_name']) && $where['real_name'] !==  '', function ($query) use($where) {
+                    $query->whereLike('real_name',"%{$where['real_name']}%");
+                })
+                ->when(isset($where['nickname']) && $where['nickname'] !==  '', function ($query) use($where) {
+                    $query->whereLike('nickname',"%{$where['nickname']}%");
+                });
+            $query->where(true);
+        });
+        $query->when(isset($where['search_type']) && $where['search_type'] !== '', function ($query) use ($where) {
+                if(isset($where['keyword']) && $where['keyword'] !==  ''){
+                    if($where['search_type'] == 'all'){
+                        $query->whereLike('Community.title|Community.content|User.nickname',"%{$where['keyword']}%");
+                    }
+                    if($where['search_type'] == 'content'){
+                        $query->whereLike('Community.title|Community.content',"%{$where['keyword']}%");
+                    }
+                    if($where['search_type'] == 'user'){
+                        $query->whereLike('User.nickname',"%{$where['keyword']}%");
+                    }
+                }
+            },function ($query) use ($where) {   // 兼容之前逻辑
+                if(isset($where['keyword']) && $where['keyword'] !==  ''){
+                    $query->whereLike('Community.title',"%{$where['keyword']}%");
+                }
+            })
+//            ->when(isset($where['keyword']) && $where['keyword'] !==  '', function ($query) use($where) {
+//                $query->whereLike('Community.title',"%{$where['keyword']}%");
+//            })
+            ->when(isset($where['uid']) && $where['uid'] !==  '', function ($query) use($where) {
+                $query->where('Community.uid',$where['uid']);
+            })
+            ->when(isset($where['uids']) && $where['uids'] !==  '', function ($query) use($where) {
+                $query->whereIn('Community.uid',$where['uids']);
+            })
+            ->when(isset($where['topic_id']) && $where['topic_id'] !==  '', function ($query) use($where) {
+                $query->where('Community.topic_id',$where['topic_id']);
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !==  '', function ($query) use($where) {
+                $query->where('Community.mer_id',$where['mer_id']);
+            })
+            ->when(isset($where['community_id']) && $where['community_id'] !==  '', function ($query) use($where) {
+                $query->where('Community.community_id',$where['community_id']);
+            })
+            ->when(isset($where['not_id']) && $where['not_id'] !==  '', function ($query) use($where) {
+                $query->whereNotIn('Community.community_id',$where['not_id']);
+            })
+            ->when(isset($where['in_id']) && $where['in_id'] !==  '', function ($query) use($where) {
+                $query->whereOr(function($query) use($where){
+                    $query->whereIn('Community.community_id',$where['in_id']);
+                });
+            })
+            ->when(isset($where['community_ids']) && $where['community_ids'] !==  '', function ($query) use($where) {
+                $query->whereIn('Community.community_id',$where['community_ids']);
+            })
+            ->when(isset($where['is_type']) && $where['is_type'] !==  '', function ($query) use($where) {
+                $query->whereIn('Community.is_type',$where['is_type']);
+            })
+            ->when(isset($where['is_show']) && $where['is_show'] !==  '', function ($query) use($where) {
+                $query->where('Community.is_show',$where['is_show']);
+            })
+            ->when(isset($where['status']) && $where['status'] !==  '', function ($query) use($where) {
+                $query->where('Community.status',$where['status']);
+            })
+            ->when(isset($where['start']) && $where['start'] !==  '', function ($query) use($where) {
+                $query->where('Community.start',$where['start']);
+            })
+            ->when(isset($where['is_del']) && $where['is_del'] !==  '', function ($query) use($where) {
+                $query->where('Community.is_del',$where['is_del']);
+            })
+            ->when(isset($where['category_id']) && $where['category_id'] !==  '', function ($query) use($where) {
+                $query->where('Community.category_id',$where['category_id']);
+            })
+            ->when(isset($where['spu_id']) && $where['spu_id'] !==  '', function ($query) use($where) {
+                $id = Relevance::where('right_id', $where['spu_id'])
+                    ->where('type',RelevanceRepository::TYPE_COMMUNITY_PRODUCT)
+                    ->column('left_id');
+                $query->where('community_id','in', $id);
+            });
+
+        $order = 'Community.create_time DESC';
+
+        if (isset($where['order']) && $where['order'] == 'start') {
+            $order = 'Community.start DESC,Community.create_time DESC';
+        }
+
+        $query->order($order);
+        return $query;
+    }
+
+    /**
+     * 查询用户是否发过帖子
+     * @param int $id
+     * @param int $uid
+     * @return bool
+     * @throws \think\db\exception\DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function uidExists(int $id, int $uid)
+    {
+        return $this->getModel()::getDb()->where('uid',$uid)->where($this->getPk(),$id)->count() > 0;
+    }
+
+    /**
+     * id查询帖子是否存在
+     * @param int $id
+     * @return bool
+     * @throws \think\db\exception\DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function exists(int $id)
+    {
+        return $this->getModel()::getDb()->where('is_del',0)->where($this->getPk(),$id)->count() > 0;
+    }
+
+    /**
+     * 删除某个用户的帖子
+     * @param $uid
+     * @return int
+     * @throws \think\db\exception\DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function destoryByUid($uid)
+    {
+        return $this->getModel()::getDb()->where('uid' ,$uid)->update(['is_del' =>  1]);
+    }
+
+    /**
+     * 关联用户
+     * @param $where
+     * @return \think\db\BaseQuery
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function joinUser($where)
+    {
+        return Community::hasWhere('relevanceRight',function($query) use($where){
+            $query->where('type',RelevanceRepository::TYPE_COMMUNITY_START)->where('left_id',$where['uid']);
+        })
+            ->when(isset($where['is_type']) && $where['is_type'] !==  '', function ($query) use($where) {
+                $query->whereIn('Community.is_type',$where['is_type']);
+            })
+            ->when(isset($where['is_show']) && $where['is_show'] !==  '', function ($query) use($where) {
+                $query->where('Community.is_show',$where['is_show']);
+            })
+            ->when(isset($where['status']) && $where['status'] !==  '', function ($query) use($where) {
+                $query->where('Community.status',$where['status']);
+            })
+            ->when(isset($where['is_del']) && $where['is_del'] !==  '', function ($query) use($where) {
+                $query->where('Community.is_del',$where['is_del']);
+            });
+    }
+
+    /**
+     * 统计每个用户的帖子数量
+     * @return mixed
+     *
+     * @date 2023/10/21
+     * @author yyw
+     */
+    public function getCountByGroupUid()
+    {
+        return $this->getModel()::getDb()->where('is_del', 0)->field('uid,count(community_id) as count')->group('uid')->select()->toArray();
+    }
+
+    public function isApprove(int $id)
+    {
+        return $this->getModel()::getDb()->where('is_del',0)->where('status',1)->where($this->getPk(),$id)->count() > 0;
+    }
+}

+ 77 - 0
app/common/dao/community/CommunityReplyDao.php

@@ -0,0 +1,77 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\community;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\community\CommunityReply;
+
+class CommunityReplyDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return CommunityReply::class;
+    }
+
+    /**
+     * 查询用户的帖子回复
+     * @param int $id
+     * @param int $uid
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function uidExists(int $id, int $uid)
+    {
+        return $this->getModel()::getDb()->where($this->getPk(), $id)->where('uid', $uid)->where('is_del', 0)->find();
+    }
+
+    /**
+     * 查询帖子回复
+     * @param array $where
+     * @return \think\db\BaseQuery
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/12
+     */
+    public function search(array $where)
+    {
+        $query = CommunityReply::hasWhere('author',function($query) use($where) {
+            $query->when(isset($where['username']) && $where['username'] !== '', function ($query) use($where) {
+                $query->whereLike('nickname',"%{$where['username']}%");
+            });
+            $query->where(true);
+        });
+
+        $query->when(isset($where['keyword']) && $where['keyword'] !== '', function ($query) use($where) {
+            $query->whereLike('content',"%{$where['keyword']}%");
+        });
+        $query->when(isset($where['date']) && $where['date'] !== '', function ($query) use($where) {
+            getModelTime($query, $where['date'], 'CommunityReply.create_time');
+        });
+        $query->when(isset($where['is_del']) && $where['is_del'] !== '', function ($query) use($where) {
+           $query->where('is_del',$where['is_del']);
+        });
+
+        $query->when(isset($where['pid']) && $where['pid'] !== '', function ($query) use($where) {
+            $query->where('pid',$where['pid']);
+        });
+
+        $query->when(isset($where['community_id']) && $where['community_id'] !== '', function ($query) use($where) {
+            $query->where('community_id',$where['community_id']);
+        });
+        return $query->order('CommunityReply.create_time DESC');
+    }
+}

+ 60 - 0
app/common/dao/community/CommunityTopicDao.php

@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\community;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\community\CommunityTopic;
+
+class CommunityTopicDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return CommunityTopic::class;
+    }
+
+    /**
+     * 数量增加
+     * @param int $id
+     * @param string $filed
+     * @param int $inc
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function countInc(int $id, string $filed, int $inc = 1)
+    {
+        return $this->getModel()::getDb()->where($this->getPk(), $id)->inc($filed, $inc)->update();
+    }
+
+    /**
+     * 数量减少
+     * @param int $id
+     * @param string $filed
+     * @param int $dec
+     * @return mixed|void
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function countDec(int $id, string $filed, int $dec = 1)
+    {
+        try{
+            return $this->getModel()::getDb()->where($this->getPk(), $id)->dec($filed, $dec)->update();
+        }catch (\Exception $exception) {
+
+        }
+    }
+}

+ 23 - 0
app/common/dao/delivery/DeliveryOrderDao.php

@@ -0,0 +1,23 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\delivery;
+
+use app\common\dao\BaseDao;
+use app\common\model\delivery\DeliveryOrder;
+
+class DeliveryOrderDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return DeliveryOrder::class;
+    }
+}

+ 23 - 0
app/common/dao/delivery/DeliveryServiceDao.php

@@ -0,0 +1,23 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\delivery;
+
+use app\common\dao\BaseDao;
+use app\common\model\delivery\DeliveryService;
+
+class DeliveryServiceDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return DeliveryService::class;
+    }
+}

+ 23 - 0
app/common/dao/delivery/DeliveryStationDao.php

@@ -0,0 +1,23 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\delivery;
+
+use app\common\dao\BaseDao;
+use app\common\model\delivery\DeliveryStation;
+
+class DeliveryStationDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return DeliveryStation::class;
+    }
+}

+ 46 - 0
app/common/dao/openapi/OpenAuthDao.php

@@ -0,0 +1,46 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\openapi;
+
+use app\common\model\openapi\OpenAuth;
+use app\common\dao\BaseDao;
+
+class OpenAuthDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return OpenAuth::class;
+    }
+
+    /**
+     * 查询按照创建时间排序
+     * @param array $where
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function search(array $where)
+    {
+        $query = $this->getModel()::getDB()
+            ->when(isset($where['type']) && $where['type'] !== '',function($query) use($where){
+                $query->where('type',$where['type']);
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '',function($query) use($where){
+                $query->where('mer_id',$where['mer_id']);
+            });
+        $query->order('create_time DESC');
+        return $query;
+    }
+}

+ 87 - 0
app/common/dao/store/CityAreaDao.php

@@ -0,0 +1,87 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\CityArea;
+use think\exception\ValidateException;
+
+class CityAreaDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return CityArea::class;
+    }
+
+    public function search(array $where)
+    {
+        return CityArea::getDB()->when(isset($where['pid']) && $where['pid'] !== '', function ($query) use ($where) {
+            $query->where('parent_id', $where['pid']);
+        })->when(isset($where['address']) && $where['address'] !== '', function ($query) use ($where) {
+            $address = explode('/', trim($where['address'], '/'));
+            $p = array_shift($address);
+            $_p = $p;
+            if (mb_strlen($p) - 1 === mb_strpos($p, '市')) {
+                $p = mb_substr($p, 0, -1);
+            } elseif (mb_strlen($p) - 1 === mb_strpos($p, '省')) {
+                $p = mb_substr($p, 0, -1);
+            } elseif (mb_strlen($p) - 3 === mb_strpos($p, '自治区')) {
+                $p = mb_substr($p, 0, -3);
+            }
+            $pcity = $this->search([])->where('name', $p)->find();
+            if (!$pcity) $pcity = $this->search([])->where('name', $_p)->find();
+            if (!$pcity) throw new ValidateException('获取地址失败'.$_p);
+            $street = array_pop($address);
+            if ($pcity) {
+                $path = '/' . $pcity->id . '/';
+                $query->whereLike('path', "/{$pcity->id}/%");
+                foreach ($address as $item) {
+                    $id = $this->search([])->whereLike('path', $path . '%')->where('name', $item)->value('id');
+                    if ($id) {
+                        $path .= $id . '/';
+                    } else {
+                        break;
+                    }
+                }
+            }
+            $query->whereLike('path', $path . '%')->where('name', $street);
+        });
+    }
+
+    /**
+     * 获取城市列表
+     *
+     * 本函数用于根据给定的城市区域对象,获取该城市区域的所有父级城市直到根城市的一个列表。
+     * 这个功能适用于需要展示城市层级关系的场景,比如导航菜单或选择框。
+     *
+     * @param CityArea $city 城市区域对象,包含城市区域的信息。
+     * @return array 返回一个包含城市区域对象的数组,从根城市到给定城市的父级城市。
+     */
+    public function getCityList(CityArea $city)
+    {
+        // 如果城市区域没有父级ID,则说明它是根城市,直接返回该城市自身
+        if (!$city->parent_id) return [$city];
+
+        // 通过查询找到给定城市的所有父级城市,路径用'/'分隔
+        $lst = $this->search([])->where('id', 'in', explode('/', trim($city->path, '/')))->order('id ASC')->select();
+
+        // 将给定的城市对象添加到结果列表末尾,确保列表以给定城市结尾
+        $lst[] = $city;
+
+        // 返回包含所有父级城市和给定城市的列表
+        return $lst;
+    }
+}

+ 78 - 0
app/common/dao/store/ExcelDao.php

@@ -0,0 +1,78 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store;
+
+use app\common\model\store\Excel;
+use app\common\dao\BaseDao;
+
+class ExcelDao extends BaseDao
+{
+
+    /**
+     *
+     * @return string
+     * @author Qinii
+     * @day 2020-07-30
+     */
+    protected function getModel(): string
+    {
+        return Excel::class;
+    }
+
+
+    public function search(array $where)
+    {
+        $query = $this->getModel()::getDB()
+            ->when(isset($where['type']) && $where['type'] !== '',function($query) use($where){
+                $query->where('type',$where['type']);
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '',function($query) use($where){
+                $query->where('mer_id',$where['mer_id']);
+            })
+            ->when(isset($where['admin_id']) && $where['admin_id'] !== '',function($query) use($where){
+                $query->where('admin_id',$where['admin_id']);
+            });
+        $query->order('create_time DESC');
+        return $query;
+    }
+
+    /**
+     *  获取小于某个时间的文件
+     * @param $time
+     * @return mixed
+     * @author Qinii
+     * @day 2020-08-15
+     */
+    public function getDelByTime($time)
+    {
+        return $this->getModel()::getDB()->whereTime('create_time','<',$time)->column('path','excel_id');
+    }
+
+    /**
+     *  类型
+     * @return array
+     * @author Qinii
+     * @day 9/28/21
+     */
+    public function getTypeData()
+    {
+        $data = (new Excel())->typeData;
+        foreach ($data as $k => $v) {
+            $ret[] = [
+                'key' => $k,
+                'value' => $v,
+            ];
+        }
+        return $ret;
+    }
+}

+ 25 - 0
app/common/dao/store/GuaranteeDao.php

@@ -0,0 +1,25 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\Guarantee;
+
+class GuaranteeDao extends BaseDao
+{
+
+
+    protected function getModel(): string
+    {
+        return Guarantee::class;
+    }
+
+}

+ 41 - 0
app/common/dao/store/GuaranteeTemplateDao.php

@@ -0,0 +1,41 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\GuaranteeTemplate;
+
+class GuaranteeTemplateDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return GuaranteeTemplate::class;
+    }
+
+    /**
+     * 清除特定字段值对应的数据记录
+     *
+     * 本函数用于根据指定的字段值和该值对应的ID,从数据库中删除相应的记录。
+     * 这是个通用函数,可以通过传入不同的字段名和ID值来删除不同表中的数据。
+     *
+     * @param mixed $id 需要删除的数据记录的ID值,可以是数字、字符串等
+     * @param string $field 指定的字段名,用于查询和删除数据
+     */
+    public function clear($id,$field)
+    {
+        // 使用模型获取数据库实例,并构造删除语句,根据字段和ID删除数据
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+
+
+}

+ 54 - 0
app/common/dao/store/GuaranteeValueDao.php

@@ -0,0 +1,54 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\GuaranteeValue;
+
+class GuaranteeValueDao extends BaseDao
+{
+
+
+    protected function getModel(): string
+    {
+        return GuaranteeValue::class;
+    }
+
+    /**
+     * 更改保证状态
+     *
+     * 本函数用于根据给定的保证ID更新保证的状态。它通过查询数据库找到对应ID的保证记录,
+     * 然后将其状态更新为新提供的状态值。
+     *
+     * @param int $id 保证的唯一标识ID。此ID用于在数据库中定位特定的保证记录。
+     * @param int $status 新的状态值。此值将被设置为指定保证记录的状态,以反映其新的状态。
+     */
+    public function chageStatus(int $id,int $status)
+    {
+        // 通过getModel方法获取数据库操作对象,并使用where语句定位到特定ID的保证记录,然后更新其状态为$status
+        $this->getModel()::getDB()->where('guarantee_id',$id)->update(['status' => $status]);
+    }
+
+    /**
+     * 清除关联的保证模板数据
+     *
+     * 本函数用于根据给定的模板ID,从数据库中删除与该模板相关的所有数据。
+     * 这是数据维护操作的一部分,用于在模板更新或废弃时清理旧数据。
+     *
+     * @param int $id 保证模板的唯一标识ID
+     */
+    public function clear($id)
+    {
+        // 通过模型获取数据库实例,并基于guarantee_template_id删除相关数据
+        $this->getModel()::getDB()->where('guarantee_template_id',$id)->delete();
+    }
+
+}

+ 50 - 0
app/common/dao/store/PriceRuleDao.php

@@ -0,0 +1,50 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\PriceRule;
+use app\common\repositories\system\RelevanceRepository;
+
+class PriceRuleDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return PriceRule::class;
+    }
+
+    public function search(array $where)
+    {
+        return PriceRule::getDB()->when(isset($where['keyword']) && $where['keyword'] !== '', function ($query) use ($where) {
+            $query->whereLike('rule_name', "%{$where['keyword']}%");
+        })->when(isset($where['is_show']) && $where['is_show'] !== '', function ($query) use ($where) {
+            $query->where('is_show', $where['is_show']);
+        })->when(isset($where['cate_id']) && $where['cate_id'] !== '', function ($query) use ($where) {
+            $ids = app()->make(RelevanceRepository::class)->query([
+                'type' => RelevanceRepository::PRICE_RULE_CATEGORY
+            ])->where(function ($query) use ($where) {
+                if (is_array($where['cate_id'])) {
+                    $query->whereIn('right_id', $where['cate_id']);
+                } else {
+                    $query->where('right_id', (int)$where['cate_id']);
+                }
+            })->group('left_id')->column('left_id');
+            $ids[] = -1;
+            $query->where(function ($query) use ($ids) {
+                $query->whereIn('rule_id', $ids)->whereOr('is_default', 1);
+            });
+        });
+    }
+}

+ 72 - 0
app/common/dao/store/StoreActivityDao.php

@@ -0,0 +1,72 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\StoreActivity;
+use app\common\repositories\system\RelevanceRepository;
+use think\exception\ValidateException;
+
+/**
+ *
+ * Class StoreActivityDao
+ * @package app\common\dao\system\merchant
+ */
+class StoreActivityDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return StoreActivity::class;
+    }
+
+    public function search(array $where = [], array $with = [])
+    {
+        $where['is_del'] = 0;
+        return $this->getSearch($where)->when(!empty($with), function ($query) use ($with) {
+            $query->with($with);
+        });
+    }
+
+    /**
+     * 增加指定ID的数据的total字段值。
+     * 此函数主要用于对数据库中的某些记录进行总量控制和更新,确保数据的正确性和一致性。
+     *
+     * @param int $id 需要更新的记录的ID。
+     * @param int $inc 需要增加的值,默认为1,表示增加1。
+     * @throws ValidateException 如果找不到相关数据或更新后的总量超过了预设的限制,则抛出异常。
+     */
+    public function incTotal($id, $inc = 1)
+    {
+        // 根据ID查询数据库记录。
+        $res = $this->getModel()::getDb()->where($this->getPk(), $id)->find();
+
+        // 如果查询结果为空,则抛出异常,表示数据异常。
+        if (empty($res)) {
+            throw new ValidateException('活动数据异常');
+        }
+
+        // 计算更新后的total值。
+        $total = $res['total'] + $inc;
+
+        // 如果当前记录的count值存在且小于更新后的total值,则抛出异常,表示超出总数限制。
+        if ($res['count'] && $res['count'] < $total) {
+            throw new ValidateException('超出总数限制');
+        }
+
+        // 更新记录的total值,并保存到数据库。
+        $res->total = $total;
+        $res->save();
+    }
+}

+ 45 - 0
app/common/dao/store/StoreActivityRelatedDao.php

@@ -0,0 +1,45 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\StoreActivity;
+use app\common\model\store\StoreActivityRelated;
+
+/**
+ *
+ * Class StoreActivityDao
+ * @package app\common\dao\system\merchant
+ */
+class StoreActivityRelatedDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return StoreActivityRelated::class;
+    }
+
+    /**
+     * 根据条件搜索数据
+     *
+     * 本函数旨在通过提供的条件数组来执行搜索操作。它封装了搜索逻辑,使调用者可以通过简单地传递条件数组来执行复杂的搜索。
+     * 条件数组可以包含多个条件,每个条件由字段名和对应的值组成,可以用于构建SQL查询中的WHERE子句。
+     *
+     * @param array $where 搜索条件数组,包含一个或多个搜索条件
+     * @return mixed 返回搜索结果,具体类型取决于getSearch方法的实现
+     */
+    public function search(array $where = [])
+    {
+        return $this->getSearch($where);
+    }
+}

+ 168 - 0
app/common/dao/store/StoreAttrTemplateDao.php

@@ -0,0 +1,168 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\StoreAttrTemplate;
+use think\db\BaseQuery;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\Model;
+
+/**
+ * Class StoreAttrTemplateDao
+ * @package app\common\dao\store
+ * @author xaboy
+ * @day 2020-05-06
+ */
+class StoreAttrTemplateDao extends BaseDao
+{
+
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    protected function getModel(): string
+    {
+        return StoreAttrTemplate::class;
+    }
+
+    /**
+     * 根据条件搜索商店属性模板
+     *
+     * 本函数用于查询特定商家ID下的属性模板信息。支持通过关键词进行模糊搜索。
+     *
+     * @param string $merId 商家ID,用于限定查询的商家范围。
+     * @param array $where 查询条件数组,可选参数,其中'keyword'键用于指定搜索关键词。
+     * @return BaseQuery|\think\Paginator
+     */
+    public function search($merId, array $where = [])
+    {
+        return StoreAttrTemplate::getDB()->when(isset($where['keyword']),function($query) use($where){
+            $query->whereLike('template_name',"%{$where['keyword']}%");
+        })->where('mer_id', $merId)->order('attr_template_id DESC');
+    }
+
+    /**
+     * 检查指定商户是否存在指定的ID
+     *
+     * 本函数通过调用merFieldExists方法来判断指定商户ID在特定字段中是否存在。
+     * 主要用于验证商户ID的有效性,以及在某些情况下排除特定ID的检查。
+     *
+     * @param int $merId 商户的ID,用于标识特定的商户。
+     * @param int $id 需要检查的ID,看它是否属于指定的商户。
+     * @param mixed $except 可选参数,用于指定需要排除的ID,默认为null。
+     * @return bool 如果指定的ID存在于商户中则返回true,否则返回false。
+     */
+    public function merExists(int $merId, int $id, $except = null)
+    {
+        // 调用merFieldExists方法来检查指定的ID是否存在于商户ID字段中
+        return $this->merFieldExists($merId, $this->getPk(), $id, $except);
+    }
+
+    /**
+     * 检查指定商家是否存在特定字段的特定值。
+     *
+     * 该方法用于查询数据库中是否存在特定条件的记录。具体来说,它首先检查传入的除外条件($except),
+     * 如果除外条件存在,则在查询时排除这些条件。然后,它查询指定商家($merId)的记录中,
+     * 指定字段($field)的值是否等于传入的值($value)。如果存在匹配的记录,则返回true,否则返回false。
+     *
+     * @param int $merId 商家ID,用于限定查询的商家范围。
+     * @param string $field 要检查的字段名。
+     * @param mixed $value 要检查的字段值。
+     * @param mixed $except 除外条件,即在查询时不应该匹配的值。
+     * @return bool 如果存在匹配的记录则返回true,否则返回false。
+     */
+    public function merFieldExists(int $merId, $field, $value, $except = null)
+    {
+        // 获取模型对应的数据库实例,并根据$except参数应用条件。
+        return ($this->getModel())::getDB()->when($except, function ($query, $except) use ($field) {
+                // 如果$except存在,则添加不等于($<>_)查询条件。
+                $query->where($field, '<>', $except);
+            })->where('mer_id', $merId)->where($field, $value)->count() > 0;
+    }
+
+    /**
+     * 根据ID和商家ID获取数据
+     *
+     * 本函数用于从数据库中检索指定ID和商家ID对应的数据行。
+     * 它首先通过调用getModel方法来获取模型实例,然后使用该实例的getDB方法来获得数据库操作对象。
+     * 接着,通过where方法指定查询条件为商家ID为$merId,最后使用find方法根据$id查找数据。
+     *
+     * @param int $id 数据行的唯一标识ID
+     * @param int $merId 商家的唯一标识ID,默认为0,表示系统默认商家
+     * @return object 返回查询结果的对象,如果未找到则为null
+     */
+    public function get( $id, $merId = 0)
+    {
+        return ($this->getModel())::getDB()->where('mer_id', $merId)->find($id);
+    }
+
+    /**
+     * 根据ID和商家ID删除记录
+     *
+     * 本函数用于删除指定ID的记录。它可以处理单个ID和多个ID的删除请求。
+     * 删除操作会根据提供的商家ID对数据进行过滤,确保只删除属于该商家的数据。
+     *
+     * @param mixed $id 需要删除的记录的ID,可以是单个ID或ID数组
+     * @param int $merId 商家ID,用于指定删除哪个商家的数据,默认为0,表示删除所有商家的数据
+     * @return int 返回删除的记录数
+     */
+    public function delete($id, $merId = 0)
+    {
+        // 获取数据库实例并根据商家ID建立查询条件
+        $query = ($this->getModel())::getDB()->where('mer_id', $merId);
+
+        // 判断传入的ID类型,如果是数组,则使用whereIn方法删除多个记录;否则,直接使用where方法删除单个记录
+        if (is_array($id)) {
+            $query->where($this->getPk(), 'in',$id);
+        } else {
+            $query->where($this->getPk(), $id);
+        }
+
+        // 执行删除操作并返回删除的记录数
+        return $query->delete();
+    }
+
+
+    /**
+     * 获取指定商户的属性模板列表
+     *
+     * 本函数旨在通过指定的商户ID,从数据库中检索该商户的所有属性模板信息。
+     * 返回的结果包括属性模板ID、模板名称和模板值。
+     *
+     * @param int $merId 商户ID,用于指定要查询的商户。
+     * @return array 返回一个包含属性模板信息的数组,每个元素包含属性模板ID、名称和值。
+     */
+    public function getList($merId)
+    {
+        // 使用模型获取数据库实例,并设置查询条件为指定的商户ID,然后指定查询的字段,最后执行查询操作。
+        return ($this->getModel())::getDB()->where('mer_id',$merId)->field('mer_id,attr_template_id,template_name,template_value')->select()
+            ->each(function($item) {
+                $template_value = $item['template_value'];
+                if ($template_value) {
+                    foreach ($template_value as &$item1) {
+                        $detail = $item1['detail'];
+                        $de = array_map(function($v) use($item1){return ['pic' => '', 'value' => $v];},$detail);
+                        $item1['detail'] = $de;
+                    }
+                    $item['template_value'] = $template_value;
+                }
+            });
+    }
+}

+ 118 - 0
app/common/dao/store/StoreBrandCategoryDao.php

@@ -0,0 +1,118 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\StoreBrandCategory as model;
+use crmeb\traits\CategoresDao;
+
+class StoreBrandCategoryDao extends BaseDao
+{
+
+    use CategoresDao;
+
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+    public function getMaxLevel()
+    {
+        return 2;
+    }
+
+    /**
+     * 获取所有符合条件的数据
+     *
+     * 此方法用于查询数据库中所有满足指定条件的数据。它允许指定商家ID和状态来过滤结果。
+     * 默认情况下,它将返回所有商家ID为0且状态为任意值的数据。可以通过传递不同的参数来修改查询条件。
+     *
+     * @param int $mer_id 商家ID,用于指定查询特定商家的数据。默认为0,表示查询所有商家。
+     * @param int|null $status 数据的状态,用于指定查询特定状态的数据。如果为null,则不按状态过滤。
+     * @return array 返回查询结果的数组,包含所有符合条件的数据。
+     */
+    public function getAll($mer_id = 0,$status = null)
+    {
+        // 获取模型实例并链式调用数据库查询方法
+        return $this->getModel()::getDB()->when(($status !== null),function($query)use($status){
+            // 如果指定了状态,则在查询中添加状态过滤条件
+            $query->where($this->getStatus(),$status);
+        })->order('sort DESC')->select();
+    }
+
+
+    /**
+     * 检查指定字段在指定条件下的存在性。
+     *
+     * 该方法用于确定数据库中是否存在满足特定条件的记录。
+     * 具体来说,它检查给定字段的值是否在数据库中唯一存在,
+     * 可以通过排除特定值来细化查询条件。
+     *
+     * @param int $merId 商户ID,用于限定查询的范围。
+     * @param string $field 要检查的字段名。
+     * @param mixed $value 字段应该具有的值。
+     * @param mixed $except [可选]需要排除的值,防止查询到该值。
+     * @return bool 如果存在满足条件的记录,则返回true;否则返回false。
+     */
+    public function merFieldExists(int $merId, $field, $value, $except = null)
+    {
+        // 获取数据库实例
+        return ($this->getModel())::getDB()
+                // 当$except有值时,添加排除条件
+                ->when($except, function ($query, $except) use ($field) {
+                    $query->where($field, '<>', $except);
+                })
+                // 检查字段值是否存在
+                ->where($field, $value)->count() > 0;
+    }
+
+
+    /**
+     * 根据指定字段和值获取所有不包括特定ID的数据
+     *
+     * 此方法用于从数据库中检索所有符合特定字段值条件的记录,
+     * 可选地,可以排除某些特定的ID。这在需要获取满足某一条件的
+     * 所有数据,但又需要排除某些特定数据的情况下非常有用。
+     *
+     * @param string $field 要查询的字段名
+     * @param mixed $value 字段应该等于的值
+     * @param mixed $except 可选参数,指定需要排除的ID
+     * @return \Illuminate\Database\Eloquent\Collection|static[] 返回符合查询条件的数据集合
+     */
+    public function getAllByField( $field, $value, $except = null)
+    {
+        // 获取数据库实例
+        return ($this->getModel())::getDB()
+                // 如果提供了$except参数,则添加一个排除条件
+                ->when($except, function ($query, $except) use ($field) {
+                    // 对应于排除条件的查询语句
+                    $query->where($field, '<>', $except);
+                })
+                // 添加主要的查询条件
+                ->where($field, $value);
+    }
+
+    /**
+     * 获取展示状态为启用的分类选项
+     *
+     * 本函数用于查询数据库中is_show字段为1的记录,这些记录代表了需要在前端展示的分类。
+     * 查询结果按照sort字段降序排序,并以store_brand_category_id为键,pid和cate_name为值返回结果集。
+     * 这样处理的目的是为了在前端形成一个易于使用的分类选项列表,列表中的每个项包含了分类的ID、父ID和名称。
+     *
+     * @return array 返回一个数组,数组的键是store_brand_category_id,值是一个包含pid和cate_name的数组。
+     */
+    public function options()
+    {
+        // 查询数据库中is_show为1的记录,按照sort降序排序,返回pid和cate_name列
+        return model::getDB()->where('is_show', 1)->order('sort DESC')->column('pid,cate_name', 'store_brand_category_id');
+    }
+}

+ 120 - 0
app/common/dao/store/StoreBrandDao.php

@@ -0,0 +1,120 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\StoreBrand as model;
+use crmeb\traits\CategoresDao;
+
+class StoreBrandDao extends BaseDao
+{
+
+    use CategoresDao;
+
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+
+    /**
+     * 获取所有品牌信息,包括品牌分类和品牌详情。
+     * 此方法专门设计用于获取品牌列表,列表中包括一个特殊的项代表“其他”品牌。
+     *
+     * @return array 返回一个包含所有品牌信息的数组,其中最后一个元素是特殊的“其他”品牌项。
+     */
+    public function getAll()
+    {
+        // 根据品牌分类中的"is_show"字段为1来筛选品牌分类
+        $query = $this->getModel()::hasWhere('brandCategory',function($query){
+            $query->where('is_show',1);
+        });
+        // 筛选品牌表中"is_show"字段为1的品牌
+        $query->where('StoreBrand.is_show',1);
+        // 按品牌的排序和创建时间降序排列,并获取所有符合条件的品牌数据
+        $list = $query->order('StoreBrand.sort DESC,StoreBrand.create_time DESC')->select()->toArray();
+
+        // 向品牌列表中添加一个特殊的“其他”品牌项
+        array_push($list,[
+            "brand_id" => 0,
+            "brand_category_id" => 0,
+            "brand_name" => "其他",
+            "sort" => 999,
+            "pic" => "",
+            "is_show" => 1,
+            "create_time" => "",
+        ]);
+
+        // 返回处理后的品牌列表
+        return $list;
+    }
+
+
+
+    /**
+     * 检查指定字段是否存在特定值。
+     *
+     * 该方法用于查询数据库中指定字段的值是否已存在。它支持排除特定值的查询,
+     * 这使得可以在检查存在性时忽略特定的记录。
+     *
+     * @param string $field 要查询的字段名。
+     * @param mixed $value 要查询的字段值。
+     * @param mixed $except 可选参数,用于指定需要排除的值。
+     * @return bool 如果找到匹配的记录,则返回true;否则返回false。
+     */
+    public function merFieldExists($field, $value, $except = null)
+    {
+        // 从模型中获取数据库实例。
+        return ($this->getModel())::getDB()
+                // 当$except有值时,添加一个不等于($field, '<>')的查询条件。
+                ->when($except, function ($query, $except) use ($field) {
+                    $query->where($field, '<>', $except);
+                })
+                // 添加等于($field, $value)的查询条件。
+                ->where($field, $value)
+                // 统计符合条件的记录数,如果大于0,则表示存在。
+                ->count() > 0;
+    }
+
+
+    /**
+     * 根据条件搜索品牌信息
+     *
+     * 本函数用于根据传入的条件数组搜索品牌的数据库记录。条件包括品牌分类ID、品牌名称和品牌ID列表。
+     * 查询结果将按照排序和创建时间倒序返回。
+     *
+     * @param array $where 搜索条件数组,包含品牌分类ID、品牌名称和品牌ID列表等条件。
+     * @return \think\db\Query 查询结果的查询对象,可用于进一步的查询操作或获取结果。
+     */
+    public function search(array $where)
+    {
+        // 获取数据库查询对象
+        $query = $this->getModel()::getDB();
+
+        // 如果条件数组中包含品牌分类ID,并且该ID不为空,则添加到查询条件中
+        if(isset($where['brand_category_id']) && $where['brand_category_id'])
+            $query->where('brand_category_id',$where['brand_category_id']);
+
+        // 如果条件数组中包含品牌名称,并且该名称不为空,则添加到查询条件中,使用LIKE进行模糊匹配
+        if(isset($where['brand_name']) && $where['brand_name'])
+            $query->where('brand_name','like','%'.$where['brand_name'].'%');
+
+        // 如果条件数组中包含品牌ID列表,并且该列表不为空,则添加到查询条件中,查询包含在列表中的品牌
+        if((isset($where['ids']) && $where['ids']))
+            $query->where($this->getPk(),'in',$where['ids']);
+
+        // 对查询结果按照排序和创建时间进行倒序排序
+        return $query->order('sort DESC,create_time desc');
+    }
+
+}

+ 253 - 0
app/common/dao/store/StoreCategoryDao.php

@@ -0,0 +1,253 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\StoreCategory as model;
+use crmeb\traits\CategoresDao;
+
+class StoreCategoryDao extends BaseDao
+{
+
+    use CategoresDao;
+
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     * 获取所有符合条件的数据
+     *
+     * 本函数用于查询数据库中满足特定条件的所有记录。它支持三个参数来过滤结果:
+     * - $mer_id: 商户ID,用于查询特定商户的数据。默认值为0,表示查询所有商户的数据。
+     * - $status: 状态,用于查询特定状态的记录。如果此参数不为null,则查询结果将限制在指定状态的记录。否则,查询结果不受状态限制。
+     * - $type: 类型,用于查询特定类型的记录。默认值为0,表示查询所有类型的记录。
+     *
+     * 查询结果将按照'sort'字段降序和主键降序排序。最后,查询结果将附加一个'has_product'字段。
+     *
+     * @param int $mer_id 商户ID
+     * @param null $status 状态值,用于筛选数据
+     * @param int $type 类型值,用于筛选数据
+     * @return \think\Collection 查询结果,是一个包含数据的集合
+     */
+    public function getAll($mer_id = 0,$status = null, $type = 0)
+    {
+        // 通过getModel方法获取模型实例,并调用其getDB方法来获取数据库对象
+        return $this->getModel()::getDB()
+            // 根据$mer_id和$type条件构建查询
+            ->where('mer_id', $mer_id)
+            ->where('type',$type)
+            // 如果$status不为null,则添加额外的状态条件
+            ->when(($status !== null),function($query)use($status){
+                $query->where($this->getStatus(),$status);
+            })
+            // 按照排序字段和主键降序排序
+            ->order('sort DESC,'.$this->getPk().' DESC')
+            // 执行查询并返回结果,附加'has_product'字段
+            ->select()
+            ->append(['has_product']);
+    }
+
+
+    /**
+     * 查找给定ID的子分类ID列表
+     *
+     * 本函数通过查询数据库,找出路径中包含给定ID的分类记录,
+     * 并返回这些记录的store_category_id列作为子分类ID的列表。
+     * 使用了like查询来匹配路径中包含指定ID的情况,确保了灵活性。
+     *
+     * @param int $id 分类的唯一标识ID
+     * @return array 包含子分类ID的数组
+     */
+    public function findChildrenId($id)
+    {
+        // 使用模型的数据库访问方法,查询路径中包含给定ID的分类记录的store_category_id列
+        return model::getDB()->whereLike('path', '%/'. $id . '/%')->column('store_category_id');
+    }
+
+    /**
+     * 根据给定的ID数组,查询所有子分类的ID。
+     *
+     * 此方法用于从数据库中检索指定分类ID的所有子分类ID。它通过检查每个给定ID的路径中是否包含该ID来确定子分类。
+     * 使用了数据库的whereOr和like操作来实现这一逻辑。
+     *
+     * @param array $ids 分类ID数组,用于查询子分类。
+     * @return array 返回一个包含所有子分类ID的数组,如果输入ID数组为空或不是数组,则返回空数组。
+     */
+    public function selectChildrenId(array $ids)
+    {
+        // 检查输入的ID数组是否为空或不是数组,如果是,则直接返回空数组
+        if (!is_array($ids) || empty($ids))  return [];
+
+        // 构建查询,使用whereOr和like来查询所有路径中包含给定ID的分类
+        $query = model::getDB()->where(function($query) use($ids){
+            foreach ($ids as $id) {
+                // 对每个ID,查询路径中包含该ID的所有分类
+                $query->whereOr('path', 'like','%/'. $id . '/%');
+            }
+        });
+
+        // 返回查询结果中store_category_id列的数组
+        return $query->column('store_category_id');
+    }
+
+
+    /**
+     * 检查指定字段的值是否存在,同时支持排除特定值和商家ID的条件筛选。
+     *
+     * 此函数用于查询数据库中是否存在指定字段的值,同时允许排除某些特定值,并根据需要筛选特定商家的数据。
+     * 主要用于在进行数据操作前验证数据的唯一性或存在性,以避免重复或错误的数据插入。
+     *
+     * @param int|null $merId 商家ID,用于筛选特定商家的数据。如果为null,则不进行商家ID的筛选。
+     * @param string $field 要检查的字段名。
+     * @param mixed $value 要检查的字段值。
+     * @param mixed|null $except 排除的特定值,如果为null,则不进行排除筛选。
+     * @return bool 返回一个布尔值,表示指定字段的值是否存在(不考虑排除条件)。
+     */
+    public function fieldExistsList(?int $merId,$field,$value,$except = null)
+    {
+        // 获取数据库实例,并根据条件动态构建查询语句
+        return ($this->getModel()::getDB())->when($except ,function($query)use($field,$except){
+            // 如果存在需要排除的值,则添加不等于排除值的条件
+            $query->where($field,'<>',$except);
+        })->when(($merId !== null) ,function($query)use($merId){
+            // 如果提供了商家ID,则添加商家ID的条件
+            $query->where('mer_id',$merId);
+        })->where($field,$value);
+
+    }
+
+
+    /**
+     * 获取二级分类列表
+     * 本函数用于查询并返回指定商户下的二级分类ID、分类名称和父分类ID。
+     * 主要用于前端展示或进一步的数据处理,例如商品分类导航等。
+     *
+     * @param int $merId 商户ID,用于查询指定商户的分类数据。默认为0,表示查询所有商户的数据。
+     * @return array 返回一个包含分类ID、分类名称和父分类ID的数组,每个元素代表一个二级分类。
+     */
+    public function getTwoLevel($merId = 0)
+    {
+        // 查询顶级分类的ID,这些分类属于指定的商户,且显示状态为开启,类型为0。
+        $pid = model::getDB()->where('pid', 0)->where('is_show',1)->where('type',0)->where('mer_id', $merId)->order('sort DESC')->column('store_category_id');
+
+        // 根据上一步查询得到的顶级分类ID,进一步查询其下的二级分类ID、分类名称和父分类ID。
+        // 这里限制了查询结果的数量为20条,并按照排序降序返回。
+        return model::getDB()->whereIn('pid', $pid)->where('is_show', 1)->where('mer_id', $merId)->limit(20)->order('sort DESC')->column('store_category_id,cate_name,pid');
+    }
+
+    /**
+     * 获取指定父级ID和商户ID下的分类信息
+     *
+     * 此函数用于查询数据库中特定父分类ID和商户ID下的分类信息,并且只返回显示状态为正常的分类。
+     * 结果按照排序降序返回分类的ID、名称和图片信息。
+     *
+     * @param integer $pid 父分类ID,用于指定查询的父分类。
+     * @param integer $merId 商户ID,可选参数,默认为0,用于指定查询的商户分类。
+     * @return array 返回一个包含分类ID、名称和图片的数组。
+     */
+    public function children($pid, $merId = 0)
+    {
+        // 通过模型获取数据库实例,然后进行条件查询:指定父ID、商户ID和显示状态为1的分类,按排序降序获取分类的ID、名称和图片信息。
+        return model::getDB()->where('pid', $pid)->where('mer_id', $merId)->where('is_show', 1)->order('sort DESC')->column('store_category_id,cate_name,pic');
+    }
+
+
+    /**
+     * 获取所有子分类的ID列表
+     *
+     * 本函数旨在通过给定的分类ID(可以是单个ID或ID数组),获取该分类及其所有子分类的ID列表。
+     * 这对于需要对一系列分类进行操作,例如遍历或筛选,非常有用。
+     *
+     * @param int|array $id 分类ID,可以是单个ID或ID数组
+     * @return array 包含所有子分类ID的数组,如果找不到任何子分类,则返回空数组
+     */
+    public function allChildren($id)
+    {
+        // 根据给定的分类ID(单个或数组),查询数据库中所有对应分类的路径
+        $path = model::getDB()->where('store_category_id', is_array($id) ? 'IN' : '=', $id)->where('mer_id', 0)->column('path', 'store_category_id');
+
+        // 如果查询结果为空,则直接返回空数组
+        if (!count($path)) return [];
+
+        // 继续查询所有路径包含之前查询到的分类路径的分类ID,并按排序降序排列
+        // 这里使用了WHERE子句的回调功能来构建一个或多个LIKE条件,以匹配所有子分类的路径
+        return model::getDB()->where(function ($query) use ($path) {
+            foreach ($path as $k => $v) {
+                $query->whereOr('path', 'LIKE', "$v$k/%");
+            }
+        })->where('mer_id', 0)->order('sort DESC')->column('store_category_id');
+    }
+
+    /**
+     * 根据给定的一组子分类ID,获取所有相关分类的ID。
+     * 此方法通过查询数据库,找出给定子分类ID列表中所有分类的路径,
+     * 然后根据这些路径来获取所有父分类的ID。
+     *
+     * @param array $ids 子分类的ID列表
+     * @return array 返回所有相关分类的ID列表
+     */
+    public function idsByAllChildren(array $ids)
+    {
+        // 查询数据库,找出所有属于给定子分类ID列表且mer_id为0的分类的路径
+        $paths = model::getDB()->whereIn('store_category_id', $ids)->where('mer_id', 0)->column('path');
+
+        // 如果没有找到任何路径,则直接返回空数组
+        if (!count($paths)) return [];
+
+        // 查询所有路径匹配且mer_id为0的分类ID,按排序降序排列
+        // 这里使用了where函数内的循环,来构建一个或多个path匹配的条件
+        return model::getDB()->where(function ($query) use ($paths) {
+            foreach ($paths as $path) {
+                // 对每个路径使用whereOr,确保任何匹配路径的分类都被选中
+                $query->whereOr('path', 'LIKE', "$path%");
+            }
+        })->where('mer_id', 0)->order('sort DESC')->column('store_category_id');
+    }
+
+    /**
+     * 获取最大级别
+     * 此函数用于确定给定商户ID的最大级别。如果商户ID被提供并且有效,返回2;否则,返回3。
+     *
+     * @param int|null $merId 商户ID。如果提供,将用于确定级别的值。
+     * @return int 返回商户的最大级别。可能的值为2(如果提供了有效的商户ID)或3(如果未提供商户ID)。
+     */
+    public function getMaxLevel($merId = null)
+    {
+        if($merId) return 2;
+        return 3;
+    }
+
+
+    public function searchLevelAttr($query, $value)
+    {
+        $query->where('level', $value);
+    }
+
+    /**
+     * 清除特定字段中具有指定ID的记录。
+     *
+     * 此方法通过提供的ID和字段名称,从数据库中删除符合条件的记录。
+     * 它首先获取模型对应的数据库实例,然后使用提供的字段和ID构建删除条件,
+     * 最后执行删除操作。
+     *
+     * @param int $id 主键ID,用于指定要删除的记录。
+     * @param string $field 要用于删除条件的字段名称。
+     */
+    public function clear(int $id, string $field)
+    {
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+}

+ 26 - 0
app/common/dao/store/StorePrinterDao.php

@@ -0,0 +1,26 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\StorePrinter;
+
+class StorePrinterDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return StorePrinter::class;
+    }
+
+}

+ 260 - 0
app/common/dao/store/StoreSeckillActiveDao.php

@@ -0,0 +1,260 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store;
+
+use app\common\model\store\StoreSeckillActive;
+use app\common\dao\BaseDao;
+use app\common\repositories\store\order\StoreOrderProductRepository;
+use app\common\repositories\store\order\StoreOrderRepository;
+use app\common\repositories\store\product\ProductAttrValueRepository;
+use app\common\repositories\store\product\ProductRepository;
+use app\common\repositories\store\product\SpuRepository;
+use app\common\repositories\store\StoreSeckillActiveRepository;
+use think\facade\Log;
+
+class StoreSeckillActiveDao extends BaseDao
+{
+
+    /**
+     *
+     * @return string
+     * @author Qinii
+     * @day 2020-07-30
+     */
+    public function getModel(): string
+    {
+        return StoreSeckillActive::class;
+    }
+
+
+    /**
+     * 搜索
+     * @param array $where
+     * @return mixed
+     * FerryZhao 2024/4/12
+     */
+    public function search(array $where)
+    {
+        $query = $this->getModel()::getDB()
+            ->when(isset($where['name']) && $where['name'] !== '', function ($query) use ($where) {
+                $query->where('name', 'like', '%' . $where['name'] . '%');
+            })
+            ->when(isset($where['active_status']) && $where['active_status'] !== '', function ($query) use ($where) {
+                $query->where('active_status', $where['active_status']);
+            })
+            ->when(isset($where['seckill_active_status']) && $where['seckill_active_status'] !== '', function ($query) use ($where) {
+                $query->where('status', $where['seckill_active_status']);
+            })
+            ->when(isset($where['seckill_active_id']) && $where['seckill_active_id'] !== '', function ($query) use ($where) {
+                $query->where('seckill_active_id', $where['seckill_active_id']);
+            })
+            ->when(isset($where['active_name']) && $where['active_name'] !== '', function ($query) use ($where) {
+                $query->whereLike('name', "%{$where['active_name']}%");
+            })
+            ->when(isset($where['active_name']) && $where['active_name'] !== '', function ($query) use ($where) {
+                $query->whereLike('name', "%{$where['active_name']}%");
+            })
+            ->when(!isset($where['sign']), function ($query) use ($where) {
+                $query->where('sign', 1);
+            })
+            ->when(isset($where['date']) && $where['date'], function ($query) use ($where) {
+                $timeWhere = explode('-', $where['date']);
+                $query->whereTime('start_day', '>=', $timeWhere[0]);
+                $query->whereTime('end_day', '<=', date('Y-m-d', strtotime($timeWhere[1]) + 24 * 60 * 60));
+            });
+
+        $query->order('status desc,seckill_active_id desc');
+        return $query;
+    }
+
+    /**
+     * 检测活动状态关闭spu
+     * @return void
+     * FerryZhao 2024/4/19
+     */
+    public function valActiveStatus()
+    {
+        try {
+            $storeSeckillActiveRepository = app()->make(StoreSeckillActiveRepository::class);
+            $changeStartIds = $this->getModel()::getDB()->where(['active_status' => 0])->whereTime('start_day', '<', date('Y-m-d H:i:s', time()))->whereTime('end_day', '>', time())->column('seckill_active_id');
+            if (!empty($changeStartIds)) {
+                $storeSeckillActiveRepository->getSearch([])->whereIn('seckill_active_id', $changeStartIds)->update(['active_status' => 1]);
+            }
+            $changeStartingIds = $this->getModel()::getDB()->where(['active_status' => 1])->whereTime('end_day', '<', time())->column('seckill_active_id');
+            if (!empty($changeStartingIds)) {
+                $storeSeckillActiveRepository->getSearch([])->whereIn('seckill_active_id', $changeStartingIds)->update(['active_status' => '-1']);
+                app()->make(SpuRepository::class)->getSearch([
+                    'product_type' => 1,
+                    'activity_ids' => $changeStartingIds
+                ])->update(['status' => 0]);
+            }
+        } catch (\Exception $e) {
+            Log::error('检测活动状态关闭spu失败,' . $e->getMessage());
+        }
+
+    }
+
+    /**
+     *  不同状态商品
+     * @param $status
+     * @return mixed
+     * @author Qinii
+     * @day 2020-08-19
+     */
+    public function getStatus($status)
+    {
+        $day = date('Y-m-d', time());
+        $_h = date('H', time());
+        $query = $this->getModel()::getDB();
+        if ($status == 1) //未开始
+            $query->where('status', '<>', -1)->where(function ($query) use ($day, $_h) {
+                $query->whereTime('start_day', '>', $day)->whereOr(function ($query) use ($day, $_h) {
+                    $query->whereTime('start_day', '<=', $day)->where('start_time', '>', $_h);
+                });
+            });
+
+        if ($status == 2)//进行中
+            $query->where('status', 1)
+                ->whereTime('start_day', '<=', $day)->whereTime('end_day', '>', $day)
+                ->where('start_time', '<=', $_h)->where('end_time', '>', $_h);
+
+        if ($status == 3) //结束
+            $query->where('status', -1)->whereOr(function ($query) use ($day, $_h) {
+                $query->whereTime('end_day', '<', $day)
+                    ->whereOr(function ($query) use ($day, $_h) {
+                        $query->whereTime('start_day', '<=', $day)->whereTime('end_day', '>=', $day)->where('end_time', '<=', $_h);
+                    });
+            });
+        return $query;
+    }
+
+    /**
+     * 活动参与人列表统计
+     * @param $activeId
+     * @param $merId
+     * @param $where
+     * @param int $page
+     * @param int $limit
+     * @return void
+     * FerryZhao 2024/4/28
+     */
+    public function chartPeople($activeId, $merId = null, $where, int $page = 1, int $limit = 10)
+    {
+        $storeOrderRepository = app()->make(StoreOrderRepository::class);
+        $query = $storeOrderRepository->getSearch([])->alias('ORDERA')
+            ->leftJoin('StoreOrderProduct ORDERB', 'ORDERA.order_id = ORDERB.order_id')
+            ->leftJoin('User USER', 'USER.uid = ORDERA.uid')
+            ->when($merId, function ($query) use ($merId) {
+                $query->where('ORDERA.mer_id', '=', $merId);
+            })
+            ->when(isset($where['keyword']) && $where['keyword'], function ($query) use ($where) {
+                $query->whereLike('USER.uid|USER.nickname|USER.phone', "%{$where['keyword']}%");
+            })
+            ->when(isset($where['date']) && $where['date'], function ($query) use ($where) {
+                getModelTime($query, $where['date'], 'ORDERA.create_time');
+            })
+            ->where([
+                'ORDERB.activity_id' => $activeId,
+                'ORDERA.paid' => 1,
+                'ORDERA.activity_type' => 1,
+            ])
+            ->where('ORDERA.status','>',-1)
+            ->group('ORDERA.uid');
+        $count = $query->count();
+        $list = $query->page($page, $limit)
+            ->field('sum(ORDERA.total_num) as sum_total_num,sum(ORDERA.pay_price) as sum_pay_price,USER.nickname,USER.uid,count(ORDERA.order_id) as order_count,max(ORDERA.create_time) as create_time,ORDERA.is_del,ORDERA.paid,ORDERA.order_type')
+            ->select();
+        return compact('count', 'list');
+    }
+
+
+    /**
+     * 活动订单统计列表
+     * @param $activeId
+     * @param $merId
+     * @param $where
+     * @param int $page
+     * @param int $limit
+     * @return void
+     * FerryZhao 2024/4/28
+     */
+    public function chartOrder($activeId, $merId = null, $where, $statusWhere = [], int $page = 1, int $limit = 10)
+    {
+        $storeOrderRepository = app()->make(StoreOrderRepository::class);
+        $orderWhere = [
+            'ORDERB.activity_id' => $activeId,
+        ];
+        if (isset($where['status']) && $where['status'] != '') {
+            $orderWhere['StoreOrder.status'] = $where['status'];
+        }
+        $query = $storeOrderRepository->getSearch([])->alias('StoreOrder')
+            ->leftJoin('User USER', 'StoreOrder.uid = USER.uid')
+            ->leftJoin('StoreOrderProduct ORDERB', 'StoreOrder.order_id = ORDERB.order_id')
+            ->when(isset($where['keyword']) && $where['keyword'], function ($query) use ($where) {
+                $query->whereLike('USER.uid|USER.nickname|USER.phone', "%{$where['keyword']}%");
+            })
+            ->when($merId, function ($query) use ($merId) {
+                $query->where('StoreOrder.mer_id', '=', $merId);
+            })
+            ->when(isset($where['date']) && $where['date'], function ($query) use ($where) {
+                getModelTime($query, $where['date'], 'StoreOrder.create_time');
+            })
+            ->where($orderWhere)->where($statusWhere);
+        $count = $query->count();
+        $list = $query->page($page, $limit)
+            ->field('StoreOrder.order_sn,USER.nickname,USER.uid,StoreOrder.status,StoreOrder.pay_price,StoreOrder.total_num,StoreOrder.create_time,StoreOrder.pay_time,StoreOrder.is_del,StoreOrder.paid,StoreOrder.order_type')
+            ->order('StoreOrder.order_id desc')
+            ->select();
+        return compact('count', 'list');
+    }
+
+    /**
+     * 活动商品统计列表
+     * @param $activeId
+     * @param $merId
+     * @param $where
+     * @param int $page
+     * @param int $limit
+     * @return void
+     * FerryZhao 2024/4/28
+     */
+    public function chartProduct($activeId, $merId = null, $where, int $page = 1, int $limit = 10)
+    {
+        $productRepository = app()->make(ProductRepository::class);
+        $storeOrderRepository = app()->make(StoreOrderRepository::class);
+        $storeOrderProductRepository = app()->make(StoreOrderProductRepository::class);
+        $productAttrValueRepository = app()->make(ProductAttrValueRepository::class);
+        $productWhere = [
+            'product_type' => 1,
+            'seckill_active_id' => $activeId
+        ];
+        if (isset($where['keyword']) && $where['keyword']) {
+            $productWhere['keyword'] = $where['keyword'];
+        }
+        $with = ['attr', 'attrValue', 'merCateId.category', 'storeCategory', 'content', 'seckillActive',
+            'merchant' => function ($query) {
+                $query->with(['typeName', 'categoryName'])->field('mer_id,category_id,type_id,mer_avatar,mer_name,is_trader');
+            },
+
+        ];
+        $query = $productRepository->search($merId, $productWhere)->with($with);
+        $count = $query->count();
+        $filed = 'Product.product_id,Product.active_id,Product.mer_id,brand_id,unit_name,spec_type,mer_status,rate,reply_count,store_info,cate_id,Product.image,slider_image,Product.store_name,Product.keyword,Product.sort,Product.is_show,Product.sales,Product.price,extension_type,refusal,cost,U.ot_price,stock,is_gift_bag,Product.care_count,Product.status,is_used,Product.create_time,Product.product_type,old_product_id,integral_total,integral_price_total,mer_labels,Product.is_good,Product.is_del,type,param_temp_id,mer_svip_status,svip_price,svip_price_type';
+        $list = $query->page($page, $limit)->setOption('field', [])->where(['Product.is_del' => 0, 'Product.delete' => 0])->field($filed)->select();
+        foreach ($list as &$item){
+            $item['sales'] = $storeOrderRepository->seckillOrderCounut($item['active_id'], $item['product_id']);
+            $item['stock'] = $productAttrValueRepository->getSearch([])->where(['product_id'=>$item['product_id']])->sum('stock');
+        }
+        return compact('count', 'list');
+    }
+}

+ 112 - 0
app/common/dao/store/StoreSeckillTimeDao.php

@@ -0,0 +1,112 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store;
+
+use app\common\model\store\StoreSeckillTime;
+use app\common\dao\BaseDao;
+
+class StoreSeckillTimeDao extends BaseDao
+{
+
+    /**
+     *
+     * @return string
+     * @author Qinii
+     * @day 2020-07-30
+     */
+    protected function getModel(): string
+    {
+        return StoreSeckillTime::class;
+    }
+
+    public function getTime($status)
+    {
+        foreach (StoreSeckillTime::ISTIME as $k => $item){
+            if($status && $k !== 24){
+                $time [] = ['value' => $k,  'label' => $item];
+            }
+            if(!$status && $k !== 0){
+                $time [] = ['value' => $k,  'label' => $item];
+            }
+        }
+        return $time;
+    }
+
+
+    public function search(array $where)
+    {
+        $query = $this->getModel()::getDB()
+            ->when(isset($where['status']) && $where['status'] !== '',function($query) use($where){
+                $query->where('status',$where['status']);
+            })
+            ->when(isset($where['title']) && $where['title'] !== '',function($query) use($where){
+                $query->where('title','like','%'.$where['title'].'%');
+            })
+            ->when(isset($where['start_time']) && $where['start_time'] !== '',function($query) use($where){
+                $query->whereTime('start_time','<=',intval($where['start_time']));
+            })
+            ->when(isset($where['end_time']) && $where['end_time'] !== '',function($query) use($where){
+                $query->whereTime('end_time','>=',intval($where['end_time']));
+            });
+        $query->order('start_time ASC');
+        return $query;
+    }
+
+    /**
+     *  开始时间 在别的时间段中
+     * @param $time
+     * @return mixed
+     * @author Qinii
+     * @day 2020-07-31
+     */
+    public function valStartTime($time,$id)
+    {
+        return $this->getModel()::getDB()
+            ->when($id,function ($query)use($id){
+                $query->where($this->getPk(),'<>',$id);
+            })->where('start_time','<=',$time)->where('end_time','>',$time)->count();
+    }
+
+    /**
+     *  结束时间在别的时间段中
+     * @param $time
+     * @param $id
+     * @return mixed
+     * @author Qinii
+     * @day 2020-07-31
+     */
+    public function valEndTime($time,$id)
+    {
+        return $this->getModel()::getDB()
+            ->when($id,function ($query)use($id){
+                $query->where($this->getPk(),'<>',$id);
+            })->where('start_time','<',$time)->where('end_time','>=',$time)->count();
+    }
+
+    /**
+     *  时间段包含了别的时间段
+     * @param array $data
+     * @param $id
+     * @return mixed
+     * @author Qinii
+     * @day 2020-07-31
+     */
+    public function valAllTime(array $data,$id)
+    {
+        return $this->getModel()::getDB()
+            ->when($id,function ($query)use($id){
+                $query->where($this->getPk(),'<>',$id);
+            })->where('start_time','>',$data['start_time'])->where('end_time','<=',$data['end_time'])->count();
+    }
+
+}

+ 79 - 0
app/common/dao/store/broadcast/BroadcastAssistantDao.php

@@ -0,0 +1,79 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store\broadcast;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\broadcast\BroadcastAssistant;
+use think\exception\ValidateException;
+
+/**
+ * 直播间主播
+ * @author wuhaotian
+ * @email 442384644@qq.com
+ * @date 2024/7/13
+ */
+class BroadcastAssistantDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return BroadcastAssistant::class;
+    }
+
+    /**
+     * 查询是否存在
+     * @param int $id
+     * @param int $merId
+     * @return bool
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function merExists(int $id, int $merId)
+    {
+        return $this->existsWhere([$this->getPk() => $id, 'is_del' => 0, 'mer_id' => $merId]);
+    }
+
+    /**
+     * 查询所有的主播id
+     * @param string|null $ids
+     * @param int $merId
+     * @return int[]|mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function intersection(?string $ids, int $merId)
+    {
+        if (!$ids)  return [0];
+        return $this->getModel()::getDb()->whereIn('assistant_id',$ids)->where('mer_id', $merId)->column('assistant_id');
+    }
+
+    /**
+     * 查询主播是否存在
+     * @param $ids
+     * @param $merId
+     * @return bool
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function existsAll($ids, $merId)
+    {
+        foreach ($ids as $id) {
+            $has = $this->getModel()::getDb()->where('assistant_id',$id)->where('mer_id',$merId)->count();
+            if (!$has) throw new ValidateException('ID:'.$id.' 不存在');
+        }
+
+        return true;
+    }
+}

+ 175 - 0
app/common/dao/store/broadcast/BroadcastGoodsDao.php

@@ -0,0 +1,175 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\broadcast;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\broadcast\BroadcastGoods;
+use app\common\repositories\system\merchant\MerchantRepository;
+use think\db\BaseQuery;
+use think\db\exception\DbException;
+
+/**
+ * Class BroadcastGoodsDao
+ * @package app\common\dao\store\broadcast
+ * @author xaboy
+ * @day 2020/7/29
+ */
+class BroadcastGoodsDao extends BaseDao
+{
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/7/29
+     */
+    protected function getModel(): string
+    {
+        return BroadcastGoods::class;
+    }
+
+    /**
+     * 删除商品
+     * @param int $id
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function delete(int $id)
+    {
+        return $this->update($id, ['is_del' => 1]);
+    }
+
+    /**
+     * 查询商品是否存在
+     * @param int $id
+     * @return bool
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function exists(int $id)
+    {
+        return $this->existsWhere(['broadcast_goods_id' => $id, 'is_del' => 0]);
+    }
+
+    /**
+     * 查询商户的商品是否存在
+     * @param int $id
+     * @param int $merId
+     * @return bool
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function merExists(int $id, int $merId)
+    {
+        return $this->existsWhere(['broadcast_goods_id' => $id, 'is_del' => 0, 'is_mer_del' => 0, 'mer_id' => $merId]);
+    }
+
+    /**
+     * 商品查询
+     * @param array $where
+     * @return BaseQuery
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function search(array $where)
+    {
+        if (isset($where['is_trader']) && $where['is_trader'] !== '') {
+            $query = BroadcastGoods::hasWhere('merchant', function ($query) use ($where) {
+                $query->where('is_trader', $where['is_trader']);
+            });
+        } else {
+            $query = BroadcastGoods::getDB()->alias('BroadcastGoods');
+        }
+        $query->when(isset($where['mer_id']), function ($query) use ($where) {
+            $query->where('BroadcastGoods.mer_id', $where['mer_id']);
+        })->when(isset($where['keyword']) && $where['keyword'] !== '', function ($query) use ($where) {
+            $query->whereLike('BroadcastGoods.goods_id|BroadcastGoods.mark|BroadcastGoods.name|BroadcastGoods.broadcast_goods_id', "%{$where['keyword']}%");
+        })->when(isset($where['valid']) && $where['valid'] !== '', function ($query) use ($where) {
+            $query->where('BroadcastGoods.is_show', 1);
+        })->when(isset($where['mer_valid']) && $where['mer_valid'] !== '', function ($query) use ($where) {
+            $query->where('BroadcastGoods.is_show', 1)->where('BroadcastGoods.is_mer_show', 1);
+        })->when(isset($where['broadcast_goods_id']) && $where['broadcast_goods_id'] !== '', function ($query) use ($where) {
+            $query->where('BroadcastGoods.broadcast_goods_id', $where['broadcast_goods_id']);
+        })->when(isset($where['status_tag']) && $where['status_tag'] !== '', function ($query) use ($where) {
+            if ($where['status_tag'] == 1) {
+                $query->where('BroadcastGoods.status', 2);
+            } else if ($where['status_tag'] == -1) {
+                $query->where('BroadcastGoods.status', -1);
+            } else if ($where['status_tag'] == 0) {
+                $query->whereIn('BroadcastGoods.status', [0, 1]);
+            }
+        })->where('BroadcastGoods.is_del', 0)->where('BroadcastGoods.is_mer_del', 0);
+        return $query;
+    }
+
+    /**
+     * 获取所有的商品状态
+     * @return array
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function goodsStatusAll()
+    {
+        return BroadcastGoods::getDB()->where('goods_id', '>', 0)->whereIn('audit_status', [0, 1])->column('audit_status', 'goods_id');
+    }
+
+    /**
+     * 更新商品信息
+     * @param $goods_id
+     * @param $data
+     * @return int
+     * @throws DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function updateGoods($goods_id, $data)
+    {
+        return BroadcastGoods::getDB()->where('goods_id', $goods_id)->update($data);
+    }
+
+    /**
+     * 商品列表
+     * @param $merId
+     * @param array $ids
+     * @return array|\think\Collection|BaseQuery[]
+     * @throws DbException
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function goodsList($merId, array $ids)
+    {
+        return BroadcastGoods::getDB()->whereIn('broadcast_goods_id', $ids)->where('mer_id', $merId)->where('is_show', 1)->where('is_mer_show', 1)->where('is_del', 0)->where('status', 2)->select();
+    }
+
+    /**
+     * 删除商品
+     * @param int $id
+     * @return int
+     * @throws DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function merDelete(int $id)
+    {
+        return $this->update($id, ['is_mer_del' => 1]);
+    }
+}

+ 177 - 0
app/common/dao/store/broadcast/BroadcastRoomDao.php

@@ -0,0 +1,177 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\broadcast;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\broadcast\BroadcastRoom;
+use app\common\repositories\system\merchant\MerchantRepository;
+use think\db\BaseQuery;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\Model;
+
+/**
+ * Class BroadcastRoomDao
+ * @package app\common\dao\store\broadcast
+ * @author xaboy
+ * @day 2020/7/29
+ */
+class BroadcastRoomDao extends BaseDao
+{
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/7/29
+     */
+    protected function getModel(): string
+    {
+        return BroadcastRoom::class;
+    }
+
+    /**
+     * 删除直播间
+     * @param int $id
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function delete(int $id)
+    {
+        return $this->update($id, ['is_del' => 1]);
+    }
+
+    /**
+     * 直播间是否存在
+     * @param int $id
+     * @return bool
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function exists(int $id)
+    {
+        return $this->existsWhere(['broadcast_room_id' => $id, 'is_del' => 0]);
+    }
+
+    /**
+     * 商户直播间是否存在
+     * @param int $id
+     * @param int $merId
+     * @return bool
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function merExists(int $id, int $merId)
+    {
+        return $this->existsWhere(['broadcast_room_id' => $id, 'is_del' => 0, 'is_mer_del' => 0, 'mer_id' => $merId]);
+    }
+
+    /**
+     * 删除直播间
+     * @param int $id
+     * @return int
+     * @throws DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function merDelete(int $id)
+    {
+        return $this->update($id, ['is_mer_del' => 1]);
+    }
+
+    /**
+     * 查询直播间
+     * @param array $where
+     * @return BaseQuery
+     * @author xaboy
+     * @day 2020/7/30
+     */
+    public function search(array $where)
+    {
+        if(isset($where['is_trader']) && $where['is_trader'] !== ''){
+            $query = BroadcastRoom::hasWhere('merchant',function($query)use($where){
+                $query->where('is_trader',$where['is_trader']);
+            });
+        }else{
+            $query = BroadcastRoom::getDB()->alias('BroadcastRoom');
+        }
+        $query->when(isset($where['keyword']) && $where['keyword'] !== '', function ($query) use ($where) {
+            $query->whereLike('room_id|name|anchor_name|anchor_wechat|broadcast_room_id', "%{$where['keyword']}%");
+        })->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+            $query->where('BroadcastRoom.mer_id', $where['mer_id']);
+        })->when(isset($where['live_status']) && $where['live_status'] !== '', function ($query) use ($where) {
+            $query->where('BroadcastRoom.live_status', $where['live_status']);
+        })->when(isset($where['star']) && $where['star'] !== '', function ($query) use ($where) {
+            $query->where('BroadcastRoom.star', $where['star']);
+        })->when(isset($where['show_tag']) && $where['show_tag'] !== '', function ($query) use ($where) {
+            $query->where('is_show', 1)->where('is_mer_show', 1)->where('status', 2);
+        })->when(isset($where['hot']) && $where['hot'] !== '', function ($query) use ($where) {
+            $query->order('live_status ASC,star DESC,sort DESC');
+        })->when(isset($where['broadcast_room_id']) && $where['broadcast_room_id'] !== '', function ($query) use ($where) {
+            $query->where('BroadcastRoom.broadcast_room_id', $where['broadcast_room_id']);
+        })->when(isset($where['status_tag']) && $where['status_tag'] !== '', function ($query) use ($where) {
+            if ($where['status_tag'] == 1) {
+                $query->where('BroadcastRoom.status', 2);
+            } else if ($where['status_tag'] == -1) {
+                $query->where('BroadcastRoom.status', -1);
+            } else if ($where['status_tag'] == 0) {
+                $query->whereIn('BroadcastRoom.status', [0, 1]);
+            }
+        })->when(isset($where['show_type']) && $where['show_type'] !== '', function ($query) use ($where) {
+            if ($where['show_type'] == 3) {
+                $query->where('BroadcastRoom.is_mer_show', 1)->where('BroadcastRoom.is_show', 1);
+            } else if ($where['show_type'] == 2) {
+                $query->where('BroadcastRoom.is_mer_show', 0)->where('BroadcastRoom.is_show', 1);
+            } else if ($where['show_type'] == 1) {
+                $query->where('BroadcastRoom.is_mer_show', 1)->where('BroadcastRoom.is_show', 0);
+            } else if ($where['show_type'] == 0) {
+                $query->where('BroadcastRoom.is_mer_show', 0)->where('BroadcastRoom.is_show', 0);
+            }
+        })->where('BroadcastRoom.is_del', 0)->where('BroadcastRoom.is_mer_del', 0);
+
+        return $query;
+    }
+
+    /**
+     * 验证直播间
+     * @param $roomId
+     * @param $merId
+     * @return array|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/7/31
+     */
+    public function validRoom($roomId, $merId)
+    {
+        return BroadcastRoom::getDB()->where('broadcast_room_id', $roomId)->where('mer_id', $merId)->where('status', 2)->where('is_show', 1)->find();
+    }
+
+    /**
+     * 获取直播间状态
+     * @param array $roomIds
+     * @return array
+     * @author xaboy
+     * @day 2020/7/31
+     */
+    public function getRooms(array $roomIds)
+    {
+        return BroadcastRoom::getDB()->whereIn('room_id', $roomIds)->column('live_status,broadcast_room_id', 'room_id');
+    }
+}

+ 118 - 0
app/common/dao/store/broadcast/BroadcastRoomGoodsDao.php

@@ -0,0 +1,118 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\broadcast;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\broadcast\BroadcastRoomGoods;
+use app\common\repositories\store\order\StoreCartRepository;
+
+class BroadcastRoomGoodsDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return BroadcastRoomGoods::class;
+    }
+
+    /**
+     * 清除直播间商品
+     * @param $id
+     * @return int
+     * @throws \think\db\exception\DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function clear($id)
+    {
+        return BroadcastRoomGoods::getDB()->where('broadcast_room_id', $id)->delete();
+    }
+
+    /**
+     * 获取直播间的所有商品id
+     * @param $id
+     * @return array
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function goodsId($id)
+    {
+        return BroadcastRoomGoods::getDB()->where('broadcast_room_id', $id)->column('broadcast_goods_id');
+    }
+
+    /**
+     * 删除单个商品
+     * @param $goodsId
+     * @param $roomId
+     * @return int
+     * @throws \think\db\exception\DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function rmGoods($goodsId, $roomId)
+    {
+        return BroadcastRoomGoods::getDB()->where('broadcast_room_id', $roomId)->where('broadcast_goods_id', $goodsId)->delete();
+    }
+
+    /**
+     * 获取直播间商品列表
+     * @param $roomId
+     * @param $page
+     * @param $limit
+     * @return array
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function getGoodsList($roomId, $page, $limit)
+    {
+        $query = BroadcastRoomGoods::getDB()->where('broadcast_room_id', $roomId);
+        $count = $query->count();
+        $list = $query->page($page, $limit)->with('goods.product')->select()->toArray();
+        $ids = array_column($list, 'broadcast_goods_id');
+        if (count($ids)) {
+            $sourcePayInfo = app()->make(StoreCartRepository::class)->getSourcePayInfo(1, $ids);
+            $data = [];
+            foreach ($sourcePayInfo as $item) {
+                $data[$item['source_id']] = $item;
+            }
+            foreach ($list as $k => $goods) {
+                $list[$k]['goods']['pay_num'] = $data[$goods['broadcast_goods_id']]['pay_num'] ?? 0;
+                $list[$k]['goods']['pay_price'] = $data[$goods['broadcast_goods_id']]['pay_price'] ?? 0;
+            }
+        }
+        return compact('list', 'count');
+    }
+
+    /**
+     * 删除商品
+     * @param $goodsId
+     * @return int
+     * @throws \think\db\exception\DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function deleteGoods($goodsId)
+    {
+        return BroadcastRoomGoods::getDB()->where('broadcast_goods_id', $goodsId)->delete();
+    }
+}

+ 399 - 0
app/common/dao/store/coupon/StoreCouponDao.php

@@ -0,0 +1,399 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\coupon;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\coupon\StoreCoupon;
+use app\common\model\store\coupon\StoreCouponUser;
+use app\common\repositories\store\coupon\StoreCouponRepository;
+use app\common\repositories\system\merchant\MerchantRepository;
+use think\Collection;
+use think\db\BaseQuery;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\Model;
+
+/**
+ * Class StoreCouponIssueDao
+ * @package app\common\dao\store\coupon
+ * @author xaboy
+ * @day 2020-05-14
+ */
+class StoreCouponDao extends BaseDao
+{
+
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    protected function getModel(): string
+    {
+        return StoreCoupon::class;
+    }
+
+    /**
+     * 查询优惠券列表
+     * @param int $merId
+     * @param array $where
+     * @return BaseQuery
+     * @author xaboy
+     * @day 2020-05-14
+     */
+    public function search(?int $merId, array $where)
+    {
+        if(isset($where['is_trader']) && $where['is_trader'] !== ''){
+            $query = StoreCoupon::hasWhere('merchant',function($query)use($where){
+                $query->where('is_trader',$where['is_trader']);
+            });
+        }else{
+            $query = StoreCoupon::getDB()->alias('StoreCoupon');
+        }
+        $query->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+            $query->where('StoreCoupon.status', (int)$where['status']);
+        })->when(isset($where['coupon_name']) && $where['coupon_name'] !== '', function ($query) use ($where) {
+            $query->whereLike('title', "%{$where['coupon_name']}%");
+        })->when(isset($where['coupon_id']) && $where['coupon_id'] !== '', function ($query) use ($where) {
+            $query->where('coupon_id', (int)$where['coupon_id']);
+        })->when(isset($where['send_type']) && $where['send_type'] !== '', function ($query) use ($where) {
+            $query->where('send_type', (int)$where['send_type']);
+        })->when(isset($where['not_send_type']) && $where['not_send_type'] !== '', function ($query) use ($where) {
+            $query->whereNotIn('send_type', $where['not_send_type']);
+        })->when(isset($where['type']) && $where['type'] !== '', function ($query) use ($where) {
+            $query->where('type', (int)$where['type']);
+        })->when($merId !== null, function ($query) use ($merId) {
+            $query->where('StoreCoupon.mer_id', $merId);
+        })->when(isset($where['is_mer']) &&  $where['is_mer'] !== '', function ($query) use ($merId) {
+            $query->where('type', '<', 10);
+        })->when(isset($where['coupon_ids']) && $where['coupon_ids'] !== '', function ($query) use ($where) {
+            $query->whereIn('coupon_id', $where['coupon_ids']);
+        });
+        return $query->where('StoreCoupon.is_del', 0)->order(($merId ? 'StoreCoupon.sort DESC,' : '') . 'coupon_id DESC');
+    }
+
+    /**
+     * 验证查询参数
+     * @param int|null $type
+     * @param int $send_type
+     * @return BaseQuery
+     * @author xaboy
+     * @day 2020/6/18
+     */
+    public function validCouponQuery($type = null, $send_type = 0)
+    {
+        $query = StoreCoupon::getDB()
+            ->where('status', 1)
+            ->where('send_type', $send_type)
+            ->where('is_del', 0)
+            ->order('sort DESC,coupon_id DESC')
+            ->when(!is_null($type), function ($query) use ($type) {
+                $query->where('type', $type);
+            });
+        $query->where(function (BaseQuery $query) {
+            $query->where('is_limited', 0)->whereOr(function (BaseQuery $query) {
+                $query->where('is_limited', 1)->where('remain_count', '>', 0);
+            });
+        })
+        ->where(function (BaseQuery $query) {
+            $query->where('is_timeout', 0)->whereOr(function (BaseQuery $query) {
+                $time = date('Y-m-d H:i:s');
+                $query->where('is_timeout', 1)->where('start_time', '<', $time)->where('end_time', '>', $time);
+            });
+        })
+        ->where(function (BaseQuery $query) {
+            $query->where('coupon_type', 0)->whereOr(function (BaseQuery $query) {
+                $query->where('coupon_type', 1)->where('use_end_time', '>', date('Y-m-d H:i:s'));
+            });
+        });
+        return $query;
+    }
+
+    /**
+     * 验证带商户的查询参数
+     * @param $where
+     * @param null $uid
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function validCouponQueryWithMerchant($where,$uid = null)
+    {
+        $query = StoreCoupon::alias('C')->leftJoin('Merchant M','C.mer_id = M.mer_id')
+            ->where('C.status', 1)
+            ->where('C.is_del', 0)
+            ->when(isset($where['type']) && !is_null($where['type']), function ($query) use ($where) {
+                if ($where['type'] == '') {
+                    $query->where('C.type', 'in', [0,10,11,12]);
+                } else {
+                    $query->where('C.type', $where['type']);
+                }
+            })
+            ->when(isset($where['send_type']) && $where['send_type'] !== '', function($query) use($where){
+                $query->where('C.send_type', $where['send_type']);
+            })
+            ->when(isset($where['not_svip']) && $where['not_svip'] !== '', function($query) use($where){
+                $query->where('C.send_type', '<>',StoreCouponRepository::GET_COUPON_TYPE_SVIP);
+            })
+            ->when($uid, function($query) use($uid){
+                $couponId = StoreCouponUser::where('uid',$uid)->whereIn('status',[1,2])->column('coupon_id');
+                $query->whereNotIn('C.coupon_id', $couponId);
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function($query) use($where){
+                $query->where('C.mer_id', $where['mer_id']);
+            })
+            ->where(function (BaseQuery $query) {
+                $query->where('is_limited', 0)->whereOr(function (BaseQuery $query) {
+                    $query->where('is_limited', 1)->where('remain_count', '>', 0);
+                });
+            })
+            ->where(function (BaseQuery $query) {
+                $query->where('is_timeout', 0)->whereOr(function (BaseQuery $query) {
+                    $time = date('Y-m-d H:i:s');
+                    $query->where('is_timeout', 1)->where('start_time', '<', $time)->where('end_time', '>', $time);
+                });
+            })
+            ->where(function (BaseQuery $query) {
+                $query->where('C.mer_id', 0)->whereOr(function (BaseQuery $query) {
+                    $query->where('M.is_del',0)->where('M.status',1)->where('M.mer_state',1);
+                });
+            })
+            ->where(function (BaseQuery $query) {
+                $query->where('coupon_type', 0)->whereOr(function (BaseQuery $query) {
+                    $query->where('coupon_type', 1)->where('use_end_time', '>', date('Y-m-d H:i:s'));
+                });
+            });
+        return $query->order('C.sort DESC,C.create_time DESC');
+    }
+
+
+    /**
+     * 获取优惠券详情
+     * @param $id
+     * @param $uid
+     * @return array|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/19
+     */
+    public function validCoupon($id, $uid)
+    {
+        return $this->validCouponQuery()->when($uid, function (BaseQuery $query, $uid) {
+            $query->with(['issue' => function (BaseQuery $query) use ($uid) {
+                $query->where('uid', $uid);
+            }]);
+        })->where('coupon_id', $id)->find();
+    }
+
+    /**
+     * 获取svip优惠券详情
+     * @param $id
+     * @param $uid
+     * @return array|mixed|BaseQuery|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function validSvipCoupon($id, $uid)
+    {
+        return $this->validCouponQuery(null,StoreCouponRepository::GET_COUPON_TYPE_SVIP)->when($uid, function (BaseQuery $query, $uid) {
+            $query->with(['svipIssue' => function (BaseQuery $query) use ($uid) {
+                $query->where('uid', $uid);
+            }]);
+        })->where('coupon_id', $id)->find();
+    }
+
+    /**
+     * 查询商户的优惠券列表
+     * @param $merId
+     * @param null $uid
+     * @return Collection
+     * @throws DbException
+     * @throws DataNotFoundException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/1
+     */
+    public function validMerCoupon($merId, $uid = null, $type = 0)
+    {
+        return $this->validCouponQuery($type)->when($uid, function (BaseQuery $query, $uid) {
+            $query->with(['issue' => function (BaseQuery $query) use ($uid) {
+                $query->where('uid', $uid);
+            }]);
+        })->where('mer_id', $merId)->select();
+    }
+
+    /**
+     * 商户的优惠券是否存在
+     * @param $merId
+     * @param null $uid
+     * @return int
+     * @author xaboy
+     * @day 2020/6/19
+     */
+    public function validMerCouponExists($merId, $uid = null)
+    {
+        return $this->validCouponQuery(0)->when($uid, function (BaseQuery $query, $uid) {
+            $query->with(['issue' => function (BaseQuery $query) use ($uid) {
+                $query->where('uid', $uid);
+            }]);
+        })->where('mer_id', $merId)->count();
+    }
+
+    /**
+     * 查询商品的优惠券列表
+     * @param array $couponIds
+     * @param null $uid
+     * @return Collection
+     * @throws DbException
+     * @throws DataNotFoundException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/1
+     */
+    public function validProductCoupon(array $couponIds, $uid = null)
+    {
+        return $this->validCouponQuery(1)->when($uid, function (BaseQuery $query, $uid) {
+            $query->with(['issue' => function (BaseQuery $query) use ($uid) {
+                $query->where('uid', $uid);
+            }]);
+        })->whereIn('coupon_id', $couponIds)->select();
+    }
+
+    /**
+     * 查询商品优惠券数量
+     * @param array $couponIds
+     * @param null $uid
+     * @return int
+     * @author Qinii
+     */
+    public function validProductCouponExists(array $couponIds, $uid = null)
+    {
+        return $this->validCouponQuery(1)->when($uid, function (BaseQuery $query, $uid) {
+            $query->with(['issue' => function (BaseQuery $query) use ($uid) {
+                $query->where('uid', $uid);
+            }]);
+        })->whereIn('coupon_id', $couponIds)->count();
+    }
+
+    /**
+     * 删除优惠券
+     * @param int $id
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020-05-13
+     */
+    public function delete(int $id)
+    {
+        return StoreCoupon::getDB()->where($this->getPk(), $id)->update(['is_del' => 1]);
+    }
+
+    /**
+     * 查询优惠券是否存在
+     * @param int $id
+     * @return bool
+     * @author xaboy
+     * @day 2020-05-13
+     */
+    public function exists(int $id)
+    {
+        return StoreCoupon::getDB()->where($this->getPk(), $id)->where('is_del', 0)->count($this->getPk()) > 0;
+    }
+
+    /**
+     * 删除商户的优惠券
+     * @param int $merId
+     * @param int $id
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020-05-13
+     */
+    public function merDelete(int $merId, int $id)
+    {
+        return StoreCoupon::getDB()->where($this->getPk(), $id)->where('mer_id', $merId)->update(['is_del' => 1]);
+    }
+
+    /**
+     * 商户优惠券是否存在
+     * @param int $merId
+     * @param int $id
+     * @return bool
+     * @author xaboy
+     * @day 2020-05-13
+     */
+    public function merExists(int $merId, int $id)
+    {
+        return StoreCoupon::getDB()->where($this->getPk(), $id)->where('mer_id', $merId)->where('is_del', 0)->count($this->getPk()) > 0;
+    }
+
+    /**
+     *  新人券获取
+     * @return Collection
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/18
+     */
+    public function newPeopleCoupon()
+    {
+        if (!systemConfig('newcomer_status') || !systemConfig('register_coupon_status'))
+            return [];
+        $register_give_coupon = systemConfig('register_give_coupon');
+        return $this->validCouponQuery(null, StoreCouponRepository::GET_COUPON_TYPE_ADMIN)
+            ->whereIn('coupon_id', $register_give_coupon)->select();
+    }
+
+    /**
+     * 查询赠送的优惠券
+     * @param array|null $ids
+     * @return Collection
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/19
+     */
+    public function getGiveCoupon(array $ids = null)
+    {
+        return $this->validCouponQuery(null, 3)->when($ids, function ($query, $ids) {
+            $query->whereIn('coupon_id', $ids);
+        })->select();
+    }
+
+    /**
+     * 清除优惠券
+     * @param $id
+     * @param $field
+     * @throws DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/13
+     */
+    public function clear($id,$field)
+    {
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+}

+ 56 - 0
app/common/dao/store/coupon/StoreCouponIssueUserDao.php

@@ -0,0 +1,56 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\coupon;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\coupon\StoreCouponIssueUser;
+use think\db\BaseQuery;
+
+/**
+ * Class StoreCouponIssueUserDao
+ * @package app\common\dao\store\coupon
+ * @author xaboy
+ * @day 2020/6/2
+ */
+class StoreCouponIssueUserDao extends BaseDao
+{
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/6/2
+     */
+    protected function getModel(): string
+    {
+        return StoreCouponIssueUser::class;
+    }
+
+    /**
+     * 查询发布的优惠券
+     * @param array $where
+     * @return BaseQuery
+     * @author xaboy
+     * @day 2020/6/2
+     */
+    public function search(array $where)
+    {
+        return StoreCouponIssueUser::getDB()->when(isset($where['coupon_id']) && $where['coupon_id'] != '', function ($query) use ($where) {
+            $query->where('coupon_id', $where['coupon_id']);
+        })->when(isset($where['uid']) && $where['uid'] != '', function ($query) use ($where) {
+            $query->where('uid', $where['uid']);
+        })->order('create_time');
+    }
+}

+ 103 - 0
app/common/dao/store/coupon/StoreCouponProductDao.php

@@ -0,0 +1,103 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\coupon;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\coupon\StoreCouponProduct;
+use think\Collection;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+
+/**
+ * Class StoreCouponProductDao
+ * @package app\common\dao\store\coupon
+ * @author xaboy
+ * @day 2020-05-13
+ */
+class StoreCouponProductDao extends BaseDao
+{
+
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    protected function getModel(): string
+    {
+        return StoreCouponProduct::class;
+    }
+
+    /**
+     * 新增数据
+     * @param array $data
+     * @return int
+     * @author xaboy
+     * @day 2020-05-13
+     */
+    public function insertAll(array $data)
+    {
+        return StoreCouponProduct::getDB()->insertAll($data);
+    }
+
+    /**
+     * 根据优惠券id清除数据
+     * @param $couponId
+     * @return int
+     * @throws DbException
+     * @author xaboy
+     * @day 2020-05-13
+     */
+    public function clear($couponId)
+    {
+        return StoreCouponProduct::getDB()->where('coupon_id', $couponId)->delete();
+    }
+
+    /**
+     * 根据商品id查询优惠券id
+     * @param $productId
+     * @return array
+     * @author xaboy
+     * @day 2020/6/1
+     */
+    public function productByCouponId($productId)
+    {
+        return StoreCouponProduct::getDB()->whereIn('product_id', $productId)->column('coupon_id');
+    }
+
+    /**
+     * 根据条件搜索优惠券产品信息。
+     *
+     * 本函数旨在根据传入的条件数组,查询优惠券产品数据库。条件数组可以包含coupon_id和type两个字段,
+     * 函数将根据这两个字段的值进行查询条件的动态添加,实现灵活的数据库查询。
+     *
+     * @param array $where 包含查询条件的数组,可能包含coupon_id和type两个键。
+     * @return \think\db\BaseQuery|\think\db\Query
+     */
+    public function search(array $where)
+    {
+        // 获取数据库操作对象
+        return StoreCouponProduct::getDB()
+            // 当传入的$where数组中包含coupon_id键且值不为空时,添加where条件查询coupon_id
+            ->when(isset($where['coupon_id']) && $where['coupon_id'] !== '', function ($query) use ($where) {
+                return $query->where('coupon_id', $where['coupon_id']);
+            })
+            // 当传入的$where数组中包含type键且值不为空时,添加where条件查询type
+            ->when(isset($where['type']) && $where['type'] !== '', function ($query) use ($where) {
+                return $query->where('type', $where['type']);
+            });
+    }
+}

+ 74 - 0
app/common/dao/store/coupon/StoreCouponSendDao.php

@@ -0,0 +1,74 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\coupon;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\coupon\StoreCouponSend;
+
+class StoreCouponSendDao extends BaseDao
+{
+
+    /**
+     * 获取模型类名
+     *
+     * 本方法返回一个字符串,代表了StoreCouponSend类的完全限定名。
+     * 它被设计为一个受保护的方法,意味着它只能在当前类或其子类中被调用。
+     * 这种设计模式常用于框架中,通过这种方式,子类可以重写此方法以返回不同的模型类名,
+     * 从而实现不同的行为或逻辑,而不需要修改父类的其他部分。
+     *
+     * @return string 返回StoreCouponSend类的完全限定名
+     */
+    protected function getModel(): string
+    {
+        return StoreCouponSend::class;
+    }
+
+
+    /**
+     * 根据条件搜索优惠券发送记录
+     *
+     * 本函数用于根据提供的条件从数据库中检索优惠券发送记录。它支持搜索优惠券名称、
+     * 发送日期范围、优惠券类型、状态和商家ID。搜索结果基于这些条件的组合。
+     *
+     * @param array $where 搜索条件数组,包含可能的键:coupon_name(优惠券名称)、
+     *                     date(日期范围)、coupon_type(优惠券类型)、status(状态)、mer_id(商家ID)。
+     * @return \think\db\Query 返回一个数据库查询对象,该对象可用于进一步的查询操作或数据检索。
+     */
+    public function search(array $where)
+    {
+        // 初始化查询,指定别名为A,并左连接到StoreCoupon表(别名为B) on B.coupon_id = A.coupon_id
+        return StoreCouponSend::getDB()->alias('A')->leftJoin('StoreCoupon B', 'B.coupon_id = A.coupon_id')
+            // 当coupon_name条件存在且不为空时,添加LIKE条件到查询中
+            ->when(isset($where['coupon_name']) && $where['coupon_name'] !== '', function ($query) use ($where) {
+                $query->whereLike('B.title', "%{$where['coupon_name']}%");
+            })
+            // 当date条件存在且不为空时,调用getModelTime函数来处理日期范围条件
+            ->when(isset($where['date']) && $where['date'] !== '', function ($query) use ($where) {
+                getModelTime($query, $where['date'], 'A.create_time');
+            })
+            // 当coupon_type条件存在且不为空时,添加类型条件到查询中
+            ->when(isset($where['coupon_type']) && $where['coupon_type'] !== '', function ($query) use ($where) {
+                $query->where('B.type', $where['coupon_type']);
+            })
+            // 当status条件存在且不为空时,添加状态条件到查询中
+            ->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+                $query->where('A.status', $where['status']);
+            })
+            // 当mer_id条件存在且不为空时,添加商家ID条件到查询中
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+                $query->where('A.mer_id', $where['mer_id']);
+            });
+    }
+}

+ 191 - 0
app/common/dao/store/coupon/StoreCouponUserDao.php

@@ -0,0 +1,191 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\coupon;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\coupon\StoreCouponUser;
+use think\facade\Db;
+
+/**
+ * Class StoreCouponUserDao
+ * @package app\common\dao\store\coupon
+ * @author xaboy
+ * @day 2020-05-14
+ */
+class StoreCouponUserDao extends BaseDao
+{
+
+    /**
+     * @return BaseModel
+     * @author xaboy
+     * @day 2020-03-30
+     */
+    protected function getModel(): string
+    {
+        return StoreCouponUser::class;
+    }
+
+    /**
+     * 根据条件搜索优惠券用户信息
+     *
+     * 该方法通过接收一个包含搜索条件的数组,动态构建查询语句,以搜索符合指定条件的优惠券用户信息。
+     * 支持的搜索条件包括用户名、优惠券类型、优惠券名称或ID、状态、用户ID、商家ID、优惠券ID、发送者ID、是否为商家以及状态标签。
+     *
+     * @param array $where 搜索条件数组,包含各种可能的搜索参数。
+     */
+    public function search(array $where)
+    {
+        // 使用查询构建器实例化StoreCouponUser模型
+        return StoreCouponUser::when(isset($where['username']) && $where['username'] !== '', function ($query) use ($where) {
+            // 如果指定了用户名,则添加对用户昵称的模糊搜索条件
+            $query->hasWhere('user', [['nickname', 'LIKE', "%{$where['username']}%"]]);
+        })->when(isset($where['coupon_type']) && $where['coupon_type'] !== '', function ($query) use ($where) {
+            // 如果指定了优惠券类型,则添加对优惠券类型的条件查询
+            $query->hasWhere('coupon', ['type' => $where['coupon_type']]);
+        })->alias('StoreCouponUser')->when(isset($where['coupon']) && $where['coupon'] !== '', function ($query) use ($where) {
+            // 如果指定了优惠券名称或ID,则添加对优惠券标题的模糊搜索条件
+            $query->whereLike('StoreCouponUser.coupon_title', "%{$where['coupon']}%");
+        })->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+            // 如果指定了状态,则添加对状态的条件查询
+            $query->where('StoreCouponUser.status', $where['status']);
+        })->when(isset($where['uid']) && $where['uid'] !== '', function ($query) use ($where) {
+            // 如果指定了用户ID,则添加对用户ID的条件查询
+            $query->where('StoreCouponUser.uid', $where['uid']);
+        })->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+            // 如果指定了商家ID,则添加对商家ID的条件查询
+            $query->where('StoreCouponUser.mer_id', $where['mer_id']);
+        })->when(isset($where['coupon_id']) && $where['coupon_id'] !== '', function ($query) use ($where) {
+            // 如果指定了优惠券ID,则添加对优惠券ID的条件查询
+            $query->where('StoreCouponUser.coupon_id', $where['coupon_id']);
+        })->when(isset($where['coupon']) && $where['coupon'] !== '', function ($query) use ($where) {
+            // 如果再次指定了优惠券名称或ID,则添加对优惠券标题或ID的模糊搜索条件
+            $query->whereLike('StoreCouponUser.coupon_title|StoreCouponUser.coupon_id', "%{$where['coupon']}%");
+        })->when(isset($where['type']) && $where['type'] !== '', function ($query) use ($where) {
+            // 如果指定了类型,则添加对类型的条件查询
+            $query->where('StoreCouponUser.type', $where['type']);
+        })->when(isset($where['send_id']) && $where['send_id'] !== '', function ($query) use ($where) {
+            // 如果指定了发送者ID,并且类型为发送,则添加相应的条件查询
+            $query->where('StoreCouponUser.send_id', $where['send_id'])->where('StoreCouponUser.type', 'send');
+        })->when(isset($where['is_mer']) && $where['is_mer'] !== '', function ($query) use ($where) {
+            // 如果指定了是否为商家,则添加对商家ID的非空查询
+            $query->where('StoreCouponUser.mer_id', '<>',0);
+        })->when(isset($where['statusTag']) && $where['statusTag'] !== '', function ($query) use ($where) {
+            // 根据状态标签的不同,添加不同的条件查询,包括未使用的优惠券或在指定时间内使用的优惠券
+            if ($where['statusTag'] == 1) {
+                $query->where('StoreCouponUser.status', 0);
+            } else {
+                $query->whereIn('StoreCouponUser.status', [1, 2])->where('StoreCouponUser.create_time', '>', date('Y-m-d H:i:s', strtotime('-60 day')));
+            }
+        })->order('StoreCouponUser.coupon_user_id DESC');
+    }
+
+    public function validIntersection($merId, $uid, array $ids): array
+    {
+        $time = date('Y-m-d H:i:s');
+        return StoreCouponUser::getDB()->whereIn('coupon_user_id', $ids)->where('start_time', '<', $time)->where('end_time', '>', $time)
+            ->where('is_fail', 0)->where('status', 0)->where('mer_id', $merId)->where('uid', $uid)->column('coupon_user_id');
+    }
+
+    public function validQuery($type)
+    {
+        $time = date('Y-m-d H:i:s');
+        return StoreCouponUser::getDB()
+            ->when($type, function ($query) use($time){
+                $query->where('start_time', '<', $time);
+            })
+            ->where('end_time', '>', $time)->where('is_fail', 0)->where('status', 0);
+    }
+
+    public function failCoupon($uid = 0)
+    {
+        $time = date('Y-m-d H:i:s');
+        $res = StoreCouponUser::getDB()
+            ->where('end_time', '<', $time)
+            ->where('is_fail', 0)
+            ->where('status', 0)
+            ->when($uid, function($query) use($uid){
+                $query->where('uid', $uid);
+            })
+            ->update(['status' => 2]);
+        return $res;
+    }
+
+    public function userTotal($uid, $type = 1)
+    {
+        return $this->validQuery($type)->where('uid', $uid)->count();
+    }
+
+    public function usedNum($couponId)
+    {
+        return StoreCouponUser::getDB()->where('coupon_id', $couponId)->where('status', 1)->count();
+    }
+
+    public function sendNum($couponId, $sendId = null, $status = null)
+    {
+        return StoreCouponUser::getDB()->where('coupon_id', $couponId)->when($sendId, function ($query, $sendId) {
+            $query->where('type', 'send')->where('send_id', $sendId);
+        })->when(isset($status), function ($query) use ($status) {
+            $query->where('status', $status);
+        })->count();
+    }
+
+    public function validUserPlatformCoupon($uid)
+    {
+        $time = date('Y-m-d H:i:s');
+        return StoreCouponUser::getDB()->where('uid', $uid)->where('mer_id', 0)->where('start_time', '<', $time)->where('end_time', '>', $time)
+            ->where('is_fail', 0)->where('status', 0)
+            ->with(['product' => function ($query) {
+                $query->field('coupon_id,product_id');
+            }, 'coupon' => function ($query) {
+                $query->field('coupon_id,type,send_type');
+            }])->order('coupon_price DESC, coupon_user_id ASC')->select();
+    }
+    /**
+     * 获取用户可用优惠券
+     *
+     * @param integer $uid
+     * @param integer $merId
+     * @return void
+     */
+    public function validUserCoupon(int $uid, int $merId = 0, array $ids = [])
+    {
+        $time = date('Y-m-d H:i:s');
+        $query = StoreCouponUser::getDB()
+            ->where('uid', $uid)
+            ->where('start_time', '<', $time)
+            ->where('end_time', '>', $time)
+            ->where('is_fail', 0)
+            ->where('status', 0)
+            ->with(['product','coupon'])
+            ->order('coupon_price DESC, coupon_user_id ASC');
+
+        if ($merId) {
+            $query->whereIn('mer_id', [0, $merId]);
+        }
+
+        if ($ids) {
+            $query->whereIn('coupon_user_id', $ids);
+        }
+
+        return $query->select();
+    }
+
+    public function clear($id,$field)
+    {
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+}

+ 83 - 0
app/common/dao/store/order/MerchantReconciliationDao.php

@@ -0,0 +1,83 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\order;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\MerchantReconciliation as model;
+use app\common\repositories\system\admin\AdminRepository;
+use app\common\repositories\system\merchant\MerchantRepository;
+
+class MerchantReconciliationDao extends BaseDao
+{
+   public function getModel(): string
+   {
+       return model::class;
+   }
+
+   /**
+    * 根据条件搜索数据。
+    *
+    * 该方法用于根据传入的条件数组来构建数据库查询条件,灵活地支持多种搜索逻辑,
+    * 包括对特定字段的条件查询以及关键词的全文搜索。搜索条件的构建使用了Laravel的查询构建器,
+    * 通过链式调用where和when方法来动态添加查询条件。
+    *
+    * @param array $where 搜索条件数组,包含各种可能的查询条件。
+    * @return \Illuminate\Database\Eloquent\Builder|static 返回构建好的查询构建器实例,可用于进一步的操作或直接执行查询。
+    */
+   public function search(array $where)
+   {
+       // 获取模型对应的数据库实例
+       $query = ($this->getModel()::getDB())
+           // 当'mer_id'字段存在且不为空时,添加对'mer_id'字段的查询条件
+           ->when(isset($where['mer_id']) && $where['mer_id'] != '' ,function($query)use($where){
+               $query->where('mer_id',$where['mer_id']);
+           })
+           // 当'status'字段存在且不为空时,添加对'status'字段的查询条件
+           ->when(isset($where['status']) && $where['status'] != '' ,function($query)use($where){
+               $query->where('status',$where['status']);
+           })
+           // 当'is_accounts'字段存在且不为空时,添加对'is_accounts'字段的查询条件
+           ->when(isset($where['is_accounts']) && $where['is_accounts'] != '' ,function($query)use($where){
+               $query->where('is_accounts',$where['is_accounts']);
+           })
+           // 当'date'字段存在且不为空时,调用getModelTime函数添加对日期的查询条件
+           ->when(isset($where['date']) && $where['date'] != '' ,function($query)use($where){
+               getModelTime($query,$where['date']);
+           })
+           // 当'reconciliation_id'字段存在且不为空时,添加对'reconciliation_id'字段的查询条件
+           ->when(isset($where['reconciliation_id']) && $where['reconciliation_id'] != '' ,function($query)use($where){
+               $query->where('reconciliation_id',$where['reconciliation_id']);
+           })
+           // 当'keyword'字段存在且不为空时,执行关键词搜索逻辑
+           ->when(isset($where['keyword']) && $where['keyword'] !== '',function($query)use($where){
+               // 实例化管理员仓库,用于获取管理员ID列表
+               $make = app()->make(AdminRepository::class);
+               $admin_id = $make->getSearch(['real_name' => $where['keyword']],null,false)->column('admin_id');
+               // 根据是否存在'mer_id'字段,构建不同的查询条件
+               $query->where(function($query) use($admin_id,$where){
+                   if(isset($where['mer_id'])){
+                       $query->where('admin_id','in',$admin_id);
+                   }else {
+                       // 实例化商家仓库,用于获取商家ID列表
+                       $mer_make = app()->make(MerchantRepository::class);
+                       $mer_id = $mer_make->getSearch(['keyword' => $where['keyword']])->column('mer_id');
+                       // 添加对管理员ID或商家ID的查询条件
+                       $query->where('admin_id','in',$admin_id)->whereOr('mer_id','in',$mer_id);
+                   }
+               });
+           });
+       // 设置查询结果的排序方式
+       return $query->order('create_time DESC,status DESC');
+   }
+
+}

+ 54 - 0
app/common/dao/store/order/MerchantReconciliationOrderDao.php

@@ -0,0 +1,54 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\order;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\MerchantReconciliationOrder as model;
+
+class MerchantReconciliationOrderDao extends BaseDao
+{
+   public function getModel(): string
+   {
+       return model::class;
+   }
+
+
+   /**
+    * 根据条件搜索数据。
+    *
+    * 本函数用于根据提供的条件搜索数据库中的记录。它支持两种条件:'reconciliation_id' 和 'type'。
+    * 只有当条件存在且不为空时,才会在查询中添加相应的 WHERE 子句。
+    *
+    * @param array $where 包含搜索条件的数组。可能的条件键包括 'reconciliation_id' 和 'type'。
+    * @return \Illuminate\Database\Eloquent\Builder|static 返回构建器对象,可用于进一步的查询操作或数据检索。
+    */
+   public function search($where)
+   {
+       // 获取模型对应的数据库实例
+       return ($this->getModel()::getDB())->when(
+           // 检查是否有 'reconciliation_id' 条件,并且它不是空的
+           isset($where['reconciliation_id']) && $where['reconciliation_id'] !== '',
+           function ($query) use ($where) {
+               // 如果存在 'reconciliation_id',则在查询中添加对应的 WHERE 条件
+               $query->where('reconciliation_id', $where['reconciliation_id']);
+           }
+       )->when(
+           // 检查是否有 'type' 条件,并且它不是空的
+           isset($where['type']) && $where['type'] !== '',
+           function ($query) use ($where) {
+               // 如果存在 'type',则在查询中添加对应的 WHERE 条件
+               $query->where('type', $where['type']);
+           }
+       );
+   }
+}

+ 105 - 0
app/common/dao/store/order/PresellOrderDao.php

@@ -0,0 +1,105 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\order\PresellOrder;
+
+class PresellOrderDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return PresellOrder::class;
+    }
+
+    /**
+     * 根据条件搜索预售订单。
+     *
+     * 该方法通过接收一个包含搜索条件的数组,动态地构建查询语句来搜索预售订单。
+     * 可以根据支付方式、支付状态、商家ID和订单ID列表来过滤结果。
+     *
+     * @param array $where 包含搜索条件的数组,其中可能包含pay_type、paid、mer_id和order_ids键。
+     * @return \think\db\Query|null 返回构建的查询对象,或者在没有符合条件的查询时返回null。
+     */
+    public function search(array $where)
+    {
+        // 获取数据库查询对象
+        return PresellOrder::getDB()->when(isset($where['pay_type']) && $where['pay_type'] !== '', function ($query) use ($where) {
+            // 如果支付方式被指定且不为空,则添加支付方式的查询条件
+            $query->whereIn('pay_type', $where['pay_type']);
+        })->when(isset($where['paid']) && $where['paid'] !== '', function ($query) use ($where) {
+            // 如果支付状态被指定且不为空,则添加支付状态的查询条件
+            $query->where('paid', $where['paid']);
+        })->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+            // 如果商家ID被指定且不为空,则添加商家ID的查询条件
+            $query->where('mer_id', $where['mer_id']);
+        })->when(isset($where['order_ids']) && $where['order_ids'] !== '', function ($query) use ($where) {
+            // 如果订单ID列表被指定且不为空,则添加订单ID在指定列表中的查询条件
+            $query->where('order_id','in',$where['order_ids']);
+        });
+    }
+
+    /**
+     * 根据用户ID和订单ID查询预售订单信息
+     *
+     * 本函数旨在通过用户ID和订单ID从数据库中检索特定的预售订单。它使用了PresellOrder类的数据库访问方法来执行查询。
+     * 查询条件是用户ID(uid)和订单ID(order_id)匹配,返回的是匹配的订单数据。
+     *
+     * @param int $uid 用户ID,用于指定订单所属的用户。
+     * @param string $orderId 订单ID,用于唯一标识订单。
+     * @return array|false 返回匹配的预售订单数据,如果找不到则返回false。
+     */
+    public function userOrder($uid, $orderId)
+    {
+        // 使用PresellOrder类的静态方法getDB来获取数据库实例,并构造查询条件,最后执行查询并返回结果
+        return PresellOrder::getDB()->where('uid', $uid)->where('order_id', $orderId)->find();
+    }
+
+    /**
+     * 获取过期未支付的预售订单ID列表
+     *
+     * 本函数用于查询数据库中状态为进行中(status=1)、未支付(paid=0)、且结束时间早于指定时间($time)的预售订单ID。
+     * 这些ID可用于后续的过期订单处理,比如提醒用户、或者自动取消订单等操作。
+     *
+     * @param int|string $time 结束时间的判断标准。可以是Unix时间戳或者符合数据库查询格式的日期字符串。
+     * @return array 过期未支付的预售订单ID列表。
+     */
+    public function getTimeOutIds($time)
+    {
+        // 使用查询构建器查询符合条件的预售订单,并只返回presell_order_id列
+        return PresellOrder::getDB()->where('status', 1)->where('paid', 0)
+            ->where('final_end_time', '<', $time)->column('presell_order_id');
+    }
+
+    /**
+     * 获取指定日期内的未支付预订单的短信ID列表
+     *
+     * 本函数用于查询指定日期内,状态为有效(1)且未支付的预订单的订单ID列表。
+     * 查询条件基于订单的支付状态和最终开始时间,通过like操作符匹配日期部分。
+     *
+     * @param string $date 日期字符串,格式为Y-m-d,用于查询订单的最终开始时间。
+     * @return array 返回符合条件的预订单ID列表。
+     */
+    public function sendSmsIds($date)
+    {
+        // 使用预订单模型的数据库查询方法,指定查询条件为状态为1,未支付,且最终开始时间like传入日期字符串加百分号。
+        // 返回查询结果中订单ID一列的值。
+        return PresellOrder::getDB()->where('status', 1)->where('paid', 0)
+            ->whereLike('final_start_time', $date . '%')->column('order_id');
+    }
+
+}

+ 317 - 0
app/common/dao/store/order/StoreCartDao.php

@@ -0,0 +1,317 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\order\StoreCart;
+use app\common\model\user\UserAddress;
+use app\common\model\store\shipping\ShippingTemplate;
+use app\common\repositories\store\order\StoreCartRepository;
+use think\model\Relation;
+
+class StoreCartDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return StoreCart::class;
+    }
+
+    /**
+     * 检查购物车项是否有效并存在交叉
+     *
+     * 本函数用于验证给定的购物车ID列表中,是否存在属于特定用户($uid)且满足特定条件(如未删除、未失败、未支付)的购物车项。
+     * 如果提供了$merId,则还会检查这些购物车项是否属于指定的商家。
+     *
+     * @param array $ids 购物车ID列表
+     * @param int $uid 用户ID
+     * @param int|null $merId 商家ID(可选),用于过滤属于特定商家的购物车项
+     * @return array 有效并存在交叉的购物车ID列表
+     */
+    public function validIntersection(array $ids, $uid, int $merId = null, string $touristUniqueKey = ''): array
+    {
+        // 从数据库中获取满足条件的购物车项的cart_id列
+        return StoreCart::getDB()->whereIn('cart_id', $ids)
+            // 如果提供了商家ID,则进一步过滤出属于该商家的购物车项
+            ->when($merId, function ($query, $merId) {
+                $query->where('mer_id', $merId);
+            })
+            ->when($touristUniqueKey, function ($query, $touristUniqueKey) {
+                $query->where('tourist_unique_key', $touristUniqueKey);
+            })
+            // 过滤出未删除、未失败、未支付的购物车项
+            ->where('is_del', 0)->where('is_fail', 0)->where('is_pay', 0)
+            // 过滤出属于指定用户的购物车项
+            ->where('uid', $uid)->column('cart_id');
+    }
+
+    /**
+     * 获取用户的所有购物车商品
+     *
+     * 本函数用于根据用户ID查询该用户购物车中所有未删除、未标记为新、未支付的商品。
+     * 查询时会包括商品的基本信息、属性信息,以及(注释掉的)商家信息。
+     * 查询结果按照创建时间降序排列,并限制返回结果的数量。
+     *
+     * @param int $uid 用户ID
+     * @return \think\Paginator 返回查询结果的分页器对象
+     */
+    public function getAll(int $uid)
+    {
+        // 构建查询条件,查询用户购物车中符合条件的商品
+        $query = ($this->getModel())::where(['uid' => $uid, 'is_del' => 0, 'is_new' => 0, 'is_pay' => 0])
+            ->with([
+                // 加载商品相关信息,包括商品基础信息和库存价格等
+                'product' => function ($query) {
+                    $query->field('product_id,image,store_name,is_show,status,is_del,unit_name,price,mer_status,is_used,product_type,once_max_count,once_min_count,pay_limit,mer_svip_status,svip_price_type');
+                },
+                // 加载商品属性信息,包括SKU和特殊价格等
+                'productAttr' => function ($query) {
+                    $query->field('product_id,stock,price,unique,sku,image,svip_price,bar_code');
+                },
+                // 注释掉的商家信息加载部分,可能用于未来扩展
+//                'merchant' => function ($query) {
+//                    $query->field('mer_id,mer_name,mer_state,mer_avatar,is_trader,type_id')->with(['type_name']);
+//                }
+            ])
+            // 限制返回的购物车项目数量
+            ->limit(StoreCartRepository::CART_LIMIT_COUNT)
+            // 按照创建时间降序排列
+            ->order('create_time DESC')
+            // 执行查询并返回结果
+            ->select();
+
+        return $query;
+    }
+
+
+    /**
+     * 根据传入的用户ID和购物车ID数组,以及用户地址,获取购物车商品信息。
+     * 此函数详细说明了如何从数据库中获取与用户购物车相关的商品数据,
+     * 包括商品的基本信息、属性信息以及配送方式和费用等。
+     *
+     * @param array $ids 购物车项ID数组
+     * @param int $uid 用户ID
+     * @param UserAddress|null $address 用户地址对象,可能为null
+     * @return array 返回符合查询条件的购物车商品数据集合
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function cartIbByData(array $ids, int $uid, ?UserAddress $address)
+    {
+
+        // 通过用户ID和购物车ID数组从数据库获取购物车商品信息
+        return StoreCart::getDb()->where('uid', $uid)->with([
+            // 获取商品基本信息,包括产品ID、分类ID、图片、商店名称等
+            'product' => function (Relation $query) use ($address) {
+                $query->field('product_id,cate_id,image,store_name,is_show,status,is_del,unit_name,price,mer_status,temp_id,give_coupon_ids,is_gift_bag,is_used,product_type,old_product_id,integral_rate,delivery_way,delivery_free,type,extend,pay_limit,once_max_count,once_min_count,mer_svip_status,svip_price_type,refund_switch,mer_form_id,active_id');
+                $query->with([
+                    'storeCategory' => function (Relation $query) {
+                        $query->field('store_category_id,cate_name,path');
+                    }
+                ]);
+                
+                // 如果用户地址存在,进一步获取与地址相关的配送模板和免运费模板信息
+                if ($address) {
+                    $cityIds = array_filter([$address->province_id, $address->city_id, $address->district_id, $address->street_id]);
+                    $query->with([
+                        'temp' => [
+                            'region' => function (Relation $query) use ($cityIds) {
+                                // 根据用户地址的省市ID,查询适用的配送区域
+                                $query->where(function ($query) use ($cityIds) {
+                                    foreach ($cityIds as $v) {
+                                        $query->whereOr('city_id', 'like', "%/{$v}/%");
+                                    }
+                                    $query->whereOr('city_id', '0');
+                                })->order('shipping_template_region_id DESC')->withLimit(1);
+                            },
+                            'undelives' => function ($query) use ($cityIds) {
+                                // 查询不能配送的区域
+                                foreach ($cityIds as $v) {
+                                    $query->whereOr('city_id', 'like', "%/{$v}/%");
+                                }
+                            },
+                            'free' => function (Relation $query) use ($cityIds) {
+                                // 查询免运费模板
+                                foreach ($cityIds as $v) {
+                                    $query->whereOr('city_id', 'like', "%/{$v}/%");
+                                }
+                                $query->order('shipping_template_free_id DESC')->withLimit(1);
+                            }
+                        ]
+                    ]);
+                }
+            },
+            // 获取商品属性信息,包括属性值ID、图片、库存、价格等
+            'productAttr' => function (Relation $query) {
+                $query->field('value_id,image,extension_one,extension_two,product_id,stock,price,unique,sku,volume,weight,ot_price,cost,svip_price,library_id,bar_code,bar_code_number')->with(['product'])->append(['bc_extension_one', 'bc_extension_two']);
+            },
+            'reservation',
+            'reservationAttr'
+            // 注释掉的商家信息部分,原注释说明了这部分代码的作用是获取商家的基本信息、优惠券、配置信息等
+            // 'merchant' => function (Relation $query) use ($uid) {
+            //     ...
+            // }
+        ])->whereIn('cart_id', $ids)->order('product_type DESC,cart_id DESC')->select();
+    }
+
+    public function getCartItemsByIds(array $ids, int $uid, $cityIds = [])
+    {
+        if (!$ids && !$uid) return ;
+        // 查询购物车并加载关联
+        $cartItems = StoreCart::getDb()
+            //->where('uid', $uid)
+            ->whereIn('cart_id', $ids)
+            ->with(['product', 'productAttr' => function($query){
+                $query->field('value_id,image,extension_one,extension_two,product_id,stock,price,unique,sku,volume,weight,ot_price,cost,svip_price,library_id,bar_code,bar_code_number');
+            }])
+            ->order('product_type DESC,cart_id DESC')
+            ->select();
+        // 批量加载配送模板
+        $this->batchLoadShippingTemplates($cartItems, $cityIds);
+
+        return $cartItems;
+    }
+
+    private function batchLoadShippingTemplates($cartItems, array $cityIds)
+    {
+        if (empty($cityIds)) {
+            return;
+        }
+
+        // 获取所有模板 ID
+        $templateIds = $cartItems->column('product.temp_id'); // 提取模板 ID
+        $templateIds = array_unique($templateIds);           // 去重
+
+        // 一次性查询所有模板数据,包括 region、free 和 undelives
+        $shippingTemplates = ShippingTemplate::whereIn('id', $templateIds)
+            ->with([
+                'region' => function ($query) use ($cityIds) {
+                    foreach ($cityIds as $cityId) {
+                        $query->whereOr('city_id', 'like', "%/{$cityId}/%");
+                    }
+                    $query->whereOr('city_id', '0'); // 适配所有区域
+                    $query->order('shipping_template_region_id DESC')->withLimit(1);
+                },
+                'free' => function ($query) use ($cityIds) {
+                    foreach ($cityIds as $cityId) {
+                        $query->whereOr('city_id', 'like', "%/{$cityId}/%");
+                    }
+                    $query->order('shipping_template_free_id DESC')->withLimit(1);
+                },
+                'undelives' => function ($query) use ($cityIds) {
+                    foreach ($cityIds as $cityId) {
+                        $query->whereOr('city_id', 'like', "%/{$cityId}/%");
+                    }
+                }
+            ])
+            ->select()
+            ->column(null, 'id'); // 按模板 ID 索引结果
+
+        // 将模板数据分配给购物车项
+        foreach ($cartItems as $item) {
+            $item->product->shipping_template = $shippingTemplates[$item->product->temp_id] ?? null;
+        }
+    }
+
+    /**
+     * 批量删除购物车中指定ID的商品
+     *
+     * 此方法通过传入的用户ID和购物车ID数组,批量从数据库中删除对应的购物车记录。
+     * 主要用于处理用户批量清除购物车商品的场景,或者在特定情况下批量清理数据库中的购物车数据。
+     *
+     * @param array $cartIds 购物车项ID数组,代表需要被删除的购物车记录的ID。
+     * @param int $uid 用户ID,用于指定哪些购物车记录属于该用户,从而进行精准删除。
+     * @return int 返回删除操作的影响行数,即被删除的购物车记录数量。
+     */
+    public function batchDelete(array $cartIds, int $uid)
+    {
+        // 通过模型获取数据库实例,并构造删除条件,最终执行删除操作。
+        return ($this->getModel()::getDB())->where('uid', $uid)->whereIn('cart_id', $cartIds)->delete();
+    }
+
+    /**
+     * 获取用户购物车中商品的数量
+     *
+     * 本函数用于查询指定用户ID的购物车中,未删除、未标记为新、未支付的商品总数。
+     * 这对于显示用户的购物车总数或者进行相关统计非常有用。
+     *
+     * @param int $uid 用户ID,用于指定查询哪个用户的购物车数据。
+     * @return array 返回一个包含商品总数的数组,如果商品数量为0,则数组中的计数为0。
+     */
+    public function getCartCount(int $uid)
+    {
+        // 通过模型获取数据库实例,并根据条件查询购物车商品数量,条件包括用户ID、未删除、未标记为新、未支付。
+        $data = ($this->getModel()::getDB())->where(['uid' => $uid, 'is_del' => 0, 'is_new' => 0, 'is_pay' => 0])->field('SUM(cart_num) as count')->select();
+
+        // 确保返回的数据中count字段不为空,如果为空则默认为0。
+        $data[0]['count'] = $data[0]['count'] ? $data[0]['count'] : 0;
+
+        // 返回查询结果。
+        return $data;
+    }
+
+    /**
+     * 获取用户购物车中商品的数量
+     *
+     * 本函数用于查询指定用户ID的购物车中,未删除、未标记为新、未支付的商品总数。
+     * 这对于显示用户的购物车总数或者进行相关统计非常有用。
+     *
+     * @param int $uid 用户ID,用于指定查询哪个用户的购物车数据。
+     * @return array 返回一个包含商品总数的数组,如果商品数量为0,则数组中的计数为0。
+     */
+    public function getMerchantCartCount(int $uid, $cartIds)
+    {
+        // 通过模型获取数据库实例,并根据条件查询购物车商品数量,条件包括用户ID、未删除、未标记为新、未支付。
+        $cartTotalNum = ($this->getModel()::getDB())->hasWhere('product', function($query) {
+            $query->where('is_show', 1)->where('status', 1)->whereColumn('stock', '>=', 'StoreCart.cart_num');
+        })->whereIn('StoreCart.cart_id', $cartIds)->where(['StoreCart.uid' => $uid, 'StoreCart.is_del' => 0, 'StoreCart.is_new' => 0, 'StoreCart.is_pay' => 0])->sum('StoreCart.cart_num');
+
+        $data[0]['count'] = $cartTotalNum ?? 0;
+        // 返回查询结果。
+        return $data;
+    }
+
+    /**
+     * 获取指定来源的支付信息
+     *
+     * 本函数用于查询指定来源($source)的支付详情,包括支付数量和支付金额。
+     * 如果提供了$ids参数,则只会查询这些ID对应的支付信息。
+     *
+     * @param string $source 来源标识,用于指定查询哪个来源的支付信息。
+     * @param array|null $ids 可选参数,指定查询的来源ID列表。如果不提供,则查询所有来源。
+     * @return array 返回一个包含支付信息的数组,每个元素包含支付数量(pay_num)、支付金额(pay_price)和来源ID(source_id)。
+     */
+    public function getSourcePayInfo($source, ?array $ids = null)
+    {
+        // 使用数据库查询工具,指定别名为A,查询满足条件的支付信息。
+        return StoreCart::getDB()->alias('A')
+            // 筛选来源为指定值且已支付的购物车项。
+            ->where('A.source', $source)->where('A.is_pay', 1)
+            // 如果提供了ID列表,则进一步筛选来源ID在列表中的项。
+            ->when($ids, function ($query, $ids) {
+                $query->whereIn('A.source_id', $ids);
+            })
+            // 左连接订单产品表,以获取购物车项对应的订单产品信息。
+            ->leftJoin('StoreOrderProduct B', 'A.cart_id = B.cart_id')
+            // 选择计算总支付数量和总支付金额的字段,以及来源ID。
+            ->field('sum(B.product_num) as pay_num,sum(B.product_price) as pay_price,A.source_id')
+            // 按来源ID分组,以聚合支付数量和金额。
+            ->group('A.source_id')
+            // 执行查询并返回结果集。
+            ->select();
+    }
+}

+ 66 - 0
app/common/dao/store/order/StoreCartPriceDao.php

@@ -0,0 +1,66 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreCartPrice;
+use think\model\Relation;
+
+class StoreCartPriceDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return StoreCartPrice::class;
+    }
+
+    public function getCartPriceInfo(int $cartId)
+    {
+        return $this->getWhere(['cart_id' => $cartId]);
+    }
+
+    public function add(int $cartId, array $data, $isBatch = false)
+    {
+        $cartPriceData = $isBatch ? $data : $this->getCartPriceData($cartId, $data);
+        return $this->create($cartPriceData);
+    }
+
+    public function edit(int $id, int $cartId, array $data, $isBatch = false)
+    {
+        $cartPriceData = $isBatch ? $data : $this->getCartPriceData($cartId, $data);
+        return $this->update($id, $cartPriceData);
+    }
+
+    private function getCartPriceData(int $cartId, array $data) : array
+    {
+        $newPrice = $data['new_price'] ?? 0;
+        if($data['type'] == 1){ // 立减
+            $newPrice = bcsub($data['old_price'], $data['reduce_price'], 2);
+        }
+        if($data['type'] == 2){ // 折扣
+            $newPrice = bcmul($data['old_price'], $data['discount_rate']/100, 2);
+        }
+
+        $storeCartPriceData['cart_id'] = $cartId;
+        $storeCartPriceData['old_price'] = $data['old_price'];
+        $storeCartPriceData['type'] = $data['type'];
+        $storeCartPriceData['reduce_price'] = $data['reduce_price'] ?? 0;
+        $storeCartPriceData['discount_rate'] = $data['discount_rate'] ?? 0;
+        $storeCartPriceData['new_price'] = $newPrice;
+        $storeCartPriceData['is_batch'] = 0;
+        $storeCartPriceData['update_time'] = date('Y-m-d H:i:s', time());
+
+        return $storeCartPriceData;
+    }
+}

+ 148 - 0
app/common/dao/store/order/StoreGroupOrderDao.php

@@ -0,0 +1,148 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreGroupOrder;
+
+/**
+ * Class StoreGroupOrderDao
+ * @package app\common\dao\store\order
+ * @author xaboy
+ * @day 2020/6/9
+ */
+class StoreGroupOrderDao extends BaseDao
+{
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/6/9
+     */
+    protected function getModel(): string
+    {
+        return StoreGroupOrder::class;
+    }
+
+    /**
+     * 计算用户未支付订单的数量
+     *
+     * 本函数用于查询并返回指定用户(如果提供了UID)的未支付订单数量。
+     * 未支付订单是指那些标记为未删除(is_del = 0)且尚未付款(paid = 0)的订单。
+     *
+     * @param int|null $uid 用户ID。如果为null,则查询所有用户的未支付订单数量;如果提供了具体的UID,则只查询该用户的未支付订单数量。
+     * @return int 返回未支付订单的数量。注意,这个数量仅仅是未删除且未支付的订单数量。
+     * @throws \think\db\exception\DbException
+     */
+    public function orderNumber($uid = null)
+    {
+        // 使用search方法查询满足条件(未删除、未支付)的订单,并统计数量
+        return $this->search(['uid' => $uid,'is_del' => 0,'paid' => 0],0)->count();
+    }
+
+    /**
+     * 根据条件搜索店铺分组订单
+     *
+     * 此函数用于根据提供的条件搜索店铺分组订单。它支持搜索已支付、用户ID、是否为积分订单、以及是否已删除的订单。
+     * 通过条件构造查询语句,并根据这些条件来过滤订单数据。
+     *
+     * @param array $where 搜索条件数组,包含paid、uid、is_del等键值对
+     * @param null $is_points 是否为积分订单,null表示不筛选,true/false分别表示是/否
+     * @return \think\db\BaseQuery 查询构建器或StoreGroupOrder实例,用于进一步的查询操作或数据获取
+     */
+    public function search(array $where,$is_points = null)
+    {
+        // 获取数据库实例并根据条件逐步构建查询语句
+        $query = StoreGroupOrder::getDB()
+            // 当paid字段在$where数组中且不为空时,添加where条件筛选支付状态
+            ->when(isset($where['paid']) && $where['paid'] !== '', function ($query) use ($where) {
+                $query->where('paid', $where['paid']);
+            })
+            // 当uid字段在$where数组中且不为空时,添加where条件筛选用户ID
+            ->when(isset($where['uid']) && $where['uid'] !== '', function ($query) use ($where) {
+                $query->where('uid', $where['uid']);
+            })
+            // 当$is_points不为null时,根据$is_points的值添加where条件筛选是否为积分订单
+            ->when(!is_null($is_points), function ($query) use ($is_points) {
+                if ($is_points) {
+                    $query->where('activity_type', 20);
+                } else {
+                    $query->where('activity_type', '<>',20);
+                }
+            })
+            // 当is_del字段在$where数组中且不为空时,添加where条件筛选删除状态;否则,添加默认的where条件筛选未删除的订单
+            ->when(isset($where['is_del']) && $where['is_del'] !== '', function ($query) use ($where) {
+                $query->where('is_del', $where['is_del']);
+            }, function ($query) {
+                $query->where('is_del', 0);
+            });
+        // 返回按照创建时间降序排序的查询构建器,用于进一步的查询操作或数据获取
+        return $query->order('create_time DESC');
+    }
+
+    /**
+     * 获取超时订单ID列表
+     * 此函数用于查询并返回在指定时间之前创建的、未支付的订单ID列表。如果指定了需要提醒的订单,则只返回尚未提醒的订单ID。
+     *
+     * @param int $time 查询的截止时间,以时间戳形式表示。
+     * @param bool $is_remind 是否查询需要提醒的订单,默认为false,表示不查询需要提醒的订单。
+     * @return array 返回符合条件的订单ID列表。
+     */
+    public function getTimeOutIds($time, $is_remind = false)
+    {
+        // 根据条件构造查询语句,查询未删除、未支付的订单
+        return StoreGroupOrder::getDB()->where('is_del', 0)->where('paid', 0)
+            // 如果需要提醒,添加额外的查询条件,查询未提醒的订单
+            ->when($is_remind, function ($query) {
+                $query->where('is_remind', 0);
+            })
+            // 查询创建时间早于等于指定时间的订单
+            ->where('create_time', '<=', $time)
+            // 返回满足条件的订单的group_order_id列
+            ->column('group_order_id');
+    }
+
+
+    /**
+     * 设置团购订单的提醒状态
+     *
+     * 本函数用于将指定团购订单的提醒状态设置为已提醒(1)。通过传入订单ID,查询到该订单后进行状态更新。
+     * 主要用于在用户需要时,对特定团购订单设置提醒标记,以便系统或用户知道该订单已经进行过提醒操作。
+     *
+     * @param int $id 团购订单的ID
+     * @return bool 更新操作的结果,成功返回true,失败返回false
+     * @throws \think\db\exception\DbException
+     */
+    public function isRemind($id)
+    {
+        // 使用StoreGroupOrder类的静态方法getDB来获取数据库操作对象,并根据group_order_id为$id更新is_remind字段为1
+        return StoreGroupOrder::getDB()->where('group_order_id', $id)->update(['is_remind' => 1]);
+    }
+
+    /**
+     * 计算用户当前未支付订单的总金额
+     *
+     * 本函数通过查询数据库中指定用户的未支付订单,计算出这些订单的总支付价格。
+     * 未支付订单是指支付类型(pay_type)为0的订单。
+     *
+     * @param int $uid 用户ID
+     * @return float|int 返回用户未支付订单的总金额,如果没有未支付订单则返回0。
+     */
+    public function totalNowMoney($uid)
+    {
+        // 使用StoreGroupOrder的数据库实例,并指定查询条件为支付类型为0且用户ID为$uid的订单,计算这些订单的支付价格总和
+        return StoreGroupOrder::getDB()->where('pay_type', 0)->where('uid', $uid)->sum('pay_price') ?: 0;
+    }
+}

+ 33 - 0
app/common/dao/store/order/StoreImportDao.php

@@ -0,0 +1,33 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreImport;
+
+class StoreImportDao extends BaseDao
+{
+
+    /**
+     *
+     * @return string
+     * @author Qinii
+     * @day 3/16/21
+     */
+    protected function getModel(): string
+    {
+        return StoreImport::class;
+    }
+}

+ 33 - 0
app/common/dao/store/order/StoreImportDeliveryDao.php

@@ -0,0 +1,33 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreImportDelivery;
+
+class StoreImportDeliveryDao extends BaseDao
+{
+
+    /**
+     *
+     * @return string
+     * @author Qinii
+     * @day 3/16/21
+     */
+    protected function getModel(): string
+    {
+        return StoreImportDelivery::class;
+    }
+}

+ 1248 - 0
app/common/dao/store/order/StoreOrderDao.php

@@ -0,0 +1,1248 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreOrder;
+use app\common\model\store\order\StoreOrderProduct;
+use app\common\model\store\order\StoreOrderStatus;
+use app\common\model\user\User;
+use app\common\repositories\store\order\StoreOrderStatusRepository;
+use app\common\repositories\store\product\ProductAssistSetRepository;
+use app\common\repositories\store\product\ProductGroupBuyingRepository;
+use think\db\BaseQuery;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
+use think\facade\Db;
+use think\Model;
+
+/**
+ * Class StoreOrderDao
+ * @package app\common\dao\store\order
+ * @author xaboy
+ * @day 2020/6/8
+ */
+class StoreOrderDao extends BaseDao
+{
+    //订单状态(0:待发货;1:待收货;2:待评价;3:已完成; 9: 拼团中 10:  待付尾款 11:尾款超时未付 -1:已退款)
+    const ORDER_STATUS_BE_SHIPPED = 0;
+    const ORDER_STATUS_BE_RECEIVE = 1;
+    const ORDER_STATUS_REPLY = 2;
+    const ORDER_STATUS_SUCCESS = 3;
+    const ORDER_STATUS_SPELL = 9;
+    const ORDER_STATUS_TAIL = 10;
+    const ORDER_STATUS_TAIL_FAIL = 11;
+    const ORDER_STATUS_REFUND = -1;
+
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/6/8
+     */
+    protected function getModel(): string
+    {
+        return StoreOrder::class;
+    }
+
+    /**
+     * 订单搜索
+     * @param array $where
+     * @param int $sysDel
+     * @return BaseQuery
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function search(array $where, $sysDel = 0)
+    {
+        $query = StoreOrder::hasWhere('merchant', function ($query) use ($where) {
+            // 分组权限
+            if (app('request')->hasMacro('regionAuthority') && $region = app('request')->regionAuthority()) {
+                $query->whereIn('mer_id', $region);
+            }
+            if (isset($where['is_trader']) && $where['is_trader'] !== '') {
+                $query->where('is_trader', $where['is_trader']);
+            }
+            $query->where('is_del', 0);
+        });
+        $query = $this->whereSearch($query, $where, $sysDel);
+        return $query;
+    }
+
+    public function whereSearch($query, $where, $sysDel = 0)
+    {
+        $query->where('StoreOrder.activity_type', '<>', 20);
+        $query->when(($sysDel !== null), function ($query) use ($sysDel) {
+            $query->where('is_system_del', $sysDel);
+        });
+        $query->when(isset($where['order_type']) && $where['order_type'] >= 0 && $where['order_type'] !== '', function ($query) use ($where) {
+            if ($where['order_type'] == 2) {
+                $query->where('is_virtual', 1);
+            } else if ($where['order_type'] == 3) { // 卡密商品
+                $query->where('is_virtual', 2);
+            } else if ($where['order_type'] == 4) { // 预约商品订单
+                $query->where('is_virtual', 4);
+            } else {
+                $query->where('order_type', $where['order_type']);
+            }
+        });
+        $query->when(isset($where['activity_type']) && $where['activity_type'] !== '', function ($query) use ($where) {
+            $query->where('StoreOrder.activity_type', $where['activity_type']);
+        })
+            ->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+                switch ($where['status']) {
+                    case 0 :
+                        $query->whereIn('StoreOrder.status', [0, 9]);
+                        break;
+                    case -2 :
+                        $query->where('paid', 1)->whereNotIn('StoreOrder.status', [10, 11]);
+                        break;
+                    case 10 :
+                        $query->where('paid', 1)->whereIn('StoreOrder.status', [10, 11]);
+                        break;
+                    default:
+                        $query->where('StoreOrder.status', $where['status']);
+                        break;
+                }
+            })
+            ->when(isset($where['uid']) && $where['uid'] !== '', function ($query) use ($where) {
+                $query->where('uid', $where['uid']);
+            })
+            ->when(isset($where['is_spread']) && $where['is_spread'] !== '', function ($query) use ($where) {
+                if ($where['is_spread']) {
+                    $query->where(function ($query) {
+                        $query->where('StoreOrder.spread_uid', '>', 0)->whereOr('StoreOrder.top_uid', '>', 0);
+                    });
+                }
+            })
+            ->when(isset($where['is_user']) && $where['is_user'] !== '', function ($query) use ($where) {
+                $query->where(function ($query) {
+                    $query->where('order_type', 0)->whereOr(function ($query) {
+                        $query->where('order_type', 1)->where('main_id', 0);
+                    });
+                });
+            })
+            //待核销订单
+            ->when(isset($where['is_verify']) && $where['is_verify'], function ($query) use ($where) {
+                $query->where('paid', 1);
+                $query->where(function($query) use($where){
+                    $query->where(function ($query) {
+                        $query->where('StoreOrder.order_type', 1)->where('StoreOrder.status', 0);
+                    })->whereOr(function ($query) {
+                        $query->where('StoreOrder.order_type', 0)->where('StoreOrder.status', 20)->where('is_virtual', 4);
+                    })->whereOr(function ($query) {
+                        $query->where('StoreOrder.order_type', 1)->where('StoreOrder.status', 1)->where('enable_assigned', 1)->where('is_virtual', 4);
+                    });
+                });
+            })
+            ->when(isset($where['pay_type']) && $where['pay_type'] !== '', function ($query) use ($where) {
+                if (is_int($where['pay_type'])) {
+                    $query->where('StoreOrder.pay_type', $where['pay_type']);
+                } else {
+                    $query->whereIn('StoreOrder.pay_type', $where['pay_type']);
+                }
+            })
+            ->when(isset($where['order_ids']) && $where['order_ids'] !== '', function ($query) use ($where) {
+                $query->whereIn('order_id', $where['order_ids']);
+            })
+            ->when(isset($where['order_id']) && $where['order_id'] !== '', function ($query) use ($where) {
+                $query->where('StoreOrder.order_id', $where['order_id']);
+            })
+            ->when(isset($where['take_order']) && $where['take_order'] != '', function ($query) use ($where) {
+                $query->whereNotNull('verify_time');
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+                $query->where('StoreOrder.mer_id', $where['mer_id']);
+            })
+            ->when(isset($where['mer_ids']) && $where['mer_ids'] !== '', function ($query) use ($where) {
+                $query->whereIn('StoreOrder.mer_id', $where['mer_ids']);
+            })
+            ->when(isset($where['date']) && $where['date'] !== '', function ($query) use ($where) {
+                getModelTime($query, $where['date'], 'StoreOrder.create_time');
+            })
+            ->when(isset($where['verify_date']) && $where['verify_date'] !== '', function ($query) use ($where) {
+                getModelTime($query, $where['verify_date'], 'verify_time');
+            })
+            ->when(isset($where['order_sn']) && $where['order_sn'] !== '', function ($query) use ($where) {
+                $query->where('order_sn', 'like', '%' . $where['order_sn'] . '%');
+            })
+            ->when(isset($where['paid']) && $where['paid'] !== '', function ($query) use ($where) {
+                $query->where('StoreOrder.paid', $where['paid']);
+            })
+            ->when(isset($where['is_del']) && $where['is_del'] !== '', function ($query) use ($where) {
+                $query->where('StoreOrder.is_del', $where['is_del']);
+            })
+            ->when(isset($where['service_id']) && $where['service_id'] !== '', function ($query) use ($where) {
+                $query->where('service_id', $where['service_id']);
+            })
+            ->when(isset($where['delivery_id']) && $where['delivery_id'] !== '', function ($query) use ($where) {
+                $query->where('delivery_id', $where['delivery_id']);
+            })
+            ->when(isset($where['delivery_name']) && $where['delivery_name'] !== '', function ($query) use ($where) {
+                $query->whereLike('StoreOrder.real_name', "%{$where['delivery_name']}%");
+            })
+            ->when(isset($where['delivery_phone']) && $where['delivery_phone'] !== '', function ($query) use ($where) {
+                $query->whereLike('user_phone', "%{$where['delivery_phone']}%");
+            })
+            ->when(isset($where['username']) && $where['username'] !== '', function ($query) use ($where) {
+                $query->join('User U', 'StoreOrder.uid = U.uid')
+                    ->where(function ($query) use ($where) {
+                        $query->where('nickname', 'like', "%{$where['username']}%")
+                            ->whereOr('phone', 'like', "%{$where['username']}%")
+                            ->whereOr('StoreOrder.user_phone', 'like', "%{$where['username']}%");
+                    });
+            })
+            ->when(isset($where['spread_name']) && $where['spread_name'] !== '', function ($query) use ($where) {
+                $uid = User::where('nickname', 'like', "%{$where['spread_name']}%")->column('uid');
+                $query->whereIn('StoreOrder.uid', $uid);
+            })
+            ->when(isset($where['phone']) && $where['phone'] !== '', function ($query) use ($where) {
+                $uid = User::where('phone', 'like', "%{$where['phone']}%")->column('uid');
+                $query->whereIn('StoreOrder.uid', $uid);
+            })
+            ->when(isset($where['nickname']) && $where['nickname'] !== '', function ($query) use ($where) {
+                $uid = User::where('nickname', 'like', "%{$where['nickname']}%")->column('uid');
+                $query->whereIn('StoreOrder.uid', $uid);
+            })
+            ->when(isset($where['real_name']) && $where['real_name'] !== '', function ($query) use ($where) {
+                $uid = User::where('real_name', 'like', "%{$where['real_name']}%")->column('uid');
+                $query->whereIn('StoreOrder.spread_uid', $uid);
+            })
+            ->when(isset($where['top_spread_name']) && $where['top_spread_name'] !== '', function ($query) use ($where) {
+                $uid = User::where('nickname', 'like', "%{$where['top_spread_name']}%")->column('uid');
+                $query->whereIn('StoreOrder.top_uid', $uid);
+            })
+            ->when(isset($where['enable_assigned']) && $where['enable_assigned'] !== '', function ($query) use ($where) {
+                $query->where('enable_assigned', $where['enable_assigned']);
+            })
+            ->when(isset($where['reservation_date']) && $where['reservation_date'] !== '', function ($query) use ($where) {
+                $query->join('StoreOrderProduct op', 'op.order_id = StoreOrder.order_id');
+                if(strtotime($where['reservation_date']) !== false) {
+                    $query->where('op.reservation_date', $where['reservation_date']);
+                } else {
+                    list($startTime, $endTime) = explode('-', $where['reservation_date']);
+                    $query->whereBetweenTime('op.reservation_date', date('Y-m-d',strtotime($startTime)), date('Y-m-d',strtotime($endTime)));
+                }
+            })
+            ->when(isset($where['store_name']) && $where['store_name'] !== '', function ($query) use ($where) {
+                $orderId = StoreOrderProduct::alias('op')
+                    ->join('storeProduct sp', 'op.product_id = sp.product_id')
+                    ->whereLike('store_name', "%{$where['store_name']}%")
+                    ->when((isset($where['sp.mer_id']) && $where['mer_id'] !== ''), function ($query) use ($where) {
+                        $query->where('mer_id', $where['mer_id']);
+                    })->column('op.order_id');
+                $query->whereIn('StoreOrder.order_id', $orderId ?: '');
+            })
+            ->when(isset($where['search']) && $where['search'] !== '', function ($query) use ($where) {
+                $orderId = StoreOrderProduct::alias('op')
+                    ->join('storeProduct sp', 'op.product_id = sp.product_id')
+                    ->whereLike('store_name', "%{$where['search']}%")
+                    ->when((isset($where['sp.mer_id']) && $where['mer_id'] !== ''), function ($query) use ($where) {
+                        $query->where('mer_id', $where['mer_id']);
+                    })->column('op.order_id');
+
+                $query->where(function ($query) use ($orderId, $where) {
+                    if ($orderId)$query->whereIn('StoreOrder.order_id', $orderId);
+                        $query->whereOr('order_sn', 'like', "%{$where['search']}%")
+                        ->whereOr('StoreOrder.user_phone', 'like', "%{$where['search']}%");
+                });
+            })
+            ->when(isset($where['group_order_sn']) && $where['group_order_sn'] !== '', function ($query) use ($where) {
+                $query->join('StoreGroupOrder GO', 'StoreOrder.group_order_id = GO.group_order_id')->where('group_order_sn', $where['group_order_sn']);
+            })
+            ->when(isset($where['group_order_id']) && $where['group_order_id'] !== '', function ($query) use ($where) {
+                $query->where('group_order_id', $where['group_order_id']);
+            })
+            ->when(isset($where['keywords']) && $where['keywords'] !== '', function ($query) use ($where) {
+                $query->where(function ($query) use ($where) {
+                    $query->whereLike('StoreOrder.real_name|StoreOrder.user_phone|order_sn', "%" . $where['keywords'] . "%");
+                });
+            })
+            ->when(isset($where['filter_delivery']) && $where['filter_delivery'] !== '', function ($query) use ($where) {
+                //1 快递 2 配送 3 虚拟
+                //按发货方式:1快递订单、2配送订单、4核销订单、3虚拟发货、6自动发货
+                switch ($where['filter_delivery']) {
+                    case 1:
+                        $query->whereIn('delivery_type', '1,4');
+                        break;
+                    case 2:
+                        $query->whereIn('delivery_type', '2,5');
+                        break;
+                    case 3:
+                        $query->whereIn('delivery_type', '3');
+                        break;
+                    case 4:
+                        $query->where('order_type', 1);
+                        break;
+                    case 6:
+                        $query->whereIn('delivery_type', '6');
+                        break;
+                }
+            })
+            ->when(isset($where['filter_product']) && $where['filter_product'] !== '', function ($query) use ($where) {
+                // 1 实物商品、2虚拟商品、3卡密商品
+                switch ($where['filter_product']) {
+                    case 1:
+                        $query->where('is_virtual', 0);
+                        break;
+                    case 2:
+                        $query->where('is_virtual', 1);
+                        break;
+                    case 3:
+                        $query->where('is_virtual', 2);
+                        break;
+                    case 4:
+                        $query->where('is_virtual', 4);
+                        break;
+                    default:
+                        break;
+                }
+            })
+            ->when((isset($where['is_behalf']) && $where['is_behalf'] !== ''), function ($query) use ($where) {
+                $query->where('StoreOrder.is_behalf', $where['is_behalf']);
+            })
+            ->when((isset($where['staffs_id']) && $where['staffs_id'] !== ''), function ($query) use ($where) {
+                $query->where('StoreOrder.staffs_id', $where['staffs_id']);
+            })
+            ->when((isset($where['staffs_ids']) && $where['staffs_ids'] !== ''), function ($query) use ($where) {
+                $query->whereIn('StoreOrder.staffs_id', $where['staffs_ids']);
+            })
+            ->when(isset($where['service_type']) && $where['service_type']!== '', function ($query) use ($where) {
+                $query->where('StoreOrder.order_type', $where['service_type']);
+            })
+            ->when(isset($where['reservation_keyword']) && $where['reservation_keyword']!== '', function ($query) use ($where) {
+                $query->where(function ($query) use ($where) {
+                    $query->where('StoreOrder.real_name',  'like', "%{$where['reservation_keyword']}%")->whereOr('StoreOrder.user_phone', 'like', "%{$where['reservation_keyword']}%");
+                });
+            })
+            ->when(isset($where['reservation_status']) && !empty($where['reservation_status']), function ($query) use ($where) {
+                $query->where(function ($query) use ($where) {
+                    foreach($where['reservation_status'] as $reservationStatus){
+                        switch ($reservationStatus) {
+                            case 1: // 待服务
+                                $query->whereOr(function ($query) {
+                                    $query->whereIn('status', '0,1');
+                                });
+                                break;
+                            case 2: // 服务中 只有上门
+                                $query->whereOr(function ($query) {
+                                    $query->where('status', '20')->where('order_type', 0);
+                                });
+                                break;
+                            case 3: // 已完成
+                                $query->whereOr(function ($query) {
+                                    $query->whereIn('status', '2,3');
+                                });
+                                break;
+                            default:
+                                $query->whereOr(function ($query) {
+                                    $query->where(function ($query) { // 上门
+                                        $query->where('status', '1')->where('order_type', 0);
+                                    });
+                                    $query->whereOr(function ($query) { // 到店
+                                        $query->whereIn('status', '0,1')->where('order_type', 1);
+                                    });
+                                });
+                                break;
+                        }
+                    }
+                });
+            })
+            ->order('StoreOrder.create_time DESC');
+
+        return $query;
+    }
+
+    public function reservationSearch(array $where, $sysDel = 0)
+    {
+        $query = StoreOrder::getDB()->alias('StoreOrder');
+        $query = $this->whereSearch($query, $where, $sysDel);
+        return $query;
+    }
+
+    /**
+     * 搜索全部
+     * @param array $where
+     * @param int $sysDel
+     * @param null $is_points
+     * @return BaseQuery
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function searchAll(array $where, $sysDel = 0, $is_points = null)
+    {
+        $query = StoreOrder::getDB()->alias('StoreOrder');
+        $query->when(($sysDel !== null), function ($query) use ($sysDel) {
+            $query->where('is_system_del', $sysDel);
+        });
+        $query->when(!is_null($is_points), function ($query) use ($is_points) {
+            if ($is_points) {
+                $query->where('activity_type', 20);
+            } else {
+                $query->where('activity_type', '<>', 20);
+            }
+        });
+        $query->when(isset($where['order_type']) && $where['order_type'] >= 0 && $where['order_type'] !== '', function ($query) use ($where) {
+            if ($where['order_type'] == 2) {
+                $query->where('is_virtual', 1);
+            } else if ($where['order_type'] == 0) { //实体发货订单
+                $query->where('order_type', 0)->where('is_virtual', 0);
+            } else if ($where['order_type'] == 3) { //发货订单
+                $query->where('order_type', 0);
+            } else {
+                $query->where('order_type', $where['order_type']);
+            }
+        });
+        $query->when(isset($where['activity_type']) && $where['activity_type'] !== '', function ($query) use ($where) {
+            $query->where('activity_type', $where['activity_type']);
+        })
+            ->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+                switch ($where['status']) {
+                    case 0 :
+                        $query->whereIn('StoreOrder.status', [0, 9])->where('paid', 1)->where('is_del', 0);
+                        break;
+                    case -2 :
+                        $query->where('paid', 1)->whereNotIn('StoreOrder.status', [10, 11])->where('is_del', 0);
+                        break;
+                    case -3 :
+                        $query->where('paid', 0)->where('is_del', 0);
+                        break;
+                    case 10 :
+                        $query->where('paid', 1)->whereIn('StoreOrder.status', [10, 11])->where('is_del', 0);
+                        break;
+                    default:
+                        $query->where('StoreOrder.status', $where['status'])->where('is_del', 0);
+                        break;
+                }
+
+            })
+            ->when(isset($where['uid']) && $where['uid'] !== '', function ($query) use ($where) {
+                $query->where('uid', $where['uid']);
+            })
+            ->when(isset($where['is_user']) && $where['is_user'] !== '', function ($query) use ($where) {
+                $query->where(function ($query) {
+                    $query->where('order_type', 0)->whereOr(function ($query) {
+                        $query->where('order_type', 1)->where('main_id', 0);
+                    });
+                });
+            })
+            //待核销订单
+            ->when(isset($where['is_verify']) && $where['is_verify'], function ($query) use ($where) {
+                $query->where('StoreOrder.order_type', 1)->where('StoreOrder.status', 0);
+            })
+            ->when(isset($where['pay_type']) && $where['pay_type'] !== '', function ($query) use ($where) {
+                $query->where('StoreOrder.pay_type', $where['pay_type']);
+            })
+            ->when(isset($where['order_ids']) && $where['order_ids'] !== '', function ($query) use ($where) {
+                $query->whereIn('order_id', $where['order_ids']);
+            })
+            ->when(isset($where['order_id']) && $where['order_id'] !== '', function ($query) use ($where) {
+                $query->where('order_id', $where['order_id']);
+            })
+            ->when(isset($where['take_order']) && $where['take_order'] != '', function ($query) use ($where) {
+                $query->where('order_type', 1)->whereNotNull('verify_time');
+            })
+            ->when(isset($where['date']) && $where['date'] !== '', function ($query) use ($where) {
+                getModelTime($query, $where['date'], 'StoreOrder.create_time');
+            })
+            ->when(isset($where['order_sn']) && $where['order_sn'] !== '', function ($query) use ($where) {
+                $query->where('order_sn', 'like', '%' . $where['order_sn'] . '%');
+            })
+            ->when(isset($where['paid']) && $where['paid'] !== '', function ($query) use ($where) {
+                $query->where('StoreOrder.paid', $where['paid']);
+            })
+            ->when(isset($where['is_del']) && $where['is_del'] !== '', function ($query) use ($where) {
+                $query->where('StoreOrder.is_del', $where['is_del']);
+            })
+            ->when(isset($where['service_id']) && $where['service_id'] !== '', function ($query) use ($where) {
+                $query->where('service_id', $where['service_id']);
+            })
+            ->when(isset($where['username']) && $where['username'] !== '', function ($query) use ($where) {
+                $query->join('User U', 'StoreOrder.uid = U.uid')
+                    ->where(function ($query) use ($where) {
+                        $query->where('nickname', 'like', "%{$where['username']}%")
+                            ->whereOr('phone', 'like', "%{$where['username']}%")
+                            ->whereOr('user_phone', 'like', "%{$where['username']}%");
+                    });
+            })
+            ->when(isset($where['nickname']) && $where['nickname'] !== '', function ($query) use ($where) {
+                $query->whereLike('StoreOrder.real_name', "%" . $where['nickname'] . "%");
+            })
+            ->when(isset($where['phone']) && $where['phone'] !== '', function ($query) use ($where) {
+                $query->join('User U', 'StoreOrder.uid = U.uid')->where('phone', 'like', "%{$where['phone']}%");
+            })
+            ->when(isset($where['store_name']) && $where['store_name'] !== '', function ($query) use ($where) {
+                $orderId = StoreOrderProduct::alias('op')
+                    ->join('storeProduct sp', 'op.product_id = sp.product_id')
+                    ->whereLike('store_name', "%{$where['store_name']}%")
+                    ->when((isset($where['sp.mer_id']) && $where['mer_id'] !== ''), function ($query) use ($where) {
+                        $query->where('mer_id', $where['mer_id']);
+                    })->column('order_id');
+                $query->whereIn('order_id', $orderId ?: '');
+            })
+            ->when(isset($where['search']) && $where['search'] !== '', function ($query) use ($where) {
+                $orderId = StoreOrderProduct::alias('op')
+                    ->join('storeProduct sp', 'op.product_id = sp.product_id')
+                    ->whereLike('store_name', "%{$where['search']}%")
+                    ->when((isset($where['sp.mer_id']) && $where['mer_id'] !== ''), function ($query) use ($where) {
+                        $query->where('mer_id', $where['mer_id']);
+                    })->column('order_id');
+                $query->where(function ($query) use ($orderId, $where) {
+                    $query->whereIn('order_id', $orderId ? $orderId : '')
+                        ->whereOr('order_sn', 'like', "%{$where['search']}%")
+                        ->whereOr('user_phone', 'like', "%{$where['search']}%");
+                });
+            })
+            ->when(isset($where['product_id']) && $where['product_id'] !== '', function ($query) use ($where) {
+                $orderId = StoreOrderProduct::alias('op')
+                    ->join('storeProduct sp', 'op.product_id = sp.product_id')
+                    ->where('sp.product_id', $where['product_id'])
+                    ->column('order_id');
+                $query->whereIn('order_id', $orderId ? $orderId : '');
+            })
+            ->when(isset($where['group_order_sn']) && $where['group_order_sn'] !== '', function ($query) use ($where) {
+                $query->join('StoreGroupOrder GO', 'StoreOrder.group_order_id = GO.group_order_id')->where('group_order_sn', $where['group_order_sn']);
+            })
+            ->when(isset($where['keywords']) && $where['keywords'] !== '', function ($query) use ($where) {
+                $query->where(function ($query) use ($where) {
+                    $query->whereLike('StoreOrder.real_name|StoreOrder.user_phone|order_sn', "%" . $where['keywords'] . "%");
+                });
+            })
+            ->order('StoreOrder.create_time DESC');
+
+        return $query;
+    }
+
+    /**
+     * 查询用户的订单
+     * @param $id
+     * @param $uid
+     * @return array|Model|null
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author xaboy
+     * @day 2020/6/11
+     */
+    public function userOrder($id, $uid)
+    {
+        return StoreOrder::getDB()->where('order_id', $id)->where('uid', $uid)->where('is_del', 0)->where('paid', 1)->where('is_system_del', 0)->find();
+    }
+
+    /**
+     * 条件查询用户订单
+     * @param array $where
+     * @param $ids
+     * @return BaseQuery
+     * @author xaboy
+     * @day 2020/6/26
+     */
+    public function usersOrderQuery(array $where, $ids, $uid)
+    {
+        return StoreOrder::getDB()->where(function ($query) use ($uid, $ids) {
+            $query->when(isset($ids['spread_uid']) && $ids['spread_uid'] !== '',function($query) use($ids){
+                $query->where('spread_uid', $ids['spread_uid']);
+            })->when(isset($ids['top_uid']) && $ids['top_uid'] !== '',function($query) use($ids){
+                $query->where('top_uid', $ids['top_uid']);
+            })->when(isset($ids['spread_uids']) && $ids['spread_uids'] !== '',function($query) use($ids){
+                $query->whereOr('spread_uid', $ids['spread_uids'])->whereOr('top_uid', $ids['spread_uids']);
+            });
+            $query->whereOr(function ($query) use ($uid) {
+                if ($uid) {$query->where('uid', $uid)->where('is_selfbuy', 1);}
+            });
+        })->when(isset($where['date']) && $where['date'] !== '', function ($query) use ($where) {
+            getModelTime($query, $where['date'], 'pay_time');
+        })->when(isset($where['create_time']) && $where['create_time'] !== '', function ($query) use ($where) {
+            getModelTime($query, $where['create_time'], 'create_time');
+        })->when(isset($where['keyword']) && $where['keyword'] !== '', function ($query) use ($where) {
+            $_uid = User::where('nickname', 'like', "%{$where['keyword']}%")->column('uid');
+            $orderId = StoreOrderProduct::alias('op')
+                ->join('storeProduct sp', 'op.product_id = sp.product_id')
+                ->whereLike('store_name', "%{$where['keyword']}%")
+                ->column('order_id');
+            $query->where(function ($query) use ($orderId, $where, $_uid) {
+                $query->whereLike('order_id|order_sn', "%{$where['keyword']}%")->whereOr('order_id', 'in', $orderId)->whereOr('uid', 'in', $_uid);
+            });
+        })->where('paid', 1)->where('status', '>',0)->order('pay_time DESC');
+    }
+
+    /**
+     * 查询订单是否存在
+     * @param $field
+     * @param $value
+     * @param int|null $except
+     * @return bool
+     * @author xaboy
+     * @day 2020/6/11
+     */
+    public function fieldExists($field, $value, ?int $except = null): bool
+    {
+        return ($this->getModel()::getDB())->when($except, function ($query) use ($field, $except) {
+                $query->where($field, '<>', $except);
+            })->where($field, $value)->count() > 0;
+    }
+
+    /**
+     * 获取商户id
+     * @param $id
+     * @return mixed
+     * @author xaboy
+     * @day 2020/6/12
+     */
+    public function getMerId($id)
+    {
+        return StoreOrder::getDB()->where('order_id', $id)->value('mer_id');
+    }
+
+    /**
+     * 查询商户订单是否存在
+     * @param array $where
+     * @return bool
+     * @author Qinii
+     * @day 2020-06-12
+     */
+    public function merFieldExists(array $where)
+    {
+        return ($this->getModel()::getDB())->where($where)->count() > 0;
+    }
+
+    /**
+     *  对账更新
+     * @param $reconciliation_id
+     * @return mixed
+     * @author Qinii
+     * @day 2020-06-15
+     */
+    public function reconciliationUpdate($reconciliation_id)
+    {
+        return ($this->getModel()::getDB())->whereIn('reconciliation_id', $reconciliation_id)->update(['reconciliation_id' => 0]);
+    }
+
+    /**
+     * 每日订单数量
+     * @param $day
+     * @param null $merId
+     * @return int
+     * @throws DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function dayOrderNum($day, $merId = null)
+    {
+        return StoreOrder::getDB()->where('paid', 1)->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->when($day, function ($query, $day) {
+            getModelTime($query, $day, 'pay_time');
+        })->count();
+    }
+
+    /**
+     * 每日订单金额
+     * @param $day
+     * @param null $merId
+     * @return float
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function dayOrderPrice($day, $merId = null)
+    {
+        return getModelTime(StoreOrder::getDB()->where('paid', 1)->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        }), $day, 'pay_time')->sum('pay_price');
+    }
+
+    /**
+     * 时间区间订单金额
+     * @param $date
+     * @param null $merId
+     * @return float
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function dateOrderPrice($date, $merId = null)
+    {
+        return StoreOrder::getDB()->where('paid', 1)->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->when($date, function ($query, $date) {
+            getModelTime($query, $date, 'pay_time');
+        })->sum('pay_price');
+    }
+
+    /**
+     * 时间区间订单数量
+     * @param $date
+     * @param null $merId
+     * @return int
+     * @throws DbException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function dateOrderNum($date, $merId = null)
+    {
+        return StoreOrder::getDB()->where('paid', 1)->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->when($date, function ($query, $date) {
+            getModelTime($query, $date, 'pay_time');
+        })->count();
+    }
+
+    /**
+     * 每日下单用户数量
+     * @param $day
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function dayOrderUserNum($day, $merId = null)
+    {
+        return StoreOrder::getDB()->where('paid', 1)->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->when($day, function ($query, $day) {
+            getModelTime($query, $day, 'pay_time');
+        })->group('uid')->count();
+    }
+
+    /**
+     * 时间区间订单数量
+     * @param $date
+     * @param null $paid
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function orderUserNum($date, $paid = null, $merId = null)
+    {
+        return StoreOrder::getDB()->when($paid, function ($query, $paid) {
+            $query->where('paid', $paid);
+        })->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->when($date, function ($query, $date) use ($paid) {
+            if (!$paid) {
+                getModelTime($query, $date);
+            } else
+                getModelTime($query, $date, 'pay_time');
+        })->group('uid')->count();
+    }
+
+    /**
+     * 查询下单用户数量,金额
+     * @param $date
+     * @param null $paid
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function orderUserGroup($date, $paid = null, $merId = null)
+    {
+        return StoreOrder::getDB()->when($paid, function ($query, $paid) {
+            $query->where('paid', $paid);
+        })->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->when($date, function ($query, $date) {
+            getModelTime($query, $date, 'pay_time');
+        })->group('uid')->field(Db::raw('uid,sum(pay_price) as pay_price,count(order_id) as total'))->select();
+    }
+
+    /**
+     * 查询下单用户数量
+     * @param array $ids
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function oldUserNum(array $ids, $merId = null)
+    {
+        return StoreOrder::getDB()->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->whereIn('uid', $ids)->where('paid', 1)->group('uid')->count();
+    }
+
+    /**
+     * 查询下单用户id
+     * @param array $ids
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function oldUserIds(array $ids, $merId = null)
+    {
+        return StoreOrder::getDB()->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->whereIn('uid', $ids)->where('paid', 1)->group('uid')->column('uid');
+    }
+
+    /**
+     * 查询订单金额
+     * @param $date
+     * @param null $paid
+     * @param null $merId
+     * @return float
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function orderPrice($date, $paid = null, $merId = null)
+    {
+        return StoreOrder::getDB()->when($paid, function ($query, $paid) {
+            $query->where('paid', $paid);
+        })->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->when($date, function ($query, $date) use ($paid) {
+            if (!$paid) {
+                $query->where(function ($query) use ($date) {
+                    $query->where(function ($query) use ($date) {
+                        $query->where('paid', 1);
+                        getModelTime($query, $date, 'pay_time');
+                    })->whereOr(function ($query) use ($date) {
+                        $query->where('paid', 0);
+                        getModelTime($query, $date);
+                    });
+                });
+            } else
+                getModelTime($query, $date, 'pay_time');
+        })->sum('pay_price');
+    }
+
+    /**
+     * 查询订单数量
+     * @param $date
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function orderGroupNum($date, $merId = null)
+    {
+        $field = Db::raw('sum(pay_price) as pay_price,count(*) as total,count(distinct uid) as user,pay_time,from_unixtime(unix_timestamp(pay_time),\'%m-%d\') as `day`');
+        if ($date == 'year') {
+            $field = Db::raw('sum(pay_price) as pay_price,count(*) as total,count(distinct uid) as user,pay_time,from_unixtime(unix_timestamp(pay_time),\'%m\') as `day`');
+        }
+        $query = StoreOrder::getDB()->field($field)
+            ->where('paid', 1)->when($date, function ($query, $date) {
+                getModelTime($query, $date, 'pay_time');
+            })->when($merId, function ($query, $merId) {
+                $query->where('mer_id', $merId);
+            });
+        return $query->order('pay_time ASC')->group('day')->select();
+    }
+
+
+    /**
+     *  根据时间筛选订单 统计数据
+     * @param $where
+     * @param $page
+     * @param $limit
+     * @param $merId
+     * @return mixed
+     * @author Qinii
+     */
+    public function orderGroupNumPage($where, $page, $limit, $merId = null)
+    {
+        return StoreOrder::getDB()->when(isset($where['dateRange']), function ($query) use ($where) {
+            getModelTime($query, date('Y/m/d 00:00:00', $where['dateRange']['start']) . '-' . date('Y/m/d 00:00:00', $where['dateRange']['stop']), 'pay_time');
+        })->field(Db::raw('sum(pay_price) as pay_price,count(*) as total,count(distinct uid) as user,pay_time,from_unixtime(unix_timestamp(pay_time),\'%m-%d\') as `day`'))
+            ->where('paid', 1)->when($merId, function ($query, $merId) {
+                $query->where('mer_id', $merId);
+            })->order('pay_time DESC')->page($page, $limit)->group('day')->select();
+    }
+
+    /**
+     *  按照日期分组统计订单金额
+     * @param $date
+     * @param $merId
+     * @return mixed
+     * @author Qinii
+     */
+    public function dayOrderPriceGroup($date, $merId = null)
+    {
+        return StoreOrder::getDB()->field(Db::raw('sum(pay_price) as price, from_unixtime(unix_timestamp(pay_time),\'%H:%i\') as time'))
+            ->where('paid', 1)->when($date, function ($query, $date) {
+                getModelTime($query, $date, 'pay_time');
+            })->when($merId, function ($query, $merId) {
+                $query->where('mer_id', $merId);
+            })->group('time')->select();
+    }
+
+    /**
+     * 按照日期分组统计订单数量
+     * @param $date
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function dayOrderNumGroup($date, $merId = null)
+    {
+        return StoreOrder::getDB()->field(Db::raw('count(*) as total, from_unixtime(unix_timestamp(pay_time),\'%H:%i\') as time'))
+            ->where('paid', 1)->when($date, function ($query, $date) {
+                getModelTime($query, $date, 'pay_time');
+            })->when($merId, function ($query, $merId) {
+                $query->where('mer_id', $merId);
+            })->group('time')->select();
+    }
+
+    /**
+     * 按照日期分组统计订单用户数量
+     * @param $date
+     * @param null $merId
+     * @return mixed
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function dayOrderUserGroup($date, $merId = null)
+    {
+        return StoreOrder::getDB()->field(Db::raw('count(DISTINCT uid) as total, from_unixtime(unix_timestamp(pay_time),\'%H:%i\') as time'))
+            ->where('paid', 1)->when($date, function ($query, $date) {
+                getModelTime($query, $date, 'pay_time');
+            })->when($merId, function ($query, $merId) {
+                $query->where('mer_id', $merId);
+            })->group('time')->select();
+    }
+
+    /**
+     * 获取当前时间到指定时间的支付金额 管理员
+     * @param string $start 开始时间
+     * @param string $stop 结束时间
+     * @return mixed
+     */
+    public function chartTimePrice($start, $stop, $merId = null)
+    {
+        return StoreOrder::getDB()->where('paid', 1)
+            ->where('pay_time', '>=', $start)
+            ->where('pay_time', '<', $stop)
+            ->when($merId, function ($query, $merId) {
+                $query->where('mer_id', $merId);
+            })
+            ->field('sum(pay_price) as num,FROM_UNIXTIME(unix_timestamp(pay_time), \'%Y-%m-%d\') as time')
+            ->group('time')
+            ->order('pay_time ASC')->select()->toArray();
+    }
+
+    /**
+     * 获取当前时间到指定时间的支付订单数量 管理员
+     * @param $date
+     * @param null $merId
+     * @return mixed
+     */
+    public function chartTimeNum($date, $merId = null)
+    {
+        return StoreOrder::getDB()->where('paid', 1)->when($date, function ($query) use ($date) {
+            getModelTime($query, $date, 'pay_time');
+        })->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        })->field('count(order_id) as num,FROM_UNIXTIME(unix_timestamp(pay_time), \'%Y-%m-%d\') as time')
+            ->group('time')
+            ->order('pay_time ASC')->select()->toArray();
+    }
+
+    /**
+     * 获取完成以及到期的订单id
+     * @param $end
+     * @return mixed
+     * @author xaboy
+     * @day 2020/9/16
+     */
+    public function getFinishTimeoutIds($end)
+    {
+        return StoreOrderStatus::getDB()->alias('A')->leftJoin('StoreOrder B', 'A.order_id = B.order_id')
+            ->where('A.change_type', 'take')
+            ->where('A.change_time', '<', $end)->where('B.paid', 1)->where('B.status', 2)
+            ->column('A.order_id');
+    }
+
+
+    /**
+     *  参与人数
+     * @param array $data
+     * @param int|null $uid
+     * @return BaseQuery
+     * @author Qinii
+     * @day 2020-11-11
+     */
+    public function getTattendCount(array $data, ?int $uid)
+    {
+        $query = StoreOrder::hasWhere('orderProduct', function ($query) use ($data, $uid) {
+            $query->when(isset($data['activity_id']), function ($query) use ($data) {
+                $query->where('activity_id', $data['activity_id']);
+            })
+                ->when(isset($data['product_sku']), function ($query) use ($data) {
+                    $query->where('product_sku', $data['product_sku']);
+                })
+                ->when(isset($data['product_id']), function ($query) use ($data) {
+                    $query->where('product_id', $data['product_id']);
+                })
+                ->when(isset($data['exsits_id']), function ($query) use ($data) {
+                    switch ($data['product_type']) {
+                        case 3:
+                            $make = app()->make(ProductAssistSetRepository::class);
+                            $id = 'product_assist_id';
+                            break;
+                        case 4:
+                            $make = app()->make(ProductGroupBuyingRepository::class);
+                            $id = 'product_group_id';
+                            break;
+                    }
+                    $where = [$id => $data['exsits_id']];
+                    $activity_id = $make->getSearch($where)->column($make->getPk());
+                    if ($activity_id) {
+                        $id = array_unique($activity_id);
+                        $query->where('activity_id', 'in', $id);
+                    } else {
+                        $query->where('activity_id', '<', 0);
+                    }
+                })
+                ->where('product_type', $data['product_type']);
+            if ($uid) $query->where('uid', $uid);
+        });
+        $query->where('activity_type', $data['product_type']);
+        switch ($data['product_type']) {
+            case 0:
+                $query->where(function ($query) {
+                    $query->where(function ($query) {
+                        $query->where('paid', 1);
+                    })->whereOr(function ($query) {
+                        $query->where('paid', 0)->where('is_del', 0);
+                    });
+                });
+                break;
+            case 1: //秒杀
+                $query->where(function ($query) {
+                    $query->where(function ($query) {
+                        $query->where('paid', 1);
+                    })->whereOr(function ($query) {
+                        $query->where('paid', 0)->where('is_del', 0);
+                    });
+                })->when(isset($data['day']), function ($query) use ($data) {
+                    $query->whereDay('StoreOrder.create_time', $data['day']);
+                });
+                break;
+            case 2: //预售
+
+                /**
+                 * 第一阶段参与人数:所有人
+                 * 第二阶段参与人数:支付了第一阶段
+                 */
+                //第二阶段
+                if ($data['type'] == 1) {
+                    $query->where(function ($query) {
+                        $query->where('paid', 1)->whereOr(function ($query) {
+                            $query->where('paid', 0)->where('is_del', 0);
+                        });
+                    });
+                }
+                if ($data['type'] == 2) $query->where('paid', 1)->where('status', 'in', [0, 1, 2, 3, -1]);
+                break;
+            case 3: //助力
+                $query->where(function ($query) {
+                    $query->where('paid', 1)->whereOr(function ($query) {
+                        $query->where('paid', 0)->where('is_del', 0);
+                    });
+                });
+                break;
+            case 4: //
+                $query->where(function ($query) {
+                    $query->where('paid', 1)->whereOr(function ($query) {
+                        $query->where('paid', 0)->where('is_del', 0);
+                    })
+                        ->where('status', '>', -1);
+                });
+                break;
+        }
+        return $query;
+    }
+
+    /**
+     *  未使用
+     *  成功支付人数
+     * @param int $productType
+     * @param int $activityId
+     * @param int|null $uid
+     * @param int|null $status
+     * @author Qinii
+     * @day 2020-10-30
+     */
+    public function getTattendSuccessCount($data, ?int $uid)
+    {
+        $query = StoreOrder::hasWhere('orderProduct', function ($query) use ($data, $uid) {
+            $query->when(isset($data['activity_id']), function ($query) use ($data) {
+                $query->where('activity_id', $data['activity_id']);
+            })
+                ->when(isset($data['product_sku']), function ($query) use ($data) {
+                    $query->where('product_sku', $data['product_sku']);
+                })
+                ->when(isset($data['product_id']), function ($query) use ($data) {
+                    $query->where('product_id', $data['product_id']);
+                })
+                ->when(isset($data['exsits_id']), function ($query) use ($data) {
+                    switch ($data['product_type']) {
+                        case 3:
+                            $make = app()->make(ProductAssistSetRepository::class);
+                            $id = 'product_assist_id';
+                            break;
+                        case 4:
+                            $make = app()->make(ProductGroupBuyingRepository::class);
+                            $id = 'product_group_id';
+                            break;
+                    }
+                    $where = [$id => $data['exsits_id']];
+                    $activity_id = $make->getSearch($where)->column($make->getPk());
+                    if ($activity_id) {
+                        $id = array_unique($activity_id);
+                        $query->where('activity_id', 'in', $id);
+                    } else {
+                        $query->where('activity_id', '<', 0);
+                    }
+                })
+                ->where('product_type', $data['product_type']);
+            if ($uid) $query->where('uid', $uid);
+        });
+        $query->where('activity_type', $data['product_type'])->where('paid', 1);
+
+        switch ($data['product_type']) {
+            case 1: //秒杀
+                $query->where(function ($query) {
+                    $query->where(function ($query) {
+                        $query->where('paid', 1);
+                    });
+                })->when(isset($data['day']), function ($query) use ($data) {
+                    $query->whereDay('StoreOrder.create_time', $data['day']);
+                });
+                break;
+            case 2: //预售
+                if ($data['type'] == 1) {    //第一阶段
+                    $query->where('status', 'in', [0, 1, 2, 3, 10]);
+                } else {        //第二阶段
+                    $query->where('status', 'in', [0, 1, 2, 3]);
+                }
+                break;
+            case 3: //助力
+                break;
+            case 4:
+                break;
+        }
+        return $query;
+    }
+
+
+    /**
+     *  获取退款单数量
+     * @param $where
+     * @return mixed
+     * @author Qinii
+     * @day 1/4/21
+     */
+    public function getSeckillRefundCount($where, $type = 1)
+    {
+        $query = StoreOrderProduct::getDB()->alias('P')->join('StoreRefundOrder R', 'P.order_id = R.order_id');
+        $query->join('StoreOrder O', 'O.order_id = P.order_id');
+        $query
+            ->when(isset($where['activity_id']), function ($query) use ($where) {
+                $query->where('P.activity_id', $where['activity_id']);
+            })
+            ->when(isset($where['product_id']), function ($query) use ($where) {
+                $query->where('P.product_id', $where['product_id']);
+            })
+            ->when(isset($where['product_sku']), function ($query) use ($where) {
+                $query->where('P.product_sku', $where['product_sku']);
+            })
+            ->when(isset($where['day']), function ($query) use ($where) {
+                $query->whereDay('P.create_time', $where['day']);
+            })
+            ->when($type == 1, function ($query) use ($where) {
+                $query->where('O.verify_time', null)->where('O.delivery_type', null);
+            }, function ($query) {
+                $query->where('R.refund_type', 2);
+            })
+            ->where('P.product_type', 1)->where('R.status', 3);
+        return $query->sum('R.refund_num');
+    }
+
+
+    /**
+     *  用户的某个商品购买数量
+     * @param int $uid
+     * @param int $productId
+     * @return int
+     * @author Qinii
+     * @day 2022/9/26
+     */
+    public function getMaxCountNumber(int $uid, int $productId)
+    {
+        return (int)StoreOrderProduct::hasWhere('orderInfo', function ($query) use ($uid) {
+            $query->where('uid', $uid)->where(function ($query) {
+                $query->where('is_del', 0)->whereOr(function ($query) {
+                    $query->where('is_del', 1)->where('paid', 1);
+                });
+            });
+        })->where('product_id', $productId)->sum('product_num');
+    }
+
+    public function getOrderSn($order_id)
+    {
+        return StoreOrder::getDB()->where($this->getPk(), $order_id)->value('order_sn', '');
+    }
+
+    public function getSubOrderNotSend(int $group_order_id, int $order_id)
+    {
+        return StoreOrder::getDB()->where('group_order_id', $group_order_id)->where('status', 0)->where('order_id', '<>', $order_id)->count();
+    }
+
+
+    /**
+     * 获取商户排行
+     * @param string $date
+     * @param string $type
+     * @param string $sort
+     * @return mixed
+     */
+    public function getMerchantTop(string $date, string $type = 'sales', string $sort = 'desc')
+    {
+
+        $query = StoreOrder::getDB()->with(['merchant' => function ($query) {
+            $query->field('mer_id,mer_name,mer_avatar,care_count');
+        }]);
+        $query = getModelTime($query, $date)->where('paid', 1)->where('mer_id', '>', 0)->field('mer_id,sum(total_num) as sales,sum(pay_price + pay_postage) as price')->group('mer_id');
+        switch ($type) {
+            case 'sales':
+                $query->order('sales ' . $sort);
+                break;
+            case 'price':
+                $query->order('price ' . $sort);
+                break;
+        }
+
+        return $query->limit(0, 10)->select()->toArray();
+    }
+}

+ 296 - 0
app/common/dao/store/order/StoreOrderProductDao.php

@@ -0,0 +1,296 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\order\StoreOrderProduct;
+use think\facade\Db;
+use think\model\Relation;
+use app\common\repositories\store\order\OrderStatus;
+
+/**
+ * Class StoreOrderProductDao
+ * @package app\common\dao\store\order
+ * @author xaboy
+ * @day 2020/6/10
+ */
+class StoreOrderProductDao extends BaseDao
+{
+    const ORDER_VERIFY_STATUS_ = 1;
+    const ORDER_VERIFY_STATUS_SUCCESS = 3;
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/6/10
+     */
+    protected function getModel(): string
+    {
+        return StoreOrderProduct::class;
+    }
+
+    /**
+     * 根据用户ID和订单产品ID获取订单产品信息
+     * 此函数用于查询特定用户订单中的特定产品详情,包括订单的基本信息。
+     * @param int $id 订单产品ID,用于精确查询特定的订单产品。
+     * @param int $uid 用户ID,用于查询特定用户下的订单产品。
+     * @return StoreOrderProduct|null 返回符合查询条件的订单产品对象,如果未找到则返回null。
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function userOrderProduct($id, $uid)
+    {
+        // 使用StoreOrderProduct的数据库查询方法,根据订单产品ID和用户ID进行查询。
+        // 同时,通过with方法加载订单信息,只查询状态为2的订单(表示已完成的订单),并只获取订单ID、订单号和商家ID。
+        return StoreOrderProduct::getDB()->where('uid', $uid)->where('order_product_id', $id)->with(['orderInfo' => function (Relation $query) {
+            $query->field('order_id,order_sn,mer_id')->where('status', 2);
+        }])->find();
+    }
+
+    /**
+     * 计算未回复的售后产品数量
+     *
+     * 本函数用于查询特定订单中,未被回复的售后产品的数量。
+     * 它通过筛选订单产品表中特定条件的记录,来得出未处理的售后产品数量。
+     * 具体的筛选条件包括:订单ID、售后状态不为已退款、以及是否收到回复。
+     *
+     * @param string $orderId 订单ID
+     * @return int 未回复的售后产品数量
+     * @throws \think\db\exception\DbException
+     */
+    public function noReplyProductCount($orderId)
+    {
+        // 使用数据库查询工具,设定查询条件:特定订单ID、售后状态不为已退款、未回复
+        // 然后计算满足条件的记录数量,返回这个数量作为未回复的售后产品数量。
+        return StoreOrderProduct::getDB()->where('order_id', $orderId)->where('is_refund','<>','3')->where('is_reply', 0)
+            ->count();
+    }
+
+    /**
+     * 根据条件查询用户退款产品信息
+     *
+     * 本函数用于查询与用户退款相关的商品信息。它支持根据产品ID、用户ID、订单ID以及退款状态进行过滤。
+     * 这些条件可以通过函数的参数进行定制,允许灵活地查询特定的退款产品数据。
+     *
+     * @param array $ids 产品ID列表,用于查询指定ID的产品信息
+     * @param int $uid 用户ID,用于查询指定用户的相关产品信息
+     * @param string|null $orderId 订单ID,可选,用于查询指定订单中的产品信息
+     * @param int $refund_switch 退款开关,可选,用于筛选退款状态为开的产品信息
+     * @return array 返回符合条件的退款产品信息列表
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function userRefundProducts(array $ids, $uid, $orderId = null,$refund_switch = 1)
+    {
+        // 获取数据库实例
+        return StoreOrderProduct::getDB()
+            // 当提供产品ID列表时,筛选出在列表中的产品
+            ->when($ids,function($query) use($ids){
+                $query->whereIn('order_product_id', $ids);
+            })
+            // 当提供订单ID时,筛选出属于该订单的产品
+            ->when($orderId, function ($query) use($orderId) {
+                 $query->where('order_id', $orderId);
+            })
+            // 当提供用户ID时,筛选出属于该用户的产品
+            ->when($uid, function ($query) use($uid) {
+                 $query->where('uid', $uid);
+            })
+            // 当提供退款开关时,筛选出退款开关为开启的产品
+            ->when($refund_switch, function ($query) use($refund_switch) {
+                $query->where('refund_switch', $refund_switch);
+            })
+            // 筛选出退款数量大于0的产品
+            ->where('refund_num', '>', 0)->select();
+    }
+
+
+    /**
+     * 根据指定条件分组统计商品订单数量及详情
+     * 本函数用于查询指定日期、商家ID下的订单产品分组统计信息,返回每个产品的订单总数和产品详情。
+     *
+     * @param string $date 查询的日期范围,用于筛选指定日期内的订单。
+     * @param int|null $merId 商家ID,用于筛选指定商家的订单。
+     * @param int $limit 返回结果的数量限制,默认为7条。
+     * @return array 返回符合查询条件的商品订单分组统计信息列表。
+     */
+    public function orderProductGroup($date, $merId = null, $limit = 7)
+    {
+        // 从StoreOrderProduct表中获取数据库对象
+        return StoreOrderProduct::getDB()->alias('A')->leftJoin('StoreOrder B', 'A.order_id = B.order_id')
+            // 选择计算总订单数量和产品ID、购物车信息的字段
+            ->field(Db::raw('sum(A.product_num) as total,A.product_id,cart_info'))
+            // 使用withAttr处理cart_info字段,将其解析为数组
+            ->withAttr('cart_info', function ($val) {
+                return json_decode($val, true);
+            })
+            // 根据$date参数条件,添加查询时间范围的条件
+            ->when($date, function ($query, $date) {
+                getModelTime($query, $date, 'B.pay_time');
+            })
+            // 根据$merId参数条件,添加查询特定商家的条件
+            ->when($merId, function ($query, $merId) {
+                $query->where('B.mer_id', $merId);
+            })
+            // 筛选已支付的订单
+            ->where('B.paid', 1)
+            // 按产品ID分组
+            ->group('A.product_id')
+            // 限制返回结果的数量
+            ->limit($limit)
+            // 按总订单数量降序排序
+            ->order('total DESC')
+            // 执行查询并返回结果
+            ->select();
+    }
+
+    /**
+     * 计算指定日期内的商品销售总数
+     *
+     * 本函数通过查询数据库,统计指定日期内所有已支付订单的商品数量总和。
+     * 使用左连接查询订单产品表和订单表,确保即使某些订单没有产品信息也能被包含在查询结果中。
+     * 查询条件包括订单的支付时间在指定日期内且订单状态为已支付。
+     *
+     * @param string $date 指定的日期,用于查询该日期内的订单商品数量。
+     * @return int 返回指定日期内的商品销售总数。
+     */
+    public function dateProductNum($date)
+    {
+        // 使用数据库查询语句,统计指定日期内已支付订单的商品总数
+        return StoreOrderProduct::getDB()->alias('A')->leftJoin('StoreOrder B', 'A.order_id = B.order_id')->when($date, function ($query, $date) {
+            // 当传入日期时,条件查询订单的支付时间在指定日期内
+            getModelTime($query, $date, 'B.pay_time');
+        })->where('B.paid', 1)->sum('A.product_num');
+    }
+
+
+    /**
+     *  用户购买活动商品数量
+     * @param int $activityId
+     * @param int $uid
+     * @param int $orderType
+     * @return int
+     * @author Qinii
+     * @day 2020-10-23
+     */
+    public function getUserPayCount(array $unique,$startWhere,$endWhere)
+    {
+        $query = StoreOrderProduct::hasWhere('orderInfo',function($query){
+            //未支付
+            $query->where('is_del',0)->whereOr(function($query){
+                $query->where('paid',1)->where('is_del',1);
+            });
+        });
+        $count = $query
+            ->where('is_refund', '=', 0)
+            ->whereTime('StoreOrderProduct.create_time', '>=', $startWhere)
+            ->whereTime('StoreOrderProduct.create_time', '<=',$endWhere)
+            ->where('product_type', '=', 1)
+            ->where('product_sku', 'in', $unique)
+            ->sum('product_num');
+        return $count;
+    }
+
+
+    /**
+     * 根据关键词和用户ID获取用户支付商品的查询构建器
+     *
+     * 此方法用于构建一个查询用户支付商品的查询构建器。它允许根据关键词过滤商品,
+     * 专门筛选出产品类型为0的商品,并限定查询的商品属于指定的用户。
+     *
+     * @param string|null $keyword 搜索关键词,用于过滤商品名称包含该关键词的商品
+     * @param int $uid 用户ID,用于限定查询的商品属于该用户
+     * @return \Illuminate\Database\Eloquent\Builder|StoreOrderProduct 查询构建器,用于进一步的查询操作或数据获取
+     */
+    public function getUserPayProduct(?string  $keyword, int $uid)
+    {
+        // 初始化查询构建器,针对StoreOrderProduct表中spu列进行条件查询
+        $query = StoreOrderProduct::hasWhere('spu',function($query) use($keyword){
+            // 当关键词存在时,添加模糊搜索条件
+            $query->when($keyword, function ($query) use($keyword) {
+               $query->whereLike('store_name',"%{$keyword}%");
+            });
+            // 筛选产品类型为0的商品
+            $query->where('product_type',0);
+        });
+        // 限定查询的商品属于指定的用户,并且产品类型为0
+        $query->where('uid', $uid)->where('StoreOrderProduct.product_type',0);
+        // 返回构建好的查询构建器
+        return  $query;
+    }
+
+
+
+    /**
+     *  统计已支付的订单 商品相关信息
+     * @param int $mer_id
+     * @param $type 统计类型 金额/数量/支付时间
+     * @param $date 统计时间段
+     * @param $limit
+     * @return mixed
+     * @author Qinii
+     * @day 2023/11/28
+     */
+    public function getProductRate(int $mer_id, $date = '', $type = 'number', $limit = 10, string $group = 'P.product_id')
+    {
+        $query = StoreOrderProduct::getDB()->alias('P')->leftJoin('StoreOrder O', 'O.order_id = P.order_id')
+            ->with(['product' => function ($query) {
+                $query->field('product_id,store_name,image');
+            }])->where('O.paid', 1);
+        switch($type){
+            case 'number':
+                $field = "P.product_id,O.pay_price as number,sum(P.product_num) as count,P.create_time, P.order_id,O.mer_id";
+                break;
+            case 'count':
+                $field = "P.product_id,sum(P.product_num) as count,O.pay_price as number,P.create_time, P.order_id,O.mer_id";
+                break;
+            case 'paytime':
+                $field = 'P.product_id,O.pay_time paytime,O.pay_price as number,P.create_time, P.order_id,O.mer_id';
+                break;
+            default:
+                $field = 'P.*';
+                break;
+        }
+        $query->when($mer_id, function ($query) use($mer_id) {
+            $query->where('O.mer_id', $mer_id);
+        })->when($date, function($query) use ($date) {
+            getModelTime($query, $date,'P.create_time');
+        });
+
+        return $query->field($field)->when(!empty($group), function ($query) use ($group) {
+            $query->group($group);
+        })->order("$type DESC")->limit($limit)->select();
+    }
+
+    public function getReservationSum($productId, $date, $reservationId)
+    {
+        // 使用数据库查询语句,统计指定日期内已支付订单的商品总数
+        $stock = StoreOrderProduct::getDB()->alias('A')
+            ->leftJoin('StoreOrder B', 'A.order_id = B.order_id')
+            ->where(function($query){
+               $query->where('is_del',0)->whereOr(function($query){
+                    $query->where('is_del',1)->where('paid',1);
+                });
+            })
+            ->where('B.status','<>',OrderStatus::ORDER_STATUS_REFUND)
+            ->where('A.product_id', $productId)
+            ->where('A.reservation_date', $date)
+            ->where('A.reservation_id', $reservationId)
+            ->sum('A.product_num');
+        return $stock;
+    }
+}

+ 114 - 0
app/common/dao/store/order/StoreOrderProfitsharingDao.php

@@ -0,0 +1,114 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\order;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreOrderProfitsharing;
+
+class StoreOrderProfitsharingDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return StoreOrderProfitsharing::class;
+    }
+
+    /**
+     * 生成订单编号
+     *
+     * 本函数用于生成唯一的订单编号。编号由前缀、时间戳和随机数组成,确保了订单编号的唯一性和可追踪性。
+     * - 前缀为"pr",用于标识订单类型或业务领域。
+     * - 时间戳部分基于微秒级时间,提高了编号的唯一性,避免了在高并发场景下的重复问题。
+     * - 随机数部分确保即使在微秒级别的时间内有重复,也可以通过随机数避免编号重复。
+     *
+     * @return string 返回生成的订单编号
+     * @throws \Exception
+     */
+    public function getOrderSn()
+    {
+        // 获取当前时间的微秒和秒部分
+        list($msec, $sec) = explode(' ', microtime());
+
+        // 将微秒和秒合并,并转换为毫秒,去除了小数点,确保数字部分全是整数
+        $msectime = number_format((floatval($msec) + floatval($sec)) * 1000, 0, '', '');
+
+        // 生成订单编号:前缀 + 毫秒时间戳 + 4位随机数
+        // 随机数生成考虑了微秒时间戳的重复性和随机性,避免了直接使用微秒时间戳可能导致的重复问题
+        $orderId = 'pr' . $msectime . random_int(10000, max(intval($msec * 10000) + 10000, 98369));
+
+        return $orderId;
+    }
+
+
+    /**
+     * 根据条件搜索分佣记录
+     *
+     * @param array $where 搜索条件
+     * @return \think\Collection|\think\db\BaseQuery
+     */
+    public function search(array $where)
+    {
+        // 使用分佣记录模型获取数据库实例
+        return StoreOrderProfitsharing::getDB()->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+            // 如果条件中包含商户ID,则添加对应查询条件
+            $query->where('mer_id', $where['mer_id']);
+        })->when(isset($where['order_id']) && $where['order_id'] !== '', function ($query) use ($where) {
+            // 如果条件中包含订单ID,则添加对应查询条件
+            $query->where('order_id', $where['order_id']);
+        })->when(isset($where['type']) && $where['type'] !== '', function ($query) use ($where) {
+            // 如果条件中包含类型,则添加对应查询条件
+            $query->where('type', $where['type']);
+        })->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+            // 如果条件中包含状态,则添加对应查询条件
+            $query->where('status', $where['status']);
+        })->when(isset($where['date']) && $where['date'] !== '', function ($query) use ($where) {
+            // 如果条件中包含日期,则调用getModelTime函数添加查询条件
+            getModelTime($query, $where['date']);
+        })->when(isset($where['profit_date']) && $where['profit_date'] !== '', function ($query) use ($where) {
+            // 如果条件中包含分佣时间,则调用getModelTime函数添加查询条件,并指定字段
+            getModelTime($query, $where['profit_date'], 'profitsharing_time');
+        })->when(isset($where['keyword']) && $where['keyword'] !== '', function ($query) use ($where) {
+            // 如果条件中包含关键字,则添加模糊查询条件
+            $query->whereLike('keyword', "%{$where['keyword']}%");
+        });
+    }
+
+    /**
+     * 获取自动分配的利润分享ID列表
+     *
+     * 本函数用于查询在特定时间点之前已验证但未分配的订单利润分享ID。
+     * 它通过连接订单和利润分享信息表,筛选出符合条件的利润分享记录。
+     * 具体的筛选条件包括订单状态和利润分享状态,以及订单的验证时间。
+     *
+     * @param int $time 用于筛选的特定时间点,以UNIX时间戳形式表示。
+     * @return array 返回符合条件的利润分享ID列表。
+     */
+    public function getAutoProfitsharing($time)
+    {
+        // 使用数据库查询工具并设置别名为A
+        return StoreOrderProfitsharing::getDB()->alias('A')
+            // 加入订单表B,并指定连接条件
+            ->join('StoreOrder B', 'A.order_id = B.order_id', 'left')
+            // 筛选订单状态为大于1或为-1的记录
+            ->where(function ($query) {
+                $query->where('B.status', '>', 1)->whereOr('B.status', -1);
+            })
+            // 筛选利润分享状态为0的记录
+            ->where('A.status', 0)
+            // 筛选已验证且验证时间早于指定时间的订单
+            ->where(function ($query) use ($time) {
+                $query->whereNotNull('B.verify_time')->where('B.verify_time', '<', $time);
+            })
+            // 返回满足条件的利润分享ID列表
+            ->column('A.profitsharing_id');
+    }
+}

+ 151 - 0
app/common/dao/store/order/StoreOrderReceiptDao.php

@@ -0,0 +1,151 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\order;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreOrderReceipt;
+use app\common\model\user\User;
+
+class StoreOrderReceiptDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return StoreOrderReceipt::class;
+    }
+
+    /**
+     * 根据条件搜索订单收据信息
+     *
+     * @param array $where 搜索条件
+     * @return \think\db\Query 查询结果对象
+     */
+    public function search(array $where)
+    {
+        // 当订单类型或关键字存在时,细化查询条件
+        if((isset($where['order_type']) && $where['order_type'] !== '') || (isset($where['keyword']) && $where['keyword'] !== '')){
+            $query = StoreOrderReceipt::hasWhere('storeOrder',function($query)use($where){
+                // 根据订单类型设置查询条件
+                switch ($where['order_type'])
+                {
+                    case 1: // 未支付
+                        $query->where('StoreOrder.paid',0)->where('StoreOrder.is_del',0);
+                        break;
+                    case 2: // 待发货
+                        $query->where('StoreOrder.paid',1)->where('StoreOrder.status',0)->where('StoreOrder.is_del',0);
+                        break;
+                    case 3: // 待收货
+                        $query->where('StoreOrder.status',1)->where('StoreOrder.is_del',0);
+                        break;
+                    case 4: // 待评价
+                        $query->where('StoreOrder.status',2)->where('StoreOrder.is_del',0);
+                        break;
+                    case 5: // 交易完成
+                        $query->where('StoreOrder.status',3)->where('StoreOrder.is_del',0);
+                        break;
+                    case 6: // 已退款
+                        $query->where('StoreOrder.status',-1)->where('StoreOrder.is_del',0);
+                        break;
+                    case 7: // 已删除
+                        $query->where('StoreOrder.is_del',1);
+                        break;
+                    case 8: // 全部
+                        $query->where('StoreOrder.is_del', 0);
+                        break;
+                    default:
+                        $query->where(true);
+                        break;
+                }
+                // 当关键字存在时,搜索订单号、真实姓名或用户电话中包含关键字的记录
+                $query->when(isset($where['keyword']) && $where['keyword'] !== '' ,function($query)use($where){
+                    $query->whereLike("order_sn|real_name|user_phone","%{$where['keyword']}%");
+                });
+            });
+        }else{
+            // 如果没有指定订单类型和关键字,直接查询所有订单收据
+            $query = StoreOrderReceipt::alias('StoreOrderReceipt');
+        }
+
+        // 根据状态筛选
+        $query->when(isset($where['status']) && $where['status'] !== '' ,function($query)use($where){
+            $query->where('StoreOrderReceipt.status',$where['status']);
+        });
+        // 根据日期范围筛选
+        $query->when(isset($where['date']) && $where['date'] !== '' ,function($query)use($where){
+            getModelTime($query,$where['date'],'StoreOrderReceipt.create_time');
+        });
+        // 根据收据号筛选
+        $query->when(isset($where['receipt_sn']) && $where['receipt_sn'] !== '' ,function($query)use($where){
+            $query->where('StoreOrderReceipt.receipt_sn',$where['receipt_sn']);
+        });
+        // 根据用户名筛选
+        $query->when(isset($where['username']) && $where['username'] !== '' ,function($query)use($where){
+            $uid = User::whereLike('nickname|phone',"%{$where['username']}%")->column('uid');
+            $query->where('StoreOrderReceipt.uid','in',$uid);
+        });
+        $query->when(isset($where['phone']) && $where['phone'] !== '' ,function($query)use($where){
+            $uid = User::whereLike('phone',"%{$where['phone']}%")->column('uid');
+            $query->whereIn('StoreOrderReceipt.uid',$uid);
+        });
+        $query->when(isset($where['nickname']) && $where['nickname'] !== '' ,function($query)use($where){
+            $uid = User::whereLike('nickname',"%{$where['nickname']}%")->column('uid');
+            $query->where('StoreOrderReceipt.uid','in',$uid);
+        });
+        // 根据商户ID筛选
+        $query->when(isset($where['mer_id']) && $where['mer_id'] !== '' ,function($query)use($where){
+            $query->where('StoreOrderReceipt.mer_id',$where['mer_id']);
+        });
+        // 根据用户ID筛选
+        $query->when(isset($where['uid']) && $where['uid'] !== '' ,function($query)use($where){
+            $query->where('StoreOrderReceipt.uid',$where['uid']);
+        });
+
+        // 按创建时间降序排序
+        return $query->order('StoreOrderReceipt.create_time DESC');
+    }
+
+    /**
+     * 根据收据编号更新数据
+     *
+     * 本函数旨在通过给定的收据序列号($receipt_sn)更新相关数据。它利用了Model的数据库操作方法,
+     * 通过查询到匹配收据序列号的记录,然后对这些记录进行数据更新操作。
+     *
+     * @param string $receipt_sn 收据序列号,用于定位需要更新的记录
+     * @param array $data 包含需要更新的数据的数组
+     * @return int 返回影响的行数,表示更新操作的结果
+     */
+    public function updateBySn(string $receipt_sn, $data)
+    {
+        // 调用getModel方法获取Model实例,并通过链式调用getDB方法获取数据库操作对象
+        // 使用where方法指定更新条件为receipt_sn字段等于传入的收据序列号
+        // 最后调用update方法执行数据更新操作,并返回影响的行数
+        return $this->getModel()::getDB()->where('receipt_sn', $receipt_sn)->update($data);
+    }
+
+
+    /**
+     * 根据订单ID删除相关记录
+     *
+     * 本函数旨在通过指定的订单ID,从数据库中删除与该订单相关的记录。
+     * 这是一个高级操作,需要谨慎使用,以避免误删数据。
+     *
+     * @param int $id 订单ID,用于定位要删除的记录
+     * @return int 返回删除操作影响的行数,用于确认删除操作的效果
+     */
+    public function deleteByOrderId($id)
+    {
+        // 调用getModel方法获取模型实例,并直接调用其getDB方法获取数据库连接
+        // 然后使用where方法指定删除条件为order_id等于$id,最后执行delete方法进行删除操作
+        return $this->getModel()::getDB()->where('order_id',$id)->delete();
+    }
+
+}

+ 101 - 0
app/common/dao/store/order/StoreOrderStatusDao.php

@@ -0,0 +1,101 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\order\StoreOrderStatus;
+use app\common\repositories\store\order\StoreOrderStatusRepository;
+
+/**
+ * Class StoreOrderStatusDao
+ * @package app\common\dao\store\order
+ * @author xaboy
+ * @day 2020/6/12
+ */
+class StoreOrderStatusDao extends BaseDao
+{
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/6/12
+     */
+    protected function getModel(): string
+    {
+        return StoreOrderStatus::class;
+    }
+
+    /**
+     * 根据条件搜索数据。
+     *
+     * 本函数用于构建查询条件,根据传入的$where数组,动态地添加查询条件到数据库查询中。
+     * 这样做的目的是为了提高代码的灵活性和可维护性,避免硬编码的查询条件,同时允许根据不同的条件进行数据检索。
+     *
+     * @param array $where 包含搜索条件的数组。数组的键是条件的字段名,值是条件的值。
+     *                    如果值为空字符串或者未设置,该条件将被忽略。
+     * @return \Illuminate\Database\Query\Builder|static 返回构建好的查询对象,可以进一步调用其他查询方法,比如获取数据。
+     */
+    public function search($where)
+    {
+        // 获取数据库实例,并通过链式调用进行条件构建
+        $query = ($this->getModel()::getDB())
+            // 当'id'字段在$where数组中存在且不为空时,添加where条件查询订单号
+            ->when(isset($where['id']) && $where['id'] !== '', function ($query) use ($where) {
+                $query->where('order_id', $where['id']);
+            })
+            // 当'type'字段在$where数组中存在且不为空时,添加where条件查询类型
+            ->when(isset($where['type']) && $where['type'] !== '', function ($query) use ($where) {
+                $query->where('type', $where['type']);
+            })
+            // 当'user_type'字段在$where数组中存在且不为空时,添加where条件查询用户类型
+            ->when(isset($where['user_type']) && $where['user_type'] !== '', function ($query) use ($where) {
+                $query->where('user_type', $where['user_type']);
+            })
+            // 当'user_type'字段在$where数组中存在且不为空时,添加where条件查询用户类型
+            ->when(isset($where['change_type']) && $where['change_type'] !== '', function ($query) use ($where) {
+                is_array($where['change_type']) ? $query->whereIn('change_type', $where['change_type']) : $query->where('change_type', $where['change_type']);
+            })
+            // 当'date'字段在$where数组中存在且不为空时,调用getModelTime函数动态添加时间查询条件
+            ->when(isset($where['date']) && $where['date'] !== '', function ($query) use ($where) {
+                getModelTime($query, $where['date'], 'change_time');
+            });
+
+        // 返回构建好的查询对象
+        return $query;
+    }
+
+
+    /**
+     * 获取超时未配送的订单ID列表
+     *
+     * 本函数通过查询订单状态数据库,筛选出在指定时间内,订单状态为配送中、待配送或已配送但未支付的订单ID。
+     * 主要用于统计或处理超时未完成配送的订单。
+     *
+     * @param string $start 查询开始时间,格式为日期字符串
+     * @param string $end 查询结束时间,格式为日期字符串
+     * @return array 返回符合条件的订单ID列表
+     */
+    public function getTimeoutDeliveryOrder($start, $end)
+    {
+        // 使用数据库查询语言构造查询语句
+        return StoreOrderStatus::getDB()->alias('A')->leftJoin('StoreOrder B', 'A.order_id = B.order_id')
+            ->whereIn('A.change_type', [StoreOrderStatusRepository::ORDER_DELIVERY_SELF, StoreOrderStatusRepository::ORDER_DELIVERY_NOTHING, StoreOrderStatusRepository::ORDER_DELIVERY_COURIER])
+            ->where('A.type', 'order')
+            ->whereBetweenTime('A.change_time', $start, $end)
+            ->where('B.paid', 1)->where('B.status', 1)
+            ->column('A.order_id');
+    }
+}

+ 257 - 0
app/common/dao/store/order/StoreRefundOrderDao.php

@@ -0,0 +1,257 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\BaseModel;
+use app\common\model\store\order\StoreOrder;
+use app\common\model\store\order\StoreRefundOrder;
+use app\common\model\user\User;
+use app\common\repositories\system\merchant\MerchantRepository;
+use think\db\BaseQuery;
+use think\db\exception\DbException;
+
+class StoreRefundOrderDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return StoreRefundOrder::class;
+    }
+
+    /**
+     * 根据条件搜索退款订单
+     *
+     * @param array $where 搜索条件
+     * @return \think\db\Query 查询结果对象
+     */
+    public function search(array $where)
+    {
+        // 当is_trader条件存在时,特定条件查询
+        $query = StoreRefundOrder::hasWhere('merchant', function ($query) use ($where) {
+            // 分组权限
+            if (app('request')->hasMacro('regionAuthority') && $region = app('request')->regionAuthority()) {
+                $query->whereIn('mer_id', $region);
+            }
+            $query->when(isset($where['is_trader']) && $where['is_trader'] !== '', function ($query) use ($where) {
+                $query->where('is_trader', $where['is_trader']);
+            });
+        });
+
+        // 添加条件查询:商家ID
+        $query->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+            $query->where('StoreRefundOrder.mer_id', $where['mer_id']);
+        });
+        // 添加条件查询:订单号
+        $query->when(isset($where['order_sn']) && $where['order_sn'] !== '', function ($query) use ($where) {
+            $ids = StoreOrder::where('order_sn', 'like', '%' . $where['order_sn'] . '%')->column('order_id');
+            $query->where('order_id', 'in', $ids);
+        });
+        // 添加条件查询:退款订单号
+        $query->when(isset($where['refund_order_sn']) && $where['refund_order_sn'] !== '', function ($query) use ($where) {
+            $query->where('refund_order_sn', 'like', '%' . $where['refund_order_sn'] . '%');
+        });
+        // 添加条件查询:订单状态
+        $query->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+            $query->where('StoreRefundOrder.status', $where['status']);
+        });
+        // 添加条件查询:用户ID
+        $query->when(isset($where['uid']) && $where['uid'] !== '', function ($query) use ($where) {
+            $query->where('uid', $where['uid']);
+        });
+        // 添加条件查询:退款订单ID
+        $query->when(isset($where['id']) && $where['id'] !== '', function ($query) use ($where) {
+            $query->where('refund_order_id', $where['id']);
+        });
+        // 添加条件查询:删除状态
+        $query->when(isset($where['is_del']) && $where['is_del'] !== '', function ($query) use ($where) {
+            $query->where('StoreRefundOrder.is_del', $where['is_del']);
+        });
+        // 添加条件查询:订单类型
+        $query->when(isset($where['type']) && $where['type'] == 1, function ($query) {
+            $query->whereIn('StoreRefundOrder.status', [0, 1, 2, 4]);
+        });
+        // 添加条件查询:退款类型
+        $query->when(isset($where['type']) && $where['type'] == 2, function ($query) {
+            $query->whereIn('StoreRefundOrder.status', [-1, 3, -10]);
+        });
+        // 添加条件查询:退款方式
+        $query->when(isset($where['refund_type']) && $where['refund_type'] !== '', function ($query) use ($where) {
+            $query->where('refund_type', $where['refund_type']);
+        });
+        // 添加条件查询:日期范围
+        $query->when(isset($where['date']) && $where['date'] !== '', function ($query) use ($where) {
+            getModelTime($query, $where['date'], 'StoreRefundOrder.create_time');
+        });
+        // 添加条件查询:订单ID
+        $query->when(isset($where['order_id']) && $where['order_id'] !== '', function ($query) use ($where) {
+            $query->where('order_id', $where['order_id']);
+        });
+        // 添加条件查询:配送单ID
+        $query->when(isset($where['delivery_id']) && $where['delivery_id'] !== '', function ($query) use ($where) {
+            $query->where('StoreRefundOrder.delivery_id', $where['delivery_id']);
+        });
+        // 添加条件查询:用户类型
+        $query->when(isset($where['user_type']) && $where['user_type'] !== '', function ($query) use ($where) {
+            $query->where('StoreRefundOrder.user_type', $where['user_type']);
+        });
+        // 添加条件查询:用户名
+        $query->when(isset($where['username']) && $where['username'] !== '', function ($query) use ($where) {
+            $uid = User::whereLike('nickname|phone|real_name', "%{$where['username']}%")->column('uid');
+            $query->whereIn('uid', $uid);
+        });
+
+        $query->when(isset($where['phone']) && $where['phone'] !== '', function ($query) use ($where) {
+            $uid = User::whereLike('phone', "%{$where['phone']}%")->column('uid');
+            $query->whereIn('uid', $uid);
+        });
+        $query->when(isset($where['nickname']) && $where['nickname'] !== '', function ($query) use ($where) {
+            $uid = User::whereLike('nickname', "%{$where['nickname']}%")->column('uid');
+            $query->whereIn('uid', $uid);
+        });
+        $query->when(isset($where['real_name']) && $where['real_name'] !== '', function ($query) use ($where) {
+            $uid = User::whereLike('real_name', "%{$where['real_name']}%")->column('uid');
+            $query->whereIn('uid', $uid);
+        });
+        // 按创建时间降序排序
+        return $query->order('StoreRefundOrder.create_time DESC');
+    }
+
+    /**
+     * 根据ID获取单个订单信息
+     *
+     * 本函数通过指定的订单ID,查询并返回相应的订单数据。查询过程中,会同时加载订单相关的多个关联数据,
+     * 包括退款产品、用户信息、订单产品等,以提供更完整的订单详情。
+     *
+     * @param int $id 订单的主键ID
+     * @return \think\Model|null 返回查询到的订单模型实例,如果未找到则返回null
+     */
+    public function getOne($id)
+    {
+        // 使用动态模型方式查询订单,根据ID定位特定订单
+        $res = $this->getModel()::where($this->getPk(), $id)
+            // 加载订单关联的退款产品及其产品信息
+            ->with([
+                'refundProduct.product',
+                // 加载订单的用户信息,但只获取uid、nickname和phone字段
+                'user' => function ($query) {
+                    $query->field('uid, nickname, phone');
+                },
+                // 加载订单关联的产品信息
+                'order.orderProduct', 'platform'
+            ])
+            // 查找并返回订单数据
+            ->find();
+        // 添加附加信息'create_user'到返回结果中
+        if ($res) $res->append(['create_user']);
+        return $res;
+    }
+
+    /**
+     * 检查数据库中是否存在满足条件的字段
+     *
+     * 本函数用于通过指定的条件查询数据库,以确定是否存在满足条件的字段。
+     * 它首先获取模型对应的数据库实例,然后使用where方法应用条件,最后通过count方法统计满足条件的记录数。
+     * 如果记录数大于0,则表示存在满足条件的字段;否则,表示不存在。
+     *
+     * @param array|string $where 查询条件,可以是数组或字符串形式。
+     * @return bool 如果存在满足条件的字段返回true,否则返回false。
+     */
+    public function getFieldExists($where)
+    {
+        // 获取模型对应的数据库实例,并应用查询条件,然后统计满足条件的记录数
+        return (($this->getModel()::getDB())->where($where)->count()) > 0;
+    }
+
+
+    /**
+     * 删除用户的退款订单
+     *
+     * 本函数用于将特定用户的特定退款订单标记为已删除,并更新其状态时间。
+     * 只有处于特定状态(3)的订单才能被删除。
+     *
+     * @param int $uid 用户ID,表示订单所属的用户。
+     * @param int $id 退款订单ID,表示要删除的具体退款订单。
+     * @return bool 返回更新操作的结果,成功为true,失败为false。
+     * @throws DbException
+     */
+    public function userDel($uid, $id)
+    {
+        // 根据用户ID、退款订单ID和状态查询退款订单,并更新删除状态和删除时间
+        return StoreRefundOrder::getDB()->where('uid', $uid)->where('refund_order_id', $id)->where('status', 3)->update(['is_del' => 1, 'status_time' => date('Y-m-d H:i:s')]);
+    }
+
+
+    /**
+     * 获取超时的退款订单ID列表
+     * 此函数用于查询在指定时间之前,满足特定退款状态的订单的退款订单ID。
+     * 具体来说,它查询了两种情况:一是退款类型为1且状态为0的订单;
+     * 二是退款类型为2且状态为2的订单。
+     * @param int $time 用于比较的超时时间点,订单状态时间必须小于等于这个时间才算超时。
+     * @return array 返回一个包含超时退款订单ID的数组。
+     */
+    public function getTimeOutIds($time)
+    {
+        // 从模型中获取数据库实例,并构造查询条件
+        // 首先筛选status_time小于等于指定时间的记录
+        return ($this->getModel()::getDB())->where('status_time', '<=', $time)
+            // 然后通过嵌套的where子句,分别查询两种特定条件的订单
+            ->where(function ($query) {
+                // 第一种条件:退款类型为1,且状态为0
+                $query->where(function ($query) {
+                    $query->where('refund_type', 1)->where('status', 0);
+                })->whereOr(function ($query) {
+                    // 第二种条件:退款类型为2,且状态为2
+                    $query->where('refund_type', 2)->where('status', 2);
+                });
+            })->column('refund_order_id');
+        // 最终返回满足条件的订单的退款订单ID列表
+    }
+
+
+    /**
+     * 用于调和数据更新的函数。
+     * 此函数旨在将指定的调和ID列表中的所有调和ID更新为0。
+     * 这是一个具体的操作数据库的函数,它依赖于getModel方法来获取数据库实例。
+     *
+     * @param array $reconciliation_id 调和ID的数组,这些ID将被更新为0。
+     * @return int 返回影响的行数,表示更新操作的结果。
+     */
+    public function reconciliationUpdate($reconciliation_id)
+    {
+        // 通过模型获取数据库实例,并使用whereIn方法指定更新条件,然后更新reconciliation_id为0
+        return ($this->getModel()::getDB())->whereIn('reconciliation_id', $reconciliation_id)->update(['reconciliation_id' => 0]);
+    }
+
+
+    /**
+     * 根据订单ID数组计算退款总额
+     *
+     * 本函数通过查询数据库,计算指定订单ID列表中状态为3(假设代表已退款)的订单的退款总额。
+     * 使用了whereIn和where方法来筛选订单ID和订单状态,sum方法用于计算退款金额总和。
+     *
+     * @param array $orderIds 订单ID数组,用于查询指定订单的退款金额
+     * @return float 返回计算得到的退款总额
+     */
+    public function refundPirceByOrder(array $orderIds)
+    {
+        // 调用getModel方法获取模型实例,然后通过该实例的getDB方法获取数据库连接
+        // 使用whereIn方法筛选出订单ID在指定数组中的记录,再使用where方法筛选出状态为3的记录
+        // 最后使用sum方法计算这些记录的refund_price列的总和,并返回该值
+        return $this->getModel()::getDB()->whereIn('order_id', $orderIds)->where('status', 3)->sum('refund_price');
+    }
+
+
+}

+ 77 - 0
app/common/dao/store/order/StoreRefundProductDao.php

@@ -0,0 +1,77 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreRefundProduct;
+
+class StoreRefundProductDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return StoreRefundProduct::class;
+    }
+
+    /**
+     * 根据条件搜索订单。
+     *
+     * 本函数用于根据提供的条件数组搜索订单。它支持通过订单ID进行筛选,并且总是按照创建时间对结果进行排序。
+     * 这种方法的设计允许灵活地添加更多的搜索条件,而不需要修改函数的主体结构。
+     *
+     * @param array $where 包含搜索条件的数组。其中可能包含订单ID等用于筛选订单的键值对。
+     * @return \Illuminate\Database\Eloquent\Builder|static 返回一个构建器对象,用于进一步的查询操作或数据检索。
+     */
+    public function search(array $where)
+    {
+        // 从模型中获取数据库实例,并根据条件应用订单ID的筛选。
+        $query = $this->getModel()::getDB()
+            ->when(isset($where['order_id']) && $where['order_id'] !== '', function($query) use ($where) {
+                // 如果提供了订单ID,并且不为空,则在查询中添加订单ID的条件。
+                $query->where('order_id', $where['order_id']);
+            });
+
+        // 按照创建时间对查询结果进行排序。
+        return $query->order('create_time');
+    }
+
+    /**
+     * 根据订单产品ID数组,计算退款详情
+     *
+     * 本函数通过查询订单退款详情和退款订单信息,统计指定订单产品ID数组中每个订单产品的退款总金额、平台退款金额、退款邮费、退款积分等信息。
+     * 主要用于在用户退款时,快速汇总相关退款数据。
+     *
+     * @param array $ids 订单产品ID数组
+     * @return array 返回一个键为订单产品ID,值为包含各种退款详情的数组
+     */
+    public function userRefundPrice(array $ids)
+    {
+        // 构建查询语句,左连接退款订单表,筛选出状态大于-1的退款记录,按订单产品ID分组,计算各项退款金额和积分
+        $lst = $this->getModel()::getDB()->alias('A')->leftJoin('StoreRefundOrder B', 'A.refund_order_id = B.refund_order_id')
+            ->where('B.status', '>', -1)
+            ->whereIn('A.order_product_id', $ids)->group('A.order_product_id')
+            ->field('A.order_product_id, SUM(A.refund_price) as refund_price, SUM(A.platform_refund_price) as platform_refund_price, SUM(A.refund_postage) as refund_postage, SUM(A.refund_integral) as refund_integral')
+            ->select()->toArray();
+
+        // 将查询结果重新组织为以订单产品ID为键的数组,方便后续根据订单产品ID快速查找退款详情
+        $data = [];
+        foreach ($lst as $item) {
+            $data[$item['order_product_id']] = $item;
+        }
+
+        // 返回重新组织后的数据数组
+        return $data;
+    }
+}

+ 44 - 0
app/common/dao/store/order/StoreRefundStatusDao.php

@@ -0,0 +1,44 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\order;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\order\StoreRefundStatus;
+
+class StoreRefundStatusDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return StoreRefundStatus::class;
+    }
+
+    /**
+     * 根据退款订单ID查询退款状态
+     *
+     * 本函数旨在通过退款订单ID从数据库中检索相应的退款状态信息。
+     * 它使用了StoreRefundStatus类的getDB方法来获取数据库对象,并基于此对象构建一个查询,
+     * 该查询专门针对refund_order_id字段与传入ID匹配的记录。
+     *
+     * @param int $id 退款订单的唯一标识符
+     * @return \think\db\Query 查询结果,返回一个查询对象,可用于进一步的查询操作或获取数据
+     */
+    public function search($id)
+    {
+        // 根据传入的ID查询退款状态
+        return $query = StoreRefundStatus::getDB()->where('refund_order_id', $id);
+    }
+
+}

+ 42 - 0
app/common/dao/store/parameter/ParameterDao.php

@@ -0,0 +1,42 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\parameter;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\parameter\Parameter;
+
+class ParameterDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return Parameter::class;
+    }
+
+
+    /**
+     * 清除特定字段中具有指定ID的记录。
+     *
+     * 此方法通过提供的ID和字段名称,从数据库中删除符合条件的记录。
+     * 它首先获取模型对应的数据库实例,然后使用提供的字段和ID构建删除条件,
+     * 最后执行删除操作。
+     *
+     * @param int $id 主键ID,用于指定要删除的记录。
+     * @param string $field 要用于删除条件的字段名称。
+     */
+    public function clear(int $id, string $field)
+    {
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+}

+ 42 - 0
app/common/dao/store/parameter/ParameterProductDao.php

@@ -0,0 +1,42 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\parameter;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\parameter\ParameterProduct;
+
+class ParameterProductDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return ParameterProduct::class;
+    }
+
+
+    /**
+     * 清除特定字段中具有指定ID的记录。
+     *
+     * 此方法通过提供的ID和字段名称,从数据库中删除符合条件的记录。
+     * 它首先获取模型对应的数据库实例,然后使用提供的字段和ID构建删除条件,
+     * 最后执行删除操作。
+     *
+     * @param int $id 主键ID,用于指定要删除的记录。
+     * @param string $field 要用于删除条件的字段名称。
+     */
+    public function clear($id, string $field)
+    {
+        $this->getModel()::getDB()->whereIn($field, $id)->delete();
+    }
+
+}

+ 41 - 0
app/common/dao/store/parameter/ParameterTemplateDao.php

@@ -0,0 +1,41 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\parameter;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\parameter\ParameterTemplate;
+
+class ParameterTemplateDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return ParameterTemplate::class;
+    }
+
+    /**
+     * 清除特定字段中具有指定ID的记录。
+     *
+     * 此方法通过提供的ID和字段名称,从数据库中删除符合条件的记录。
+     * 它首先获取模型对应的数据库实例,然后使用提供的字段和ID构建删除条件,
+     * 最后执行删除操作。
+     *
+     * @param int $id 主键ID,用于指定要删除的记录。
+     * @param string $field 要用于删除条件的字段名称。
+     */
+    public function clear(int $id, string $field)
+    {
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+}

+ 43 - 0
app/common/dao/store/parameter/ParameterValueDao.php

@@ -0,0 +1,43 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\parameter;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\parameter\ParameterValue;
+
+class ParameterValueDao extends BaseDao
+{
+
+    protected function getModel(): string
+    {
+        return ParameterValue::class;
+    }
+
+    /**
+     * 清除特定字段值对应的数据记录
+     *
+     * 本函数用于根据指定的字段值和该值对应的ID,从数据库中删除相应的记录。
+     * 这是个通用函数,可以通过传入不同的字段名和ID值来删除不同表中的数据。
+     *
+     * @param mixed $id 需要删除的数据记录的ID值,可以是数字、字符串等
+     * @param string $field 指定的字段名,用于查询和删除数据
+     */
+    public function clear($id,$field)
+    {
+        // 使用模型获取数据库实例,并构造删除语句,根据字段和ID删除数据
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+
+
+}

+ 124 - 0
app/common/dao/store/product/CdkeyLibraryDao.php

@@ -0,0 +1,124 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\product;
+
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\CdkeyLibrary;
+use think\facade\Db;
+
+/**
+ * Class StoreServiceDao
+ * @package app\common\dao\store\service
+ * @author xaboy
+ * 卡密库
+ */
+class CdkeyLibraryDao extends BaseDao
+{
+
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/5/29
+     */
+    protected function getModel(): string
+    {
+        return CdkeyLibrary::class;
+    }
+
+    /**
+     * 根据条件搜索数据。
+     *
+     * 本函数用于根据提供的条件数组搜索数据库中的记录。它支持多个条件,
+     * 包括mer_id(商户ID)、product_id(产品ID)和product_attr_value_id(产品属性值ID)。
+     * 条件是可选的,只有在数组中存在且不为空时才会被应用。
+     *
+     * @param array $where 包含搜索条件的数组。
+     * @param int $is_del 删除状态标志,默认为0表示未删除。
+     * @return \Illuminate\Database\Query\Builder|static 返回查询构建器实例或静态调用的结果。
+     */
+    public function search(array $where, $is_del = 0)
+    {
+        // 获取数据库实例
+        return $this->getModel()::getDB()->alias('CdkeyLibrary')
+            // 当'mer_id'字段存在且不为空时,添加where条件
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+                $query->where('mer_id', $where['mer_id']);
+            })
+            // 当'product_id'字段存在且不为空时,添加where条件
+            ->when(isset($where['product_id']) && $where['product_id'] !== '', function ($query) use ($where) {
+                $query->where('product_id', $where['product_id']);
+            })
+            // 当'product_attr_value_id'字段存在且不为空时,添加where条件
+            ->when(isset($where['product_attr_value_id']) && $where['product_attr_value_id'] !== '', function ($query) use ($where) {
+                $query->where('product_attr_value_id', $where['product_attr_value_id']);
+            })
+            ->when(isset($where['keyword']) && $where['keyword'] !== '', function ($query) use ($where) {
+                $query->where('name', 'like', '%' . $where['keyword'] . '%');
+            })
+            ->when(isset($where['name']) && $where['name'] !== '', function ($query) use ($where) {
+                $query->where('name', 'like', '%' . $where['name'] . '%');
+            })
+            ->when(isset($where['productName']) && $where['productName'] !== '', function ($query) use ($where) {
+                $query->hasWhere('product', function ($query) use ($where) {
+                    $query->where('store_name', 'like', '%' . $where['productName'] . '%');
+                });
+            })
+            // 指定删除状态条件
+            ->where('CdkeyLibrary.is_del', $is_del);
+    }
+
+
+    /**
+     *  统计总 cdkey 数量
+     * @param $id
+     * @param $num
+     * @return void
+     * @author Qinii
+     */
+    public function incTotalNum($id, $num = 1)
+    {
+        $this->getModel()::getDB()->where('id', $id)->update([
+            'total_num' => Db::raw('total_num+' . $num),
+        ]);
+    }
+
+    /**
+     * 减少指定ID的商品或资源的总数
+     *
+     * 此方法用于更新数据库中特定ID记录的总数量字段,将其减少指定的数量。
+     * 主要应用于商品库存管理、资源下载计数等领域,通过减少总量来反映消耗或使用情况。
+     *
+     * @param int $id 需要更新数量的记录的ID
+     * @param int $num 需要减少的数量,默认为1,表示减少一个单位
+     */
+    public function decTotalNum($id, $num = 1)
+    {
+        // 使用模型的数据库操作方法,根据ID更新记录的total_num字段
+        // 通过Db::raw直接执行SQL表达式,实现数量的减少
+        $this->getModel()::getDb()->where('id', $id)->update([
+            'total_num' => Db::raw('total_num-' . $num),
+        ]);
+    }
+
+
+    public function incUsedNum($id, $num = 1)
+    {
+        $this->getModel()::getDb()->where('id', $id)->update([
+            'used_num' => Db::raw('used_num+' . $num),
+        ]);
+    }
+
+
+}

+ 181 - 0
app/common/dao/store/product/ProductAssistDao.php

@@ -0,0 +1,181 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductAssist;
+use app\common\repositories\store\product\SpuRepository;
+use app\common\repositories\system\merchant\MerchantRepository;
+
+class ProductAssistDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return ProductAssist::class;
+    }
+
+    /**
+     * 商品查询条件
+     * @param array $where
+     * @return \think\db\BaseQuery
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/16
+     */
+    public function search(array $where)
+    {
+        $query = ProductAssist::hasWhere('product',function($query)use($where){
+            $query->when(isset($where['product_show']) && $where['product_show'] !== '',function($query)use($where){
+                    $query->where('is_del',0)->where('mer_status',1)->where('product_type',3);
+                })
+                ->where('status',1);
+        });
+        $query->join('StoreSpu U', 'Product.product_id = U.product_id')->where('U.product_type', 3);
+        $query->when(isset($where['product_assist_id']) && $where['product_assist_id'] !== '',function($query)use($where){
+                $query->where('product_assist_id',$where['product_assist_id']);
+            })
+            ->when(isset($where['keyword']) && $where['keyword'] !== '',function($query)use($where){
+                $query->whereLike('ProductAssist.store_name|ProductAssist.product_id',"%{$where['keyword']}%");
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '',function($query)use($where){
+                $query->where('ProductAssist.mer_id',$where['mer_id']);
+            })
+            ->when(isset($where['type']) && $where['type'] !== '',function($query)use($where){
+                switch ($where['type']){
+                    case 0: //未开始
+                        $query->whereTime('start_time','>',time());
+                        break;
+                    case 1: //进行中
+                        $query->whereTime('start_time','<=',time())->whereTime('end_time','>',time())
+                        ->where('ProductAssist.product_status',1)->where('ProductAssist.status',1)->where('ProductAssist.is_show',1);
+                        break;
+                    case 2: //已结束
+                        $query->where(function($query){
+                            $query->where('action_status',-1)->whereOr('end_time','<= TIME',time());
+                        });
+                        break;
+                }
+            })
+            ->when(isset($where['status']) && $where['status'] !== '',function($query)use($where){
+                $query->where('ProductAssist.status',$where['status']);
+            })
+            ->when(isset($where['is_show']) && $where['is_show'] !== '',function($query)use($where){
+                $query->where('ProductAssist.is_show',$where['is_show']);
+            })
+            ->when(isset($where['is_del']) && $where['is_del'] !== '',function($query)use($where){
+                $query->where('ProductAssist.is_del',$where['is_del']);
+            })
+            ->when(isset($where['mer_name']) && $where['mer_name'] !== '',function($query)use($where){
+                $make = app()->make(MerchantRepository::class);
+                $mer_id = $make->search(['keyword' => $where['mer_name']])->column('mer_id');
+                $query->whereIn('ProductAssist.mer_id',$mer_id);
+            })
+            ->when(isset($where['product_status']) && $where['product_status'] !== '',function($query)use($where){
+                if($where['product_status'] == -1){
+                    $query->where('ProductAssist.product_status','in',[-1,-2]);
+                }else{
+                    $query->where('ProductAssist.product_status',$where['product_status']);
+                }
+            })
+            ->when(isset($where['is_trader']) && $where['is_trader'] !== '',function($query)use($where){
+                $make = app()->make(MerchantRepository::class);
+                $mer_id = $make->search(['is_trader' => $where['is_trader']])->column('mer_id');
+                $query->whereIn('ProductAssist.mer_id',$mer_id);
+            })
+            ->when(isset($where['us_status']) && $where['us_status'] !== '',function($query)use($where){
+                if($where['us_status'] == 0) {
+                    $query->where('ProductAssist.is_show',0)->where('ProductAssist.status',1)->where('ProductAssist.product_status',1);
+                }
+                if($where['us_status'] == 1) {
+                    $query->where('ProductAssist.is_show',1)->where('ProductAssist.status',1)->where('ProductAssist.product_status',1);
+                }
+                if($where['us_status'] == -1) {
+                    $query->where(function($query){
+                        $query->where('ProductAssist.status',0)->whereOr('ProductAssist.product_status','<>',1);
+                    });
+                }
+            })
+            ->when(isset($where['mer_labels']) && $where['mer_labels'] !== '', function ($query) use ($where) {
+                $query->whereLike('U.mer_labels', "%,{$where['mer_labels']},%");
+            })
+            ->when(isset($where['sys_labels']) && $where['sys_labels'] !== '', function ($query) use ($where) {
+                $query->whereLike('U.sys_labels', "%,{$where['sys_labels']},%");
+            })
+            ->when(isset($where['star']),function($query)use($where){
+                $query->when($where['star'] !== '', function ($query) use ($where) {
+                    $query->where('U.star', $where['star']);
+                });
+                $query->order('U.star DESC,U.rank DESC,ProductAssist.create_time DESC');
+            });
+        $query->where('ProductAssist.is_del',0);
+        return $query;
+    }
+
+    /**
+     *  移动端展示 条件
+     * @return array
+     * @author Qinii
+     * @day 2020-10-19
+     */
+    public function assistShow()
+    {
+        return [
+            'product_show' => 1,
+            'product_status' => 1,
+            'status' => 1,
+            'is_show' => 1,
+            'type' => 1,
+            'is_del' => 0
+        ];
+    }
+
+    /**
+     * 检查并更新过期活动的状态
+     * 该方法用于定期检查所有活动的结束时间,如果活动已经结束,则将其状态设置为失效(-1)。
+     * 同时,它也会更新关联的SPU(Special Product Unit)的状态,将这些SPU的活动状态设置为失效。
+     * 这样做的目的是为了维护数据库中活动和SPU状态的准确性,确保只有当前有效的活动和SPU被展示或参与。
+     */
+    public function valActiveStatus()
+    {
+        // 根据当前时间,查询所有已经结束的活动,这些活动的状态为1(进行中)
+        $query = $this->getModel()::getDB()->whereTime('end_time','<=',time())->where('action_status',1);
+        // 获取这些活动的ID
+        $id = $query->column($this->getPk());
+
+        // 如果查询到了已结束的活动ID
+        if($id) {
+            // 更新这些活动的状态为-1(失效)
+            $this->getModel()::getDB()->where($this->getPk(),'in',$id)->update(['action_status' => -1]);
+            // 准备更新SPU状态的条件,特定类型的SPU且关联的活动ID在已结束的活动中
+            $where = [
+                'product_type' => 3,
+                'activity_ids' => $id
+            ];
+            // 更新所有符合条件的SPU的状态为0(失效)
+            app()->make(SpuRepository::class)->getSearch($where)->update(['status' => 0]);
+        }
+    }
+
+
+    /**
+     *  软删除商户的所有商品
+     * @param $merId
+     * @author Qinii
+     * @day 5/15/21
+     */
+    public function clearProduct($merId)
+    {
+        $this->getModel()::getDb()->where('mer_id', $merId)->update(['is_del' => 1]);
+    }
+}
+

+ 106 - 0
app/common/dao/store/product/ProductAssistSetDao.php

@@ -0,0 +1,106 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductAssistSet;
+use app\common\model\system\merchant\Merchant;
+use app\common\repositories\system\merchant\MerchantRepository;
+use think\Exception;
+
+class ProductAssistSetDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return ProductAssistSet::class;
+    }
+
+
+    /**
+     * 增加数值统计
+     * 该方法用于根据类型增加指定ID的分享数或浏览数。
+     * @param int $type 类型标识,1代表增加分享数,2代表增加浏览数。
+     * @param int $id 要操作的数据ID。
+     * @param int $inc 增加的数值,默认为1。
+     */
+    public function incNum(int $type,int $id,int $inc = 1)
+    {
+        try{
+            // 根据ID获取模型实例,并准备更新操作
+            $query = $this->getModel()::where($this->getPk(),$id);
+
+            // 根据$type的值,执行不同的增量更新操作
+            if($type == 1) {
+                // 增加分享数
+                $query->inc('share_num',$inc)->update();
+            }
+            if($type == 2) {
+                // 增加浏览数
+                $query->inc('view_num',$inc)->update();
+            }
+        }catch (Exception $exception){
+            // 捕获并处理异常,此处为空实现,可根据需要添加日志记录等操作
+        }
+    }
+
+
+    /**
+     * 获取用户数量及最近活跃用户的列表
+     *
+     * 本函数用于查询数据库中用户总数,并获取最近活跃的10位用户的信息。
+     * 活跃用户的信息中包括用户ID和头像URL。
+     *
+     * @return array 返回包含用户总数和最近活跃用户列表的数组。
+     */
+    public function userCount()
+    {
+        // 查询数据库中的用户总数
+        $count = $this->getModel()::getDB()->count("*");
+
+        // 查询最近活跃的10位用户的信息,按创建时间降序排列,并包含用户ID和头像信息
+        $res = $this->getModel()::getDB()
+                    ->order('create_time DESC')
+                    ->with(['user' => function($query){
+                        // 仅获取用户ID和头像URL
+                        $query->field('uid,avatar avatar_img');
+                    }])
+                    ->limit(10)
+                    ->group('uid')
+                    ->select()
+                    ->toArray();
+
+        // 筛选带有有效头像URL的用户信息
+        $list = [];
+        foreach ($res as $item){
+            if(isset($item['user']['avatar_img']) && $item['user']['avatar_img']){
+                $list[] = $item['user'];
+            }
+        }
+
+        // 返回用户总数和活跃用户列表
+        return compact('count','list');
+    }
+
+
+    /**
+     *  更新状态
+     * @param int $id
+     * @author Qinii
+     * @day 2020-11-25
+     */
+    public function changStatus(int $id)
+    {
+        $this->getModel()::getDB()->where($this->getPk(),$id)->update(['status' => 20]);
+    }
+}
+

+ 84 - 0
app/common/dao/store/product/ProductAssistSkuDao.php

@@ -0,0 +1,84 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductAssistSku;
+use think\facade\Db;
+
+class ProductAssistSkuDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return ProductAssistSku::class;
+    }
+
+    /**
+     * 清除关联数据
+     *
+     * 本函数用于根据指定的ID删除数据库中与产品辅助表相关的记录。
+     * 它不返回任何值,而是通过调用数据库操作来直接影响数据库。
+     *
+     * @param int $id 需要删除的数据的ID。这个ID用于在数据库查询中定位特定的记录。
+     */
+    public function clear($id)
+    {
+        // 通过模型获取数据库实例,并使用where子句指定product_assist_id为$id,然后执行删除操作。
+        $this->getModel()::getDB()->where('product_assist_id',$id)->delete();
+    }
+
+
+    /**
+     * 减少商品辅助项的库存
+     *
+     * 本函数用于更新数据库中特定商品辅助项的库存数量。
+     * 它通过传入的商品辅助项ID、唯一标识和减少的数量来定位特定的库存记录,
+     * 并将库存数量减少指定的数值。
+     *
+     * @param int $product_assist_id 商品辅助项ID,用于定位特定的商品辅助项。
+     * @param string $unique 唯一标识,与商品辅助项ID配合使用,确保更新操作的准确性。
+     * @param int $desc 库存减少的数量,一个正整数,表示库存将减少的量。
+     * @return bool 更新操作的结果,成功返回true,失败返回false。
+     */
+    public function descStock(int $product_assist_id, string $unique, int $desc)
+    {
+        // 使用模型的数据库操作方法,通过指定的条件更新库存字段
+        // 使用Db::raw处理库存的数学运算,确保库存是减去指定的数量
+        return $this->getModel()::getDB()->where('product_assist_id', $product_assist_id)->where('unique', $unique)->update([
+            'stock' => Db::raw('stock-' . $desc)
+        ]);
+    }
+
+
+    /**
+     * 增加辅助商品库存
+     *
+     * 本函数用于更新指定辅助商品的库存数量。通过传入辅助商品ID和唯一标识符,
+     * 以及要增加的库存数量,来实现库存的动态调整。此功能特别适用于需要对商品
+     * 库存进行精确控制的场景,例如库存管理系统的后台操作。
+     *
+     * @param int $product_assist_id 辅助商品的唯一标识ID,用于定位特定的商品。
+     * @param string $unique 商品的唯一标识字符串,用于进一步确保操作的准确性。
+     * @param int $desc 要增加的库存数量,以整数形式表示。
+     * @return bool 更新操作的结果,成功返回true,失败返回false。
+     */
+    public function incStock(int $product_assist_id, string $unique, int $desc)
+    {
+        // 使用模型获取数据库实例,并通过where子句指定更新条件,然后更新库存字段
+        return $this->getModel()::getDB()->where('product_assist_id', $product_assist_id)->where('unique', $unique)->update([
+            'stock' => Db::raw('stock+' . $desc) // 直接对库存字段进行数学运算,以增加库存
+        ]);
+    }
+
+}
+

+ 48 - 0
app/common/dao/store/product/ProductAssistUserDao.php

@@ -0,0 +1,48 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductAssistUser;
+
+class ProductAssistUserDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return ProductAssistUser::class;
+    }
+
+
+    /**
+     * 获取用户数量及列表
+     *
+     * 本函数用于查询数据库中的用户总数,并返回最近创建的用户列表。
+     * 列表默认包含最近创建的3个用户,但可以通过传入参数$limit来调整返回的用户数量。
+     *
+     * @param int $limit 控制返回的用户数量,默认为3。
+     * @return array 包含两个元素的数组,'count'表示用户总数,'list'表示用户列表。
+     */
+    public function userCount(int $limit = 3)
+    {
+        // 查询数据库中的用户总数
+        $count = $this->getModel()::getDB()->count("*");
+
+        // 查询最近创建的用户列表,限制返回的数量为$limit,并按创建时间降序排序
+        $list = $this->getModel()::getDB()->limit($limit)->order('create_time DESC')->select();
+
+        // 返回包含用户总数和用户列表的数组
+        return compact('count','list');
+    }
+
+}
+

+ 78 - 0
app/common/dao/store/product/ProductAttrDao.php

@@ -0,0 +1,78 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductAttr as model;
+
+class ProductAttrDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     * 根据条件搜索数据库。
+     *
+     * 本函数旨在根据传入的条件参数,构建并返回一个数据库查询查询对象。
+     * 它允许条件参数中的`product_id`来限定查询结果,只有当`product_id`被明确设置且非空时,才会添加相应的查询条件。
+     *
+     * @param array $where 查询条件数组,可能包含多个条件,本函数处理其中的`product_id`条件。
+     * @return \yii\db\Query 查询对象,可用于进一步的查询操作或直接执行查询。
+     */
+    public function search($where)
+    {
+        // 获取模型对应的数据库查询对象
+        $query = $this->getModel()::getDb();
+
+        // 当`product_id`存在于条件数组中且非空时,添加相应的查询条件
+        $query->when(isset($where['product_id']) && $where['product_id'] != '', function($query) use($where) {
+            $query->where('product_id',$where['product_id']);
+        });
+
+        // 返回构建好的查询对象
+        return $query;
+    }
+
+    /**
+     * 清除指定产品的属性
+     *
+     * 本函数用于删除数据库中与指定产品ID相关联的所有属性记录。
+     * 通过调用关联模型的where方法来筛选出特定产品ID的属性记录,然后调用delete方法进行删除。
+     *
+     * @param int $productId 产品的ID,用于指定要清除属性的产品。
+     * @return int 返回受影响的行数,即被删除的属性记录数。
+     */
+    public function clearAttr(int $productId)
+    {
+        // 使用动态模型删除与指定产品ID相关的所有属性记录
+        return ($this->getModel())::where('product_id',$productId)->delete();
+    }
+
+    /**
+     * 插入数据到数据库。
+     *
+     * 本函数用于将给定的数据数组批量插入到数据库。它首先通过getModel方法获取模型对象,
+     * 然后调用该对象的getDB方法来获取数据库连接对象,最后通过调用insertAll方法来执行数据插入操作。
+     *
+     * @param array $data 包含多条待插入数据的数组,每条数据是一个子数组。
+     * @return mixed 返回数据库操作的结果。具体类型取决于数据库库的实现。
+     */
+    public function insert(array $data)
+    {
+        // 通过模型获取数据库对象,并执行批量插入操作
+        return ($this->getModel()::getDB())->insertAll($data);
+    }
+
+}

+ 466 - 0
app/common/dao/store/product/ProductAttrValueDao.php

@@ -0,0 +1,466 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductAttrValue as model;
+use app\common\repositories\store\product\CdkeyLibraryRepository;
+use app\common\repositories\store\product\ProductCdkeyRepository;
+use app\common\repositories\store\product\ProductRepository;
+use think\db\exception\DbException;
+use think\facade\Db;
+
+/**
+ * Class ProductAttrValueDao
+ * @package app\common\dao\store\product
+ * @author xaboy
+ * @day 2020/6/9
+ */
+class ProductAttrValueDao extends BaseDao
+{
+    /**
+     * @return string
+     * @author xaboy
+     * @day 2020/6/9
+     */
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     *  商品规格添加操作
+     * @param $data
+     * @param $isType
+     * @return void
+     * @author Qinii
+     */
+    public function add($data,$isType)
+    {
+        switch ($isType) {
+            case 2: //网盘信息
+                $cdkey = [];
+                foreach ($data as $datum) {
+                    $sku_cdkey = $datum['cdkey'][0];
+                    unset($datum['cdkey']);
+                    $sku = $this->create($datum);
+                    $sku_cdkey['value_id'] = $sku['value_id'];
+                    $cdkey[] = $sku_cdkey;
+                }
+                if ($cdkey) {
+                    app()->make(ProductCdkeyRepository::class)->insertAll($cdkey);
+                }
+                break;
+            case 3: // 一次性卡密关联
+                $cdkeyLibraryRepository = app()->make(CdkeyLibraryRepository::class);
+                foreach ($data as $datum) {
+                    $sku = $this->create($datum);
+                    if ($datum['library_id']) {
+                        $cdkeyLibraryRepository->update($datum['library_id'], [
+                            'product_id' => $datum['product_id'],
+                            'product_attr_value_id' => $sku['value_id']
+                        ]);
+                    }
+                }
+                break;
+            case 4: // 预约商品
+                $sku = $this->create($data);
+                $this->createReservation($sku, $data['reservation']);
+                break;
+            default:
+                $this->insertAll($data);
+                break;
+        }
+    }
+
+    /**
+     *  清楚所有规格
+     * @Author:Qinii
+     * @Date: 2020/5/9
+     * @param int $productId
+     * @return mixed
+     */
+    public function clearAttr(int $productId)
+    {
+        ($this->getModel())::where('product_id', $productId)->delete();
+    }
+
+    /**
+     *  根据条件获取某字段的值合集
+     * @Author:Qinii
+     * @Date: 2020/5/9
+     * @param int $merId
+     * @param $field
+     * @param $value
+     * @param null $except
+     * @return mixed
+     */
+    public function getFieldColumnt($key, $value, $field, $except = null)
+    {
+        return ($this->getModel()::getDB())->when($except, function ($query, $except) use ($field) {
+            $query->where($field, '<>', $except);
+        })->where($key, $value)->column($field);
+    }
+
+    /**
+     * 计算指定字段的总和,排除特定值。
+     *
+     * 此函数用于根据给定的条件计算数据表中某个字段的总和,
+     * 支持排除特定的值以获得更精确的计算结果。
+     *
+     * @param string $key 查询条件的关键字。
+     * @param mixed $value 查询条件的关键字对应的值。
+     * @param string $field 需要计算总和的字段名。
+     * @param mixed $except 排除的特定值,可选参数。
+     * @return int 返回计算得到的字段总和。
+     */
+    public function getFieldSum($key, $value, $field, $except = null)
+    {
+        // 使用模型的数据库实例,并根据$except参数应用排除条件
+        return ($this->getModel()::getDB())->when($except, function ($query, $except) use ($field) {
+            // 如果$except有值,则添加不等于($field, '<>', $except)的条件
+            $query->where($field, '<>', $except);
+        })->where($key, $value)->sum($field);
+    }
+
+    /**
+     * 插入数据到数据库。
+     *
+     * 本函数用于将给定的数据数组批量插入到数据库。它首先通过getModel方法获取模型对象,
+     * 然后调用该对象的getDB方法来获取数据库连接对象,最后通过调用insertAll方法来执行数据插入操作。
+     *
+     * @param array $data 包含多条待插入数据的数组,每条数据是一个子数组。
+     * @return mixed 返回数据库操作的结果。具体类型取决于数据库库的实现。
+     */
+    public function insert(array $data)
+    {
+        // 通过模型获取数据库对象,并执行批量插入操作
+        return ($this->getModel()::getDB())->insertAll($data);
+    }
+
+    /**
+     * 检查指定字段的值在数据库中是否存在。
+     *
+     * 此函数用于确定给定字段的特定值是否在数据库中出现。它支持条件筛选,可以通过`merId`来限定查询范围,也可以通过`except`参数排除特定值。
+     * 主要用于在进行数据操作前验证数据的唯一性或存在性,以避免重复数据或错误的数据操作。
+     *
+     * @param int|null $merId 商户ID,用于限定查询的范围。如果为null,则不进行商户ID的筛选。
+     * @param string $field 要检查的字段名。
+     * @param mixed $value 要检查的字段值。
+     * @param mixed|null $except 排除的值,如果不为null,则查询时会排除掉这个值。
+     * @return bool 如果存在返回true,否则返回false。
+     */
+    public function merFieldExists(?int $merId, $field, $value, $except = null)
+    {
+        // 获取数据库实例,并根据条件构建查询。
+        return ($this->getModel())::getDB()->when($except, function ($query, $except) use ($field) {
+                // 如果有排除值,则添加不等于条件。
+                $query->where($field, '<>', $except);
+            })->when($merId, function ($query, $merId) {
+                // 如果有商户ID,则添加等于条件。
+                $query->where('mer_id', $merId);
+            })->where($field, $value)->count() > 0;
+    }
+
+    /**
+     * 根据产品ID获取SKU
+     *
+     * 本函数旨在通过产品ID检索与之相关联的SKU。SKU (Stock Keeping Unit) 是一种产品库存管理单位,通常用于唯一标识一个产品变体。
+     * 使用本函数需要提供一个产品ID,然后它将返回一个查询构建器实例,该实例已配置为查询与指定产品ID相关联的SKU。
+     *
+     * @param int $id 产品的唯一标识ID。这个ID用于在数据库查询中定位特定的产品。
+     * @return \Illuminate\Database\Eloquent\Builder|static 返回一个Eloquent查询构建器实例,该实例已准备好根据产品ID查询SKU。
+     */
+    public function getSku($id)
+    {
+        // 通过调用$this->getModel()获取模型实例,并立即使用where子句过滤查询结果,只包括product_id为$id的记录。
+        return ($this->getModel())::where('product_id', $id);
+    }
+
+    /**
+     * 检查指定字段的值是否存在,可选地排除特定值或限制商户ID。
+     *
+     * 此方法用于查询数据库中是否存在指定字段的值,可以根据需要排除特定值或限制查询的商户。
+     * 主要用于数据验证或数据存在性检查场景。
+     *
+     * @param int|null $merId 商户ID,用于限制查询的范围。
+     * @param string $field 要检查的字段名。
+     * @param string $value 要检查的字段值。
+     * @param string|null $except 要排除的特定值,如果不为空,则查询时不包括此值。
+     * @return bool 查询结果,如果存在则返回true,否则返回false。
+     */
+    public function getFieldExists(?int $merId, $field, $value, $except = null)
+    {
+        // 获取模型对应的数据库实例
+        return ($this->getModel())::getDB()->when($except, function ($query, $except) use ($field) {
+            // 如果有需要排除的值,则添加不等于条件
+            $query->where($field, '<>', $except);
+        })->when($merId, function ($query, $merId) {
+            // 如果提供了商户ID,则添加条件限制查询结果只包含指定商户
+            $query->where('mer_id', $merId);
+        })->where($field, $value);
+        // 添加字段等于条件,完成查询条件构建
+    }
+
+    /**
+     * 减少商品库存并增加销售数量
+     *
+     * 该方法用于更新数据库中指定商品的库存和销售数量。它通过传入的产品ID和唯一标识符
+     * 来定位特定的记录,并对库存进行减少,同时对销售数量进行增加。这种方法适用于
+     * 实时更新库存系统,例如在处理订单时减少可用库存并记录已销售数量。
+     *
+     * @param int $productId 商品ID,用于在数据库中定位商品记录。
+     * @param string $unique 商品的唯一标识符,用于确保操作的准确性。
+     * @param int $desc 库存减少的数量,同时也是销售数量增加的数量。
+     * @return mixed 返回数据库更新操作的结果,可能是布尔值或影响的行数。
+     * @throws DbException
+     */
+    public function descStock(int $productId, string $unique, int $desc)
+    {
+        // 使用模型获取数据库实例,并通过WHERE子句定位到特定的商品记录。
+        // 然后更新该记录的'stock'和'sales'字段,分别进行库存减少和销售数量增加的操作。
+        return model::getDB()->where('product_id', $productId)->where('unique', $unique)->update([
+            'stock' => Db::raw('stock-' . $desc), // 直接使用数据库原生语法减少库存
+            'sales' => Db::raw('sales+' . $desc)  // 直接使用数据库原生语法增加销售数量
+        ]);
+    }
+
+    /**
+     * 更新SKU库存和销量
+     *
+     * 该方法用于减少指定SKU的库存并增加其销售量。通过传入产品ID、SKU和销售数量,
+     * 直接对数据库进行更新操作,避免了不必要的数据检索和处理步骤,提高了数据库操作的效率。
+     *
+     * @param int $productId 产品ID,用于定位特定产品的SKU信息。
+     * @param string $sku 特定产品的SKU,用于唯一标识产品的一个变种。
+     * @param int $desc 销售描述数量,表示此次销售的数量,将从库存中扣除,并加到销售量中。
+     * @return mixed 返回数据库更新操作的结果,可能是布尔值或影响行数。
+     * @throws DbException
+     */
+    public function descSkuStock(int $productId, string $sku, int $desc)
+    {
+        // 使用模型的数据库访问方法,直接构造并执行更新语句
+        // 通过where子句定位到特定的产品ID和SKU,然后更新库存和销量字段
+        return model::getDB()->where('product_id', $productId)->where('sku', $sku)->update([
+            'stock' => Db::raw('stock-' . $desc), // 直接从库存字段减去销售数量
+            'sales' => Db::raw('sales+' . $desc)  // 直接将销售数量加到销量字段
+        ]);
+    }
+
+    /**
+     * 增加商品销售量
+     *
+     * 该方法用于更新数据库中指定产品的销售量。它通过传入的产品ID和SKU来定位特定的产品行,
+     * 然后将销售量字段的值增加指定的数量。
+     *
+     * @param int $productId 产品ID,用于定位特定产品
+     * @param string $sku 产品的SKU(库存单位),进一步唯一标识产品
+     * @param int $desc 销售量增加的数量,表示要将当前销售量增加的值
+     *
+     * @return int 返回更新操作的影响行数,用于确认更新是否成功
+     * @throws DbException
+     */
+    public function incSales(int $productId, string $sku, int $desc)
+    {
+        // 使用Db::raw()来构建SQL的增量更新语句,确保销售量字段正确增加
+        return model::getDB()->where('product_id', $productId)->where('sku', $sku)->update([
+            'sales' => Db::raw('sales+' . $desc)
+        ]);
+    }
+
+    /**
+     * 增加商品库存并减少对应销量
+     *
+     * 本函数用于在数据库中增加指定商品的库存量,并同时减少相同数量的已销售量。
+     * 这样做的目的是为了准确跟踪商品的库存和销售情况,确保数据的准确性。
+     *
+     * @param int $productId 商品ID,用于定位特定商品
+     * @param string $unique 商品的唯一标识,用于进一步确认特定商品
+     * @param int $inc 需要增加的库存数量,同时也是需要减少的销售数量
+     */
+    public function incStock(int $productId, string $unique, int $inc)
+    {
+        // 增加商品库存
+        model::getDB()->where('product_id', $productId)->where('unique', $unique)->inc('stock', $inc)->update();
+        // 减少商品销量,但只针对销量大于等于增加的库存数量的部分进行减少
+        model::getDB()->where('product_id', $productId)->where('unique', $unique)->where('sales', '>=', $inc)->dec('sales', $inc)->update();
+    }
+
+    /**
+     * 增加SKU库存并调整销售数量
+     *
+     * 该方法用于在数据库中增加指定产品的指定SKU的库存数量,并且如果该SKU的销售数量大于增加的库存数量,
+     * 则相应地减少销售数量。这种方法适用于处理库存和销售数据的同步更新,确保数据的一致性。
+     *
+     * @param int $productId 产品ID,用于定位特定产品的SKU
+     * @param string $sku 指定产品的SKU,用于唯一标识一个产品的SKU
+     * @param int $inc 需要增加的库存数量,这个数量将被加到SKU的库存上
+     */
+    public function incSkuStock(int $productId, string $sku, int $inc)
+    {
+        // 增加SKU的库存数量
+        model::getDB()->where('product_id', $productId)->where('sku', $sku)->inc('stock', $inc)->update();
+
+        // 如果SKU的销售数量大于增加的库存数量,则减少销售数量
+        // 这里假设销售数量不应减少到小于增加的库存数量,确保数据的合理性
+        model::getDB()->where('product_id', $productId)->where('sku', $sku)->where('sales', '>', $inc)->dec('sales', $inc)->update();
+    }
+
+    /**
+     * 检查产品属性是否存在
+     *
+     * 本函数用于查询数据库中是否存在指定产品ID和唯一标识符对应的属性记录。
+     * 主要用于验证某个产品的特定属性是否已经被定义。
+     *
+     * @param int $productId 产品ID,用于查询产品属性时的条件过滤。
+     * @param string $unique 唯一标识符,用于查询产品属性时的条件过滤。
+     * @return bool 如果找到匹配的属性记录,则返回true,否则返回false。
+     * @throws DbException
+     */
+    public function attrExists(int $productId, string $unique): bool
+    {
+        // 通过模型获取数据库实例,并构造查询条件,检查是否存在指定产品ID和唯一标识符的属性记录。
+        return model::getDB()->where('product_id', $productId)->where('unique', $unique)->count() > 0;
+    }
+
+    /**
+     * 检查SKU是否存在于数据库中。
+     *
+     * 本函数用于确定给定的产品ID和SKU组合是否在数据库中存在记录。
+     * 通过查询产品ID和SKU匹配的记录数量,如果数量大于0,则表示SKU存在。
+     *
+     * @param int $productId 产品ID,用于查询特定产品的SKU记录。
+     * @param string $sku 商品SKU,用于唯一标识一个商品。
+     * @return bool 如果SKU存在则返回true,否则返回false。
+     * @throws DbException
+     */
+    public function skuExists(int $productId, string $sku): bool
+    {
+        // 使用模型获取数据库实例,并构造查询条件,检查是否存在指定产品ID和SKU的记录。
+        return model::getDB()->where('product_id', $productId)->where('sku', $sku)->count() > 0;
+    }
+
+    /**
+     *  商品佣金是否大于设置佣金比例
+     * @param $productId
+     * @return bool
+     * @author Qinii
+     * @day 2020-06-25
+     */
+    public function checkExtensionById($productId)
+    {
+        $extension_one_rate = systemConfig('extension_one_rate');
+        $extension_two_rate = systemConfig('extension_two_rate');
+
+        $count = ($this->getModel()::getDb())->where(function($query)use($productId,$extension_one_rate){
+            $query->where('product_id',$productId)->whereRaw('price * '.$extension_one_rate.' > extension_one');
+        })->whereOr(function($query)use($productId,$extension_two_rate){
+            $query->where('product_id',$productId)->whereRaw('price * '.$extension_two_rate.' > extension_two');
+        })->count();
+        return $count ? false : true;
+    }
+
+    /**
+     * 根据条件搜索记录。
+     *
+     * 该方法用于构建查询条件,根据传入的数组$where动态添加查询条件到查询语句中。
+     * 支持根据产品ID、SKU和唯一标识符进行搜索。只有当相应字段的值被设置且不为空时,才会添加对应的查询条件。
+     *
+     * @param array $where 搜索条件数组,可能包含产品ID、SKU和唯一标识符。
+     * @return \yii\db\Query 查询对象,带有应用了搜索条件的查询语句。
+     */
+    public function search(array $where)
+    {
+        // 获取数据库查询对象,并根据$where数组中的条件动态构建查询语句
+        $query = ($this->getModel()::getDb())
+            // 当产品ID存在且不为空时,添加到查询条件中
+            ->when(isset($where['product_id']) && $where['product_id'] !== '',function($query)use($where){
+                $query->where('product_id',$where['product_id']);
+            })
+            // 当SKU存在且不为空时,添加到查询条件中
+            ->when(isset($where['sku']) && $where['sku'] !== '',function($query)use($where){
+                $query->where('sku',$where['sku']);
+            })
+            // 当唯一标识符存在且不为空时,添加到查询条件中
+            ->when(isset($where['unique']) && $where['unique'] !== '',function($query)use($where){
+                $query->where('unique',$where['unique']);
+            });
+
+        return $query;
+    }
+
+
+    /**
+     * 批量更新指定产品ID的数据
+     *
+     * 本函数通过传入的产品ID数组和数据数组,更新数据库中对应产品ID的所有记录。
+     * 它使用了whereIn查询语句来筛选需要更新的记录,然后应用update方法进行更新。
+     * 这种方法适用于需要一次性更新多个记录的场景,可以减少数据库操作的次数,提高效率。
+     *
+     * @param array $ids 产品ID数组,指定需要更新的产品范围
+     * @param array $data 数据数组,包含需要更新的字段及其值
+     */
+    public function updates(array $ids, array $data)
+    {
+        // 获取模型实例并调用其数据库连接方法,然后使用whereIn和update进行批量更新
+        $this->getModel()::getDb()->whereIn('product_id',$ids)->update($data);
+    }
+
+    /**
+     * 批量更新产品的扩展字段
+     *
+     * 此方法用于根据提供的产品ID数组和更新数据数组,批量更新指定产品ID的扩展字段。
+     * 首先,它将所有指定产品的扩展类型设置为1,然后根据产品ID分批获取产品列表,
+     * 并对每个产品计算并更新其两个扩展字段的值。
+     *
+     * @param array $ids 产品ID数组,指定需要更新扩展字段的产品
+     * @param array $data 更新数据数组,包含扩展字段'extension_one'和'extension_two'的倍数值
+     */
+    public function updatesExtension(array $ids, array $data)
+    {
+        // 设置指定产品ID的扩展类型为1
+        app()->make(ProductRepository::class)->updates($ids,['extension_type' => 1]);
+
+        // 构建查询,准备分批更新产品扩展字段
+        $query = $this->getModel()::getDb()->where('product_id','in',$ids);
+
+        // 分批处理产品列表,每次处理100条,用于更新扩展字段
+        $query->chunk(100, function($list) use($data){
+            foreach ($list as $item) {
+                // 计算扩展字段的新值,并更新到数据库
+                $arr['extension_one'] = bcmul($item->price,$data['extension_one'],2);
+                $arr['extension_two'] = bcmul($item->price,$data['extension_two'],2);
+                $this->getModel()::getDb()->where('unique',$item->unique)->update($arr);
+            }
+        },'product_id');
+    }
+
+    public function createReservation($value, array $reservationData)
+    {
+        foreach ($reservationData as &$item) {
+            $item['attr_value_id'] = $value['value_id'];
+            $item['product_id'] = $value['product_id'];
+            $item['unique'] = $value['unique'];
+        }
+        ($this->getModel()::find($value['value_id']))->reservation()->saveAll($reservationData);
+    }
+
+    public function deleteReservation(int $productId)
+    {
+        $res = $this->getModel()::where('product_id', $productId)->with('reservation')->select();
+        foreach ($res as $item) {
+            $item->reservation()->delete();
+        }
+    }
+}

+ 26 - 0
app/common/dao/store/product/ProductAttrValueReservationDao.php

@@ -0,0 +1,26 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductAttrValueReservation as model;
+
+class ProductAttrValueReservationDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+}

+ 70 - 0
app/common/dao/store/product/ProductCateDao.php

@@ -0,0 +1,70 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductCate as model;
+
+class ProductCateDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     * 清除指定产品的属性
+     *
+     * 本函数用于删除数据库中与指定产品ID相关联的所有属性记录。
+     * 通过调用关联模型的where方法来筛选出特定产品ID的属性记录,然后调用delete方法进行删除。
+     *
+     * @param int $productId 产品的ID,用于指定要清除属性的产品。
+     * @return int 返回受影响的行数,即被删除的属性记录数。
+     */
+    public function clearAttr(int $productId)
+    {
+        // 使用动态模型删除与指定产品ID相关的所有属性记录
+        return ($this->getModel())::where('product_id',$productId)->delete();
+    }
+
+    /**
+     * 插入数据到数据库。
+     *
+     * 本函数用于将给定的数据数组批量插入到数据库。它首先通过getModel方法获取模型对象,
+     * 然后调用该对象的getDB方法来获取数据库连接对象,最后通过调用insertAll方法来执行数据插入操作。
+     *
+     * @param array $data 包含多条待插入数据的数组,每条数据是一个子数组。
+     * @return mixed 返回数据库操作的结果。具体类型取决于数据库库的实现。
+     */
+    public function insert(array $data)
+    {
+        // 通过模型获取数据库对象,并执行批量插入操作
+        return ($this->getModel()::getDB())->insertAll($data);
+    }
+
+    /**
+     * 清除特定字段中具有指定ID的记录。
+     *
+     * 此方法通过提供的ID和字段名称,从数据库中删除符合条件的记录。
+     * 它首先获取模型对应的数据库实例,然后使用提供的字段和ID构建删除条件,
+     * 最后执行删除操作。
+     *
+     * @param int $id 主键ID,用于指定要删除的记录。
+     * @param string $field 要用于删除条件的字段名称。
+     */
+    public function clear(int $id, string $field)
+    {
+        $this->getModel()::getDB()->where($field, $id)->delete();
+    }
+
+}

+ 94 - 0
app/common/dao/store/product/ProductCdkeyDao.php

@@ -0,0 +1,94 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductCdkey as model;
+
+class ProductCdkeyDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     * 根据条件搜索数据。
+     *
+     * 该方法通过动态构建查询条件来搜索数据库。它支持多个条件参数,
+     * 并且只有在参数存在时才会添加相应的查询条件。这提高了查询的灵活性,
+     * 允许根据不同的需求构建不同的查询。
+     *
+     * @param array $where 查询条件,包含多个可能的字段及其值。
+     * @return \yii\db\Query 查询对象,可用于进一步的查询操作或获取结果。
+     */
+    public function search($where)
+    {
+        // 获取数据库连接对象
+        $query = $this->getModel()::getDb();
+
+        // 动态添加查询条件基于$where数组中的键值对
+        $query
+            ->when(isset($where['cdkey_id']) && $where['cdkey_id'] !== '', function ($query) use ($where) {
+                // 如果'cdkey_id'存在,添加到查询条件中
+                $query->where('cdkey_id', $where['cdkey_id']);
+            })
+            ->when(isset($where['cdkey_ids']) && $where['cdkey_ids'] !== '', function ($query) use ($where) {
+                // 如果'cdkey_id'存在,添加到查询条件中
+                $query->whereIn('cdkey_id', $where['cdkey_ids']);
+            })
+            ->when(isset($where['library_id']) && $where['library_id'] !== '', function ($query) use ($where) {
+                // 如果'library_id'存在,添加到查询条件中
+                $query->where('library_id', $where['library_id']);
+            })
+            ->when(isset($where['product_id']) && $where['product_id'] !== '', function ($query) use ($where) {
+                // 如果'product_id'存在,添加到查询条件中
+                $query->where('product_id', $where['product_id']);
+            })
+            ->when(isset($where['status']) && $where['status'] !== '', function ($query) use ($where) {
+                // 如果'status'存在,添加到查询条件中
+                $query->where('status', $where['status']);
+            })
+            ->when(isset($where['is_type']) && $where['is_type'] !== '', function ($query) use ($where) {
+                // 如果'is_type'存在,添加到查询条件中
+                $query->where('is_type', $where['is_type']);
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+                // 如果'mer_id'存在,添加到查询条件中
+                $query->where('mer_id', $where['mer_id']);
+            })
+            ->when(isset($where['keys']) && $where['keys'] !== '', function ($query) use ($where) {
+                // 如果'keys'存在,使用其中的值作为查询条件
+                $query->whereIn('key', $where['keys']);
+            });
+
+        // 返回构建好的查询对象
+        return $query;
+    }
+
+
+    /**
+     * 清除指定产品的属性
+     *
+     * 本函数用于删除数据库中与指定产品ID相关联的所有属性记录。
+     * 通过调用关联模型的where方法来筛选出特定产品ID的属性记录,然后调用delete方法进行删除。
+     *
+     * @param int $productId 产品的ID,用于指定要清除属性的产品。
+     * @return int 返回受影响的行数,即被删除的属性记录数。
+     */
+    public function clearAttr(int $productId)
+    {
+        // 使用动态模型删除与指定产品ID相关的所有属性记录
+        return ($this->getModel())::where('product_id',$productId)->where('is_type',0)->delete();
+    }
+}

+ 48 - 0
app/common/dao/store/product/ProductContentDao.php

@@ -0,0 +1,48 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductContent as model;
+
+class ProductContentDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     * 清除指定产品的属性
+     *
+     * 此方法用于根据产品ID和可选的类型参数,从数据库中删除相应的属性记录。
+     * 如果提供了类型参数,则只会删除与该类型匹配的属性记录。
+     *
+     * @param int $productId 产品的ID,用于确定要清除属性的产品。
+     * @param int|null $type 属性的类型ID,可选参数。如果指定了类型,则只会删除指定类型的属性。
+     * @return int 返回影响的行数,即被删除的属性记录数。
+     */
+    public function clearAttr(int $productId, ?int $type)
+    {
+        // 初始化查询,根据产品ID查找属性记录
+        $query = ($this->getModel())::where('product_id', $productId);
+
+        // 如果提供了类型参数,则进一步限制查询条件为指定的类型
+        if (!is_null($type)) $query->where('type', $type);
+
+        // 执行删除操作并返回删除的记录数
+        return $query->delete();
+    }
+
+
+}

+ 71 - 0
app/common/dao/store/product/ProductCopyDao.php

@@ -0,0 +1,71 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductCopy as model;
+
+class ProductCopyDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     * 根据条件搜索数据。
+     *
+     * 本函数用于根据提供的条件数组搜索相关数据。它支持两个主要的搜索条件:'mer_id' 和 'type'。
+     * 'mer_id' 用于指定商家ID,'type' 用于指定数据类型。其中,'type' 条件支持 'copy' 类型的数据,
+     * 这种情况下,搜索将包括 'taobao'、'jd' 和 'copy' 类型的数据。
+     *
+     * @param array $where 包含搜索条件的数组。可能包含 'mer_id' 和 'type' 键。
+     * @return \Illuminate\Database\Eloquent\Builder|static 返回数据库查询构建器实例,用于进一步的查询操作或数据获取。
+     */
+    public function search(array $where)
+    {
+        // 获取模型对应的数据库实例。
+        return $this->getModel()::getDB()
+            // 当 'mer_id' 条件存在且不为空时,添加 'mer_id' 的查询条件。
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+                $query->where('mer_id', $where['mer_id']);
+            })
+            // 当 'type' 条件存在且不为空时,根据 'type' 的值添加相应的查询条件。
+            ->when(isset($where['type']) && $where['type'] !== '', function ($query) use ($where) {
+                if ($where['type'] == 'copy') {
+                    // 如果 'type' 为 'copy',则查询 'type' 为 'taobao'、'jd' 或 'copy' 的数据。
+                    $query->where('type', 'in', ['taobao', 'jd', 'copy']);
+                } else {
+                    // 否则,直接查询指定 'type' 的数据。
+                    $query->where('type', $where['type']);
+                }
+            })
+            // 按 'create_time' 降序排序。
+            ->order('create_time DESC');
+    }
+
+    /**
+     * 获取产品复制信息
+     *
+     * 本方法通过查询数据库,获取store_product_copy_id在398到467之间的产品复制信息。
+     * 主要用于特定条件下的产品数据检索,以便于进一步的操作或展示。
+     *
+     * @return array 返回符合条件的产品复制信息数组。
+     */
+    public function get2()
+    {
+        // 根据条件查询产品复制信息,限定store_product_copy_id在398到467之间,并只返回info字段
+        return $data =  model::where('store_product_copy_id','>',398)
+            ->where('store_product_copy_id','<',467)->field('info')->select();
+    }
+}

+ 1105 - 0
app/common/dao/store/product/ProductDao.php

@@ -0,0 +1,1105 @@
+<?php
+
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+
+
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\Product as model;
+use app\common\repositories\store\product\SpuRepository;
+use think\db\BaseQuery;
+use think\db\exception\DbException;
+use think\Exception;
+use think\facade\Db;
+class ProductDao extends BaseDao
+{
+    protected function getModel(): string
+    {
+        return model::class;
+    }
+
+    /**
+     * 创建或更新模型的属性。
+     *
+     * 本函数通过给定的模型ID,和一组属性数据,来创建或更新该模型的属性信息。
+     * 它首先检索指定ID的模型实例,即使该模型已被删除(使用withTrashed()确保)。
+     * 然后,它使用给定的数据批保存更新模型的属性。
+     *
+     * @param int $id 模型的唯一标识ID。
+     * @param array $data 包含属性数据的数组,每个属性作为一个子数组。
+     */
+    public function createAttr(int $id, array $data)
+    {
+        ($this->getModel()::withTrashed()->find($id))->attr()->saveAll($data);
+    }
+
+    /**
+     * 创建属性值
+     *
+     * 本函数用于根据提供的ID和数据数组创建属性值。它首先检索具有给定ID的模型实体,
+     * 然后通过关联方法保存传入的数据数组。这个函数特别处理了软删除的情况,
+     * 可以恢复和保存被软删除的模型的属性值。
+     *
+     * @param int $id 属性值关联模型的ID。这个ID用于定位特定的模型实例。
+     * @param array $data 包含要保存的属性值数据的数组。每个元素都应该符合属性值的保存要求。
+     */
+    public function createAttrValue(int $id, array $data)
+    {
+        // 使用withTrashed()来包含软删除的记录, find($id)找回指定ID的记录,然后通过attrValue()方法关联属性值,最后使用saveAll()保存数据数组
+        ($this->getModel()::withTrashed()->find($id))->attrValue()->saveAll($data);
+    }
+
+    /**
+     * 创建或更新内容信息。
+     *
+     * 本函数通过给定的ID检索特定的模型实体,即使该实体已被删除(使用withTrashed()确保能检索到软删除的记录)。
+     * 然后,它使用关联的方法(content())来保存或更新给定的数据数组到这个实体的内容字段。
+     * 这种方法的设计允许在不直接触碰实体本身的情况下,灵活地处理与实体相关的内容数据。
+     *
+     * @param int $id 模型实体的唯一标识ID,用于检索特定的实体。
+     * @param array $data 包含要保存或更新的内容数据的数组。
+     */
+    public function createContent(int $id, array $data)
+    {
+        ($this->getModel()::withTrashed()->find($id))->content()->save($data);
+    }
+
+    /**
+     * 创建或更新预约商品信息
+     *
+     * @param integer $id
+     * @param array $data
+     * @return void
+     */
+    public function createReservation(int $id, array $data)
+    {
+        ($this->getModel()::withTrashed()->find($id))->reservation()->save($data);
+    }
+
+    /**
+     * 检查指定字段的值在数据库中是否存在。
+     *
+     * 此函数用于确定给定字段的特定值是否在数据库中出现。它支持排除特定值的检查,
+     * 以及针对特定商户ID的检查。这在处理数据唯一性或验证数据是否存在时非常有用。
+     *
+     * @param int|null $merId 商户ID,用于限定查询范围。如果为null,则不进行商户ID的筛选。
+     * @param string $field 要检查的字段名。
+     * @param string $value 要检查的字段值。
+     * @param string|null $except 排除的特定值,如果不为null,则查询时会排除这个值。
+     * @return bool 如果存在返回true,否则返回false。
+     * @throws DbException
+     */
+    public function merFieldExists(?int $merId, $field, $value, $except = null)
+    {
+        // 使用withTrashed()确保查询时包括已删除的数据
+        return model::withTrashed()->when($except, function ($query, $except) use ($field) {
+                // 如果有需要排除的值,则添加不等于条件
+                $query->where($field, '<>', $except);
+            })->when($merId, function ($query, $merId) {
+                // 如果提供了商户ID,则添加条件筛选特定商户的数据
+                $query->where('mer_id', $merId);
+            })->where($field, $value)->count() > 0;
+    }
+
+    /**
+     * 检查指定字段的值在数据库中是否存在。
+     *
+     * 此函数用于通过字段值查询数据库记录,判断是否存在满足条件的记录。
+     * 它支持排除特定的值和特定的商户ID,同时确保查询的记录状态为有效。
+     *
+     * @param int $merId 商户ID,可选参数,用于限定查询的商户范围。
+     * @param string $field 要查询的字段名。
+     * @param mixed $value 字段的值,用于查询条件。
+     * @param mixed $except 排除的值,可选参数,用于不在指定值范围内的查询。
+     * @return bool 如果存在满足条件的记录,则返回true;否则返回false。
+     */
+    public function apiFieldExists(int $merId, $field, $value, $except = null)
+    {
+        // 获取数据库实例
+        $db = ($this->getModel())::getDB();
+
+        // 如果有指定的排除值,添加不等于排除值的查询条件
+        $db->when($except, function ($query, $except) use ($field) {
+            $query->where($field, '<>', $except);
+        });
+
+        // 如果指定了商户ID,添加商户ID的查询条件
+        $db->when($merId, function ($query, $merId) {
+            $query->where('mer_id', $merId);
+        });
+
+        // 确保查询的状态为有效
+        $db->where(['status' => 1]);
+
+        // 添加字段值的查询条件
+        $db->where($field, $value);
+
+        // 判断满足所有条件的记录数是否大于0,存在则返回true,否则返回false
+        return $db->count() > 0;
+    }
+
+    /**
+     * 检查是否存在已被删除的商品
+     *
+     * 本函数用于确定给定商家ID和产品ID对应的商品是否曾经被删除过。
+     * 这是通过查询数据库中被软删除(即使用了`onlyTrashed`方法)的商品记录来实现的。
+     * 如果找到了匹配的被删除的商品记录,则说明该商品曾经被删除过,返回true;
+     * 如果没有找到匹配的记录,则说明该商品从未被删除过,返回false。
+     *
+     * @param int $merId 商家ID,用于限定查询的商家范围
+     * @param int $productId 产品ID,用于查询特定的产品
+     * @return bool 如果存在被删除的商品记录则返回true,否则返回false
+     */
+    public function getDeleteExists(int $merId, int $productId)
+    {
+        // 使用onlyTrashed方法查询被删除的商品记录,限定查询条件为商家ID和产品ID
+        // 然后通过count方法统计匹配记录的数量,判断是否存在被删除的商品
+        return ($this->getModel())::onlyTrashed()->where('mer_id', $merId)->where($this->getPk(), $productId)->count() > 0;
+    }
+
+    /**
+     * 根据条件搜索产品信息。
+     *
+     * 该方法用于构建并执行产品搜索查询。它根据传入的条件数组来筛选产品,
+     * 并支持多种条件组合以满足不同的搜索需求。搜索条件包括但不限于产品属性、
+     * 商家标识、状态、标签等。此外,方法还处理了不同条件下的查询逻辑,如软删除、
+     * 商家关联、产品类型等。
+     *
+     * @param string $merId 商家ID,用于限制搜索范围。
+     * @param array $where 搜索条件数组,包含各种过滤条件。
+     * @return \think\db\Query 查询对象,可用于进一步的查询操作或获取结果。
+     */
+    public function search($merId, array $where, $search = '')
+    {
+        // 初始化用于构建查询条件的数组
+        $keyArray = $whereArr = [];
+        // 定义需要排除的搜索字段
+        $out = ['soft', 'us_status', 'mer_labels', 'sys_labels', 'order', 'hot_type', 'is_action', 'seckill_active_id','spu_ids'];
+
+        // 遍历搜索条件,构建查询字段和值的映射
+        foreach ($where as $key => $item) {
+            if ($item !== '' && !in_array($key, $out)) {
+                $keyArray[] = $key;
+                $whereArr[$key] = $item;
+            }
+        }
+        // 根据是否有软删除条件,构建查询对象
+        $query = isset($where['soft']) ? model::onlyTrashed()->alias('Product')->where('delete', 0) : model::alias('Product');
+
+        if($search) {
+            $query->hasWhere('attrValue');
+            $query->where(function ($query) use($search) {
+                $query->hasWhere('attrValue', ['bar_code_number' => $search])->whereOr("Product.store_name|Product.product_id", "like", "%{$search}%");
+            });
+        }
+        // 分组权限
+        if (app('request')->hasMacro('regionAuthority') && $region = app('request')->regionAuthority()) {
+            $query->whereIn('Product.mer_id', $region);
+        }
+        // 处理商家类型条件
+        if (isset($where['is_trader']) && $where['is_trader'] !== '') {
+            $query->hasWhere('merchant', function ($query) use ($where) {
+                $query->where('is_trader', $where['is_trader']);
+            });
+        }
+
+        // 处理搜索关键字和关联查询
+        $query->withSearch($keyArray, $whereArr)->Join('StoreSpu U', 'Product.product_id = U.product_id')->where('U.product_type', $where['product_type'] ?? 0);
+
+        // 处理类别ID条件
+        $query->when((isset($where['spu_ids']) && !empty($where['spu_ids'])), function ($query) use ($where) {
+            $query->whereIn('spu_id',$where['spu_ids']);
+        });
+
+        // 处理类别ID条件
+        $query->when((isset($where['cate_id']) && !empty($where['cate_id'])), function ($query) use ($where) {
+            $query->where(['cate_id' => $where['cate_id']]);
+        });
+
+        // 处理秒杀活动ID条件
+        $query->when((isset($where['seckill_active_id']) && is_array($where['seckill_active_id']) && !empty($where['seckill_active_id'])), function ($query) use ($where) {
+            $query->whereIn('Product.active_id', $where['seckill_active_id']);
+        });
+        $query->when((isset($where['seckill_active_id']) && !is_array($where['seckill_active_id']) && $where['seckill_active_id']), function ($query) use ($where) {
+            $query->where('Product.active_id', $where['seckill_active_id']);
+        });
+
+        // 处理商家ID条件
+        $query->when($merId, function ($query) use ($merId) {
+            if (is_array($merId)) {
+                $query->whereIn('Product.mer_id', $merId);
+            } else if (!is_array($merId)) {
+                $query->where('Product.mer_id', $merId);
+            }
+        })
+            // 处理热门类型条件
+            ->when(isset($where['hot_type']) && $where['hot_type'] !== '', function ($query) use ($where) {
+                if ($where['hot_type'] == 'new')
+                    $query->where('is_new', 1);
+                else if ($where['hot_type'] == 'hot')
+                    $query->where('is_hot', 1);
+                else if ($where['hot_type'] == 'best')
+                    $query->where('is_best', 1);
+                else if ($where['hot_type'] == 'good')
+                    $query->where('is_benefit', 1);
+            })
+            // 处理使用状态条件
+            ->when(isset($where['us_status']) && $where['us_status'] !== '', function ($query) use ($where) {
+                if ($where['us_status'] == 0) {
+                    $query->where('Product.is_show', 0)->where('Product.is_used', 1)->where('Product.status', 1);
+                }
+                if ($where['us_status'] == 1) {
+                    $query->where('Product.is_show', 1)->where('Product.is_used', 1)->where('Product.status', 1);
+                }
+                if ($where['us_status'] == -1) {
+                    $query->where(function ($query) {
+                        $query->where('Product.is_used', 0)->whereOr('Product.status', '<>', 1);
+                    });
+                }
+            })
+            // 处理标签条件
+            ->when(isset($where['mer_labels']) && $where['mer_labels'] !== '', function ($query) use ($where) {
+                $query->whereLike('U.mer_labels', "%,{$where['mer_labels']},%");
+            })
+            // 处理活动商品条件
+            ->when(isset($where['is_action']) && $where['is_action'] !== '', function ($query) use ($where) {
+                $query->where('type', '<>', 2);
+            })
+            // 处理系统标签条件
+            ->when(isset($where['sys_labels']) && $where['sys_labels'] !== '', function ($query) use ($where) {
+                $query->whereLike('U.sys_labels', "%,{$where['sys_labels']},%");
+            })
+            // 处理VIP价格类型条件
+            ->when(isset($where['svip_price_type']) && $where['svip_price_type'] !== '', function ($query) use ($where) {
+                $query->where('Product.svip_price_type', $where['svip_price_type']);
+            })
+            // 处理产品类型条件
+            ->when(isset($where['product_type']) && $where['product_type'] == 1, function ($query) use ($where) {
+                $query->where('active_id', '>', 0);
+            })
+            // 处理排序条件
+            ->when(isset($where['order']), function ($query) use ($where, $merId) {
+                if (in_array($where['order'], ['is_new', 'price_asc', 'price_desc', 'rate', 'sales'])) {
+                    if ($where['order'] == 'price_asc') {
+                        $where['order'] = 'price ASC';
+                    } else if ($where['order'] == 'price_desc') {
+                        $where['order'] = 'price DESC';
+                    } else {
+                        $where['order'] = $where['order'] . ' DESC';
+                    }
+                    $query->order($where['order'] . ',rank DESC ,create_time DESC ');
+                } else if ($where['order'] !== '') {
+                    $query->order('U.' . $where['order'] . ' DESC,U.create_time DESC');
+                } else {
+                    $query->order('U.create_time DESC');
+                }
+            })
+            // 处理星级条件
+            ->when(isset($where['star']), function ($query) use ($where) {
+                $query->when($where['star'] !== '', function ($query) use ($where) {
+                    $query->where('U.star', $where['star']);
+                });
+                $query->order('U.star DESC,U.rank DESC,Product.create_time DESC');
+            })
+            // 处理排序条件
+            ->when(isset($where['sort']), function ($query) use ($where) {
+                $query->when($where['sort'] !== '', function ($query) use ($where) {
+                    $query->where('U.sort', $where['sort']);
+                });
+                $query->order('Product.sort DESC,Product.create_time DESC');
+            })
+            // 处理优质产品条件
+            ->when(isset($where['is_good']) && $where['is_good'] !== '', function ($query) use ($where) {
+                $query->where('Product.is_good', $where['is_good']);
+            });
+
+        return $query;
+    }
+
+    /**
+     * 搜索秒杀活动商品
+     *
+     * 本函数用于构建查询秒杀活动商品的条件,根据传入的$where数组来筛选符合条件的秒杀活动及商品。
+     * 这其中包括对秒杀活动状态的检查,商品的各种状态过滤,以及针对商家和活动ID的条件查询。
+     * 最后返回构建好的查询对象,可用于进一步的查询操作或数据获取。
+     *
+     * @param array $where 查询条件数组,包含各种过滤条件如日期、时间、商家ID、活动ID等。
+     * @return \think\db\Query 返回构建好的查询对象,包含所有设定的查询条件。
+     */
+    public function seckillSearch(array $where)
+    {
+        // 根据$where数组中的条件构建秒杀活动(seckillActive)的查询条件
+        $query = model::hasWhere('seckillActive', function ($query) use ($where) {
+            // 仅查询状态为1(有效)的秒杀活动
+            $query->where('status', 1);
+            // 注释掉的代码是原本用于查询日期范围的条件,根据具体业务需求可能启用或禁用
+        });
+
+        // 加入关联查询,关联商品表中的SPU信息,过滤出产品类型为1的商品
+        $query->join('StoreSpu U', 'Product.product_id = U.product_id')->where('U.product_type', 1);
+
+        // 设置一系列固定的查询条件,包括商品的状态、是否展示、是否可用、商家状态等
+        $query->where([
+            'Product.is_show' => 1,
+            'Product.status' => 1,
+            'Product.is_used' => 1,
+            'Product.mer_status' => 1,
+            'Product.product_type' => 1,
+            'Product.is_gift_bag' => 0,
+        ])
+            // 当$where数组中包含mer_id时,添加商家ID的查询条件
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function ($query) use ($where) {
+                $query->where('Product.mer_id', $where['mer_id']);
+            })
+            // 当$where数组中包含active_id且不为空(可能是数组)时,添加活动ID的查询条件
+            ->when(isset($where['active_id']) && !empty($where['active_id']) && is_array($where['active_id']), function ($query) use ($where) {
+                $query->whereIn('Product.active_id', $where['active_id']);
+            })
+            // 当$where数组中包含star时,添加商品星级的查询条件,并按星级和排名排序
+            ->when(isset($where['star']), function ($query) use ($where) {
+                $query->when($where['star'] !== '', function ($query) use ($where) {
+                    $query->where('U.star', $where['star']);
+                });
+                // 按星级和排名降序排序
+                $query->order('U.star DESC,Product.rank DESC');
+            });
+
+        // 返回构建好的查询对象
+        return $query;
+    }
+
+    /**
+     * 删除指定ID的记录。
+     *
+     * 此方法提供了软删除和硬删除两种方式,软删除会将记录移动到回收站,硬删除则会直接从数据库中删除。
+     * 同时,此方法还会更新关联的SPU信息,并触发一个产品删除事件。
+     *
+     * @param int $id 需要删除的记录的ID。
+     * @param bool $soft 是否执行软删除,默认为false表示硬删除。
+     */
+    public function delete(int $id, $soft = false)
+    {
+        // 根据$soft参数决定使用软删除还是硬删除
+        if ($soft) {
+            // 执行软删除,找到指定ID的回收站中的记录,并强制删除
+            (($this->getModel())::onlyTrashed()->find($id))->force()->delete();
+        } else {
+            // 执行硬删除,通过更新is_del字段为1来标记删除
+            $this->getModel()::where($this->getPk(), $id)->update(['is_del' => 1]);
+        }
+        // 更新关联的SPU信息,将其标记为删除状态并禁用
+        app()->make(SpuRepository::class)->getSearch(['product_id' => $id])->update(['is_del' => 1, 'status' => 0]);
+        // 触发产品删除事件,传递删除的ID作为参数
+        event('product.delete', compact('id'));
+    }
+
+
+    /**
+     * 删除产品及相关信息。
+     *
+     * 本函数用于彻底删除指定ID的产品及其在搜索索引中的信息,并触发相应的删除事件。
+     * 具体操作包括:
+     * 1. 更新数据库中该产品的删除状态为1,实现逻辑删除。
+     * 2. 删除搜索索引中该产品的信息,确保搜索结果不再包含该产品。
+     * 3. 触发产品删除事件,允许其他监听器对此操作做出响应。
+     *
+     * @param int $id 产品的ID。
+     */
+    public function destory(int $id)
+    {
+        try {
+            // 更新数据库中指定产品ID的删除状态为1,同时处理已删除的记录。
+            $this->getModel()::withTrashed()->where('product_id', $id)->update(['delete' => 1]);
+
+            // 删除搜索索引中与指定产品ID相关的信息。
+            app()->make(SpuRepository::class)->getSearch(['product_id' => $id])->delete();
+
+            // 触发产品删除事件,传递删除的产品ID作为参数。
+            event('product.delete', compact('id'));
+        } catch (Exception $e) {
+            // 捕获并处理任何异常,避免删除操作影响到整个程序的执行。
+        }
+
+    }
+
+    /**
+     * 恢复已删除的资源。
+     *
+     * 此方法用于恢复数据库中被软删除的记录。它首先通过给定的ID找到被删除的记录,
+     * 然后将其恢复到原始状态。这个过程涉及到两个主要步骤:
+     * 1. 使用onlyTrashed()方法定位到被删除的记录,并通过find()方法找到特定ID的记录。
+     * 2. 调用restore()方法来恢复该记录到原来的表中。
+     *
+     * @param int $id 要恢复的资源的ID。
+     * @return \Illuminate\Database\Eloquent\Model 恢复的资源模型。
+     */
+    public function restore($id)
+    {
+        // 通过ID找到被删除的资源。
+        $res = ($this->getModel())::onlyTrashed()->find($id);
+
+        // 从产品仓库中删除该产品,将其状态重置为未删除。
+        app()->make(SpuRepository::class)->delProduct($id, 0);
+
+        // 恢复该资源到原始状态。
+        return $res->restore();
+    }
+
+    /**
+     * 获取只被软删除的数据
+     *
+     * 本函数用于查询特定条件下的只有被软删除的数据。它利用了Laravel框架的Eloquent ORM提供的onlyTrashed方法,
+     * 该方法用于获取仅被软删除的数据。通过结合where方法和order方法,可以对查询条件和结果排序进行进一步的定制。
+     *
+     * @param array|string $where 查询条件,可以是数组或字符串形式的SQL WHERE子句。
+     * @return \Illuminate\Database\Eloquent\Builder|static 只被软删除的数据的查询构建器。
+     */
+    public function getOnlyTranshed($where)
+    {
+        // 调用getModel方法获取模型实例,并立即使用onlyTrashed方法查询只被软删除的数据
+        // 然后使用where方法添加额外的查询条件,最后使用order方法按照product_id降序排序
+        return ($this->getModel())::onlyTrashed()->where($where)->order('product_id DESC');
+    }
+
+    /**
+     * 切换实体的状态
+     *
+     * 本函数用于根据给定的ID和新的状态数组,更新数据库中相应实体的状态。
+     * 它首先通过getModel方法获取当前实体的模型,然后利用模型的getDB方法获取数据库连接。
+     * 最后,使用where方法指定ID条件,并通过update方法更新实体的状态。
+     *
+     * @param int $id 实体的唯一标识符,用于在数据库中定位特定的实体。
+     * @param array $status 包含新状态值的数组,这些值将被更新到数据库中。
+     * @return int 返回更新操作影响的行数,用于确认更新是否成功。
+     */
+    public function switchStatus(int $id, array $status)
+    {
+        // 通过模型和数据库操作符更新实体的状态
+        return ($this->getModel()::getDB())->where($this->getPk(), $id)->update($status);
+    }
+
+    /**
+     * 根据商家ID和产品ID数组,获取对应的产品图片信息
+     *
+     * 此函数用于查询数据库,获取指定商家ID下,指定产品ID列表中每个产品的ID和图片信息。
+     * 主要用于在前端展示产品图片,或者进行与产品图片相关的操作。
+     *
+     * @param int $merId 商家ID,用于限定查询的商家范围。
+     * @param array $productIds 产品ID数组,指定需要查询的产品。
+     * @return array 返回一个包含产品ID和图片信息的数组。
+     */
+    public function productIdByImage(int $merId, array $productIds)
+    {
+        // 使用模型的数据库访问方法,查询满足条件的产品ID和图片信息
+        return model::getDB()->where('mer_id', $merId)->whereIn('product_id', $productIds)->column('product_id,image');
+    }
+
+    /**
+     * 获取与给定ID数组交集的产品ID数组
+     *
+     * 本函数用于查询数据库中,产品表中产品ID存在于给定ID数组中的所有产品ID。
+     * 通过使用whereIn查询条件,筛选出产品ID在给定数组中的记录,并只返回product_id列的值。
+     *
+     * @param array $ids 给定的产品ID数组,用于查询的条件
+     * @return array 返回查询结果中product_id列的值组成的数组,即与给定ID数组的交集
+     */
+    public function intersectionKey(array $ids): array
+    {
+        // 使用model的getDB方法获取数据库对象,然后通过whereIn方法查询产品表中product_id在$ids数组中的记录,最后使用column方法仅返回product_id列的值
+        return model::getDB()->whereIn('product_id', $ids)->column('product_id');
+    }
+
+    /**
+     * 根据产品ID获取商家ID
+     *
+     * 本函数旨在通过产品ID查询数据库,获取对应产品的商家ID。
+     * 使用了模型类的数据库操作方法,通过指定的条件查询数据库,并返回查询结果中特定列的值。
+     *
+     * @param int $id 产品ID,作为查询条件
+     * @return int 商家ID,如果找不到对应产品则返回null
+     */
+    public function productIdByMerId($id)
+    {
+        // 使用模型的数据库操作方法,根据产品ID查询商家ID
+        return model::getDB()->where('product_id', $id)->value('mer_id');
+    }
+
+    /**
+     * 减少产品库存并增加销售数量
+     *
+     * 该方法用于更新数据库中指定产品的库存和销售数量。
+     * 它通过减少库存量和增加销售量来反映产品的销售情况。
+     *
+     * @param int $productId 产品的ID,用于定位特定的产品记录。
+     * @param int $desc 库存减少的数量,同时也是销售数量增加的数量。
+     * @return mixed 返回数据库更新操作的结果,可能是布尔值或影响的行数。
+     * @throws DbException
+     */
+    public function descStock(int $productId, int $desc)
+    {
+        // 使用Db::raw来执行原生的SQL片段,这里用于更新库存和销售数量。
+        // 这种方法允许直接操作数据库字段的值,而不是通过变量绑定。
+        return model::getDB()->where('product_id', $productId)->update([
+            'stock' => Db::raw('stock-' . $desc),
+            'sales' => Db::raw('sales+' . $desc)
+        ]);
+    }
+
+    /**
+     * 增加商品库存并减少对应销售量
+     * 此函数用于在数据库中增加指定商品的库存量,并同时减少该商品的销售量。
+     * 这种操作常见于处理商品退货或库存调整的情况,需要在库存和销售数据中做出相应的调整。
+     *
+     * @param int $productId 商品ID,用于指定需要调整库存的商品。
+     * @param int $inc 库存增加的数量。此参数同时用于增加库存和减少销售量。
+     */
+    public function incStock(int $productId, int $inc)
+    {
+        // 增加商品库存
+        model::getDB()->where('product_id', $productId)->inc('stock', $inc)->update();
+        // 减少商品销售量,仅针对之前已销售的数量大于等于当前增加的库存量的部分进行调整
+        model::getDB()->where('product_id', $productId)->where('sales', '>=', $inc)->dec('sales', $inc)->update();
+    }
+
+
+    /**
+     * 减少商品销量
+     *
+     * 该方法用于更新数据库中指定商品的销量,将其减少指定的数量。
+     * 主要用于处理商品销售时的库存更新或其他需要减少销量的场景。
+     *
+     * @param int $productId 商品ID,用于定位要更新销量的商品。
+     * @param int $desc 销量减少的数量,一个正整数,表示要从当前销量中减去的数值。
+     * @return mixed 返回数据库操作的结果,可能是布尔值或影响行数。
+     * @throws DbException
+     */
+    public function descSales(int $productId, int $desc)
+    {
+        // 使用Db::raw处理SQL中的减法操作,确保操作的安全性和正确性。
+        return model::getDB()->where('product_id', $productId)->update([
+            'sales' => Db::raw('sales-' . $desc)
+        ]);
+    }
+
+
+    /**
+     * 增加商品销量
+     *
+     * 该方法用于更新指定商品的销量,销量增加的数值由参数$inc指定。
+     * 通过传入商品ID($productId)来定位到特定商品,并对销量进行增加操作。
+     * 使用Db::raw()来构建SQL的计算表达式,确保销量是原销量基础上增加$inc。
+     *
+     * @param int $productId 商品ID,用于确定要更新销量的具体商品。
+     * @param int $inc 销量增加的数值,表示要将商品的销量增加多少。
+     * @return bool 更新操作的结果,成功返回true,失败返回false。
+     * @throws DbException
+     */
+    public function incSales(int $productId, int $inc)
+    {
+        // 根据商品ID更新数据库中对应商品的销量,销量增加$inc
+        return model::getDB()->where('product_id', $productId)->update([
+            'sales' => Db::raw('sales+' . $inc)
+        ]);
+    }
+
+    /**
+     * 减少商品的积分总量和积分价格总量
+     *
+     * 本函数用于更新数据库中指定商品的积分总量和积分价格总量,实现减少操作。
+     * 主要用于处理商品积分的扣减逻辑,例如在商品退货、积分抵扣减少等场景下。
+     *
+     * @param int $productId 商品ID,用于定位要更新的商品记录。
+     * @param int $integral_total 需要减少的积分总量,表示本次操作将从商品的积分总量中减去的数值。
+     * @param int $integral_price_total 需要减少的积分价格总量,表示本次操作将从商品的积分价格总量中减去的数值。
+     * @return int 返回更新操作的影响行数,用于判断操作是否成功。
+     * @throws DbException
+     */
+    public function descIntegral(int $productId, $integral_total, $integral_price_total)
+    {
+        // 使用Db::raw包裹更新表达式,直接在SQL中进行减法操作,避免因类型转换等问题造成的错误。
+        // 通过where条件定位到指定ID的商品记录,然后更新其积分总量和积分价格总量。
+        return model::getDB()->where('product_id', $productId)->update([
+            'integral_total' => Db::raw('integral_total-' . $integral_total),
+            'integral_price_total' => Db::raw('integral_price_total-' . $integral_price_total),
+        ]);
+    }
+
+    /**
+     * 增加产品的积分和积分金额
+     *
+     * 该方法用于更新数据库中指定产品的积分总数和积分金额总数。
+     * 它通过传入的产品ID定位到特定的产品记录,然后分别增加积分总数和积分金额总数。
+     * 这种设计用于在进行相关交易或操作时,自动更新产品的积分相关数据,保持数据的实时性。
+     *
+     * @param int $productId 产品的ID,用于在数据库中定位到特定产品记录。
+     * @param int $integral_total 需要增加的积分总数,表示产品积分的增量。
+     * @param int $integral_price_total 需要增加的积分金额总数,表示产品积分金额的增量。
+     */
+    public function incIntegral(int $productId, $integral_total, $integral_price_total)
+    {
+        // 使用模型的数据库访问方法,定位到指定产品ID的记录,然后分别增加积分总数和积分金额总数,并执行更新操作。
+        model::getDB()->where('product_id', $productId)->inc('integral_total', $integral_total)->inc('integral_price_total', $integral_price_total)->update();
+    }
+
+    /**
+     * 访问产品组的方法
+     * 该方法用于查询指定日期内,每个产品组的访问量和相关信息。
+     * 可以根据日期和商家ID进行过滤,返回最近访问量最高的产品组列表。
+     *
+     * @param string $date 查询的日期,格式为YYYY-MM-DD。如果不提供,则查询最近7天的数据。
+     * @param int $merId 商家ID,可选参数。如果提供了商家ID,则只查询该商家的产品组。
+     * @param int $limit 返回结果的数量限制,默认为7。
+     * @return array 返回一个包含产品组信息的数组,每个元素包含产品ID、商店名称、图片和访问量。
+     */
+    public function visitProductGroup($date, $merId = null, $limit = 7)
+    {
+        // 从数据库中获取数据
+        return model::getDB()->alias('A')->leftJoin('UserRelation B', 'A.product_id = B.type_id')
+            ->field(Db::raw('count(B.type_id) as total,A.product_id,A.store_name,A.image'))
+            ->when($date, function ($query, $date) {
+                // 如果提供了日期,则根据日期筛选数据
+                getModelTime($query, $date, 'B.create_time');
+            })->when($merId, function ($query, $merId) {
+                // 如果提供了商家ID,则根据商家ID筛选数据
+                $query->where('A.mer_id', $merId);
+            })->where('B.type', 1)->group('A.product_id')->limit($limit)->order('total DESC')->select();
+    }
+
+    /**
+     * 获取购物车产品分组信息
+     * 该方法用于查询指定日期内,每个产品的购物车数量总和,可选地根据商家ID进行筛选。
+     * 结果将按照购物车数量降序排列,并限制返回结果的数量。
+     *
+     * @param string $date 查询的日期,格式为YYYY-MM-DD。用于筛选在指定日期内添加到购物车的产品。
+     * @param int|null $merId 商家ID,可选参数。用于筛选属于特定商家的产品。
+     * @param int $limit 返回结果的数量限制,默认为7。用于限制返回的产品分组数量。
+     * @return array 返回一个包含产品分组信息的数组,每个分组包含产品ID、商家名称、产品图片和购物车总数。
+     */
+    public function cartProductGroup($date, $merId = null, $limit = 7)
+    {
+        // 从数据库中获取数据
+        return model::getDB()->alias('A')->leftJoin('StoreCart B', 'A.product_id = B.product_id')
+            ->field(Db::raw('sum(B.cart_num) as total,A.product_id,A.store_name,A.image'))
+            // 当传入日期时,根据日期筛选数据
+            ->when($date, function ($query, $date) {
+                getModelTime($query, $date, 'B.create_time');
+            })
+            // 当传入商家ID时,根据商家ID筛选数据
+            ->when($merId, function ($query, $merId) {
+                $query->where('A.mer_id', $merId);
+            })
+            // 筛选条件:产品类型为0,未支付,未删除,非新购,未失败
+            ->where('B.product_type', 0)->where('B.is_pay', 0)->where('B.is_del', 0)
+            ->where('B.is_new', 0)->where('B.is_fail', 0)
+            // 按产品ID分组,限制返回结果的数量,并按购物车总数降序排列
+            ->group('A.product_id')->limit($limit)->order('total DESC')->select();
+    }
+
+    /**
+     * 更新商家产品的信息
+     *
+     * 本函数用于根据给定的商家ID和数据集,更新对应商家产品的信息。
+     * 它通过查询数据库中与给定商家ID匹配的记录,并应用提供的数据更新。
+     *
+     * @param string $merId 商家的唯一标识符。用于确定要更新哪个商家的产品信息。
+     * @param array $data 包含要更新的产品信息的数据数组。数组的键值对表示要更新的字段及其新值。
+     */
+    public function changeMerchantProduct($merId, $data)
+    {
+        // 通过模型获取数据库实例,并使用where子句指定mer_id,然后执行更新操作。
+        ($this->getModel()::getDB())->where('mer_id', $merId)->update($data);
+    }
+
+    /**
+     * 增加产品关注数
+     *
+     * 该方法用于指定产品的关注数增加1。它通过调用getModel方法获取模型实例,
+     * 进而连接数据库,并根据产品的主键($productId)定位到相应记录,
+     * 然后将该记录的care_count字段值增加1。
+     *
+     * @param int $productId 产品的唯一标识符,用于在数据库中定位到具体产品记录。
+     */
+    public function incCareCount(int $productId)
+    {
+        // 通过模型实例的getDB方法获取数据库连接,然后使用where方法指定条件,
+        // 使用inc方法增加care_count字段的值,并通过update方法更新数据库记录。
+        ($this->getModel()::getDB())->where($this->getPk(), $productId)->inc('care_count', 1)->update();
+    }
+
+    /**
+     * 减少指定产品关注数
+     *
+     * 此方法用于减少数据库中指定产品ID的关注数。它首先通过产品ID数组筛选出相关记录,
+     * 然后针对这些记录将关注数减少1。这种方法适用于批量处理,例如在用户取消关注多个产品时。
+     *
+     * @param array $productId 产品ID数组,表示需要减少关注数的产品集合。
+     */
+    public function decCareCount(array $productId)
+    {
+        // 检查$productId是否为空,避免不必要的数据库操作
+        if (empty($productId)) {
+            return; // 或者记录日志、抛出异常等,根据实际需求决定
+        }
+        // 通过模型获取数据库实例,并使用whereIn和where条件构建查询,然后减少care_count列的值并更新记录。
+        ($this->getModel()::getDB())->whereIn($this->getPk(), $productId)->where('care_count', '>', 0)->dec('care_count', 1)->update();
+    }
+    /**
+     * 获取商品展示配置
+     *
+     * 该方法用于返回一个配置数组,定义了商品在前端的展示行为和状态。
+     * 返回的配置包括:
+     * - is_show: 商品是否上架,值为1表示上架。
+     * - status: 商品审核状态,值为1表示审核通过。
+     * - is_used: 商品是否启用,值为1表示启用。
+     * - product_type: 商品类型,值为0表示普通商品。
+     * - mer_status: 商铺状态,值为1表示商铺正常运营。
+     * - is_gift_bag: 商品是否为礼盒,值为0表示不是礼盒。
+     *
+     * @return array 商品展示配置数组
+     */
+    public function productShow()
+    {
+        return [
+            'is_show' => 1,   // 上架
+            'status' => 1,   // 审核通过
+            'is_used' => 1,  // 显示
+            'product_type' => 0, // 普通商品
+            'mer_status' => 1,  //商铺状态正常
+            'is_gift_bag' => 0,  //不是礼包
+        ];
+    }
+
+    /**
+     *  api展示的礼包商品条件
+     * @return array
+     * @author Qinii
+     * @day 2020-08-18
+     */
+    public function bagShow()
+    {
+        return [
+            'is_show' => 1,
+            'status' => 1,
+            'is_used' => 1,
+            'mer_status' => 1,
+            'product_type' => 0,
+            'is_gift_bag' => 1,
+        ];
+    }
+
+    /**
+     *  api展示的秒杀商品条件
+     * @return array
+     * @author Qinii
+     * @day 2020-08-18
+     */
+    public function seckillShow()
+    {
+        return [
+            'is_show' => 1,
+            'status' => 1,
+            'is_used' => 1,
+            'mer_status' => 1,
+            'product_type' => 1,
+            'is_gift_bag' => 0,
+        ];
+    }
+
+    /**
+     * 根据产品ID获取产品类型,并检查是否与传入的现有类型匹配。
+     *
+     * 此方法主要用于查询数据库中指定产品ID对应的产品类型,并根据需求判断是否与预设的存在类型相匹配。
+     * 如果传入的存在类型不为空,则会在查询时添加一个额外的条件来筛选产品类型。
+     * 方法返回一个布尔值,表示查询到的产品类型是否为0(即true表示是,false表示不是)。
+     *
+     * @param int $productId 产品的唯一标识ID。
+     * @param int|null $exsistType 存在的产品类型ID,用于查询时的条件筛选。
+     * @return bool 如果查询到的产品类型为0,则返回true,否则返回false。
+     */
+    public function getProductTypeById(int $productId, ?int $exsistType)
+    {
+        // 通过模型获取数据库实例,并根据条件构造查询语句。
+        $product_type = $this->getModel()::getDB()
+            ->when($exsistType, function ($query) use ($exsistType) {
+                // 如果存在类型ID,则在查询时添加对应的产品类型条件。
+                $query->where('product_type', $exsistType);
+            })
+            ->where($this->getPk(), $productId) // 根据产品ID进行查询。
+            ->where('is_del', 0) // 排除已删除的产品。
+            ->value('product_type'); // 只返回产品类型这一列的值。
+
+        // 判断查询到的产品类型是否为0,返回相应的布尔值。
+        return $product_type == 0 ? true : false;
+    }
+
+
+    /**
+     * 根据产品ID获取失败的产品信息
+     *
+     * 本函数旨在通过产品ID检索特定产品的详细信息。特别地,它包括了产品是否被删除的状态,
+     * 这是通过使用`withTrashed`方法来实现的,意味着即使产品已被标记为删除,也能被检索到。
+     * 它返回的产品信息精简到了最相关和必要的字段,以提高查询效率和数据使用的针对性。
+     *
+     * @param int $productId 产品ID,用于精确查找特定产品。
+     * @return \think\Model|null 返回匹配给定产品ID的模型对象,如果找不到则返回null。
+     */
+    public function getFailProduct(int $productId)
+    {
+        // 使用withTrashed确保可以查询到已删除的数据
+        // 精选查询字段,只获取必要信息,提高查询效率
+        return $this->getModel()::withTrashed()->field('product_type,product_id,image,store_name,is_show,status,is_del,unit_name,price,mer_status,is_used,mer_form_id')->find($productId);
+    }
+
+    /**
+     * 获取已删除的产品信息
+     *
+     * 本方法用于查询一个特定ID的产品,包括已经被软删除的产品。通过使用`withTrashed`方法,可以包含已被删除的数据在查询结果中。
+     * 这对于需要恢复已删除数据或者查看删除状态下的数据是非常有用的。
+     *
+     * @param int $id 产品的唯一标识ID
+     * @return \Illuminate\Database\Eloquent\Model 返回查询到的产品模型,可能包括已被删除的产品。
+     */
+    public function geTrashedtProduct(int $id)
+    {
+        // 使用withTrashed方法包含软删除的记录,并根据主键ID查询产品
+        return model::withTrashed()->where($this->getPk(), $id);
+    }
+
+
+    /**
+     *  获取各种有效时间内的活动
+     * @param int $productType
+     * @return BaseQuery
+     *
+     * @date 2023/09/22
+     * @author yyw
+     */
+    public function activitSearch(int $productType)
+    {
+        $query = model::getDB()->alias('P')
+            ->where('P.is_del', 0)
+            ->where('P.mer_status', 1)
+            ->where('P.product_type', $productType);
+        switch ($productType) {
+            case 0:
+                // $query->where('P.is_show',1)
+                //     ->where('P.is_used',1)
+                //     ->field('product_id,product_type,mer_id,store_name,keyword,price,rank,sort,image,status,temp_id');
+                break;
+            case 1:
+                $query->join('StoreSeckillActive S', 'S.seckill_active_id = P.active_id')
+                    ->field('P.*,S.status,S.seckill_active_id,S.end_time');
+                break;
+            case 2:
+                $query->join('StoreProductPresell R', 'R.product_id = P.product_id')
+                    ->where('R.is_del', 0)
+                    ->field('P.*,R.product_presell_id,R.store_name,R.price,R.status,R.is_show,R.product_status,R.action_status');
+                break;
+            case 3:
+                $query->join('StoreProductAssist A', 'A.product_id = P.product_id')
+                    ->where('A.is_del', 0)
+                    ->field('P.*,A.product_assist_id,A.store_name,A.status,A.is_show,A.product_status,A.action_status');
+                break;
+            case 4:
+                $query->join('StoreProductGroup G', 'G.product_id = P.product_id')
+                    ->where('G.is_del', 0)
+                    ->field('P.*,G.product_group_id,G.price,G.status,G.is_show,G.product_status,G.action_status');
+                break;
+            default:
+                break;
+        }
+        return $query;
+    }
+
+
+    public function commandChangeProductStatus($data)
+    {
+        $ret = [];
+
+        foreach ($data as $item) {
+            $status = 0;
+            switch ($item['product_type']) {
+                case 0:
+                    if ($item['is_show'] && $item['is_used']) $status = 1;
+                    $ret[] = [
+                        'activity_id' => 0,
+                        'product_id' => $item['product_id'],
+                        'mer_id' => $item['mer_id'],
+                        'keyword' => $item['keyword'],
+                        'price' => $item['price'],
+                        'rank' => $item['rank'],
+                        'sort' => $item['sort'],
+                        'image' => $item['image'],
+                        'status' => $status,
+                        'temp_id' => $item['temp_id'],
+                        'store_name' => $item['store_name'],
+                        'product_type' => $item['product_type'],
+                    ];
+                    break;
+                case 1:
+                    if ($item['is_show'] && $item['is_used'] && $item['status'] && ($item['end_time'] > time())) $status = 1;
+                    $ret[] = [
+                        'activity_id' => $item['seckill_active_id'],
+                        'product_id' => $item['product_id'],
+                        'mer_id' => $item['mer_id'],
+                        'keyword' => $item['keyword'],
+                        'price' => $item['price'],
+                        'rank' => $item['rank'],
+                        'sort' => $item['sort'],
+                        'image' => $item['image'],
+                        'status' => $status,
+                        'temp_id' => $item['temp_id'],
+                        'store_name' => $item['store_name'],
+                        'product_type' => $item['product_type'],
+                    ];
+                    break;
+                case 2:
+                    if ($item['is_show'] && $item['action_status'] && $item['status'] && $item['product_status']) $status = 1;
+                    $ret[] = [
+                        'activity_id' => $item['product_presell_id'],
+                        'product_id' => $item['product_id'],
+                        'mer_id' => $item['mer_id'],
+                        'keyword' => $item['keyword'],
+                        'price' => $item['price'],
+                        'rank' => $item['rank'],
+                        'sort' => $item['sort'],
+                        'image' => $item['image'],
+                        'status' => $status,
+                        'temp_id' => $item['temp_id'],
+                        'store_name' => $item['store_name'],
+                        'product_type' => $item['product_type'],
+                    ];
+                    break;
+                case 3:
+                    if ($item['is_show'] && $item['action_status'] && $item['status'] && $item['product_status']) $status = 1;
+                    $ret[] = [
+                        'activity_id' => $item['product_assist_id'],
+                        'product_id' => $item['product_id'],
+                        'mer_id' => $item['mer_id'],
+                        'keyword' => $item['keyword'],
+                        'price' => $item['price'],
+                        'rank' => $item['rank'],
+                        'sort' => $item['sort'],
+                        'image' => $item['image'],
+                        'status' => $status,
+                        'temp_id' => $item['temp_id'],
+                        'store_name' => $item['store_name'],
+                        'product_type' => $item['product_type'],
+                    ];
+                    break;
+                case 4:
+                    if ($item['is_show'] && $item['action_status'] && $item['status'] && $item['product_status']) $status = 1;
+                    $ret[] = [
+                        'activity_id' => $item['product_group_id'],
+                        'product_id' => $item['product_id'],
+                        'mer_id' => $item['mer_id'],
+                        'keyword' => $item['keyword'],
+                        'price' => $item['price'],
+                        'rank' => $item['rank'],
+                        'sort' => $item['sort'],
+                        'image' => $item['image'],
+                        'status' => $status,
+                        'temp_id' => $item['temp_id'],
+                        'store_name' => $item['store_name'],
+                        'product_type' => $item['product_type'],
+                    ];
+                    break;
+                default:
+                    if ($item['is_show'] && $item['is_used']) $status = 1;
+                    $ret[] = [
+                        'activity_id' => 0,
+                        'product_id' => $item['product_id'],
+                        'mer_id' => $item['mer_id'],
+                        'keyword' => $item['keyword'],
+                        'price' => $item['price'],
+                        'rank' => $item['rank'],
+                        'sort' => $item['sort'],
+                        'image' => $item['image'],
+                        'status' => $status,
+                        'temp_id' => $item['temp_id'],
+                        'store_name' => $item['store_name'],
+                        'product_type' => $item['product_type'],
+                    ];
+                    break;
+            }
+        }
+        return $ret;
+    }
+
+    /**
+     *  软删除商户的所有商品
+     * @param $merId
+     * @author Qinii
+     * @day 5/15/21
+     */
+    public function clearProduct($merId)
+    {
+        $this->getModel()::withTrashed()->where('mer_id', $merId)->delete();
+    }
+
+    /**
+     * 获取好物推荐列表
+     * @param array|null $good_ids
+     * @param $is_mer
+     * @return array|mixed
+     *
+     * @date 2023/10/30
+     * @author yyw
+     */
+    public function getGoodList(array $good_ids, int $merId, $is_show = true)
+    {
+        if (empty($good_ids) && !$is_show) return [];
+        $filed = 'product_id,image,store_name,price,create_time,is_gift_bag,is_good,is_show,mer_id,sales,status';
+        $where = [];
+        $limit = 30;
+        if ($is_show) {
+            $where = $this->productShow();
+            $limit = 18;
+        }
+        $query = $this->getModel()::getDB()->where('mer_id', $merId)->where($where)
+            ->when(!empty($good_ids), function ($query) use ($good_ids) {
+                $query->whereIn($this->getPk(), $good_ids);
+            })->field($filed);
+        $list = $query->limit($limit)->select()->toArray();
+        return $list;
+    }
+
+    public function deleteProductFormByFormId(int $form_id, int $mer_id)
+    {
+        return $this->getModel()::getDB()->where('mer_form_id', $form_id)->where('mer_id', $mer_id)->update(['mer_form_id' => 0]);
+    }
+    /**
+     * 商品详情推荐商品列表
+     *
+     * @param array $goodIds
+     * @param integer $merId
+     * @param integer $recommendNum
+     * @return void
+     */
+    public function recommendProduct(array $goodIds, int $merId, int $recommendNum)
+    {
+        $where = $this->productShow();
+        $where['mer_id'] = $merId;
+        $filed = 'product_id, image, store_name, price, create_time, is_gift_bag, is_good, is_show, mer_id, sales, status';
+        $query = $this->getModel()::getDB()->field($filed)->where($where);
+        if (!empty($goodIds)) {
+            $query->whereIn($this->getPk(), $goodIds);
+            $diff = bcsub($recommendNum, count($goodIds));
+        }
+        $list = $query->limit($recommendNum)->select()->toArray();
+        if (isset($diff) && $diff > 0) {
+            $diffList = $query->removeWhereField($this->getPk())->whereNotIn($this->getPk(), $goodIds)->limit($diff)->select()->toArray();
+        }
+
+        return array_merge($list, $diffList ?? []);
+    }
+}

+ 83 - 0
app/common/dao/store/product/ProductGroupBuyingDao.php

@@ -0,0 +1,83 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductGroupBuying;
+
+class ProductGroupBuyingDao extends  BaseDao
+{
+    public function getModel(): string
+    {
+        return ProductGroupBuying::class;
+    }
+
+
+    /**
+     * 根据条件搜索团购信息
+     *
+     * 本函数用于构建并返回一个根据指定条件搜索产品团购信息的查询语句。
+     * 它支持多种搜索条件,包括商家ID、日期、状态、用户名、关键词和是否为交易者等。
+     * 搜索功能通过动态拼接查询条件来实现,以适应不同的搜索需求。
+     *
+     * @param array $where 搜索条件数组,包含各种可能的搜索参数。
+     * @return \yii\db\ActiveQuery 返回构建的查询语句对象。
+     */
+    public function search($where)
+    {
+        // 初始化查询,从ProductGroupBuying表开始,使用别名B
+        $query = ProductGroupBuying::getDb()->alias('B')
+            // 加入StoreProductGroup表,通过product_group_id关联
+            ->join('StoreProductGroup G','B.product_group_id = G.product_group_id');
+
+        // 动态添加条件:根据商家ID搜索
+        $query
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '', function($query)use($where){
+                // 如果指定了商家ID,则添加到查询条件中
+                $query->where('B.mer_id',$where['mer_id']);
+            })
+            // 动态添加条件:根据日期搜索
+            ->when(isset($where['date']) && $where['date'] , function($query)use($where){
+                // 如果指定了日期,则调用getModelTime函数处理,并添加到查询条件中
+                getModelTime($query,$where['date'],'B.create_time');
+            })
+            // 动态添加条件:根据状态搜索
+            ->when(isset($where['status']) && $where['status'] !== '', function($query)use($where){
+                // 如果指定了状态,则添加到查询条件中
+                $query->where('B.status',$where['status']);
+            })
+            // 动态添加条件:根据用户名搜索
+            ->when(isset($where['user_name']) && $where['user_name'] !== '', function($query)use($where){
+                // 加入StoreProductGroupUser表,通过group_buying_id关联
+                // 并搜索初始化者(is_initiator=1),用户名(uid|nickname)包含搜索关键字
+                $query->join('StoreProductGroupUser U','U.group_buying_id = B.group_buying_id')
+                    ->where('is_initiator',1)
+                    ->whereLike('uid|nickname',"%{$where['user_name']}%");
+            })
+            // 动态添加条件:根据关键词搜索
+            ->when(isset($where['keyword']) && $where['keyword'] !== '' , function($query)use($where){
+                // 加入StoreProduct表,通过product_id关联
+                // 搜索团购ID、产品ID或商店名称包含搜索关键字
+                $query->join('StoreProduct P','G.product_id = P.product_id')
+                    ->whereLike('B.group_buying_id|P.product_id|store_name',"%{$where['keyword']}%");
+            })
+            // 动态添加条件:根据是否为交易者搜索
+            ->when(isset($where['is_trader']) && $where['is_trader'] !== '', function($query)use($where){
+                // 加入Merchant表,通过mer_id关联
+                // 并根据是否为交易者(is_trader)进行筛选
+                $query->join('Merchant M','M.mer_id = B.mer_id')->where('is_trader',$where['is_trader']);
+            })
+        ;
+
+        // 返回构建好的查询语句
+        return $query;
+    }
+}

+ 212 - 0
app/common/dao/store/product/ProductGroupDao.php

@@ -0,0 +1,212 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductGroup;
+use app\common\repositories\store\product\SpuRepository;
+
+class ProductGroupDao extends  BaseDao
+{
+    public function getModel(): string
+    {
+        return ProductGroup::class;
+    }
+
+    /**
+     * 商品分组查询
+     * @param $where
+     * @return \think\db\BaseQuery
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/7/17
+     */
+    public function search($where)
+    {
+        $query = ProductGroup::hasWhere('product',function($query)use($where){
+            $query->where('status',1);
+            $query->when(isset($where['keyword']) && $where['keyword'] !== '',function($query)use($where){
+                $query->whereLike('store_name',"%{$where['keyword']}%");
+            });
+        });
+        $query->when(isset($where['is_show']) && $where['is_show'] !== '',function($query)use($where){
+                $query->where('ProductGroup.is_show',$where['is_show']);
+            })
+            ->when(isset($where['product_status']) && $where['product_status'] !== '',function($query)use($where){
+                if($where['product_status'] == -1){
+                    $query->where('ProductGroup.product_status','in',[-1,-2]);
+                }else{
+                    $query->where('ProductGroup.product_status',$where['product_status']);
+                }
+            })
+            ->when(isset($where['status']) && $where['status'] !== '',function($query)use($where){
+                $query->where('ProductGroup.status',$where['status']);
+            })
+            ->when(isset($where['end_time']) && $where['end_time'] !== '',function($query)use($where){
+                $query->whereTime('ProductGroup.end_time','>',$where['end_time']);
+            })
+            ->when(isset($where['active_type']) && $where['active_type'] !== '',function($query)use($where){
+                $query->where('ProductGroup.action_status',$where['active_type']);
+            })
+            ->when(isset($where['is_trader']) && $where['is_trader'] !== '',function($query)use($where){
+                $query->join('Merchant M','M.mer_id = ProductGroup.mer_id')->where('is_trader',$where['is_trader']);
+            })
+            ->when(isset($where['mer_id']) && $where['mer_id'] !== '',function($query)use($where){
+                $query->where('ProductGroup.mer_id',$where['mer_id']);
+            })
+            ->when(isset($where['product_group_id']) && $where['product_group_id'] !== '',function($query)use($where){
+                $query->where('ProductGroup.product_group_id',$where['product_group_id']);
+            })
+             ->when(isset($where['store_category_id']) && $where['store_category_id'] !== '',function($query)use($where){
+                 $query->join('StoreCategory C','Product.cate_id = C.store_category_id')
+                     ->whereLike('path',"/{$where['store_category_id']}/%");
+             })
+            ->when(isset($where['us_status']) && $where['us_status'] !== '',function($query)use($where){
+                if($where['us_status'] == 0) {
+                    $query->where('ProductGroup.is_show',0)->where('ProductGroup.status',1)->where('ProductGroup.product_status',1);
+                }
+                if($where['us_status'] == 1) {
+                    $query->where('ProductGroup.is_show',1)->where('ProductGroup.status',1)->where('ProductGroup.product_status',1);
+                }
+                if($where['us_status'] == -1) {
+                    $query->where(function($query){
+                        $query->where('ProductGroup.status',0)->whereOr('ProductGroup.product_status','<>',1);
+                    });
+                }
+            });
+
+        $query->join('StoreSpu U','ProductGroup.product_group_id = U.activity_id')->where('U.product_type',4);
+
+        $query->when(isset($where['star']) && $where['star'] !== '',function($query)use($where){
+                $query->where('U.star',$where['star']);
+            })
+            ->when(isset($where['level']) && $where['level'] !== '',function($query)use($where) {
+                $query->where('U.star',$where['level']);
+            })
+            ->when(isset($where['mer_labels']) && $where['mer_labels'] !== '', function ($query) use ($where) {
+                $query->whereLike('U.mer_labels', "%,{$where['mer_labels']},%");
+            })
+            ->when(isset($where['sys_labels']) && $where['sys_labels'] !== '', function ($query) use ($where) {
+                $query->whereLike('U.sys_labels', "%,{$where['sys_labels']},%");
+            });
+        if(isset($where['order'])) {
+            switch ($where['order']) {
+                case 'sort':
+                    $order = 'U.sort DESC';
+                    break;
+                case 'rank':
+                    $order = 'U.rank DESC';
+                    break;
+                case 'star':
+                    $order = 'U.star DESC,U.rank DESC';
+                    break;
+                default:
+                    $order = 'U.star DESC,U.rank DESC,U.sort DES';
+                    break;
+            }
+            $query->order($order.',ProductGroup.create_time DESC');
+        }
+
+        return $query->where('ProductGroup.is_del',0);
+    }
+
+    /**
+     * 展示动作的执行结果
+     *
+     * 本函数用于返回一个表示展示状态的数组,包含多个状态标识和当前时间。
+     * 这些状态标识用于表示展示是否应该显示、动作是否成功、产品状态等。
+     * 返回的数组包含以下键值对:
+     * - is_show: 表示展示是否应该显示,值为1表示应该显示。
+     * - action_status: 表示动作的状态,值为1表示动作执行成功。
+     * - product_status: 表示产品的状态,值为1表示产品处于有效状态。
+     * - status: 通用状态标识,值为1表示一切正常。
+     * - end_time: 表示当前时间,以Unix时间戳形式表示,用于记录动作的结束时间。
+     *
+     * @return array 包含状态信息的数组。
+     */
+    public function actionShow()
+    {
+        // 返回包含状态信息的数组
+        return [
+            'is_show' => 1,
+            'action_status' => 1,
+            'product_status' => 1,
+            'status' => 1,
+            'end_time' => time(),
+        ];
+    }
+
+    /**
+     * 获取展示状态为正常且可购买的产品分类路径列表
+     *
+     * 本函数通过查询产品组(ProductGroup)关联的产品(StoreProduct)及其分类(StoreCategory)
+     * 来获取展示状态、动作状态及产品状态都为正常的产品的分类路径。
+     * 使用了别名简化查询语句,通过连接(join)产品组、产品和分类表,筛选出符合条件的产品,
+     * 并按产品ID分组,最后返回每个产品的分类路径列表。
+     *
+     * @return array 返回符合条件的产品的分类路径列表
+     */
+    public function category()
+    {
+        // 使用别名简化表名,并连接产品组、产品和分类表
+        $query = ProductGroup::alias('G')->join('StoreProduct P','G.product_id = P.product_id')
+            ->join('StoreCategory C','P.cate_id = C.store_category_id');
+
+        // 筛选展示状态、动作状态及产品状态都为正常的产品
+        $query->where('G.is_show',1)->where('G.action_status',1)->where('G.product_status',1);
+
+        // 按产品ID分组,以确保每个产品只返回一个分类路径
+        $query->group('G.product_id');
+
+        // 返回查询结果中产品的分类路径列表
+        return $query->column('path');
+    }
+
+    /**
+     * 检查并更新过期活动的状态
+     * 该方法用于定期检查所有活动的结束时间,如果活动已经结束,则将其状态设置为失效。
+     * 同时,此方法还会更新关联的SPU的状态,将这些SPU的状态设置为不可用。
+     *
+     * @return void
+     */
+    public function valActiveStatus()
+    {
+        // 查询已结束且状态为有效的活动的ID
+        $query = $this->getModel()::getDB()->whereTime('end_time','<=',time())->where('action_status',1);
+        $id = $query->column($this->getPk());
+
+        // 如果查询到已结束的活动ID
+        if($id) {
+            // 更新这些活动的状态为失效
+            $this->getModel()::getDB()->where($this->getPk(),'in',$id)->update(['action_status' => -1]);
+
+            // 准备更新关联SPU的状态
+            $where = [
+                'product_type' => 4,
+                'activity_ids' => $id
+            ];
+            // 更新所有关联到这些活动的SPU的状态为不可用
+            app()->make(SpuRepository::class)->getSearch($where)->update(['status' => 0]);
+        }
+    }
+
+
+    /**
+     * 软删除商户的所有商品
+     * @param $merId
+     * @author Qinii
+     * @day 5/15/21
+     */
+    public function clearProduct($merId)
+    {
+        $this->getModel()::getDb()->where('mer_id', $merId)->update(['is_del' => 1]);
+    }
+}

+ 75 - 0
app/common/dao/store/product/ProductGroupSkuDao.php

@@ -0,0 +1,75 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductGroupSku;
+
+class ProductGroupSkuDao extends BaseDao
+{
+    public function getModel(): string
+    {
+        return ProductGroupSku::class;
+    }
+
+    /**
+     * 清除指定产品组的数据
+     *
+     * 本函数用于根据给定的产品组ID,从数据库中删除相关的产品组数据。
+     * 这是数据维护的一部分,可以帮助管理产品组的信息,避免过时或不必要的数据堆积。
+     *
+     * @param int $id 产品组的唯一标识符
+     * @return int 返回删除的记录数
+     */
+    public function clear($id)
+    {
+        // 通过模型获取数据库实例,并使用where子句指定删除条件,然后执行删除操作
+        return $this->getModel()::getDB()->where('product_group_id', $id)->delete();
+    }
+
+
+    /**
+     * 增加商品组SKU的库存
+     *
+     * 该方法用于指定商品组中的某个唯一SKU增加库存。通过传入商品组ID和唯一标识符来定位特定的SKU,
+     * 然后将库存数量增加指定的值。这种方法适用于需要精确控制商品库存的场景,例如在订单退款或商品回收时。
+     *
+     * @param int $product_group_id 商品组ID,用于定位商品组。
+     * @param string $unique SKU的唯一标识符,用于在商品组中定位特定的SKU。
+     * @param int $inc 库存需要增加的数量。这个值可以是正数,表示增加库存;也可以是负数,表示减少库存(虽然不符合常规的库存操作逻辑)。
+     * @return bool 更新操作的结果。如果成功更新库存,则返回true;否则返回false。
+     */
+    public function incStock($product_group_id, $unique, $inc)
+    {
+        // 使用where子句定位到特定的SKU行,然后通过inc方法增加stock列的值,并通过update方法保存更改。
+        return ProductGroupSku::getDB()->where('product_group_id', $product_group_id)->where('unique', $unique)->inc('stock', $inc)->update();
+    }
+
+    /**
+     * 减少商品组SKU的库存
+     *
+     * 此方法用于更新指定商品组ID和唯一标识的SKU的库存数量。
+     * 它通过查询数据库,找到对应的SKU记录,然后减少库存量,并执行更新操作。
+     *
+     * @param int $product_group_id 商品组ID,用于定位特定商品组。
+     * @param string $unique SKU的唯一标识,用于唯一确定一个SKU。
+     * @param int $inc 库存量减少的值,可以是正整数,表示库存减少的数量。
+     *
+     * @return int 返回更新操作影响的行数,用于确认更新是否成功。
+     */
+    public function descStock($product_group_id, $unique, $inc)
+    {
+        // 通过ProductGroupSku的数据库访问对象,构造更新语句,减少库存并执行更新操作。
+        return ProductGroupSku::getDB()->where('product_group_id', $product_group_id)->where('unique', $unique)->dec('stock', $inc)->update();
+    }
+
+
+}

+ 80 - 0
app/common/dao/store/product/ProductGroupUserDao.php

@@ -0,0 +1,80 @@
+<?php
+// +----------------------------------------------------------------------
+// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
+// +----------------------------------------------------------------------
+// | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
+// +----------------------------------------------------------------------
+// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
+// +----------------------------------------------------------------------
+// | Author: CRMEB Team <admin@crmeb.com>
+// +----------------------------------------------------------------------
+namespace app\common\dao\store\product;
+
+use app\common\dao\BaseDao;
+use app\common\model\store\product\ProductGroupUser;
+
+class ProductGroupUserDao extends  BaseDao
+{
+    public function getModel(): string
+    {
+        return ProductGroupUser::class;
+    }
+
+    /**
+     * 查询成功参与团购的用户信息
+     *
+     * 本函数用于查询特定团购活动(由$id$指定)中,状态为“成功”的用户的昵称和头像信息。
+     * 通过预先构建查询条件,筛选出参与了团购且团购状态为成功的用户,再进一步限定查询的字段为昵称和头像。
+     * 这样做的目的是为了减少数据库查询的负载,只获取必要的用户信息,提高查询效率。
+     *
+     * @param int $id 团购组的ID,用于指定查询哪个团购组的用户信息。
+     * @return array 返回一个包含符合条件的用户昵称和头像信息的数组。
+     */
+    public function successUser($id)
+    {
+        // 构建查询条件,筛选出团购状态为“成功”的用户
+        $query = ProductGroupUser::hasWhere('groupBuying',function($query){
+            $query->where('status',10);
+        });
+        // 限定查询的用户是属于指定团购组ID的
+        $query->where('ProductGroupUser.product_group_id',$id);
+        // 设置查询字段,只获取昵称和头像信息,减少数据传输量
+        return $query->setOption('field',[])->field('nickname,avatar')->select();
+    }
+
+    /**
+     * 更新团购状态
+     *
+     * 此方法用于将指定团购组的状态更新为10。这通常表示团购活动的某种特定状态,比如开启、结束等。
+     * 选择状态10的具体含义应该在业务逻辑中有所定义。
+     *
+     * @param int $groupId 团购组的ID。这个参数用于指定要更新状态的团购组。
+     * @return int 返回更新操作影响的行数。这可以用于判断更新操作是否成功。
+     */
+    public function updateStatus(int $groupId)
+    {
+        // 通过调用getModel方法获取模型实例,并直接调用其getDb方法来获取数据库连接。
+        // 然后使用where方法指定更新条件,这里是group_buying_id等于$groupId。
+        // 最后,使用update方法更新指定条件下的记录的状态为10。
+        return $this->getModel()::getDb()->where('group_buying_id',$groupId)->update(['status' => 10]);
+    }
+
+    /**
+     * 根据产品组ID获取相关订单ID列表
+     *
+     * 本函数旨在查询与特定产品组相关联的订单ID。它通过筛选满足特定条件的记录,
+     * 即产品组ID匹配且订单ID大于0,来实现这一目的。
+     *
+     * @param int $productGroupId 产品组ID,用于查询与该产品组相关的订单。
+     * @return array 返回一个包含满足条件的订单ID的数组。
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     */
+    public function groupOrderIds($productGroupId)
+    {
+        // 使用ProductGroupUser模型的数据库连接,并构造查询条件
+        // 筛选group_buying_id等于$productGroupId且order_id大于0的记录
+        return ProductGroupUser::getDB()->where('group_buying_id', $productGroupId)->where('order_id', '>', 0)->select();
+    }
+}

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