Explorar o código

【程序目录】更新v5.3.0

evoxwht hai 1 ano
pai
achega
c05e878f33
Modificáronse 100 ficheiros con 2090 adicións e 532 borrados
  1. 4 3
      crmeb/.phpstorm.meta.php
  2. 3 3
      crmeb/.version
  3. 159 0
      crmeb/README.md
  4. 29 0
      crmeb/app/README.md
  5. 1 0
      crmeb/app/Request.php
  6. 17 0
      crmeb/app/adminapi/README.md
  7. 58 5
      crmeb/app/adminapi/controller/Common.php
  8. 6 1
      crmeb/app/adminapi/controller/PublicController.php
  9. 167 254
      crmeb/app/adminapi/controller/UpgradeController.php
  10. 44 2
      crmeb/app/adminapi/controller/v1/agent/Division.php
  11. 48 0
      crmeb/app/adminapi/controller/v1/application/routine/RoutineScheme.php
  12. 1 0
      crmeb/app/adminapi/controller/v1/application/wechat/Menus.php
  13. 26 1
      crmeb/app/adminapi/controller/v1/diy/Diy.php
  14. 4 1
      crmeb/app/adminapi/controller/v1/kefu/StoreService.php
  15. 1 1
      crmeb/app/adminapi/controller/v1/marketing/StoreBargain.php
  16. 1 0
      crmeb/app/adminapi/controller/v1/order/RefundOrder.php
  17. 13 2
      crmeb/app/adminapi/controller/v1/order/StoreOrder.php
  18. 3 7
      crmeb/app/adminapi/controller/v1/setting/SystemCity.php
  19. 2 3
      crmeb/app/adminapi/controller/v1/setting/SystemConfig.php
  20. 2 0
      crmeb/app/adminapi/controller/v1/setting/SystemConfigTab.php
  21. 1 1
      crmeb/app/adminapi/controller/v1/setting/SystemCrud.php
  22. 55 0
      crmeb/app/adminapi/controller/v1/setting/SystemNotification.php
  23. 1 1
      crmeb/app/adminapi/controller/v1/system/Clear.php
  24. 2 0
      crmeb/app/adminapi/route/agent.php
  25. 7 0
      crmeb/app/adminapi/route/app.php
  26. 2 0
      crmeb/app/adminapi/route/common.php
  27. 0 85
      crmeb/app/adminapi/route/demo.php
  28. 3 0
      crmeb/app/adminapi/route/diy.php
  29. 1 1
      crmeb/app/adminapi/route/route.php
  30. 6 0
      crmeb/app/adminapi/route/setting.php
  31. 28 0
      crmeb/app/api/README.md
  32. 32 10
      crmeb/app/api/controller/v1/LoginController.php
  33. 19 0
      crmeb/app/api/controller/v1/PublicController.php
  34. 3 2
      crmeb/app/api/controller/v1/admin/StoreOrderController.php
  35. 5 1
      crmeb/app/api/controller/v1/order/StoreOrderController.php
  36. 20 1
      crmeb/app/api/controller/v1/user/DivisionController.php
  37. 2 4
      crmeb/app/api/controller/v1/user/UserSignController.php
  38. 3 1
      crmeb/app/api/route/v1.php
  39. 21 11
      crmeb/app/common.php
  40. 28 0
      crmeb/app/dao/README.md
  41. 1 0
      crmeb/app/dao/diy/PageCategoryDao.php
  42. 1 0
      crmeb/app/dao/diy/PageLinkDao.php
  43. 15 3
      crmeb/app/dao/order/StoreOrderDao.php
  44. 1 1
      crmeb/app/dao/order/StoreOrderRefundDao.php
  45. 5 4
      crmeb/app/dao/product/product/StoreProductDao.php
  46. 6 0
      crmeb/app/dao/system/SystemMenusDao.php
  47. 14 0
      crmeb/app/dao/wechat/RoutineSchemeDao.php
  48. 1 0
      crmeb/app/event.php
  49. 0 0
      crmeb/app/http/README.md
  50. 29 0
      crmeb/app/jobs/README.md
  51. 6 2
      crmeb/app/jobs/TemplateJob.php
  52. 39 0
      crmeb/app/jobs/TranslateJob.php
  53. 33 0
      crmeb/app/kefuapi/README.md
  54. 29 0
      crmeb/app/listener/README.md
  55. 159 0
      crmeb/app/listener/notice/CustomNoticeListener.php
  56. 1 1
      crmeb/app/listener/notice/NoticeListener.php
  57. 1 1
      crmeb/app/listener/order/OrderPaySuccessListener.php
  58. 2 2
      crmeb/app/listener/order/OrderShippingListener.php
  59. 7 1
      crmeb/app/listener/user/RegisterListener.php
  60. 33 0
      crmeb/app/model/README.md
  61. 15 1
      crmeb/app/model/diy/PageCategory.php
  62. 17 0
      crmeb/app/model/diy/PageLink.php
  63. 35 0
      crmeb/app/model/order/StoreOrder.php
  64. 16 0
      crmeb/app/model/system/SystemMenus.php
  65. 28 0
      crmeb/app/model/wechat/RoutineScheme.php
  66. 29 0
      crmeb/app/outapi/README.md
  67. 4 0
      crmeb/app/services/BaseServices.php
  68. 31 0
      crmeb/app/services/README.md
  69. 2 2
      crmeb/app/services/activity/bargain/StoreBargainUserServices.php
  70. 13 0
      crmeb/app/services/activity/combination/StorePinkServices.php
  71. 1 1
      crmeb/app/services/activity/integral/StorePointRecordServices.php
  72. 2 3
      crmeb/app/services/activity/lottery/LuckLotteryRecordServices.php
  73. 1 1
      crmeb/app/services/activity/seckill/StoreSeckillServices.php
  74. 12 8
      crmeb/app/services/agent/AgentLevelServices.php
  75. 1 1
      crmeb/app/services/agent/AgentManageServices.php
  76. 53 17
      crmeb/app/services/agent/DivisionAgentApplyServices.php
  77. 151 16
      crmeb/app/services/agent/DivisionServices.php
  78. 1 2
      crmeb/app/services/article/ArticleCategoryServices.php
  79. 5 2
      crmeb/app/services/message/MessageSystemServices.php
  80. 308 0
      crmeb/app/services/message/SystemNotificationServices.php
  81. 2 2
      crmeb/app/services/order/OtherOrderServices.php
  82. 1 0
      crmeb/app/services/order/OutStoreOrderRefundServices.php
  83. 16 0
      crmeb/app/services/order/StoreOrderDeliveryServices.php
  84. 24 5
      crmeb/app/services/order/StoreOrderRefundServices.php
  85. 33 30
      crmeb/app/services/order/StoreOrderServices.php
  86. 5 0
      crmeb/app/services/order/StoreOrderSuccessServices.php
  87. 31 3
      crmeb/app/services/order/StoreOrderTakeServices.php
  88. 4 0
      crmeb/app/services/order/StoreOrderWriteOffServices.php
  89. 3 0
      crmeb/app/services/out/OutInterfaceServices.php
  90. 1 1
      crmeb/app/services/product/product/CopyTaobaoServices.php
  91. 4 0
      crmeb/app/services/statistic/OrderStatisticServices.php
  92. 1 1
      crmeb/app/services/system/SystemMenusServices.php
  93. 1 0
      crmeb/app/services/system/admin/SystemAdminServices.php
  94. 2 1
      crmeb/app/services/system/config/SystemConfigServices.php
  95. 5 2
      crmeb/app/services/system/config/SystemConfigTabServices.php
  96. 14 0
      crmeb/app/services/system/lang/LangCodeServices.php
  97. 3 1
      crmeb/app/services/system/lang/LangTypeServices.php
  98. 1 1
      crmeb/app/services/system/log/ClearServices.php
  99. 3 15
      crmeb/app/services/user/LoginServices.php
  100. 0 0
      crmeb/app/services/user/UserBillServices.php

+ 4 - 3
crmeb/.phpstorm.meta.php

@@ -4,15 +4,16 @@ namespace PHPSTORM_META {
 
     use think\Container;
     use function \app;
-    // 容器注入
+
     override(
         \app(),
         map([
-            'json' => \crmeb\utils\Json::class // json类
+            'json' => \crmeb\utils\Json::class
         ])
     );
+
     override(
-        \think\Container::make(),// 容器实例化
+        \think\Container::make(),
         map([
             '' => '@'
         ])

+ 3 - 3
crmeb/.version

@@ -1,5 +1,5 @@
-version=CRMEB-KY v5.2.2
-version_code=522
-platform=gitee
+version=CRMEB-KY v5.3.0
+version_code=530
+platform=GITEE
 app_id=ze7x9rxsv09l6pvsyo
 app_key=fuF7U9zaybLa5gageVQzxtxQMFnvU2OI

+ 159 - 0
crmeb/README.md

@@ -103,6 +103,165 @@ php think workerman start --d
   - restart: 重启
 - --d : 后台执行
 
+## 开发规范
+#### 命名规范
+ThinkPHP6.0遵循PSR-2命名规范和PSR-4自动加载规范,并且注意如下规范:
+
+1. 目录和文件
+2. 目录使用小写+下划线;
+3. 类库、函数文件统一以.php为后缀;
+4. 类的文件名均以命名空间定义,并且命名空间的路径和类库文件所在路径一致;
+5. 类(包含接口和Trait)文件采用驼峰法命名(首字母大写),其它文件采用小写+下划线命名;
+6. 类名(包括接口和Trait)和文件名保持一致,统一采用驼峰法命名(首字母大写);
+
+#### 函数和类、属性命名
+
+1. 类的命名采用驼峰法(首字母大写),例如 User、UserType;
+2. common函数的命名使用小写字母和下划线(小写字母开头)的方式,例如 get_client_ip;
+3. 控制器里面的方法使用小写字母和下划线(小写字母开头)的方式,例如 get_client_ip
+4. 方法的命名使用驼峰法(首字母小写),例如 getUserName;
+5. 属性的命名使用驼峰法(首字母小写),例如 tableName、instance;
+6. 特例:以双下划线__打头的函数或方法作为魔术方法,例如 __call 和 __autoload;
+
+#### 常量和配置
+1. 常量以大写字母和下划线命名,例如 APP_PATH;
+2. 配置参数以小写字母和下划线命名,例如 url_route_on 和url_convert;
+3. 环境变量定义使用大写字母和下划线命名,例如APP_DEBUG;
+
+#### 数据表和字段
+1. 数据表和字段采用小写加下划线方式命名,并注意字段名不要以下划线开头,例如 think_user 表和 user_name字段,不建议使用驼峰和中文作为数据表及字段命名
+
+注意:请理解并尽量遵循以上命名规范,可以减少在开发过程中出现不必要的错误
+
+#### 语法规范
+1. 尽量使用php7新语法
+2. 每个 namespace 命名空间声明语句和 use 声明语句块后面,必须 插入一个空白行
+3. 类的开始花括号({) 必须 写在类声明后自成一行,结束花括号(})也 必须 写在类主体后自成一行
+4. 方法的开始花括号({) 必须 写在函数声明后自成一行,结束花括号(})也 必须 写在函数主体后自成一行。
+5. 类的属性和方法 必须 添加访问修饰符(private、protected 以及 public),abstract 以及 final 必须 声明在访问修饰符之前,而 static 必须 声明在访问修饰符之后
+6. 控制结构的关键字后 必须 要有一个空格符,而调用方法或函数时则 一定不可 有
+7. 控制结构的开始花括号({) 必须 写在声明的同一行,而结束花括号(}) 必须 写在主体后自成一行
+8. 纯 PHP 代码文件 必须 省略最后的 ?> 结束标签
+9. 所有方法,类,控制器类,都 必须 添加访问修饰符
+    ~~~
+
+    /**
+     * 中文注释
+     * @param string $str 声明类型
+     * @param array $arr
+     * @return bool
+     */
+    public function action(string $str, array $arr)
+    {
+         return true;
+    }
+    ~~~
+10. 参数列表中,每个逗号后面 必须 要有一个空格,而逗号前面 一定不可 有空格
+    ~~~
+     function foo($arg1, &$arg2, $arg3 = [])
+     {
+            // method body
+     }
+    ~~~
+11. 参数 可以 分列成多行,此时包括第一个参数在内的每个参数都 尽量 单独成行。
+    ~~~
+    <?php
+    $foo->bar(
+        $longArgument,
+        $longerArgument,
+        $muchLongerArgument
+    );
+    ~~~
+12. 标准的 if 结构如下代码所示,请留意「括号」、「空格」以及「花括号」的位置,
+    注意 else 和 elseif 都与前面的结束花括号在同一行
+    ~~~
+    <?php
+    if ($expr1) {
+        // if body
+    } elseif ($expr2) {
+        // elseif body
+    } else {
+        // else body;
+    }
+    ~~~
+13. 赋值等号前后必须加空格符
+    ~~~
+    <?php
+    $arr = [];
+    ~~~
+
+
+#### PHP 7.1+ 常用新语法
+
+1. 三元运算符
+   ~~~
+   <?php
+
+   $arr = ['crmeb'=>true];
+   之前
+   echo isset($arr['crmeb']) ? $arr['crmeb'] : '';
+   之后
+   echo $arr['crmeb'] ?? '';
+   ~~~
+2.  define() 定义常量数组
+   ~~~
+   <?php
+    define('ARR',['a','b']);
+   ~~~
+3.  命名空间优化
+   ~~~
+    <?php
+    //PHP7之前语法
+    use FooLibrary\Bar\Baz\ClassA;
+    use FooLibrary\Bar\Baz\ClassB;
+    // PHP7新语法写法
+    use FooLibrary\Bar\Baz\{ ClassA, ClassB};
+
+   ~~~
+#### CRMEB PRO规范
+ 1. 所有数据验证放在模块下的 validates 目录下
+ 2. JSON返回使用父级 AuthController类中的success 和 fail
+ 3. 错误判断抛出异常,由一个错误类统一控制输出
+    ~~~
+    <?php
+
+        throw new AuthException('错误信息',400);
+    ~~~
+ 4. 错误码和错误提示语应该统一管理,方便切换多语言
+ 5. 数据库操作使用模型类,不能使用Db::table()
+ 6. 获取表单数据使用 app\Request
+    ~~~
+    <?php
+    use app\Request;
+
+
+    public function index(Request $request) {
+
+        //获取提交的数据,并以二维数组形式返回
+        $arr = $request->getMore([
+            'name',
+            'nickname'
+        ]);
+        //获取提交的数据,并以二维数组形式返回并附加默认值
+        $arr = $request->getMore([
+           ['name','123'],
+           ['nickname','0']
+        ]);
+        //获取提交的数据,并以一维数组形式返回并附加默认值
+        [$name, $nickname] = $request->getMore([
+           ['name','123'],
+           ['nickname','0']
+        ],true);
+
+    }
+    ~~~
+ 7. 所有控制器类命令和表名对应,按照大驼峰命名规范
+ 8. 所有文件夹命名按照小写字母加下划线定义
+ 9. 所有属性名,变量名尽量遵守小驼峰命名规范
+ 10. 复杂逻辑,多状态应适当添加行内注释
+ 11. 模型里只能写关于搜索条件语句,查出数据得组合书写在services层进行处理,services创建命令:php make:services api@user/User
+
+
 ## 文档
 
 [使用手册](https://doc.crmeb.com)

+ 29 - 0
crmeb/app/README.md

@@ -0,0 +1,29 @@
+在ThinkPHP框架中,"app"目录是用于存放应用程序的核心代码和资源的目录。它是整个应用程序的主要工作目录,包含了应用的业务逻辑、控制器、模型、视图以及其他相关组件。
+
+具体来说,"app"目录通常包含以下几个子目录和文件:
+
+app/adminapi:该目录包含管理端应用的控制器文件,用于处理用户请求、业务逻辑和数据交互等操作。
+
+app/api:该目录包含用户端应用的控制器文件,用于处理用户请求、业务逻辑和数据交互等操作。
+
+app/dao: 该目录包含数据访问对象(DAO)的类文件,用于封装数据访问操作,提供统一的数据访问接口。
+
+app/http:该目录包含HTTP请求和响应跨域中间键。
+
+app/jobs:该目录包含所有的消息队列任务。
+
+app/kefuapi:该目录包含客服端应用的控制器文件,用于处理用户请求、业务逻辑和数据交互等操作。
+
+app/lang:该目录包含语言包文件,用于支持多语言功能。
+
+app/listener:该目录包含事件监听器的类文件,用于处理系统事件和事件通知。
+
+app/model:该目录包含模型类文件,用于封装数据访问操作,提供统一的数据访问接口。
+
+app/outapi:该目录包含对外接口应用的控制器文件,用于处理用户请求、业务逻辑和数据交互等操作。
+
+app/service:该目录包含服务类文件,用于封装业务逻辑和数据交互操作,提供统一的服务接口。
+
+除了上述目录外,"app"目录还可能包含其他自定义的子目录,用于组织应用的不同模块或功能。
+
+总之,"app"目录是ThinkPHP应用程序的核心目录,其中的文件和目录结构定义了应用的业务逻辑、数据交互和用户界面等方面。开发者可以在该目录下编写和组织代码,实现具体的应用功能。

+ 1 - 0
crmeb/app/Request.php

@@ -118,6 +118,7 @@ class Request extends \think\Request
     {
         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($farr, '', str_replace($host, '', $str));
         } else {

+ 17 - 0
crmeb/app/adminapi/README.md

@@ -0,0 +1,17 @@
+crmeb/app/adminapi这个目录主要是后台管理系统的API接口文件。
+
+具体来说:
+
+- adminapi目录下的文件都是后台管理系统的控制器(Controller)文件,这些控制器被用来处理后台系统的各种请求。
+
+- 每一个控制器文件对应后台管理系统某个功能模块,比如AuthController处理认证模块请求,StoreProduct处理商品模块请求等。
+
+- 控制器内有各种方法,这些方法就相当于API接口,可以处理GET、POST请求,返回JSON数据。
+
+- 浏览器或APP在调用这些API接口时,会发送请求到相应的控制器方法,例如登录接口请求到Login文件的login方法。
+
+- 控制器处理完请求后,通过返回Response对象返回处理结果给浏览器或APP。
+
+所以简单来说,adminapi目录负责后台管理系统的所有API接口,这些接口被APP或前端调用来完成各种管理操作,如查询数据、添加修改删除等。开发者在新增后台功能时,也需要在此目录增加对应的控制器和接口。
+
+它实际上负责后台系统的通信交互层,解耦了后端逻辑和前端展示,采用 RESTful规范设计。

+ 58 - 5
crmeb/app/adminapi/controller/Common.php

@@ -11,6 +11,7 @@
 namespace app\adminapi\controller;
 
 use app\services\system\config\SystemConfigServices;
+use app\services\system\config\SystemConfigTabServices;
 use app\services\system\SystemAuthServices;
 use app\services\order\StoreOrderServices;
 use app\services\product\product\StoreProductServices;
@@ -22,6 +23,7 @@ use app\services\system\SystemMenusServices;
 use app\services\user\UserServices;
 use crmeb\services\CacheService;
 use crmeb\services\HttpService;
+use think\facade\Config;
 
 /**
  * 公共接口基类 主要存放公共接口
@@ -236,28 +238,28 @@ class Common extends AuthController
             $value[] = [
                 'title' => "您有$data[ordernum]个待发货的订单",
                 'type' => 1,
-                'url' => '/admin/order/list?status=1'
+                'url' => '/' . Config::get('app.admin_prefix', 'admin') . '/order/list?status=1'
             ];
         }
         if ($data['inventory'] != 0) {
             $value[] = [
                 'title' => "您有$data[inventory]个商品库存预警",
                 'type' => 2,
-                'url' => '/admin/product/product_list?type=5',
+                'url' => '/' . Config::get('app.admin_prefix', 'admin') . '/product/product_list?type=5',
             ];
         }
         if ($data['commentnum'] != 0) {
             $value[] = [
                 'title' => "您有$data[commentnum]条评论待回复",
                 'type' => 3,
-                'url' => '/admin/product/product_reply?is_reply=0'
+                'url' => '/' . Config::get('app.admin_prefix', 'admin') . '/product/product_reply?is_reply=0'
             ];
         }
         if ($data['reflectnum'] != 0) {
             $value[] = [
                 'title' => "您有$data[reflectnum]个提现申请待审核",
                 'type' => 4,
-                'url' => '/admin/finance/user_extract/index?status=0',
+                'url' => '/' . Config::get('app.admin_prefix', 'admin') . '/finance/user_extract/index?status=0',
             ];
         }
         return app('json')->success($this->noticeData($value));
@@ -406,5 +408,56 @@ class Common extends AuthController
         return app('json')->success(100000);
     }
 
-
+    /**
+     * 系统搜索菜单
+     * @return \think\Response
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2024/2/1
+     */
+    public function menusSearch()
+    {
+        // 从请求中获取关键字
+        [$keyword] = $this->request->postMore([
+            ['keyword', ''],
+        ], true);
+        // 获取系统菜单服务实例
+        $menusServices = app()->make(SystemMenusServices::class);
+        // 查询菜单列表
+        $menusList = $menusServices->selectList([['menu_name', 'like', '%' . $keyword . '%'], ['auth_type', '=', 1]], 'menu_name as title,menu_path as path,id')->toArray();
+        // 获取系统配置服务实例
+        $configServices = app()->make(SystemConfigServices::class);
+        // 获取系统配置标签服务实例
+        $configTabServices = app()->make(SystemConfigTabServices::class);
+        // 查询配置项列表
+        $configList = $configServices->selectList([['info', 'like', '%' . $keyword . '%']], 'info as title,config_tab_id')->toArray();
+        $configTabList = $configTabServices->selectList([['title', 'like', '%' . $keyword . '%']], 'title,id as config_tab_id')->toArray();
+        $configAllList = array_merge($configList, $configTabList);
+        // 获取配置项对应的标签ID
+        $tabIds = array_unique(array_column($configAllList, 'config_tab_id'));
+        // 查询配置项标签列表
+        $tabList = $configTabServices->getColumn([['id', 'in', $tabIds]], 'menus_id', 'id');
+        // 将配置项标签列表中的菜单ID与配置项列表中的菜单ID对应起来
+        foreach ($configAllList as &$item1) {
+            $item1['menus_id'] = $tabList[$item1['config_tab_id']] ?? 0;
+        }
+        // 获取配置项标签对应的菜单ID
+        $configTabIds = array_values($tabList);
+        // 查询配置项标签对应的菜单列表
+        $configMenusList = $menusServices->getColumn([['id', 'in', $configTabIds]], 'menu_name as title,menu_path as path,id', 'id');
+        // 将配置项列表中的菜单ID与配置项标签对应的菜单ID对应起来
+        foreach ($configAllList as $item2) {
+            $menusList[] = [
+                'title' => $configMenusList[$item2['menus_id']]['title'] . '-' . $item2['title'],
+                'path' => $configMenusList[$item2['menus_id']]['path'] . '?tab_id=' . $item2['config_tab_id'],
+                'id' => $configMenusList[$item2['menus_id']]['id']
+            ];
+        }
+        // 将菜单列表中的路径前缀添加到每个菜单项的 path 属性上
+        foreach ($menusList as &$item) {
+            $item['path'] = '/' . Config::get('app.admin_prefix', 'admin') . $item['path'];
+        }
+        // 返回 JSON 格式的菜单列表
+        return app('json')->success($menusList);
+    }
 }

+ 6 - 1
crmeb/app/adminapi/controller/PublicController.php

@@ -26,8 +26,13 @@ class PublicController
      * @param string $key
      * @return Response|\think\response\File
      */
-    public function download(string $key = '')
+    public function download(Request $request, string $key = '')
     {
+        if ($key == '') {
+            $key = $request->getMore([
+                ['key', ''],
+            ], true);
+        }
         if (!$key) {
             return Response::create()->code(500);
         }

+ 167 - 254
crmeb/app/adminapi/controller/UpgradeController.php

@@ -734,307 +734,220 @@ class UpgradeController
      */
     public function upData()
     {
-        $data['new_version'] = 'CRMEB-BZ v5.2.1';
-        $data['new_code'] = 521;
+        $data['new_version'] = 'CRMEB-BZ v5.3.0';
+        $data['new_code'] = 530;
         $data['update_sql'] = [
             [
-                'code' => 521,
-                'type' => 3,
-                'table' => "agent_level",
-                'field' => "one_brokerage_percent",
-                'findSql' => "show columns from `@table` like 'one_brokerage_percent'",
-                'sql' => "ALTER TABLE `@table` ADD `one_brokerage_percent` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '一级分佣比例' AFTER `one_brokerage`"
-            ],
-            [
-                'code' => 521,
-                'type' => 3,
-                'table' => "agent_level",
-                'field' => "two_brokerage_percent",
-                'findSql' => "show columns from `@table` like 'two_brokerage_percent'",
-                'sql' => "ALTER TABLE `@table` ADD `two_brokerage_percent` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '二级分佣比例' AFTER `two_brokerage`"
-            ],
-            [
-                'code' => 521,
-                'type' => 3,
-                'table' => "store_bargain",
-                'field' => "is_commission",
-                'findSql' => "show columns from `@table` like 'is_commission'",
-                'sql' => "ALTER TABLE `@table` ADD `is_commission` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否返佣'"
-            ],
-            [
-                'code' => 521,
-                'type' => 3,
-                'table' => "store_seckill",
-                'field' => "is_commission",
-                'findSql' => "show columns from `@table` like 'is_commission'",
-                'sql' => "ALTER TABLE `@table` ADD `is_commission` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否返佣'"
-            ],
-            [
-                'code' => 521,
-                'type' => 6,
-                'table' => "system_config_tab",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `eng_title` = 'wechat_config'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, @tabId, '公众号配置', 'wechat_config', 1, 0, '', 3, 0)"
-            ],
-            [
-                'code' => 521,
-                'type' => 6,
+                'code' => 530,
+                'type' => -1,
                 'table' => "system_config_tab",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `eng_title` = 'wechat_encoding'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, @tabId, '服务器域名配置', 'wechat_encoding', 1, 0, '', 3, 0)"
+                'sql' => "truncate table `@table`"
             ],
             [
-                'code' => 521,
-                'type' => 6,
+                'code' => 530,
+                'type' => 3,
                 'table' => "system_config_tab",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `eng_title` = 'routine_config'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, @tabId, '小程序配置', 'routine_config', 1, 0, '', 3, 0)"
+                'field' => "menus_id",
+                'findSql' => "show columns from `@table` like 'menus_id'",
+                'sql' => "ALTER TABLE `@table` ADD `menus_id` int(11) NOT NULL DEFAULT '0' COMMENT '菜单ID'"
             ],
             [
-                'code' => 521,
-                'type' => 6,
+                'code' => 530,
+                'type' => -1,
                 'table' => "system_config_tab",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `eng_title` = 'routine_encoding'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, @tabId, '消息推送配置', 'routine_encoding', 1, 0, '', 3, 0)"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'wechat_appid'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'wechat_appid'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'wechat_appsecret'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'wechat_appsecret'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'wechat_token'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_encoding'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'wechat_token'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'wechat_encode'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_encoding'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'wechat_encode'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'wechat_encodingaeskey'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_encoding'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'wechat_encodingaeskey'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'wechat_qrcode'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'wechat_qrcode'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_appId'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'routine_appId'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_appsecret'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'routine_appsecret'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'api'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_encoding'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'api'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_name'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'routine_name'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_contact_type'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'routine_contact_type'"
+                'sql' => <<<SQL
+INSERT INTO `@table` (`id`, `pid`, `title`, `eng_title`, `status`, `info`, `icon`, `type`, `sort`, `menus_id`) VALUES
+(1, 129, '基础配置', 'basics', 1, 0, 'ios-settings', 0, 0, 23),
+(2, 78, '公众号配置(H5)', 'wechat', 1, 0, 'ios-chatbubbles', 3, 0, 1006),
+(4, 23, '微信支付配置', 'pay', 1, 0, 'ios-chatbubbles', 3, 0, 1063),
+(7, 78, '小程序配置', 'routine', 1, 0, 'logo-android', 3, 0, 1007),
+(9, 0, '分销配置', 'fenxiao', 1, 0, 'md-contacts', 3, 0, 28),
+(11, 100, '用户积分配置', 'point', 0, 0, 'logo-euro', 3, 0, 3423),
+(18, 65, '一号通', 'system_sms', 1, 0, 'ios-chatboxes', 3, 99, 3418),
+(21, 65, '小票打印配置', 'printing_deploy', 1, 0, 'logo-buffer', 3, 0, 1057),
+(23, 65, '商城支付配置', 'pay_config', 1, 0, 'logo-usd', 3, 70, 1063),
+(28, 100, '用户充值配置', 'recharge_site', 0, 0, '', 3, 2, 3423),
+(31, 79, '基础配置', 'base_config', 0, 0, '', 0, 0, 1012),
+(41, 65, '采集商品配置', 'copy_product', 1, 0, '', 3, 0, 1058),
+(45, 100, '用户等级配置', 'store_member', 1, 0, '', 3, 3, 3423),
+(50, 113, '发票功能配置', 'store_invoice', 1, 0, '', 3, 0, 3424),
+(63, 23, '支付宝支付配置', 'ali_pay', 1, 0, '', 3, 0, 1063),
+(64, 65, '物流查询配置', 'logistics_select', 1, 0, '', 3, 0, 1059),
+(65, 0, '接口设置', 'system_serve', 1, 0, 'md-briefcase', 3, 0, 1056),
+(66, 65, '电子面单配置', 'electronic_sheet', 1, 0, '', 3, 0, 1060),
+(67, 100, '付费会员配置', 'member_card', 0, 0, '', 3, 2, 3423),
+(69, 0, '客服配置', 'kefu_config', 1, 0, '', 3, 0, 3421),
+(70, 129, '分享配置', 'share_index_config', 1, 0, '', 0, 0, 23),
+(71, 113, '售后退款配置', 'refund_config', 1, 0, '', 3, 0, 3424),
+(72, 9, '分销模式', 'brokerage_type', 1, 0, '', 3, 0, 28),
+(73, 9, '返佣设置', 'brokerage_set', 1, 0, '', 3, 0, 28),
+(74, 9, '提现设置', 'extract_set', 1, 0, '', 3, 0, 28),
+(75, 78, 'PC站点配置', 'system_pc', 1, 0, '', 3, 0, 1010),
+(77, 78, 'APP配置', 'app', 1, 0, '', 3, 0, 1011),
+(78, 0, '应用配置', 'sys_app', 1, 0, '', 3, 0, 135),
+(79, 65, '系统存储配置', 'storage_config', 1, 0, '', 3, 0, 1012),
+(80, 79, '七牛云配置', 'qiniu_config', 0, 0, '', 0, 0, 1012),
+(81, 79, '阿里云配置', 'oss_config', 0, 0, '', 0, 0, 1012),
+(82, 79, '腾讯云配置', 'cos_config', 0, 0, '', 0, 0, 1012),
+(86, 21, '基础配置', 'print_basic', 1, 0, '', 3, 0, 1057),
+(87, 21, '易联云配置', 'yly_config', 1, 0, '', 3, 0, 1057),
+(89, 41, '基础配置', 'copy_basic', 1, 0, '', 3, 0, 1058),
+(90, 41, '99api配置', '99api_config', 1, 0, '', 3, 0, 1058),
+(91, 64, '基础配置', 'logistics_basic', 1, 0, '', 3, 0, 1059),
+(92, 64, '阿里云配置', 'logistics_aliyun', 1, 0, '', 3, 0, 1059),
+(93, 66, '基础配置', 'electronic_basic', 1, 0, '', 3, 0, 1060),
+(94, 66, '一号通配置', 'system_electronic_config', 1, 0, '', 3, 0, 1060),
+(96, 65, '短信接口配置', 'sms_config', 1, 0, '', 3, 0, 1062),
+(97, 96, '基础配置', 'sms_config_basic', 1, 0, '', 3, 0, 1062),
+(98, 96, '阿里云配置', 'sms_aliyun', 1, 0, '', 3, 0, 1062),
+(99, 96, '腾讯云配置', 'tencent_sms', 1, 0, '', 3, 0, 1062),
+(100, 0, '用户配置', 'system_user_config', 1, 0, 'md-contact', 3, 0, 3423),
+(102, 65, '对外接口配置', 'out_config', 1, 0, '', 3, 0, 0),
+(103, 102, '基础配置', 'out_basic', 1, 0, '', 3, 0, 0),
+(104, 102, '推送配置', 'out_push', 1, 0, '', 3, 0, 0),
+(105, 100, '新用户设置', 'new_user_setting', 1, 0, '', 3, 0, 3423),
+(106, 129, '翻译配置', 'online_translation', 0, 0, '', 3, 0, 23),
+(107, 21, '飞鹅云配置', 'fey_config', 1, 0, '', 3, 0, 1057),
+(108, 23, '通联支付', 'allinpay', 1, 0, '', 3, 0, 1063),
+(109, 23, '基础配置', 'pay_basic', 1, 0, '', 3, 100, 1063),
+(110, 79, '京东云配置', 'jd_oss_config', 0, 0, '', 0, 0, 1012),
+(111, 79, '华为云配置', 'obs_config', 0, 0, '', 0, 0, 1012),
+(112, 79, '天翼云配置', 'ty_oss_config', 0, 0, '', 0, 0, 1012),
+(113, 0, '订单配置', 'order_config', 1, 0, '', 3, 0, 3424),
+(114, 113, '包邮设置', 'free_shipping_config', 1, 0, '', 3, 10, 3424),
+(115, 113, '订单取消配置', 'order_cancel_config', 1, 0, '', 3, 0, 3424),
+(116, 113, '自动收货配置', 'auto_take_config', 1, 0, '', 3, 0, 3424),
+(117, 113, '自动评价配置', 'auto_reviews_config', 1, 0, '', 3, 0, 3424),
+(119, 113, '到店自提配置', 'self_mention_config', 1, 0, '', 3, 0, 3424),
+(120, 113, '警戒库存配置', 'store_stock_config', 1, 0, '', 3, 0, 3424),
+(122, 129, 'LOGO配置', 'site_logo_config', 1, 0, '', 0, 0, 23),
+(123, 129, '统计配置', 'statistics_config', 1, 0, '', 0, 0, 23),
+(124, 129, '地图配置', 'map_config', 1, 0, '', 0, 0, 23),
+(125, 129, '备案配置', 'beian_config', 1, 0, '', 0, 0, 23),
+(126, 100, '用户签到配置', 'user_sign_config', 0, 0, '', 3, 0, 3423),
+(129, 0, '系统配置', 'system_config', 1, 0, '', 0, 0, 23),
+(130, 2, '公众号配置', 'wechat_config', 1, 0, '', 3, 0, 1006),
+(131, 2, '服务器域名配置', 'wechat_encoding', 1, 0, '', 3, 0, 1006),
+(132, 7, '小程序配置', 'routine_config', 1, 0, '', 3, 0, 1007),
+(133, 7, '消息推送配置', 'routine_encoding', 1, 0, '', 3, 0, 1007),
+(134, 129, '模块配置', 'model_config', 1, 0, '', 0, 0, 23);
+SQL
             ],
             [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'weixin_ckeck_file'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'weixin_ckeck_file'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'create_wechat_user'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'create_wechat_user'"
-            ],
-            [
-                'code' => 521,
-                'type' => 7,
+                'code' => 530,
+                'type' => -1,
                 'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'get_avatar'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'get_avatar'"
+                'sql' => "UPDATE `@table` SET `parameter` = '0=>银行卡\n1=>微信\n2=>支付宝\n3=>余额' WHERE `menu_name` = 'store_free_postage'"
             ],
             [
-                'code' => 521,
-                'type' => 7,
+                'code' => 530,
+                'type' => -1,
                 'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'share_qrcode'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'share_qrcode'"
+                'sql' => "DELETE FROM `@table` WHERE `id` = 459"
             ],
             [
-                'code' => 521,
-                'type' => 7,
+                'code' => 530,
+                'type' => 6,
                 'table' => "system_config",
                 'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'order_shipping_open'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'order_shipping_open'"
+                'findSql' => "select id from @table where `menu_name` = 'model_checkbox'",
+                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='model_config'",
+                'sql' => "INSERT INTO `@table` VALUES (NULL, 'model_checkbox', 'checkbox', 'input', @tabId, 'seckill=>秒杀\nbargain=>砍价\ncombination=>拼团', 1, '', 0, 0, '[\"seckill\",\"bargain\",\"combination\"]', '模块配置', '模块配置,选中展示对应的模块,取消选中则前后端不展示模块相关内容', 0, 1)"
             ],
             [
-                'code' => 521,
-                'type' => 7,
+                'code' => 530,
+                'type' => 6,
                 'table' => "system_config",
                 'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_auth_type'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'routine_auth_type'"
+                'findSql' => "select id from @table where `menu_name` = 'sp_appid'",
+                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='pay'",
+                'sql' => "INSERT INTO `@table` VALUES (NULL, 'sp_appid', 'text', 'input', @tabId, '', 1, '', 100, 0, '\"\"', '主商户APPID', '开启服务商支付,需要配置主商户申请的时候开通的公众号服务号的APPID', 0, 1)"
             ],
             [
-                'code' => 521,
-                'type' => 7,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'wechat_template_to_miniprogram'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='wechat_config'",
-                'sql' => "UPDATE `@table` SET `config_tab_id` = @tabId WHERE `menu_name` = 'wechat_template_to_miniprogram'"
+                'code' => 530,
+                'type' => 3,
+                'table' => "system_notification",
+                'field' => "wechat_data",
+                'findSql' => "show columns from `@table` like 'wechat_data'",
+                'sql' => "ALTER TABLE `@table` ADD `wechat_data` varchar(255) NOT NULL DEFAULT '' COMMENT '模版消息参数' AFTER `wechat_tempid`"
             ],
             [
-                'code' => 521,
-                'type' => 6,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'refund_time_available'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='refund_config'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, 'refund_time_available', 'text', 'input', @tabId, '', 1, '', 100, 0, '0', '售后期限', '订单收货之后,在多少天内可以进行退款,超出天数前端不显示退款按钮,设置0则永远显示', 0, 1)"
+                'code' => 530,
+                'type' => 3,
+                'table' => "system_notification",
+                'field' => "wechat_link",
+                'findSql' => "show columns from `@table` like 'wechat_link'",
+                'sql' => "ALTER TABLE `@table` ADD `wechat_link` varchar(255) NOT NULL DEFAULT '' COMMENT '模版消息链接' AFTER `wechat_data`"
             ],
             [
-                'code' => 521,
-                'type' => 6,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_api'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_encoding'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, 'routine_api', 'text', 'input', @tabId, '', 1, '', 100, 0, '\"\\/api\\/wechat\\/miniServe\"', '接口地址', '配置小程序消息推送使用的接口地址,直接复制输入框内容(此项系统生成,无法修改)', 0, 1)"
+                'code' => 530,
+                'type' => 3,
+                'table' => "system_notification",
+                'field' => "wechat_to_routine",
+                'findSql' => "show columns from `@table` like 'wechat_to_routine'",
+                'sql' => "ALTER TABLE `@table` ADD `wechat_to_routine` int(1) NOT NULL DEFAULT '0' COMMENT '模版消息跳转小程序' AFTER `wechat_link`"
             ],
             [
-                'code' => 521,
-                'type' => 6,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_token'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_encoding'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, 'routine_token', 'text', 'input', @tabId, '', 1, '', 100, 0, '\"\"', '小程序验证TOKEN', '小程序验证TOKEN', 0, 1)"
+                'code' => 530,
+                'type' => 3,
+                'table' => "system_notification",
+                'field' => "routine_data",
+                'findSql' => "show columns from `@table` like 'routine_data'",
+                'sql' => "ALTER TABLE `@table` ADD `routine_data` varchar(255) NOT NULL DEFAULT '' COMMENT '订阅消息参数' AFTER `routine_tempid`"
             ],
             [
-                'code' => 521,
-                'type' => 6,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_encode'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_encoding'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, 'routine_encode', 'radio', 'input', @tabId, '0=>明文模式\n1=>兼容模式\n2=>安全模式', 1, '', 0, 0, '0', '消息加解密方式', '小程序消息推送中的消息加密方式,暂时仅支持明文模式', 0, 1)"
+                'code' => 530,
+                'type' => 3,
+                'table' => "system_notification",
+                'field' => "routine_link",
+                'findSql' => "show columns from `@table` like 'routine_link'",
+                'sql' => "ALTER TABLE `@table` ADD `routine_link` varchar(255) NOT NULL DEFAULT '' COMMENT '订阅消息链接' AFTER `routine_data`"
             ],
             [
-                'code' => 521,
-                'type' => 6,
-                'table' => "system_config",
-                'whereTable' => "system_config_tab",
-                'findSql' => "select id from @table where `menu_name` = 'routine_encodingaeskey'",
-                'whereSql' => "SELECT id as tabId FROM `@whereTable` WHERE `eng_title`='routine_encoding'",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, 'routine_encodingaeskey', 'text', 'input', @tabId, '', 1, '', 0, 0, '\"\"', 'EncodingAESKey', '消息加密密钥由43位字符组成,字符范围为A-Z,a-z,0-9', 0, 1)"
+                'code' => 530,
+                'type' => 3,
+                'table' => "system_notification",
+                'field' => "custom_trigger",
+                'findSql' => "show columns from `@table` like 'custom_trigger'",
+                'sql' => "ALTER TABLE `@table` ADD `custom_trigger` varchar(255) NOT NULL DEFAULT '' COMMENT '自定义消息触发位置' AFTER `add_time`"
             ],
             [
-                'code' => 521,
-                'type' => -1,
-                'table' => "system_menus",
-                'sql' => "DELETE FROM `@table` WHERE `id` = 657"
+                'code' => 530,
+                'type' => 3,
+                'table' => "system_notification",
+                'field' => "custom_variable",
+                'findSql' => "show columns from `@table` like 'custom_variable'",
+                'sql' => "ALTER TABLE `@table` ADD `custom_variable` varchar(1000) NOT NULL DEFAULT '' COMMENT '自定义消息变量' AFTER `custom_trigger`"
             ],
             [
-                'code' => 521,
+                'code' => 530,
                 'type' => -1,
                 'table' => "system_menus",
-                'sql' => "INSERT INTO `@table` VALUES (657, 656, '', '首页装修', 'admin', '', '', '', '', '[]', 100, 1, 1, 1, '/setting/pages/devise/0', '12/656', 1, '', 0, 'admin-setting-pages-devise', 0, '页面设计')"
+                'sql' => "INSERT INTO `@table` VALUES (NULL, 993, '', '小程序链接', 'admin', '', '', '', '', '[]', 0, 1, 1, 1, '/app/routine/link', '135/993', 1, '', 0, '', 0, '')"
             ],
             [
-                'code' => 521,
+                'code' => 530,
                 'type' => -1,
                 'table' => "system_menus",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, 656, '', '商品分类', 'admin', '', '', '', '', '[]', 95, 1, 1, 1, '/setting/pages/cate_page/1', '656', 1, '', 0, '', 0, '')"
+                'sql' => "INSERT INTO `@table` VALUES (NULL, 56, '', '模块配置', 'admin', '', '', '', '', '[]', 0, 1, 1, 1, '/marketing/integral/system_config/2/134', '25/56', 1, '', 0, 'system-model-system_config', 0, '')"
             ],
             [
-                'code' => 521,
-                'type' => -1,
-                'table' => "system_menus",
-                'sql' => "INSERT INTO `@table` VALUES (NULL, 656, '', '个人中心', 'admin', '', '', '', '', '[]', 90, 1, 1, 1, '/setting/pages/user_page/2', '656', 1, '', 0, '', 0, '')"
+                'code' => 530,
+                'type' => 1,
+                'table' => "routine_scheme",
+                'findSql' => "select * from information_schema.tables where table_name ='@table'",
+                'sql' => "CREATE TABLE IF NOT EXISTS `@table` (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
+  `title` varchar(255) NOT NULL DEFAULT '' COMMENT '名称',
+  `path` varchar(255) NOT NULL DEFAULT '' COMMENT '小程序页面地址',
+  `url` varchar(255) NOT NULL DEFAULT '' COMMENT '生成链接地址',
+  `add_time` int(11) NOT NULL DEFAULT '0' COMMENT '添加时间',
+  `expire_type` int(11) NOT NULL DEFAULT '-1' COMMENT '到期类型',
+  `expire_interval` int(11) NOT NULL DEFAULT '0' COMMENT '到期天数',
+  `expire_time` int(11) NOT NULL DEFAULT '0' COMMENT '到期时间',
+  `is_del` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='小程序外链'"
             ],
         ];
         return $data;

+ 44 - 2
crmeb/app/adminapi/controller/v1/agent/Division.php

@@ -100,7 +100,8 @@ class Division extends AuthController
             ['pwd', ''],
             ['conf_pwd', ''],
             ['real_name', ''],
-            ['roles', []]
+            ['roles', []],
+            ['image', []]
         ]);
         $this->services->divisionSave($data);
         return app('json')->success(100000);
@@ -134,9 +135,14 @@ class Division extends AuthController
             ['division_end_time', ''],
             ['division_status', 1],
             ['edit', 0],
+            ['image', []],
         ]);
-        $userInfo = $userServices->count(['uid' => (int)$data['uid']]);
+        if ((int)$data['uid'] == 0) $data['uid'] = $data['image']['uid'];
+        $userInfo = $userServices->getUserInfo($data['uid'], 'is_division,is_agent,is_staff');
         if (!$userInfo) throw new AdminException(100100);
+        if ($userInfo['is_division']) throw new AdminException('此用户是事业部,请勿添加为代理商');
+        if ($userInfo['is_agent']) throw new AdminException('此用户是代理商,无法重复添加');
+        if ($userInfo['is_staff']) throw new AdminException('此用户是下级员工,无法添加为代理商');
         $divisionUserInfo = $userServices->count(['uid' => (int)$data['division_id'], 'is_division' => 1, 'division_id' => $data['division_id']]);
         if (!$divisionUserInfo) throw new AdminException(100100);
         $this->services->divisionAgentSave($data);
@@ -241,4 +247,40 @@ class Division extends AuthController
         $applyServices->delApply($id);
         return app('json')->success(100002);
     }
+
+    /**
+     * 添加员工表单
+     * @param $uid
+     * @return \think\Response
+     * @throws \FormBuilder\Exception\FormBuilderException
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2024/1/22
+     */
+    public function divisionStaffCreate($uid)
+    {
+        return app('json')->success($this->services->getDivisionStaffForm((int)$uid));
+    }
+
+    /**
+     * 保存员工
+     * @return \think\Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2024/1/22
+     */
+    public function divisionStaffSave()
+    {
+        $data = $this->request->getMore([
+            ['uid', 0],
+            ['division_percent', 0],
+            ['agent_id', 0],
+            ['image', []],
+        ]);
+        $this->services->divisionStaffSave($data);
+        return app('json')->success(100000);
+    }
 }

+ 48 - 0
crmeb/app/adminapi/controller/v1/application/routine/RoutineScheme.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace app\adminapi\controller\v1\application\routine;
+
+use app\adminapi\controller\AuthController;
+use app\services\wechat\RoutineSchemeServices;
+use think\facade\App;
+
+class RoutineScheme extends AuthController
+{
+    public function __construct(App $app, RoutineSchemeServices $services)
+    {
+        parent::__construct($app);
+        $this->services = $services;
+    }
+
+    public function schemeList()
+    {
+        $where = $this->request->postMore([
+            ['title', ''],
+        ]);
+        return app('json')->success($this->services->schemeList($where));
+    }
+
+    public function schemeForm($id)
+    {
+        return app('json')->success($this->services->schemeForm($id));
+    }
+
+    public function schemeSave($id)
+    {
+        $data = $this->request->postMore([
+            ['title', ''],
+            ['path', ''],
+            ['expire_type', -1],
+            ['expire_num', 0],
+        ]);
+        $this->services->schemeSave($id, $data);
+        return app('json')->success(100000);
+    }
+
+    public function schemeDel($id)
+    {
+        $res = $this->services->delete($id);
+        if (!$res) return app('json')->fail(100008);
+        return app('json')->success(100002);
+    }
+}

+ 1 - 0
crmeb/app/adminapi/controller/v1/application/wechat/Menus.php

@@ -50,6 +50,7 @@ class Menus extends AuthController
     public function save()
     {
         $buttons = request()->post('button/a', []);
+        if(strlen($buttons[0]['name']) > 15) return app('json')->fail('菜单名称不能大于5个字');
         if (!count($buttons)) return app('json')->fail(400238);
         $this->services->saveMenu($buttons);
         return app('json')->success(100001);

+ 26 - 1
crmeb/app/adminapi/controller/v1/diy/Diy.php

@@ -347,6 +347,27 @@ class Diy extends AuthController
         return $info;
     }
 
+    /**
+     * 推荐商品展示
+     * @param $type
+     * @return \think\Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/3/11
+     */
+    public function getGroomList($type)
+    {
+        [$page, $limit] = $this->request->getMore([
+            ['page', 1],
+            ['limit', 6],
+        ], true);
+        $list = $this->get_groom_list($type, $limit);
+        return app('json')->success($list);
+    }
+
     /**
      * 获取uni-app路径
      * @return mixed
@@ -355,9 +376,13 @@ class Diy extends AuthController
     {
         $url = sys_data('uni_app_link');
         if ($url) {
-            foreach ($url as &$link) {
+            $model_checkbox = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
+            foreach ($url as $key => &$link) {
                 $link['url'] = $link['link'];
                 $link['parameter'] = trim($link['param']);
+                if (!in_array('seckill', $model_checkbox) && strpos($link['name'], '秒杀') !== false) unset($url[$key]);
+                if (!in_array('bargain', $model_checkbox) && strpos($link['name'], '砍价') !== false) unset($url[$key]);
+                if (!in_array('combination', $model_checkbox) && strpos($link['name'], '拼团') !== false) unset($url[$key]);
             }
         } else {
             /** @var CacheServices $cache */

+ 4 - 1
crmeb/app/adminapi/controller/v1/kefu/StoreService.php

@@ -63,7 +63,10 @@ class StoreService extends AuthController
             ['type', '', '', 'user_type'],
         ]);
         $where['is_del'] = 0;
-        [$list, $count] = $services->getWhereUserList($where, 'u.nickname,u.uid,u.avatar as headimgurl,w.subscribe,w.province,w.country,w.city,w.sex,u.user_type,u.is_del');
+        [$list, $count] = $services->getWhereUserList($where, 'u.nickname,u.uid,u.avatar as headimgurl,w.subscribe,w.province,w.country,w.city,w.sex,u.user_type,u.is_del,u.phone,u.add_time');
+        foreach ($list as &$item) {
+            $item['add_time'] = date('Y-m-d', $item['add_time']);
+        }
         return app('json')->success(compact('list', 'count'));
     }
 

+ 1 - 1
crmeb/app/adminapi/controller/v1/marketing/StoreBargain.php

@@ -154,7 +154,7 @@ class StoreBargain extends AuthController
         $bargainUserService = app()->make(StoreBargainUserServices::class);
         $bargainUserService->userBargainStatusFail($id, false);
         $this->services->update($id, ['status' => $status]);
-        return app('json')->success($status == 0 ? 100001 : 100007);
+        return app('json')->success(100001);
     }
 
     /**

+ 1 - 0
crmeb/app/adminapi/controller/v1/order/RefundOrder.php

@@ -189,6 +189,7 @@ class RefundOrder extends AuthController
             $wechatUserServices = app()->make(WechatUserServices::class);
             $refund_data['open_id'] = $wechatUserServices->uidToOpenid((int)$order['uid'], 'routine') ?? '';
             $refund_data['refund_no'] = $orderRefund['order_id'];
+            $refund_data['order_id'] = $orderRefund['order_id'];
             //修改订单退款状态
 //            $data['refund_price'] = $data['refunded_price'];
             unset($data['refund_price']);

+ 13 - 2
crmeb/app/adminapi/controller/v1/order/StoreOrder.php

@@ -57,7 +57,10 @@ class StoreOrder extends AuthController
     {
         $where = $this->request->getMore([
             ['data', '', '', 'time'],
-            [['type', 'd'], 0],
+            ['type', ''],
+            ['pay_type', ''],
+            ['field_key', 'all'],
+            ['real_name', ''],
         ]);
         $data = $this->services->orderCount($where);
         return app('json')->success($data);
@@ -469,6 +472,7 @@ class StoreOrder extends AuthController
     {
         $data = $this->request->postMore([
             ['refund_price', 0],
+            ['cart_ids', []]
         ]);
         if (!$id) {
             return app('json')->fail(100100);
@@ -484,7 +488,7 @@ class StoreOrder extends AuthController
             'refund_img' => json_encode([]),
         ];
 
-        $res = $services->applyRefund((int)$id, $order['uid'], $order, [], 1, (float)$data['refund_price'], $refundData);
+        $res = $services->applyRefund((int)$id, $order['uid'], $order, $data['cart_ids'], 1, (float)$data['refund_price'], $refundData);
 
         if (!$res) {
             return app('json')->fail('退款单生成失败');
@@ -527,6 +531,7 @@ class StoreOrder extends AuthController
         $wechatUserServices = app()->make(WechatUserServices::class);
         $refund_data['open_id'] = $wechatUserServices->uidToOpenid((int)$order['uid'], 'routine') ?? '';
         $refund_data['refund_no'] = $orderRefund['order_id'];
+        $refund_data['order_id'] = $orderRefund['order_id'];
         //修改订单退款状态
         unset($data['refund_price']);
         if ($services->agreeRefund($orderRefund['id'], $refund_data)) {
@@ -688,6 +693,12 @@ class StoreOrder extends AuthController
         $services->storeProductOrderRefundNo((int)$id, $refund_reason);
         //提醒推送
         event('NoticeListener', [['orderInfo' => $orderInfo], 'send_order_refund_no_status']);
+
+        //自定义消息-订单拒绝退款
+        $orderInfo['time'] = date('Y-m-d H:i:s');
+        $orderInfo['phone'] = $orderInfo['user_phone'];
+        event('CustomNoticeListener', [$orderInfo['uid'], $orderInfo, 'order_refund_fail']);
+
         return app('json')->success(100010);
     }
 

+ 3 - 7
crmeb/app/adminapi/controller/v1/setting/SystemCity.php

@@ -133,13 +133,9 @@ class SystemCity extends AuthController
      */
     public function clean_cache()
     {
-        $res1 = CacheService::delete('CITY_LIST');
-        $res2 = CacheService::delete('CITY_FULL_LIST');
-        if ($res1 && $res2) {
-            return app('json')->success(400185);
-        } else {
-            return app('json')->fail(400186);
-        }
+        CacheService::delete('CITY_LIST');
+        CacheService::delete('CITY_FULL_LIST');
+        return app('json')->success(400185);
     }
 
     /**

+ 2 - 3
crmeb/app/adminapi/controller/v1/setting/SystemConfig.php

@@ -335,7 +335,7 @@ class SystemConfig extends AuthController
             if ($post['reward_money'] < 0) return app('json')->fail('赠送余额不能小于0元');
             if ($post['reward_integral'] < 0) return app('json')->fail('赠送积分不能小于0');
         }
-        
+
         if (isset($post['sign_give_point'])) {
             if (!is_int($post['sign_give_point']) || $post['sign_give_point'] < 0) return app('json')->fail('签到赠送积分请填写大于等于0的整数');
         }
@@ -361,7 +361,7 @@ class SystemConfig extends AuthController
         if (isset($post['refund_time_available'])) {
             if (!ctype_digit($post['refund_time_available'])) return app('json')->fail('售后期限必须为大于0的整数');
         }
-
+        if (isset($post['sms_save_type']) && sys_config('sms_account', '') != '') return app('json')->success(100001);
         foreach ($post as $k => $v) {
             $config_one = $this->services->getOne(['menu_name' => $k]);
             if ($config_one) {
@@ -372,7 +372,6 @@ class SystemConfig extends AuthController
         }
         CacheService::clear();
         return app('json')->success(100001);
-
     }
 
     /**

+ 2 - 0
crmeb/app/adminapi/controller/v1/setting/SystemConfigTab.php

@@ -74,6 +74,7 @@ class SystemConfigTab extends AuthController
             ['type', 0],
             ['sort', 0],
             ['pid', 0],
+            ['menus_id', 0],
         ]);
         if (is_array($data['pid'])) $data['pid'] = end($data['pid']);
         if (!$data['title']) return app('json')->fail(400291);
@@ -119,6 +120,7 @@ class SystemConfigTab extends AuthController
             ['type', 0],
             ['sort', 0],
             ['pid', 0],
+            ['menus_id', 0],
         ]);
         if (is_array($data['pid'])) $data['pid'] = end($data['pid']);
         if (!$data['title']) return app('json')->fail(400291);

+ 1 - 1
crmeb/app/adminapi/controller/v1/setting/SystemCrud.php

@@ -112,6 +112,7 @@ class SystemCrud extends AuthController
         if (!preg_match('/^[a-zA-Z_]+$/u', $data['tableName'])) return app('json')->fail('表名称只能是英文和下划线组成');
         if (!$this->crudVerifyPath($data['filePath'])) return app('json')->fail('生成的文件位置有误,请检查后重新生成');
 
+
         $fromField = $searchField = $hasOneField = $columnField = $tableIndex = [];
 
         $dictionaryids = array_column($data['tableField'], 'dictionary_id');
@@ -924,6 +925,5 @@ class SystemCrud extends AuthController
         } else {
             return app('json')->fail('删除失败');
         }
-
     }
 }

+ 55 - 0
crmeb/app/adminapi/controller/v1/setting/SystemNotification.php

@@ -47,6 +47,54 @@ class SystemNotification extends AuthController
         return app('json')->success($this->services->getNotList($where));
     }
 
+    /**
+     * 添加消息
+     * @return \think\Response
+     * @throws \FormBuilder\Exception\FormBuilderException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/2/19
+     */
+    public function notForm($id)
+    {
+        return app('json')->success($this->services->getNotForm($id));
+    }
+
+    /**
+     * 保存自定义消息
+     * @param $id
+     * @return \think\Response
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/2/20
+     */
+    public function notFormSave($id)
+    {
+        $data = $this->request->postMore([
+            ['custom_trigger', ''],
+            ['name', ''],
+            ['mark', ''],
+        ]);
+        $this->services->notFormSave($id, $data);
+        return app('json')->success(100000);
+    }
+
+    /**
+     * 删除消息
+     * @param $id
+     * @return \think\Response
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/2/20
+     */
+    public function delNot($id)
+    {
+        if (!$id) return app('json')->fail(100100);
+        $this->services->delete($id);
+        return app('json')->success(100002);
+    }
+
+
     /**
      * 显示编辑
      * @return \think\Response
@@ -85,12 +133,19 @@ class SystemNotification extends AuthController
             ['system_title', ''],
             ['system_text', ''],
             ['tempid', ''],
+            ['tempkey', ''],
+            ['content', ''],
             ['ent_wechat_text', ''],
             ['url', ''],
             ['wechat_id', ''],
             ['routine_id', ''],
             ['mark', ''],
             ['sms_id', ''],
+            ['key_list', ''],
+            ['sms_text', ''],
+            ['wechat_link', ''],
+            ['routine_link', ''],
+            ['wechat_to_routine', ''],
         ]);
         if ($data['mark'] == 'verify_code') $data['type'] = 'is_sms';
         if (!$data['id']) return app('json')->fail(100100);

+ 1 - 1
crmeb/app/adminapi/controller/v1/system/Clear.php

@@ -33,7 +33,7 @@ class Clear extends AuthController
      */
     public function refresh_cache()
     {
-        $this->services->refreshCache();
+        $this->services->refresCache();
         return app('json')->success(400302);
     }
 

+ 2 - 0
crmeb/app/adminapi/route/agent.php

@@ -91,6 +91,8 @@ Route::group('agent', function () {
         Route::post('division/agent/save', 'v1.agent.Division/divisionAgentSave')->name('divisionAgentSave')->option(['real_name' => '事业部保存']);//代理商保存
         Route::put('division/set_status/:status/:uid', 'v1.agent.Division/setDivisionStatus')->name('setDivisionStatus')->option(['real_name' => '状态切换']);//状态切换
         Route::delete('division/del/:type/:uid', 'v1.agent.Division/delDivision')->name('delDivision')->option(['real_name' => '删除代理商']);//状态切换
+        Route::get('division/staff/create/:uid', 'v1.agent.Division/divisionStaffCreate')->name('divisionStaffCreate')->option(['real_name' => '添加事业部']);//添加代理商
+        Route::post('division/staff/save', 'v1.agent.Division/divisionStaffSave')->name('divisionStaffSave')->option(['real_name' => '事业部保存']);//代理商保存
         Route::get('division/agent_apply/list', 'v1.agent.Division/AdminApplyList')->name('AdminApplyList')->option(['real_name' => '代理商申请列表']);//代理商申请列表
         Route::get('division/examine_apply/:id/:type', 'v1.agent.Division/examineApply')->name('examineApply')->option(['real_name' => '审核表单']);//审核表单
         Route::post('division/apply_agent/save', 'v1.agent.Division/applyAgentSave')->name('applyAgentSave')->option(['real_name' => '提交审核']);//提交审核

+ 7 - 0
crmeb/app/adminapi/route/app.php

@@ -57,6 +57,13 @@ Route::group('app', function () {
         Route::get('routine/info', 'v1.application.routine.RoutineTemplate/getDownloadInfo')->option(['real_name' => '下载小程序页面数据']);
         //下载小程序模版
         Route::post('routine/download', 'v1.application.routine.RoutineTemplate/downloadTemp')->option(['real_name' => '下载小程序模版']);
+
+        Route::get('routine/scheme_list', 'v1.application.routine.RoutineScheme/schemeList')->name('schemeList')->option(['real_name' => '小程序外链列表']);
+        Route::get('routine/scheme_form/:id', 'v1.application.routine.RoutineScheme/schemeForm')->name('schemeForm')->option(['real_name' => '小程序外链添加修改表单']);
+        Route::post('routine/scheme_save/:id', 'v1.application.routine.RoutineScheme/schemeSave')->name('schemeSave')->option(['real_name' => '小程序外链添加修改保存']);
+        Route::delete('routine/scheme_del/:id', 'v1.application.routine.RoutineScheme/schemeDel')->name('schemeDel')->option(['real_name' => '小程序外链删除']);
+
+
     })->option(['parent' => 'app', 'cate_name' => '小程序']);
 
     /** 公众号渠道码 */

+ 2 - 0
crmeb/app/adminapi/route/common.php

@@ -42,6 +42,8 @@ Route::group(function () {
     Route::get('copyright', 'Common/copyright')->option(['real_name' => '申请版权']);
     //保存版权
     Route::post('copyright', 'Common/saveCopyright')->option(['real_name' => '保存版权']);
+    //后台菜单搜索
+    Route::post('menusSearch', 'Common/menusSearch')->option(['real_name' => '后台菜单搜索']);
 })->middleware([
     \app\http\middleware\AllowOriginMiddleware::class,
     \app\adminapi\middleware\AdminAuthTokenMiddleware::class,

+ 0 - 85
crmeb/app/adminapi/route/demo.php

@@ -1,85 +0,0 @@
-<?php
-// +----------------------------------------------------------------------
-// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
-// +----------------------------------------------------------------------
-// | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
-// +----------------------------------------------------------------------
-// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
-// +----------------------------------------------------------------------
-// | Author: CRMEB Team <admin@crmeb.com>
-// +----------------------------------------------------------------------
-use think\facade\Route;
-
-/**
- * 应用模块 路由示例 
- * 注意:
- * 1. 路由示例只做参考,请根据实际情况修改
- * 2. 如果需要使用路由中间件,请在路由前面加上中间件名称,如:middleware(\app\middleware\ApiAuthMiddleware::class)
- * 3. 如果需要使用路由参数,请在路由后面加上参数,如:'id' => '\d+', 多个参数用英文逗号分隔
- * 4. 如果需要使用路由别名,请在路由后面加上别名,如:'id' => 'user_id', 多个参数用英文逗号分隔
- * 5. 如果需要使用路由变量,请在路由后面加上变量,如:':id' => '\
- * 6. 如果需要使用路由正则,请在路由后面加上正则,如:':id' => '/
- * 严格按照这种方法写,可以快速生成接口文档和添加接口权限设置
- */
-Route::group('demo', function () {
-
-    /** 路由示例 */
-    Route::group('路由示例',function () {
-        //get请求路由,第一参数为路由地址,第二参数为处理函数 目录:app/controller/v1/application/wechat/menus
-        Route::get('wechat/demo1', 'v1.application.wechat.menus/index')->option([
-            'real_name' => '微信公众号菜单列表' //接口名称
-        ]);
-        //post请求路由,第一参数为路由地址,第二参数为处理函数 目录:app/controller/v1/application/wechat/menus
-        Route::post('wechat/demo2', 'v1.application.wechat.menus/save')->option([
-            'real_name' => '保存微信公众号菜单' //接口名称
-        ]);
-        //delete请求路由,第一参数为路由地址,第二参数为处理函数 目录:app/controller/v1/application/wechat/menus
-        Route::delete('wechat/demo3/:id', 'v1.application.wechat.menus/delete')->option([
-            'real_name' => '删除图文' //接口名称
-        ]);
-        //put请求路由,第一参数为路由地址,第二参数为处理函数 目录:app/controller/v1/application/wechat/menus
-        Route::put('wechat/demo3/:id', 'v1.application.wechat.menus/save')->option([
-            'real_name' => '编辑图文' //接口名称
-        ]);
-
-        //资源路由可以快速创建增删改查路由 客服反馈接口 第一参数为路由地址,第二参数为处理函数 目录:app/controller/v1/kefu/StoreServiceFeedback
-        Route::resource('feedback', 'v1.kefu.StoreServiceFeedback')->only([ //只允许index read edit update 四个操作
-            //GET请求 对应方法index
-            'index', 
-            //DELETE请求 对应方法delete
-            // 'delete', 
-            //POST请求 对应方法save
-            'save', 
-            //GET请求 对应方法read
-            'read', 
-            //POST请求 对应方法create
-            // 'create', 
-            //PUT请求 对应方法update
-            'update', 
-            //GET请求 对应方法edit
-            'edit' 
-            ])->option([
-            'real_name' => [//接口名称
-                'index' => '获取用户反馈列表', 
-                // 'delete' => '删除用户反馈列表', 
-                'save' => '保存用户反馈列表', 
-                'read' => '获取用户反馈', 
-                // 'create' => '添加用户反馈列表', 
-                'update' => '修改用户反馈', 
-                'edit' => '获取修改用户反馈表单', 
-            ]
-        ]);
-    })->option([
-        'parent' => 'app', //父级路由
-        'cate_name' => '公众号' //分组名称
-    ]);
-
-})->middleware([//中间件
-    \app\http\middleware\AllowOriginMiddleware::class, //允许跨域
-    \app\adminapi\middleware\AdminAuthTokenMiddleware::class, //后台管理员认证
-    \app\adminapi\middleware\AdminCheckRoleMiddleware::class, //后台管理员角色检测
-    \app\adminapi\middleware\AdminLogMiddleware::class //后台管理员日志
-])->option([
-    'mark' => 'demo', //标记
-    'mark_name' => 'demo演示' //标记名称
-]);

+ 3 - 0
crmeb/app/adminapi/route/diy.php

@@ -67,6 +67,9 @@ Route::group('diy', function () {
     //开屏广告
     Route::get('open_adv/info', 'v1.diy.Diy/getOpenAdv')->option(['real_name' => '获取开屏广告']);
     Route::post('open_adv/add', 'v1.diy.Diy/openAdvAdd')->option(['real_name' => '保存开屏广告']);
+    //推荐商品
+    Route::get('groom_list/:type', 'v1.diy.Diy/getGroomList')->option(['real_name' => '推荐商品']);
+
 
 })->middleware([
     \app\http\middleware\AllowOriginMiddleware::class,

+ 1 - 1
crmeb/app/adminapi/route/route.php

@@ -25,7 +25,7 @@ Route::group(function () {
     //后台登录页面数据
     Route::get('login/info', 'Login/info')->option(['real_name' => '登录信息']);
     //下载文件
-    Route::get('download/:key', 'PublicController/download')->option(['real_name' => '下载文件']);
+    Route::get('download/[:key]', 'PublicController/download')->option(['real_name' => '下载文件']);
     //验证码
     Route::get('captcha_pro', 'Login/captcha')->name('')->option(['real_name' => '获取验证码']);
     //获取验证码

+ 6 - 0
crmeb/app/adminapi/route/setting.php

@@ -278,6 +278,12 @@ Route::group('setting', function () {
     Route::group(function () {
         //系统通知列表
         Route::get('notification/index', 'v1.setting.SystemNotification/index')->option(['real_name' => '系统通知列表']);
+        //自定义消息添加修改表单
+        Route::get('notification/not_form/:id', 'v1.setting.SystemNotification/notForm')->option(['real_name' => '自定义消息添加修改表单']);
+        //删除自定义消息
+        Route::delete('notification/del_not/:id', 'v1.setting.SystemNotification/delNot')->option(['real_name' => '删除自定义消息']);
+        //自定义消息保存
+        Route::post('notification/not_form_save/:id', 'v1.setting.SystemNotification/notFormSave')->option(['real_name' => '自定义消息保存']);
         //获取单条数据
         Route::get('notification/info', 'v1.setting.SystemNotification/info')->option(['real_name' => '获取单条通知数据']);
         //保存通知设置

+ 28 - 0
crmeb/app/api/README.md

@@ -0,0 +1,28 @@
+crmeb/app/api目录是网站前端(非管理后台)的API接口目录。
+
+它与adminapi目录的区别在于:
+
+- adminapi目录下的是管理后台系统的API接口
+- api目录下的是网站前端系统(手机端/微信小程序/H5等)的API接口
+
+具体来说:
+
+- api目录下也是采用控制器(Controller)方式组织接口代码
+- 每个控制器对应一个功能模块,如OrderController负责订单相关接口等
+- 接口用于前端页面的ajax请求,获取数据用于渲染
+- 接口也采用RESTful风格设计
+
+例如:
+
+- 用户注册接口在UserController的register方法
+- 获取订单列表在OrderController的lists方法
+- 支付结果通知在PayController的notify方法
+
+和adminapi目录一样,api目录也通过定义清晰的接口,解耦了前后端,让前端更专注于业务展示。
+
+区别在于目标用户不同:
+
+- adminapi为后台管理员使用
+- api目录下的接口为前台用户(手机端、小程序端等)提供数据服务
+
+所以二者都起到了前后端分离的关键作用。

+ 32 - 10
crmeb/app/api/controller/v1/LoginController.php

@@ -140,17 +140,37 @@ class LoginController
         ], true);
 
         $keyName = 'sms.key.' . $key;
-        $nowKey = 'sms.' . date('YmdHi');
+        if (!CacheService::has($keyName)) return app('json')->fail(410003);
+
+        // 验证限制
+        // 验证码每分钟发送上限
+        $maxMinuteCountKey = 'sms.minute.' . $phone . date('YmdHi');
+        $minuteCount = 0;
+        if (CacheService::has($maxMinuteCountKey)) {
+            $minuteCount = CacheService::get($maxMinuteCountKey);
+            $maxMinuteCount = Config::get('sms.maxMinuteCount', 5);
+            if ($minuteCount > $maxMinuteCount) return app('json')->fail('同一手机号每分钟最多发送' . $maxMinuteCount . '条');
 
-        if (!CacheService::has($keyName)) {
-            return app('json')->fail(410003);
         }
 
-        $total = 1;
-        if (CacheService::has($nowKey)) {
-            $total = CacheService::get($nowKey);
-            if ($total > Config::get('sms.maxMinuteCount', 20))
-                return app('json')->success(410006);
+        // 验证码单个手机每日发送上限
+        $maxPhoneCountKey = 'sms.phone.' . $phone . '.' . date('Ymd');
+        $phoneCount = 0;
+        if (CacheService::has($maxPhoneCountKey)) {
+            $phoneCount = CacheService::get($maxPhoneCountKey);
+            $maxPhoneCount = Config::get('sms.maxPhoneCount', 20);
+            if ($phoneCount > $maxPhoneCount) return app('json')->fail('同一手机号每天最多发送' . $maxPhoneCount . '条');
+
+        }
+
+        // 验证码单个手机每日发送上限
+        $maxIpCountKey = 'sms.ip.' . app()->request->ip() . '.' . date('Ymd');
+        $ipCount = 0;
+        if (CacheService::has($maxIpCountKey)) {
+            $ipCount = CacheService::get($maxPhoneCountKey);
+            $maxIpCount = Config::get('sms.maxIpCount', 50);
+            if ($ipCount > $maxIpCount) return app('json')->fail('同一IP每天最多发送' . $maxIpCount . '条');
+
         }
 
         //二次验证
@@ -166,10 +186,12 @@ class LoginController
             return app('json')->fail($e->getError());
         }
         $time = sys_config('verify_expire_time', 1);
-        $smsCode = $this->services->verify($services, $phone, $type, $time, app()->request->ip());
+        $smsCode = $this->services->verify($services, $phone, $type, $time);
         if ($smsCode) {
             CacheService::set('code_' . $phone, $smsCode, $time * 60);
-            CacheService::set($nowKey, $total, 61);
+            CacheService::set($maxMinuteCountKey, $minuteCount + 1, 61);
+            CacheService::set($maxPhoneCountKey, $phoneCount + 1, 86401);
+            CacheService::set($maxIpCountKey, $ipCount + 1, 86401);
             return app('json')->success(410007);
         } else {
             return app('json')->fail(410008);

+ 19 - 0
crmeb/app/api/controller/v1/PublicController.php

@@ -32,6 +32,7 @@ use app\services\system\store\SystemStoreStaffServices;
 use app\services\user\UserBillServices;
 use app\services\user\UserInvoiceServices;
 use app\services\user\UserServices;
+use app\services\wechat\RoutineSchemeServices;
 use app\services\wechat\WechatUserServices;
 use app\Request;
 use crmeb\services\CacheService;
@@ -695,6 +696,24 @@ class PublicController
         $data['wechat_auth_switch'] = (int)in_array(1, sys_config('routine_auth_type'));//微信登录开关
         $data['phone_auth_switch'] = (int)in_array(2, sys_config('routine_auth_type'));//手机号登录开关
         $data['wechat_status'] = sys_config('wechat_appid') != '' && sys_config('wechat_appsecret') != '';//公众号是否配置
+        $data['site_func'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return app('json')->success($data);
     }
+
+    /**
+     * 小程序跳转链接接口
+     * @param $id
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/2/26
+     */
+    public function getSchemeUrl($id)
+    {
+        $url = app()->make(RoutineSchemeServices::class)->value($id, 'url');
+        if ($url) {
+            echo '<script>window.location.href="' . $url . '";</script>';
+        } else {
+            echo '<h1>未找到跳转路径</h1>';
+        }
+    }
 }

+ 3 - 2
crmeb/app/api/controller/v1/admin/StoreOrderController.php

@@ -445,6 +445,7 @@ class StoreOrderController
                 }
                 $refundData['pay_price'] = $orderInfo['pay_price'];
                 $refundData['refund_price'] = $price;
+                $refundData['order_id'] = $orderId;
 
 
                 //修改订单退款状态
@@ -515,7 +516,7 @@ class StoreOrderController
                 }
                 $refundOrderData['cart_info'] = json_encode(array_column($cartInfos, 'cart_info'));
                 $res = $services->save($refundOrderData);
-
+                $refund_data['order_id'] = $refundOrderData['order_id'];
 
                 //修改订单退款状态
                 if ($services->agreeRefund((int)$res->id, $refund_data)) {
@@ -616,7 +617,7 @@ class StoreOrderController
             ['order_id', ''],
             ['time', ''],
             ['refund_type', 0],
-            ['keywords', ''],
+            ['keywords', '', '', 'real_name'],
         ]);
         $where['is_cancel'] = 0;
         $data = $services->refundList($where)['list'];

+ 5 - 1
crmeb/app/api/controller/v1/order/StoreOrderController.php

@@ -241,8 +241,12 @@ class StoreOrderController
             ['quitUrl', ''],
             ['type', 0]
         ], true);
+        $payLock = CacheService::get('PAY_LOCK_' . $uni);
+        if ($payLock) return app('json')->fail('订单支付中,请勿重复支付');
+        CacheService::set('PAY_LOCK_' . $uni, 'PAY_LOCK', 2);
         if (!$uni) return app('json')->fail(100100);
         $orderInfo = $this->services->get(['order_id' => $uni]);
+        if ($orderInfo->is_del == 1 || $orderInfo->is_system_del == 1) return app('json')->fail('订单已经超过系统支付时间,无法支付,请重新下单');
         $uid = $type == 1 ? (int)$request->uid() : $orderInfo->uid;
         $orderInfo->is_channel = $this->getChennel[$request->getFromType()] ?? ($request->isApp() ? 0 : 1);
         $orderInfo->order_id = $uid != $orderInfo->pay_uid ? app()->make(StoreOrderCreateServices::class)->getNewOrderId('cp') : $uni;
@@ -812,7 +816,7 @@ class StoreOrderController
                     $update['delivery_id'] = $data['kuaidinum'];
                 }
                 if (isset($data['task_id'])) {
-                    $this->services->update(['task_id' => $data['task_id']], $update);
+                    $this->services->update(['kuaidi_task_id' => $data['task_id']], $update);
                 }
                 break;
             case 'order_take'://取件

+ 20 - 1
crmeb/app/api/controller/v1/user/DivisionController.php

@@ -6,6 +6,7 @@ namespace app\api\controller\v1\user;
 
 use app\Request;
 use app\services\agent\DivisionAgentApplyServices;
+use app\services\agent\DivisionServices;
 use app\services\other\AgreementServices;
 use app\services\user\UserServices;
 use crmeb\services\CacheService;
@@ -94,7 +95,7 @@ class DivisionController
             ['sort', ''],
         ]);
         $where['agent_id'] = $request->uid();
-        return app('json')->success($this->services->getStaffList($request->user(), $where));
+        return app('json')->success($this->services->getStaffList($request->isRoutine(), $where));
     }
 
     /**
@@ -133,4 +134,22 @@ class DivisionController
         $userService->update(['uid' => $uid, 'agent_id' => $agentId], ['division_percent' => 0, 'agent_id' => 0, 'division_id' => 0, 'staff_id' => 0, 'division_type' => 0, 'is_staff' => 0]);
         return app('json')->success(100002);
     }
+
+    /**
+     * 绑定员工方法
+     * @param Request $request
+     * @return \think\Response
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2024/2/2
+     */
+    public function agentSpread(Request $request)
+    {
+        [$agentId, $agentCode] = $request->postMore([
+            ['agent_id', 0],
+            ['agent_code', 0],
+        ], true);
+        $res = app()->make(DivisionServices::class)->agentSpreadStaff($request->uid(), (int)$agentId, (int)$agentCode);
+        return app('json')->success($res);
+    }
 }

+ 2 - 4
crmeb/app/api/controller/v1/user/UserSignController.php

@@ -74,10 +74,8 @@ class UserSignController
     public function sign_integral(Request $request)
     {
         $uid = (int)$request->uid();
-        if ($integral = $this->services->sign($uid)) {
-            return app('json')->success(410127, ['integral' => $integral], ['integral' => $integral]);
-        }
-        return app('json')->fail(410128);
+        $integral = $this->services->sign($uid);
+        return app('json')->success(410127, ['integral' => $integral], ['integral' => $integral]);
     }
 
     /**

+ 3 - 1
crmeb/app/api/route/v1.php

@@ -50,9 +50,10 @@ Route::group(function () {
 //    Route::get('ali_pay', 'v1.order.StoreOrderController/aliPay')->name('aliPay');
     //查询版权
     Route::get('copyright', 'v1.PublicController/copyright')->option(['real_name' => '申请版权'])->option(['real_name' => '查询版权']);
-
     //商城基础配置汇总接口
     Route::get('basic_config', 'v1.PublicController/getMallBasicConfig')->option(['real_name' => '商城基础配置汇总接口']);
+    //小程序跳转url接口
+    Route::get('get_scheme_url/:id', 'v1.PublicController/getSchemeUrl')->option(['real_name' => '小程序跳转url接口']);
 
 })->middleware(\app\http\middleware\AllowOriginMiddleware::class)
     ->middleware(\app\api\middleware\StationOpenMiddleware::class)
@@ -323,6 +324,7 @@ Route::group(function () {
         Route::get('agent/get_staff_list', 'v1.user.DivisionController/getStaffList')->name('getStaffList')->option(['real_name' => '员工列表']);//员工列表
         Route::post('agent/set_staff_percent', 'v1.user.DivisionController/setStaffPercent')->name('setStaffPercent')->option(['real_name' => '设置员工分佣比例']);//设置员工分佣比例
         Route::get('agent/del_staff/:uid', 'v1.user.DivisionController/delStaff')->name('delStaff')->option(['real_name' => '删除员工']);//删除员工
+        Route::post('agent/spread', 'v1.user.DivisionController/agentSpread')->name('agentSpread')->option(['real_name' => '代理商绑定员工']);//代理商绑定员工
     })->option(['mark' => 'agent', 'mark_name' => '代理商']);
 
     Route::group(function () {

+ 21 - 11
crmeb/app/common.php

@@ -791,7 +791,7 @@ if (!function_exists('get_tree_children')) {
         foreach ($data as $value) {
             $list[$value[$keyName]] = $value;
         }
-        static $tree = array(); //格式化好的树
+        $tree = array(); //格式化好的树
         foreach ($list as $item) {
             if (isset($list[$item[$pidName]])) {
                 $list[$item[$pidName]][$childrenname][] = &$list[$item[$keyName]];
@@ -832,18 +832,29 @@ if (!function_exists('get_tree_value')) {
      */
     function get_tree_value(array $data, $value)
     {
-        static $childrenValue = [];
-        foreach ($data as &$item) {
-            if ($item['value'] == $value) {
-                $childrenValue[] = $item['value'];
-                if ($item['pid']) {
-                    $value = $item['pid'];
-                    unset($item);
-                    return get_tree_value($data, $value);
+//        static $childrenValue = [];
+//        foreach ($data as &$item) {
+//            if ($item['value'] == $value) {
+//                $childrenValue[] = $item['value'];
+//                if ($item['pid']) {
+//                    $value = $item['pid'];
+//                    unset($item);
+//                    return get_tree_value($data, $value);
+//                }
+//            }
+//        }
+//        return $childrenValue;
+        $childrenValue = []; // 用于存储找到的子值的数组
+        foreach ($data as $item) {
+            if ($item['value'] == $value) { // 如果当前项的'value'键与给定值匹配
+                $childrenValue[] = $item['value']; // 将当前值添加到子值数组中
+                if ($item['pid']) { // 如果当前项有'pid'值,表示有父项
+                    // 递归调用get_tree_value函数,并将父项的'pid'值作为新的$value参数
+                    $childrenValue = array_merge($childrenValue, get_tree_value($data, $item['pid']));
                 }
             }
         }
-        return $childrenValue;
+        return $childrenValue; // 返回包含所有子值的数组
     }
 }
 
@@ -865,7 +876,6 @@ if (!function_exists('get_image_thumb')) {
             $image = $type == 'all' ? $data : $data[$type] ?? $filePath;
         } catch (\Throwable $e) {
             $image = $filePath;
-            \think\facade\Log::error('获取缩略图失败,原因:' . $e->getMessage() . '----' . $e->getFile() . '----' . $e->getLine() . '----' . $filePath);
         }
         $data = parse_url($image);
         if (!isset($data['host']) && (substr($image, 0, 2) == './' || substr($image, 0, 1) == '/')) {//不是完整地址

+ 28 - 0
crmeb/app/dao/README.md

@@ -0,0 +1,28 @@
+crmeb/app/dao目录是项目数据访问对象(DAO)层的代码目录。
+
+DAO层主要职责和作用如下:
+
+1. 实现数据持久层访问,对数据库进行CURD操作。
+
+2. 依赖数据库连接,实现对表的基本增删改查功能。
+
+3. 封装数据库操作原语(查询,插入,更新等),简化开发难度。
+
+4. 与数据库解藕,提供统一接口,便于扩展和维护。
+
+具体来说:
+
+- dao目录下每个文件对应一个数据表或业务模块
+- 文件内封装了对表基本操作的方法,如查找,插入,更新等
+- 方法的参数和返回值类型为模型对象(Model),实现数据和业务的解耦
+- 提供丰富的查询条件以方便调用
+- 底层利用ThinkPHP的ActiveRecord实现数据操作
+
+使用DAO层的好处:
+
+- 提供对象化的数据操作接口
+- 屏蔽数据库差异,提高移植性
+- 方便测试和扩展
+- 实现业务和数据层的分离
+
+所以该目录负责项目的底层数据操作,其他业务需要调用它来操作数据库。

+ 1 - 0
crmeb/app/dao/diy/PageCategoryDao.php

@@ -45,6 +45,7 @@ class PageCategoryDao extends BaseDao
      */
     public function getList(array $where, string $field = '*', int $page = 0, int $limit = 0)
     {
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return $this->search($where)->field($field)->when($page && $limit, function ($query) use ($page, $limit) {
             $query->page();
         })->order('sort desc,id DESC')->select()->toArray();

+ 1 - 0
crmeb/app/dao/diy/PageLinkDao.php

@@ -45,6 +45,7 @@ class PageLinkDao extends BaseDao
      */
     public function getList(array $where, string $field = '*', int $page = 0, int $limit = 0)
     {
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return $this->search($where)->field($field)->when($page && $limit, function ($query) use ($page, $limit) {
             $query->page();
         })->order('sort desc,id asc')->select()->toArray();

+ 15 - 3
crmeb/app/dao/order/StoreOrderDao.php

@@ -82,7 +82,7 @@ class StoreOrderDao extends BaseDao
                     $query->where('paid', 1)->where('status', 0)->where('refund_status', 0)->where('shipping_type', 2)->where('is_del', 0);
                     break;
                 case 6://已支付 已核销 没有退款
-                    $query->where('paid', 1)->whereIn('status', [2,3])->where('refund_status', 0)->where('shipping_type', 2)->where('is_del', 0);
+                    $query->where('paid', 1)->whereIn('status', [2, 3])->where('refund_status', 0)->where('shipping_type', 2)->where('is_del', 0);
                     break;
                 case -1://退款中
                     $query->where('paid', 1)->whereIn('refund_status', [1, 4])->where('is_del', 0);
@@ -167,8 +167,14 @@ class StoreOrderDao extends BaseDao
                 $query->where(trim($fieldKey), trim($realName));
             } else {
                 $query->where('id', 'in', function ($que) use ($where) {
-                    $que->name('store_order_cart_info')->whereIn('product_id', function ($q) use ($where) {
+                    $que->name('store_order_cart_info')->whereOr('product_id', 'in', function ($q) use ($where) {
                         $q->name('store_product')->whereLike('store_name|keyword', '%' . $where['real_name'] . '%')->field(['id'])->select();
+                    })->whereOr('product_id', 'in', function ($q) use ($where) {
+                        $q->name('store_bargain')->whereLike('title|info', '%' . $where['real_name'] . '%')->field(['id'])->select();
+                    })->whereOr('product_id', 'in', function ($q) use ($where) {
+                        $q->name('store_combination')->whereLike('title|info', '%' . $where['real_name'] . '%')->field(['id'])->select();
+                    })->whereOr('product_id', 'in', function ($q) use ($where) {
+                        $q->name('store_seckill')->whereLike('title|info', '%' . $where['real_name'] . '%')->field(['id'])->select();
                     })->field(['oid'])->select();
                 });
             }
@@ -177,8 +183,14 @@ class StoreOrderDao extends BaseDao
                 $que->whereLike('order_id|real_name|user_phone', '%' . $where['real_name'] . '%')->whereOr('uid', 'in', function ($q) use ($where) {
                     $q->name('user')->whereLike('nickname|uid|phone', '%' . $where['real_name'] . '%')->field(['uid'])->select();
                 })->whereOr('id', 'in', function ($que) use ($where) {
-                    $que->name('store_order_cart_info')->whereIn('product_id', function ($q) use ($where) {
+                    $que->name('store_order_cart_info')->whereOr('product_id', 'in', function ($q) use ($where) {
                         $q->name('store_product')->whereLike('store_name|keyword', '%' . $where['real_name'] . '%')->field(['id'])->select();
+                    })->whereOr('product_id', 'in', function ($q) use ($where) {
+                        $q->name('store_bargain')->whereLike('title|info', '%' . $where['real_name'] . '%')->field(['id'])->select();
+                    })->whereOr('product_id', 'in', function ($q) use ($where) {
+                        $q->name('store_combination')->whereLike('title|info', '%' . $where['real_name'] . '%')->field(['id'])->select();
+                    })->whereOr('product_id', 'in', function ($q) use ($where) {
+                        $q->name('store_seckill')->whereLike('title|info', '%' . $where['real_name'] . '%')->field(['id'])->select();
                     })->field(['oid'])->select();
                 });
             });

+ 1 - 1
crmeb/app/dao/order/StoreOrderRefundDao.php

@@ -54,7 +54,7 @@ class StoreOrderRefundDao extends BaseDao
                             $q->name('store_product')->whereLike('store_name|keyword', '%' . $where['real_name'] . '%')->field(['id'])->select();
                         })->field(['oid'])->select();
                     })->whereOr('store_order_id', 'in', function ($orderModel) use ($where) {
-                        $orderModel->name('store_order')->field('id')->whereLike('order_id', '%' . $where['real_name'] . '%');
+                        $orderModel->name('store_order')->field('id')->whereLike('order_id|user_phone', '%' . $where['real_name'] . '%');
                     });
                 });
             });

+ 5 - 4
crmeb/app/dao/product/product/StoreProductDao.php

@@ -41,12 +41,12 @@ class StoreProductDao extends BaseDao
     {
         return $this->search($where, false)->when(isset($where['sid']) && $where['sid'], function ($query) use ($where) {
             $query->whereIn('id', function ($query) use ($where) {
-                $query->name('store_product_cate')->where('cate_id', $where['sid'])->field('product_id')->select();
+                $query->name('store_product_cate')->where('cate_id', is_array($where['sid']) ? 'in' : '=', $where['sid'])->field('product_id')->select();
             });
         })->when(isset($where['cid']) && $where['cid'], function ($query) use ($where) {
             $query->whereIn('id', function ($query) use ($where) {
                 $query->name('store_product_cate')->whereIn('cate_id', function ($query) use ($where) {
-                    $query->name('store_category')->where('pid', $where['cid'])->field('id')->select();
+                    $query->name('store_category')->where('pid', is_array($where['cid']) ? 'in' : '=', $where['cid'])->field('id')->select();
                 })->field('product_id')->select();
             });
         })->when(isset($where['ids']), function ($query) use ($where) {
@@ -117,12 +117,13 @@ class StoreProductDao extends BaseDao
             $query->page($page, $limit);
         })->when(isset($where['sid']) && $where['sid'], function ($query) use ($where) {
             $query->whereIn('id', function ($query) use ($where) {
-                $query->name('store_product_cate')->where('cate_id', $where['sid'])->field('product_id')->select();
+                $query->name('store_product_cate')->where('cate_id', is_array($where['sid']) ? 'in' : '=', $where['sid'])->field('product_id')->select();
             });
         })->when(isset($where['cid']) && $where['cid'], function ($query) use ($where) {
             $query->whereIn('id', function ($query) use ($where) {
                 $query->name('store_product_cate')->whereIn('cate_id', function ($query) use ($where) {
-                    $query->name('store_category')->where('pid', $where['cid'])->whereOr('id', $where['cid'])->field('id')->select();
+                    $query->name('store_category')->where('pid', is_array($where['cid']) ? 'in' : '=', $where['cid'])
+                        ->whereOr('id', is_array($where['cid']) ? 'in' : '=', $where['cid'])->field('id')->select();
                 })->field('product_id')->select();
             });
         })->when(isset($where['coupon_category_id']) && $where['coupon_category_id'] != '', function ($query) use ($where) {

+ 6 - 0
crmeb/app/dao/system/SystemMenusDao.php

@@ -57,6 +57,7 @@ class SystemMenusDao extends BaseDao
         if (!$field) {
             $field = ['id', 'menu_name', 'icon', 'pid', 'sort', 'menu_path', 'is_show', 'header', 'is_header', 'is_show_path', 'is_show'];
         }
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return $this->search($where)->field($field)->order('sort DESC,id DESC')->failException(false)->select();
     }
 
@@ -67,6 +68,7 @@ class SystemMenusDao extends BaseDao
      */
     public function getMenusUnique(array $where)
     {
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return $this->search($where)->where('unique_auth', '<>', '')->column('unique_auth', '');
     }
 
@@ -91,6 +93,7 @@ class SystemMenusDao extends BaseDao
     public function getMenusList(array $where, array $field = ['*'])
     {
         $where = array_merge($where, ['is_del' => 0]);
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return $this->search($where)->field($field)->order('sort DESC,id ASC')->select();
     }
 
@@ -114,6 +117,7 @@ class SystemMenusDao extends BaseDao
      */
     public function column(array $where, string $field, string $key = '')
     {
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return $this->search($where)->column($field, $key);
     }
 
@@ -127,6 +131,7 @@ class SystemMenusDao extends BaseDao
      */
     public function menusSelect(array $where, $type = 1)
     {
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         if ($type == 1) {
             return $this->search($where)->field('id,pid,menu_name,menu_path,unique_auth,sort')->order('sort DESC,id DESC')->select();
         } else {
@@ -142,6 +147,7 @@ class SystemMenusDao extends BaseDao
      */
     public function getSearchList()
     {
+        $where['no_model'] = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         return $this->search(['is_show' => 1, 'auth_type' => 1, 'is_del' => 0, 'is_show_path' => 0])
             ->field('id,pid,menu_name,menu_path,unique_auth,sort')->order('sort DESC,id DESC')->select();
     }

+ 14 - 0
crmeb/app/dao/wechat/RoutineSchemeDao.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace app\dao\wechat;
+
+use app\dao\BaseDao;
+use app\model\wechat\RoutineScheme;
+
+class RoutineSchemeDao extends BaseDao
+{
+    protected function setModel(): string
+    {
+        return RoutineScheme::class;
+    }
+}

+ 1 - 0
crmeb/app/event.php

@@ -43,6 +43,7 @@ return [
         'UserLevelListener' => [\app\listener\user\UserLevelListener::class], //用户升级事件
         'UserVisitListener' => [\app\listener\user\UserVisitListener::class], //用户访问事件
         'NoticeListener' => [\app\listener\notice\NoticeListener::class], //通知->消息事件
+        'CustomNoticeListener' => [\app\listener\notice\CustomNoticeListener::class], //通知->自定义消息发送事件
         'NotifyListener' => [\app\listener\pay\NotifyListener::class],//支付异步回调
         'CrontabListener' => [\app\listener\crontab\SystemCrontabListener::class],//定时任务事件
         'OrderShipping' => [\app\listener\order\OrderShippingListener::class],//定时任务事件

+ 0 - 0
crmeb/app/http/README.md


+ 29 - 0
crmeb/app/jobs/README.md

@@ -0,0 +1,29 @@
+crmeb/app/jobs目录是CRMEB项目队列任务类的代码目录。
+
+队列任务在项目开发中有以下几个重要作用:
+
+1. 异步处理。可以将一些耗时较长的任务放入队列以异步处理,不阻塞主线程。
+
+2. 延迟处理。可以指定队列任务在一定时间后异步执行,比如发送短信或邮件。
+
+3. 分布处理。可以将队列任务分布到不同服务器进行处理,提高服务器使用效率。
+
+此目录下主要包含以下内容:
+
+- 每个任务类对应一个业务任务,实现Job接口。
+
+- 任务类内定义具体任务业务逻辑,如发送短信/邮件等。
+
+- 通过Broker进行任务的发送和异步处理。
+
+- 支持任务延时、失败重试等功能。
+
+使用队列可以使得项目性能更优:
+
+- 阻塞任务剥离出来异步执行。
+
+- 分布式下每个任务独立运行,不阻塞其他进程。
+
+- 通过Broker复用同一服务,且伸缩性好。
+
+所以此目录负责项目中所有异步任务的编写和调度,起到优化系统性能和扩展能力的重要作用。

+ 6 - 2
crmeb/app/jobs/TemplateJob.php

@@ -37,7 +37,7 @@ class TemplateJob extends BaseJobs
      * @param $color
      * @return bool|mixed
      */
-    public function doJob($type, $openid, $tempId, $data, $link, $color)
+    public function doJob($type, $openid, $tempId, $data, $link, $color, $wechatToRoutine = 0)
     {
         try {
             if (!$openid) return true;
@@ -56,7 +56,11 @@ class TemplateJob extends BaseJobs
                 }
                 $template->url($link);
             }
-            return $template->send($tempId, $data);
+            if ($type == 'wechat') {
+                return $template->send($tempId, $data, $wechatToRoutine);
+            } else {
+                return $template->send($tempId, $data);
+            }
         } catch (\Exception $e) {
             Log::error($e->getMessage());
             return true;

+ 39 - 0
crmeb/app/jobs/TranslateJob.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace app\jobs;
+
+use app\services\system\lang\LangCodeServices;
+use crmeb\basic\BaseJobs;
+use crmeb\traits\QueueTrait;
+use crmeb\utils\Translate;
+use think\facade\Log;
+
+class TranslateJob extends BaseJobs
+{
+    use QueueTrait;
+
+    public function doJob($data, $langType)
+    {
+        $translator = Translate::getInstance();
+        $translator->setAccessKey(sys_config('hs_accesskey'));
+        $translator->setSecretKey(sys_config('hs_secretkey'));
+        try {
+            $remarksData = [];
+            foreach ($data as $value) {
+                $remarksData[] = $value['remarks'];
+            }
+            $lang = $translator->translateText("", $langType, $remarksData);
+            $num = 0;
+            foreach ($data as &$value) {
+                $value['lang_explain'] = $lang[$num]['Translation'];
+                $num++;
+            }
+
+            app()->make(LangCodeServices::class)->saveAll($data);
+            return true;
+        } catch (\Exception $e) {
+            Log::error($e->getMessage());
+            return true;
+        }
+    }
+}

+ 33 - 0
crmeb/app/kefuapi/README.md

@@ -0,0 +1,33 @@
+crmeb/app/kefuapi这个目录是用来放置客服相关接口的。
+
+主要作用和特征:
+
+1. 提供客服系统与商城前后端交互的API接口。
+
+2. 接口主要用于聊天记录的增删改查以及发送消息等功能。
+
+3. 使用ThinkPHP的Restful风格定义接口请求方式和参数。
+
+4. 接口供移动端APP和PC网站调用实现客服聊天功能。
+
+5. 后台也可以调用相关接口对客服记录进行管理。
+
+具体包含:
+
+- 控制器定义各API接口方法接受请求。
+
+- 业务处理逻辑以及与数据库的交互。
+
+- 数据验证和结果输出。
+
+使用这个目录定义的客服API,可以:
+
+- 商城各端实现在线客服功能。
+
+- 查看历史记录。
+
+- 服务端管理客服信息。
+
+- 第三方也可以实现其它客服系统对接。
+
+所以总之,此目录主要对客服系统开放API接口,便于商城多端整合用户支持体系。

+ 29 - 0
crmeb/app/listener/README.md

@@ -0,0 +1,29 @@
+crmeb/app/listener目录用来定义项目的事件监听器。
+
+在ThinkPHP框架中,事件监听器是一个重要的机制。它可以用于:
+
+- 项目运行过程中的各个时间点自动调用指定的监听方法。
+
+- 监听特定事物(如请求、响应等)发生后自动执行回调。
+
+- 监听其它模块触发的事件,实现扩展钩子函数。
+
+具体来说:
+
+- 监听器类实现接口定义监听方法。
+
+- 方法内可以完成业务逻辑,也可以触发下一个监听器。
+
+- 监听器在配置中注册,在特定点自动调用定义的回调方法。
+
+- 常见监听点有请求开始、响应结束等生命周期点。
+
+这种设计可以:
+
+- 实现跨模块调用无需依赖。
+
+- 解耦业务和基础模块。
+
+- 让第三方功能易于插拔扩展。
+
+所以此目录定义的就是项目各种事件监听回调,起到系统级的扩展与定制作用。

+ 159 - 0
crmeb/app/listener/notice/CustomNoticeListener.php

@@ -0,0 +1,159 @@
+<?php
+
+namespace app\listener\notice;
+
+use app\jobs\TemplateJob;
+use app\services\message\MessageSystemServices;
+use app\services\message\SystemNotificationServices;
+use app\services\serve\ServeServices;
+use app\services\user\UserServices;
+use app\services\wechat\WechatUserServices;
+use crmeb\interfaces\ListenerInterface;
+use crmeb\services\HttpService;
+use think\facade\Log;
+
+class CustomNoticeListener implements ListenerInterface
+{
+
+    public function handle($event): void
+    {
+        [$uid, $infoData, $customTrigger] = $event;
+        $notificationServices = app()->make(SystemNotificationServices::class);
+        $noticeList = $notificationServices->getNotList(['custom_trigger' => $customTrigger]);
+        foreach ($noticeList as $item) {
+            if ($item['is_system'] == 1) $this->sendSystem($uid, $item, $infoData);
+            if ($item['is_sms'] == 1) $this->sendSms($uid, $item, $infoData);
+            if ($item['is_wechat'] == 1) $this->sendWechat($uid, $item, $infoData);
+            if ($item['is_routine'] == 1) $this->sendRoutine($uid, $item, $infoData);
+            if ($item['is_ent_wechat'] == 1) $this->sendEntWechat($uid, $item, $infoData);
+        }
+    }
+
+    public function sendSystem($uid, $noticeData, $infoData)
+    {
+        try {
+            $str = $noticeData['system_text'];
+            preg_match_all('/\{(\w+)\}/', $str, $matches);
+            $sendData = $matches[1];
+            foreach ($sendData as $sendItem) {
+                $str = str_replace("{" . $sendItem . "}", $infoData[$sendItem], $str);
+            }
+            $data = [];
+            $data['mark'] = 'custom_notice';
+            $data['uid'] = $uid;
+            $data['content'] = $str;
+            $data['title'] = $noticeData['system_title'];
+            $data['type'] = 1;
+            $data['add_time'] = time();
+            $data['data'] = json_encode($sendData);
+            /** @var MessageSystemServices $MessageSystemServices */
+            $MessageSystemServices = app()->make(MessageSystemServices::class);
+            $MessageSystemServices->save($data);
+        } catch (\Exception $e) {
+            Log::error('发送站内信失败,失败原因:' . $e->getMessage());
+            return true;
+        }
+    }
+
+    public function sendSms($uid, $noticeData, $infoData)
+    {
+        try {
+            $smsType = ['yihaotong', 'aliyun', 'tencent'];
+            $type = $smsType[sys_config('sms_type', 0)];
+            $str = $noticeData['sms_text'];
+            preg_match_all('/\{(\w+)\}/', $str, $matches);
+            $smsData = $matches[1];
+            $sendData = [];
+            foreach ($smsData as $smsItem) {
+                $sendData[$smsItem] = $infoData[$smsItem];
+            }
+            if ($type == 'tencent') {
+                $sendData = array_values($sendData);
+            }
+            app()->make(ServeServices::class)->sms($type)->send($infoData['phone'], $noticeData['sms_id'], $sendData);
+            return true;
+        } catch (\Exception $e) {
+            Log::error('发送短信失败,失败原因:' . $e->getMessage());
+            return true;
+        }
+    }
+
+    public function sendWechat($uid, $noticeData, $infoData)
+    {
+        try {
+            $openid = '';
+            $isDel = app()->make(UserServices::class)->value(['uid' => $uid], 'is_del');
+            if (!$isDel) $openid = app()->make(WechatUserServices::class)->uidToOpenid($uid, 'wechat');
+            if ($openid) {
+                $wechatData = json_decode($noticeData['wechat_data'], true);
+                $sendData = [];
+                foreach ($wechatData as $wechatItem) {
+                    $sendData[$wechatItem['key']] = $infoData[$wechatItem['value']];
+                }
+                $link = $noticeData['wechat_link'];
+                preg_match_all('/\{(\w+)\}/', $link, $matches);
+                $linkData = $matches[1];
+                foreach ($linkData as $linkItem) {
+                    $link = str_replace("{" . $linkItem . "}", $infoData[$linkItem], $link);
+                }
+                TemplateJob::dispatch('doJob', ['wechat', $openid, $noticeData['wechat_tempid'], $sendData, $link, null, $noticeData['wechat_to_routine']]);
+            }
+            return true;
+        } catch (\Exception $e) {
+            Log::error('发送微信模版消息失败,失败原因:' . $e->getMessage());
+            return true;
+        }
+    }
+
+    public function sendRoutine($uid, $noticeData, $infoData)
+    {
+        try {
+            $openid = '';
+            $isDel = app()->make(UserServices::class)->value(['uid' => $uid], 'is_del');
+            if (!$isDel) $openid = app()->make(WechatUserServices::class)->uidToOpenid($uid, 'routine');
+            if ($openid) {
+                $routineData = json_decode($noticeData['routine_data'], true);
+                $sendData = [];
+                foreach ($routineData as $routineItem) {
+                    $sendData[$routineItem['key']] = $infoData[$routineItem['value']];
+                }
+                $link = $noticeData['routine_link'];
+                preg_match_all('/\{(\w+)\}/', $link, $matches);
+                $linkData = $matches[1];
+                foreach ($linkData as $linkItem) {
+                    $link = str_replace("{" . $linkItem . "}", $infoData[$linkItem], $link);
+                }
+                TemplateJob::dispatch('doJob', ['subscribe', $openid, $noticeData['routine_tempid'], $sendData, $link, null]);
+            }
+            return true;
+        } catch (\Exception $e) {
+            Log::error('发送小程序订阅消息失败,失败原因:' . $e->getMessage());
+            return true;
+        }
+    }
+
+    public function sendEntWechat($uid, $noticeData, $infoData)
+    {
+        try {
+            $str = $noticeData['ent_wechat_text'];
+            preg_match_all('/\{(\w+)\}/', $str, $matches);
+            $sendData = $matches[1];
+            foreach ($sendData as $sendItem) {
+                $str = str_replace("{" . $sendItem . "}", $infoData[$sendItem], $str);
+            }
+
+            $s = explode('\n', $str);
+            $d = '';
+            foreach ($s as $item) {
+                $d .= $item . "\n>";
+            }
+            $d = substr($d, 0, strlen($d) - 2);
+            HttpService::postRequest($noticeData['url'], json_encode([
+                'msgtype' => 'markdown',
+                'markdown' => ['content' => $d]
+            ]));
+        } catch (\Throwable $e) {
+            Log::error('发送企业群消息失败,失败原因:' . $e->getMessage());
+        }
+    }
+}

+ 1 - 1
crmeb/app/listener/notice/NoticeListener.php

@@ -468,7 +468,7 @@ class NoticeListener implements ListenerInterface
     {
         $list = $data['list'];
         $title = $data['title'];
-        $url = '/pages/users/order_details/index?order_id=' . $list['order_id'];
+        $url = '/pages/goods/order_details/index?order_id=' . $list['order_id'];
         $title = Str::substrUTf8($title, 20, 'UTF-8', '');
 
         //站内信

+ 1 - 1
crmeb/app/listener/order/OrderPaySuccessListener.php

@@ -58,7 +58,7 @@ class OrderPaySuccessListener implements ListenerInterface
         $orderInvoiceServices->update(['order_id' => $orderInfo['id']], ['is_pay' => 1]);
 
         //虚拟商品自动发货
-        if ($orderInfo['virtual_type'] > 0) {
+        if ($orderInfo['virtual_type'] > 0 && $orderInfo['combination_id'] == 0) {
             /** @var StoreOrderDeliveryServices $orderDeliveryServices */
             $orderDeliveryServices = app()->make(StoreOrderDeliveryServices::class);
             $orderDeliveryServices->virtualSend($orderInfo);

+ 2 - 2
crmeb/app/listener/order/OrderShippingListener.php

@@ -48,7 +48,7 @@ class OrderShippingListener implements ListenerInterface
                     return;
                 }
             } else if ($order_type == 'recharge') {  // 充值订单
-                if ($order['recharge_type'] == 'routine') {
+                if ($order['recharge_type'] == 'weixin') {
                     $delivery_type = 3;
                     $item_desc = '用户充值' . $order['price'];
                     $out_trade_no = $order['order_id'];
@@ -59,7 +59,7 @@ class OrderShippingListener implements ListenerInterface
                     return;
                 }
             } else if ($order_type == 'member') {  // 会员订单
-                if ($order['pay_type'] == 'routine') {
+                if ($order['pay_type'] == 'weixin') {
                     $delivery_type = 3;
                     $item_desc = '用户购买' . $order['member_type'] . '会员卡';
                     $out_trade_no = $order['order_id'];

+ 7 - 1
crmeb/app/listener/user/RegisterListener.php

@@ -46,7 +46,13 @@ class RegisterListener implements ListenerInterface
             //记录推广绑定关系
             /** @var UserSpreadServices $userSpreadServices */
             $userSpreadServices = app()->make(UserSpreadServices::class);
-            $userSpreadServices->setSpread($uid, $spreadUid);
+            $res = $userSpreadServices->setSpread($uid, $spreadUid);
+
+            //自定义消息-下级用户绑定成功
+            if ($res) {
+                $phone = app()->make(UserServices::class)->value($uid, 'phone');
+                event('CustomNoticeListener', [$uid, ['nickname' => $name, 'time' => date('Y-m-d H:i:s'), 'phone' => $phone], 'spread_success']);
+            }
         }
 
         if ($isNew) {

+ 33 - 0
crmeb/app/model/README.md

@@ -0,0 +1,33 @@
+crmeb/app/model目录用于定义项目的数据模型类。
+
+数据模型类的主要作用和特征有:
+
+1. 每个模型类对应数据库中的一张表。
+
+2. 类属性定义表结构,与表结构一一对应。
+
+3. 包含读写数据相关方法,通过ActiveRecord实现。
+
+4. 提供数据与数据库层解耦,统一的数据访问接口。
+
+5. 数据验证机制,保证数据完整性和一致性。
+
+具体包含:
+
+- 定义模型属性,字段名称对应表结构。
+
+- 自动返回属性值和赋值属性值。
+
+- 实现基础的CRUD方法操作数据库。
+
+- 可扩展自定义数据逻辑和验证规则。
+
+使用模型类可以:
+
+- 减少直接操作数据库带来的复杂性。
+
+- 跨项目重用数据层逻辑。
+
+- 提高项目的可扩展性与复用性。
+
+所以该目录定义的数据模型层,统一封装了项目运用的数据表模型和操作方式。

+ 15 - 1
crmeb/app/model/diy/PageCategory.php

@@ -59,5 +59,19 @@ class PageCategory extends BaseModel
         if ($value != '') $query->where('status', $value);
     }
 
-
+    /**
+     * 模块检测
+     * @param Model $query
+     * @param $value
+     */
+    public function searchNoModelAttr($query, $value)
+    {
+        $query->when(!in_array('seckill', $value), function ($q1) {
+            $q1->whereNotLike('name', '%秒杀%');
+        })->when(!in_array('bargain', $value), function ($q2) {
+            $q2->whereNotLike('name', '%砍价%');
+        })->when(!in_array('combination', $value), function ($q3) {
+            $q3->whereNotLike('name', '%拼团%');
+        });
+    }
 }

+ 17 - 0
crmeb/app/model/diy/PageLink.php

@@ -57,4 +57,21 @@ class PageLink extends BaseModel
     {
         if ($value != '') $query->where('status', $value);
     }
+
+
+    /**
+     * 模块检测
+     * @param Model $query
+     * @param $value
+     */
+    public function searchNoModelAttr($query, $value)
+    {
+        $query->when(!in_array('seckill', $value), function ($q1) {
+            $q1->whereNotLike('name', '%秒杀%');
+        })->when(!in_array('bargain', $value), function ($q2) {
+            $q2->whereNotLike('name', '%砍价%');
+        })->when(!in_array('combination', $value), function ($q3) {
+            $q3->whereNotLike('name', '%拼团%');
+        });
+    }
 }

+ 35 - 0
crmeb/app/model/order/StoreOrder.php

@@ -314,6 +314,16 @@ class StoreOrder extends BaseModel
             $query->where('uid', $value);
     }
 
+    /**
+     * 不包含用户ID搜索器
+     * @param Model $query
+     * @param $value
+     */
+    public function searchNotUidAttr($query, $value)
+    {
+        $query->where('uid', '<>', $value);
+    }
+
     /**
      * 支付状态搜索器
      * @param Model $query
@@ -509,6 +519,11 @@ class StoreOrder extends BaseModel
         if ($value) $query->where('spread_uid|spread_two_uid', $value);
     }
 
+    public function searchAllSpreadAttr($query, $value)
+    {
+        if ($value) $query->where('spread_uid|spread_two_uid|division_id|agent_id|staff_id', $value);
+    }
+
     /**
      * 上级推广人
      * @param $query
@@ -590,6 +605,16 @@ class StoreOrder extends BaseModel
         if ($value !== '') $query->where('agent_id', $value);
     }
 
+    /**
+     * 代理商推广订单
+     * @param $query
+     * @param $value
+     */
+    public function searchStaffIdAttr($query, $value)
+    {
+        if ($value !== '') $query->where('staff_id', $value);
+    }
+
     /**
      * @param $query
      * @param $value
@@ -599,4 +624,14 @@ class StoreOrder extends BaseModel
         if (is_string($value)) $value = explode(',', $value);
         if (count($value)) $query->whereIn('id', $value);
     }
+
+    public function searchDivisionBrokerageGreaterAttr($query, $value)
+    {
+        $query->where('division_brokerage', '>', $value);
+    }
+
+    public function searchAgentBrokerageGreaterAttr($query, $value)
+    {
+        $query->where('agent_brokerage', '>', $value);
+    }
 }

+ 16 - 0
crmeb/app/model/system/SystemMenus.php

@@ -239,4 +239,20 @@ class SystemMenus extends BaseModel
             }
         }
     }
+
+    /**
+     * 模块检测
+     * @param Model $query
+     * @param $value
+     */
+    public function searchNoModelAttr($query, $value)
+    {
+        $query->when(!in_array('seckill', $value), function ($q1) {
+            $q1->whereNotLike('menu_name', '%秒杀%');
+        })->when(!in_array('bargain', $value), function ($q2) {
+            $q2->whereNotLike('menu_name', '%砍价%');
+        })->when(!in_array('combination', $value), function ($q3) {
+            $q3->whereNotLike('menu_name', '%拼团%');
+        });
+    }
 }

+ 28 - 0
crmeb/app/model/wechat/RoutineScheme.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace app\model\wechat;
+
+use crmeb\basic\BaseModel;
+use crmeb\traits\ModelTrait;
+
+class RoutineScheme extends BaseModel
+{
+    use ModelTrait;
+
+    /**
+     * 数据表主键
+     * @var string
+     */
+    protected $pk = 'id';
+
+    /**
+     * 模型名称
+     * @var string
+     */
+    protected $name = 'routine_scheme';
+
+    public function searchTitleAttr($query, $value)
+    {
+        if ($value !== '') $query->where('title', 'like', '%' . $value . '%');
+    }
+}

+ 29 - 0
crmeb/app/outapi/README.md

@@ -0,0 +1,29 @@
+crmeb/app/outapi目录用于定义项目对外开放的接口。
+
+具体来说:
+
+- 是项目对合作第三方开放的API接口定义目录
+
+- 这里的接口可以被第三方直接调用来获取数据或完成相关业务
+
+- 这类API接口与内部使用的API有区别:
+
+  - 对外开放,不需要登录授权
+  - 安全限制较严,只提供必要接口
+  - 接口规范遵循RESTful原则
+
+- 常见场景:
+
+  - 第方小程序/APP直接获取商品数据
+  - 第方商户后台系统同步订单信息
+  - 小程序支付回调通知接口
+
+使用这个目录定义外部接口可以:
+
+- 实现与其他系统的深度集成
+
+- 让更多场景能够使用CRMEB提供的能力
+
+- 降低对第方的侵入性,仅开放必要接口
+
+所以总结来说,outapi用于项目对外部开放的公开API,扩展第三方接入能力。

+ 4 - 0
crmeb/app/services/BaseServices.php

@@ -97,6 +97,10 @@ abstract class BaseServices
         if ($type == 'api' && !app()->make(UserServices::class)->value(['uid' => $id], 'status')) {
             throw new ApiException(410027);
         }
+        if ($type == 'api') {
+            //自定义消息-用户登录成功
+            event('CustomNoticeListener', [$id, app()->make(UserServices::class)->get($id), 'login_success']);
+        }
         return $jwtAuth->createToken($id, $type, ['pwd' => md5($pwd)]);
     }
 

+ 31 - 0
crmeb/app/services/README.md

@@ -0,0 +1,31 @@
+crmeb/app/services目录用于定义项目的业务服务类。
+
+服务类的主要特征和作用包括:
+
+1. 服务类封装具体的业务逻辑和规则。
+
+2. 完成功能模块的抽象,提供统一的业务接口。
+
+3. 解耦项目各部分,降低他们之间的耦合度。
+
+4. 提供给上下文的整个环境使用。
+
+具体来说:
+
+- 每个服务类对应一个独立的业务功能或规则集合。
+
+- 类内部可以调用其它模块完成业务需求。
+
+- 对外提供简单的业务接口,隐藏内部实现细节。
+
+- 服务类存在依赖关系,可以互相调用实现聚合服务。
+
+使用服务层设计可以:
+
+- 松耦合各个模块,提高扩展和重用能力。
+
+- 同一个业务规则在多个场景复用。
+
+- 加强项目的可测试性与维护性。
+
+所以此目录定义了项目核心业务服务模块,对外提供可复用的核心能力。

+ 2 - 2
crmeb/app/services/activity/bargain/StoreBargainUserServices.php

@@ -51,7 +51,7 @@ class StoreBargainUserServices extends BaseServices
         $ids = $this->dao->getColumn(['bargain_id' => $bargainId], 'id');
         /** @var StoreBargainUserHelpServices $bargainHelp */
         $bargainHelp = app()->make(StoreBargainUserHelpServices::class);
-        return $bargainHelp->getCount([['bargain_user_id', 'in', $ids], ['bargain_id', '=', $bargainId], ['type', '=', 1]]);
+        return $bargainHelp->getCount([['bargain_user_id', 'in', $ids], ['bargain_id', '=', $bargainId]]);
     }
 
     /**
@@ -247,7 +247,7 @@ class StoreBargainUserServices extends BaseServices
         $bargainUserHelpService = app()->make(StoreBargainUserHelpServices::class);
         $nums = $bargainUserHelpService->getNums();
         foreach ($list as &$item) {
-            $item['num'] = $item['people_num'] - ($nums[$item['id']] ?? 0);
+            $item['num'] = ($nums[$item['id']] ?? 1) - 1;
             $item['already_num'] = $nums[$item['id']] ?? 0;
             $item['now_price'] = bcsub((string)$item['bargain_price'], (string)$item['price'], 2);
             $item['add_time'] = $item['add_time'] ? date('Y-m-d H:i:s', (int)$item['add_time']) : '';

+ 13 - 0
crmeb/app/services/activity/combination/StorePinkServices.php

@@ -15,6 +15,7 @@ namespace app\services\activity\combination;
 use app\dao\activity\combination\StorePinkDao;
 use app\jobs\PinkJob;
 use app\services\BaseServices;
+use app\services\order\StoreOrderDeliveryServices;
 use app\services\order\StoreOrderRefundServices;
 use app\services\order\StoreOrderServices;
 use app\services\other\PosterServices;
@@ -408,6 +409,18 @@ class StorePinkServices extends BaseServices
                 ], 'order_user_groups_success']);
         }
         $this->dao->update([['uid', 'in', $uidAll], ['id|k_id', '=', $pid]], ['is_tpl' => 1]);
+
+        //拼团卡密和优惠券商品,成团后发放
+        $orderInfos = $orderService->getColumn([['order_id', 'in', $order_ids]], '*', 'order_id');
+        foreach ($orderInfos as $orderInfo) {
+            if ($orderInfo['virtual_type'] > 0) {
+                $orderInfo['cart_id'] = json_decode($orderInfo['cart_id'], true);
+                /** @var StoreOrderDeliveryServices $orderDeliveryServices */
+                $orderDeliveryServices = app()->make(StoreOrderDeliveryServices::class);
+                $orderDeliveryServices->virtualSend($orderInfo);
+            }
+        }
+        return true;
     }
 
     /**

+ 1 - 1
crmeb/app/services/activity/integral/StorePointRecordServices.php

@@ -107,7 +107,7 @@ class StorePointRecordServices extends BaseServices
     public function getTrend($where)
     {
         $time = explode('-', $where['time']);
-        if (count($time) != 2) throw new AdminException(100100);
+        if (count($time) != 2) throw new AdminException('请选择时间');
         $dayCount = (strtotime($time[1]) - strtotime($time[0])) / 86400 + 1;
         $data = [];
         if ($dayCount == 1) {

+ 2 - 3
crmeb/app/services/activity/lottery/LuckLotteryRecordServices.php

@@ -23,9 +23,8 @@ use app\services\user\UserServices;
 use app\services\wechat\WechatUserServices;
 use crmeb\exceptions\ApiException;
 use crmeb\services\app\WechatService;
-use think\facade\Log;
 use crmeb\services\pay\Pay;
-
+use think\facade\Log;
 
 /**
  *  抽奖记录
@@ -211,6 +210,7 @@ class LuckLotteryRecordServices extends BaseServices
                             'nickname' => $userInfo['nickname'],
                             'phone' => $userInfo['phone']
                         ], 'luck');
+
                         if (sys_config('pay_wechat_type')) {
                             $pay = new Pay('v3_wechat_pay');
                             $pay->merchantPay($openid, $wechat_order_id, $prize['num'], [
@@ -221,7 +221,6 @@ class LuckLotteryRecordServices extends BaseServices
                         } else {
                             WechatService::merchantPay($openid, $wechat_order_id, $prize['num'], '抽奖中奖红包');
                         }
-
                     }
                     break;
                 case 5:

+ 1 - 1
crmeb/app/services/activity/seckill/StoreSeckillServices.php

@@ -469,7 +469,7 @@ class StoreSeckillServices extends BaseServices
         /** @var StoreProductServices $storeProductService */
         $storeProductService = app()->make(StoreProductServices::class);
         $productInfo = $storeProductService->get($storeInfo['product_id']);
-        $storeInfo['total'] = $productInfo['sales'] + $productInfo['ficti'] + $storeInfo['sales'];
+        $storeInfo['total'] = $storeInfo['sales'];
 
         if (sys_config('share_qrcode', 0) && request()->isWechat()) {
             /** @var QrcodeServices $qrcodeService */

+ 12 - 8
crmeb/app/services/agent/AgentLevelServices.php

@@ -228,19 +228,23 @@ class AgentLevelServices extends BaseServices
 
         if ($one_agent_level) {
             $oneLevelInfo = $this->getLevelInfo($one_agent_level);
-            if ($oneLevelInfo['one_brokerage_percent'] == '0.00') {
-                $storeBrokerageRatio = $storeBrokerageRatio + (($storeBrokerageRatio * $oneLevelInfo['one_brokerage'] ?? 0) / 100);
-            } else {
-                $storeBrokerageRatio = $oneLevelInfo['one_brokerage_percent'];
+            if ($oneLevelInfo) {
+                if ($oneLevelInfo['one_brokerage_percent'] == '0.00') {
+                    $storeBrokerageRatio = $storeBrokerageRatio + (($storeBrokerageRatio * $oneLevelInfo['one_brokerage'] ?? 0) / 100);
+                } else {
+                    $storeBrokerageRatio = $oneLevelInfo['one_brokerage_percent'];
+                }
             }
         }
 
         if ($two_agent_level) {
             $twoLevelInfo = $this->getLevelInfo($two_agent_level);
-            if ($twoLevelInfo['two_brokerage_percent'] == '0.00') {
-                $storeBrokerageTwo = $storeBrokerageTwo + (($storeBrokerageTwo * $twoLevelInfo['two_brokerage'] ?? 0) / 100);
-            } else {
-                $storeBrokerageTwo = $twoLevelInfo['two_brokerage_percent'];
+            if ($twoLevelInfo) {
+                if ($twoLevelInfo['two_brokerage_percent'] == '0.00') {
+                    $storeBrokerageTwo = $storeBrokerageTwo + (($storeBrokerageTwo * $twoLevelInfo['two_brokerage'] ?? 0) / 100);
+                } else {
+                    $storeBrokerageTwo = $twoLevelInfo['two_brokerage_percent'];
+                }
             }
         }
 

+ 1 - 1
crmeb/app/services/agent/AgentManageServices.php

@@ -73,7 +73,7 @@ class AgentManageServices extends BaseServices
             if (strpos($item['headimgurl'], '/statics/system_images/') !== false) {
                 $item['headimgurl'] = set_file_url($item['headimgurl']);
             }
-            $item['spread_order'] = $orderServices->get(['spread_uid' => $item['uid'], 'paid' => 1, 'refund_status' => 0, 'pid' => 0], ['sum(pay_price) as order_price','count(id) as order_count']);
+            $item['spread_order'] = $orderServices->get(['spread_uid' => $item['uid'], 'paid' => 1, 'refund_status' => 0, 'pid' => 0], ['sum(pay_price) as order_price', 'count(id) as order_count']);
         }
         return $data;
     }

+ 53 - 17
crmeb/app/services/agent/DivisionAgentApplyServices.php

@@ -12,6 +12,7 @@ use app\services\system\attachment\SystemAttachmentServices;
 use app\services\user\UserServices;
 use crmeb\exceptions\AdminException;
 use crmeb\exceptions\ApiException;
+use crmeb\services\app\MiniProgramService;
 use crmeb\services\FormBuilder as Form;
 use app\services\other\UploadService;
 use think\facade\Config;
@@ -188,15 +189,15 @@ class DivisionAgentApplyServices extends BaseServices
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
      */
-    public function getStaffList($userInfo, $where, $field = '*')
+    public function getStaffList($isRoutine, $where, $field = '*')
     {
         /** @var UserServices $userService */
         $userService = app()->make(UserServices::class);
         /** @var StoreOrderServices $orderService */
         $orderService = app()->make(StoreOrderServices::class);
         [$page, $limit] = $this->getPageValue();
-        $count = $userService->getCount(['agent_id' => $where['agent_id'], 'is_staff' => 1]);
-        $list = $userService->getList(['agent_id' => $where['agent_id'], 'is_staff' => 1], $field, $page, $limit);
+        $count = $userService->getCount(['agent_id' => $where['agent_id'], 'is_staff' => 1, 'is_del' => 0]);
+        $list = $userService->getList(['agent_id' => $where['agent_id'], 'is_staff' => 1, 'is_del' => 0], $field, $page, $limit);
         foreach ($list as &$item) {
             $item['division_change_time'] = date('Y-m-d', $item['division_change_time']);
             $item['division_end_time'] = date('Y-m-d', $item['division_end_time']);
@@ -205,32 +206,67 @@ class DivisionAgentApplyServices extends BaseServices
             $item['numberCount'] = $orderService->sum(['uid' => $item['uid']], 'pay_price');
         }
         $codeUrl = '';
-        try {
+        if ($isRoutine) {
             /** @var SystemAttachmentServices $systemAttachment */
             $systemAttachment = app()->make(SystemAttachmentServices::class);
-            $name = 'agent_' . $where['agent_id'] . '.jpg';
-            $siteUrl = sys_config('site_url', '');
+            $name = 'routine_agent_' . $where['agent_id'] . '.jpg';
             $imageInfo = $systemAttachment->getInfo(['name' => $name]);
+            //检测远程文件是否存在
+            if (isset($imageInfo['att_dir']) && strstr($imageInfo['att_dir'], 'http') !== false && curl_file_exist($imageInfo['att_dir']) === false) {
+                $imageInfo = null;
+                $systemAttachment->delete(['name' => $name]);
+            }
+            $siteUrl = sys_config('site_url');
             if (!$imageInfo) {
                 /** @var QrcodeServices $qrCode */
                 $qrCode = app()->make(QrcodeServices::class);
-                //公众号
-                $resCode = $qrCode->getForeverQrcode('agent', $where['agent_id']);
+                $resForever = $qrCode->qrCodeForever($where['agent_id'], 'agent', '', '');
+                $resCode = MiniProgramService::appCodeUnlimitService($resForever->id, '', 280);
                 if ($resCode) {
-                    $res = ['res' => $resCode, 'id' => $resCode['id']];
+                    $res = ['res' => $resCode, 'id' => $resForever->id];
                 } else {
                     $res = false;
                 }
-                if (!$res) throw new ApiException(410167);
-                $imageInfo = $this->downloadImage($resCode['url'], $name);
-                $systemAttachment->attachmentAdd($name, $imageInfo['size'], $imageInfo['type'], $imageInfo['att_dir'], $imageInfo['att_dir'], 1, $imageInfo['image_type'], time(), 2);
-            }
-            $codeUrl = strpos($imageInfo['att_dir'], 'http') === false ? $siteUrl . $imageInfo['att_dir'] : $imageInfo['att_dir'];
-        } catch (\Exception $e) {
-            Log::error('邀请员工二维码生成失败,失败原因' . $e->getMessage());
+                if (!$res) return compact('list', 'count', 'codeUrl');
+                $uploadType = (int)sys_config('upload_type', 1);
+                $upload = UploadService::init();
+                $uploadRes = $upload->to('routine/agent/code')->validate()->setAuthThumb(false)->stream($res['res'], $name);
+                if ($uploadRes === false) return compact('list', 'count', 'codeUrl');
+                $imageInfo = $upload->getUploadInfo();
+                $imageInfo['image_type'] = $uploadType;
+                $systemAttachment->attachmentAdd($imageInfo['name'], $imageInfo['size'], $imageInfo['type'], $imageInfo['dir'], $imageInfo['thumb_path'], 1, $imageInfo['image_type'], $imageInfo['time'], 2);
+                $qrCode->setQrcodeFind($res['id'], ['status' => 1, 'url_time' => time(), 'qrcode_url' => $imageInfo['dir']]);
+                $codeUrl = $imageInfo['dir'];
+            } else $codeUrl = $imageInfo['att_dir'];
+            if ($imageInfo['image_type'] == 1) $codeUrl = $siteUrl . $codeUrl;
         }
-
         return compact('list', 'count', 'codeUrl');
+
+        //代理商邀请员工二维码为公众号渠道码,需要配置公众号并开启关注自动生成用户使用
+//        try {
+//            /** @var SystemAttachmentServices $systemAttachment */
+//            $systemAttachment = app()->make(SystemAttachmentServices::class);
+//            $name = 'agent_' . $where['agent_id'] . '.jpg';
+//            $siteUrl = sys_config('site_url', '');
+//            $imageInfo = $systemAttachment->getInfo(['name' => $name]);
+//            if (!$imageInfo) {
+//                /** @var QrcodeServices $qrCode */
+//                $qrCode = app()->make(QrcodeServices::class);
+//                //公众号
+//                $resCode = $qrCode->getForeverQrcode('agent', $where['agent_id']);
+//                if ($resCode) {
+//                    $res = ['res' => $resCode, 'id' => $resCode['id']];
+//                } else {
+//                    $res = false;
+//                }
+//                if (!$res) throw new ApiException(410167);
+//                $imageInfo = $this->downloadImage($resCode['url'], $name);
+//                $systemAttachment->attachmentAdd($name, $imageInfo['size'], $imageInfo['type'], $imageInfo['att_dir'], $imageInfo['att_dir'], 1, $imageInfo['image_type'], time(), 2);
+//            }
+//            $codeUrl = strpos($imageInfo['att_dir'], 'http') === false ? $siteUrl . $imageInfo['att_dir'] : $imageInfo['att_dir'];
+//        } catch (\Exception $e) {
+//            Log::error('邀请员工二维码生成失败,失败原因' . $e->getMessage());
+//        }
     }
 
     /**

+ 151 - 16
crmeb/app/services/agent/DivisionServices.php

@@ -3,11 +3,16 @@
 namespace app\services\agent;
 
 use app\services\BaseServices;
+use app\services\other\QrcodeServices;
 use app\services\system\admin\SystemAdminServices;
 use app\services\system\admin\SystemRoleServices;
 use app\services\user\UserServices;
 use crmeb\exceptions\AdminException;
+use crmeb\exceptions\ApiException;
 use crmeb\services\FormBuilder as Form;
+use think\db\exception\DataNotFoundException;
+use think\db\exception\DbException;
+use think\db\exception\ModelNotFoundException;
 use think\facade\Route;
 
 class DivisionServices extends BaseServices
@@ -30,7 +35,8 @@ class DivisionServices extends BaseServices
             $item['agent_count'] = $userServices->count([
                 $where['division_type'] == 1 ? 'division_id' : 'agent_id' => $item['uid'],
                 'division_type' => $where['division_type'] + 1,
-                'status' => 1
+                'status' => 1,
+                'is_del' => 0
             ]);
             unset($item['label']);
         }
@@ -54,11 +60,12 @@ class DivisionServices extends BaseServices
             $type == 2 ? 'division_id' : 'agent_id' => $uid,
             'division_type' => $type
         ];
-        $data = $userServices->getDivisionList($where + ['status' => 1], 'uid,nickname,avatar,division_percent,division_end_time,division_status');
+        $where['status'] = 1;
+        $where['is_del'] = 0;
+        $data = $userServices->getDivisionList($where, 'uid,nickname,avatar,division_percent,division_end_time,division_status');
         foreach ($data['list'] as &$item) {
-//            $item['division_end_time'] = date('Y-m-d', $item['division_end_time']);
             $item['agent_count'] = $userServices->count([
-                $type == 2 ? 'division_id' : 'agent_id' => $item['uid'],
+                'agent_id' => $item['uid'],
                 'division_type' => $type + 1,
                 'status' => 1
             ]);
@@ -92,9 +99,9 @@ class DivisionServices extends BaseServices
         $field = [];
         $title = '事业部';
         if ($uid) {
-            $field[] = Form::number('uid', '用户UID', $userInfo['uid'])->disabled(true)->style(['width' => '173px']);
+            $field[] = Form::hidden('uid', $uid);
         } else {
-            $field[] = Form::number('uid', '用户UID')->required('请填写用户UID')->style(['width' => '173px']);
+            $field[] = Form::frameImage('image', '用户', $this->url(config('app.admin_prefix', 'admin') . '/system.user/list', ['fodder' => 'image'], true))->icon('el-icon-user')->width('950px')->height('560px')->Props(['srcKey' => 'image', 'footer' => false]);
         }
         $field[] = Form::hidden('aid', $adminInfo['id'] ?? 0);
         $field[] = Form::number('division_percent', '佣金比例', $userInfo['division_percent'] ?? '')->placeholder('区域代理佣金比例1-100')->info('填写1-100,如填写50代表返佣50%')->style(['width' => '173px'])->min(0)->max(100)->required();
@@ -103,7 +110,7 @@ class DivisionServices extends BaseServices
         $field[] = Form::input('account', '管理账号', $adminInfo['account'] ?? '')->required('请填写管理员账号');
         $field[] = Form::input('pwd', '管理密码')->type('password')->placeholder('请填写管理员密码');
         $field[] = Form::input('conf_pwd', '确认密码')->type('password')->placeholder('请输入确认密码');
-        $field[] = Form::input('real_name', '区域代理姓名', $adminInfo['real_name'] ?? '')->required('请输入管理员姓名');
+        $field[] = Form::input('real_name', '代理姓名', $adminInfo['real_name'] ?? '')->required('请输入管理员姓名');
         /** @var SystemRoleServices $service */
         $service = app()->make(SystemRoleServices::class);
         $options = $service->getRoleFormSelect(1);
@@ -118,7 +125,15 @@ class DivisionServices extends BaseServices
      */
     public function divisionSave($data)
     {
+        if ((int)$data['uid'] == 0) $data['uid'] = $data['image']['uid'];
         if ((int)$data['uid'] == 0) throw new AdminException(400450);
+        /** @var UserServices $userServices */
+        $userServices = app()->make(UserServices::class);
+        $userInfo = $userServices->getUserInfo($data['uid'], 'is_division,is_agent,is_staff');
+        if (!$userInfo) throw new AdminException('用户不存在');
+        if ($userInfo['is_division']) throw new AdminException('此用户是事业部,请勿重复添加');
+        if ($userInfo['is_agent']) throw new AdminException('此用户是代理商,无法添加为事业部');
+        if ($userInfo['is_staff']) throw new AdminException('此用户是下级员工,无法添加为事业部');
         $uid = $data['uid'];
         $aid = $data['aid'];
         $agentData = [
@@ -146,9 +161,7 @@ class DivisionServices extends BaseServices
             'level' => 1,
             'division_id' => $uid
         ];
-        return $this->transaction(function () use ($uid, $agentData, $adminData, $aid) {
-            /** @var UserServices $userServices */
-            $userServices = app()->make(UserServices::class);
+        return $this->transaction(function () use ($uid, $agentData, $adminData, $aid, $userServices) {
             $agentData['division_invite'] = $userServices->value(['uid' => $uid], 'division_invite') ?: rand(10000000, 99999999);
             $userServices->update($uid, $agentData);
 
@@ -221,13 +234,17 @@ class DivisionServices extends BaseServices
         $userInfo = $userService->get($uid);
         if ($uid && !$userInfo) throw new AdminException(400214);
         $field = [];
+        $options = [];
+        $divisionList = $userService->getDivisionList(['status' => 1, 'division_type' => 1], 'uid,nickname');
+        foreach ($divisionList['list'] as $item) {
+            $options[] = ['value' => $item['uid'], 'label' => $item['nickname']];
+        }
         if ($uid) {
-            $field[] = Form::number('division_id', '事业部UID', $userInfo['division_id'] ?? '')->disabled(true)->style(['width' => '173px']);
-            $field[] = Form::number('uid', '用户UID', $userInfo['uid'] ?? '')->disabled(true)->style(['width' => '173px']);
+            $field[] = Form::hidden('uid', $uid);
             $field[] = Form::hidden('edit', 1);
         } else {
-            $field[] = Form::number('division_id', '事业部UID')->style(['width' => '173px']);
-            $field[] = Form::number('uid', '用户UID')->style(['width' => '173px']);
+            $field[] = Form::select('division_id', '事业部', $info['file_name'] ?? '')->setOptions(Form::setOptions($options))->filterable(1);
+            $field[] = Form::frameImage('image', '代理商', $this->url(config('app.admin_prefix', 'admin') . '/system.user/list', ['fodder' => 'image'], true))->icon('el-icon-user')->width('950px')->height('560px')->Props(['srcKey' => 'image', 'footer' => false]);
             $field[] = Form::hidden('edit', 0);
         }
         $field[] = Form::number('division_percent', '佣金比例', $userInfo['division_percent'] ?? '')->placeholder('代理商佣金比例1-100')->info('填写1-100,如填写50代表返佣50%,但是不能高于上级事业部的比例')->style(['width' => '173px'])->min(0)->max(100)->required();
@@ -250,6 +267,8 @@ class DivisionServices extends BaseServices
         $userServices = app()->make(UserServices::class);
         $uid = $data['uid'];
         $agentData = [
+            'spread_uid' => $data['division_id'],
+            'spread_time' => time(),
             'division_id' => $data['division_id'],
             'division_status' => $data['division_status'],
             'division_percent' => $data['division_percent'],
@@ -257,7 +276,9 @@ class DivisionServices extends BaseServices
             'division_end_time' => strtotime($data['division_end_time']),
             'division_type' => 2,
             'is_agent' => 1,
-            'agent_id' => $uid
+            'agent_id' => $uid,
+            'is_staff' => 0,
+            'staff_id' => 0
         ];
         $division_info = $userServices->getUserInfo($data['division_id'], 'division_end_time,division_percent');
         if ($division_info) {
@@ -328,6 +349,120 @@ class DivisionServices extends BaseServices
         });
     }
 
+    /**
+     * 后台添加员工
+     * @param $uid
+     * @return array
+     * @throws \FormBuilder\Exception\FormBuilderException
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2024/1/22
+     */
+    public function getDivisionStaffForm($uid)
+    {
+        $field = [];
+        $field[] = Form::frameImage('image', '员工', $this->url(config('app.admin_prefix', 'admin') . '/system.user/list', ['fodder' => 'image'], true))->icon('el-icon-user')->width('950px')->height('560px')->Props(['srcKey' => 'image', 'footer' => false]);
+        $field[] = Form::number('division_percent', '佣金比例', '')->placeholder('员工佣金比例1-100')->info('填写1-100,如填写50代表返佣50%,但是不能高于上级代理商的比例')->style(['width' => '173px'])->min(0)->max(100)->required();
+        $field[] = Form::hidden('agent_id', $uid);
+        return create_form('员工', $field, Route::buildUrl('/agent/division/staff/save'), 'POST');
+    }
+
+    /**
+     * 保存员工
+     * @param $data
+     * @return true
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2024/1/22
+     */
+    public function divisionStaffSave($data)
+    {
+        $data['uid'] = $data['image']['uid'];
+        /** @var UserServices $userServices */
+        $userServices = app()->make(UserServices::class);
+        $userInfo = $userServices->getUserInfo($data['uid'], 'is_division,is_agent,is_staff,division_id,agent_id,staff_id,division_end_time,division_percent');
+        if (!$userInfo) throw new AdminException('用户不存在');
+        if ($userInfo['is_division']) throw new AdminException('此用户是事业部,无法绑定为员工');
+        if ($userInfo['is_agent']) throw new AdminException('此用户是代理商,无法绑定为员工');
+        if ($userInfo['is_staff'] && $userInfo['agent_id'] == $data['agent_id']) throw new AdminException('此用户是您的员工,请勿重复添加');
+        $agentInfo = $userServices->getUserInfo($data['agent_id'], 'division_id,agent_id,division_end_time,division_percent');
+        $staffData = [
+            'spread_uid' => $data['agent_id'],
+            'spread_time' => time(),
+            'division_type' => 3,
+            'division_status' => 1,
+            'is_staff' => 1,
+            'division_id' => $agentInfo['division_id'],
+            'agent_id' => $agentInfo['agent_id'],
+            'staff_id' => $data['uid'],
+            'division_percent' => $data['division_percent'],
+            'division_change_time' => time(),
+            'division_end_time' => $agentInfo['division_end_time'],
+        ];
+        if ($staffData['division_percent'] > $agentInfo['division_percent']) throw new AdminException(400448);
+        if ($userInfo['agent_id'] != 0 && $userInfo['agent_id'] != $agentInfo['agent_id']) {
+            $userServices->update(['staff_id' => $userInfo['uid'], 'spread_uid' => $userInfo['uid']], ['spread_uid' => $agentInfo['agent_id'], 'staff_id' => 0]);
+            $userServices->update(['staff_id' => $userInfo['uid'], 'not_spread_uid' => $userInfo['uid']], ['staff_id' => 0]);
+        }
+        $res = $userServices->update($data['uid'], $staffData);
+        if ($res) return true;
+        throw new AdminException('保存失败');
+    }
+
+    /**
+     * 扫码绑定员工
+     * @param $uid
+     * @param int $agentId
+     * @param int $agentCode
+     * @return string
+     * @throws DataNotFoundException
+     * @throws DbException
+     * @throws ModelNotFoundException
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2024/2/2
+     */
+    public function agentSpreadStaff($uid, int $agentId = 0, int $agentCode = 0)
+    {
+        if ($agentCode && !$agentId) {
+            /** @var QrcodeServices $qrCode */
+            $qrCode = app()->make(QrcodeServices::class);
+            if ($info = $qrCode->getOne(['id' => $agentCode, 'status' => 1])) {
+                $agentId = $info['third_id'];
+            }
+        }
+        if ($uid == $agentId) return '自己不能推荐自己';
+        /** @var UserServices $userServices */
+        $userServices = app()->make(UserServices::class);
+        $agentInfo = $userServices->getUserInfo($agentId, 'division_id,agent_id,division_end_time,division_percent');
+        if (!$agentInfo) return '上级用户不存在';
+        $userInfo = $userServices->getUserInfo($uid, 'is_division,is_agent,is_staff,division_id,agent_id,staff_id,division_end_time,division_percent');
+        if (!$userInfo) return '用户不存在';
+        if ($userInfo['is_division']) return '您是事业部,不能绑定成为别人的员工';
+        if ($userInfo['is_agent']) return '您是代理商,不能绑定成为别人的员工';
+        $staffData = [
+            'spread_uid' => $agentId,
+            'spread_time' => time(),
+            'division_type' => 3,
+            'division_status' => 1,
+            'is_staff' => 1,
+            'division_id' => $agentInfo['division_id'],
+            'agent_id' => $agentInfo['agent_id'],
+            'staff_id' => $uid,
+            'division_change_time' => time(),
+        ];
+        if ($userInfo['agent_id'] != 0 && $userInfo['agent_id'] != $agentInfo['agent_id']) {
+            $userServices->update(['staff_id' => $userInfo['uid'], 'spread_uid' => $userInfo['uid']], ['spread_uid' => $agentInfo['agent_id'], 'staff_id' => 0]);
+            $userServices->update(['staff_id' => $userInfo['uid'], 'not_spread_uid' => $userInfo['uid']], ['staff_id' => 0]);
+        }
+        $res = $userServices->update($uid, $staffData);
+        if ($res) return '绑定员工成功';
+        return '绑定员工失败';
+    }
+
     /**
      * 获取返佣比例佣金比例
      * 当前方法会将获得的佣金逐步的递减
@@ -534,6 +669,6 @@ class DivisionServices extends BaseServices
                 }
             }
         }
-        return [max($storeBrokerageOne, 0), max($storeBrokerageTwo, 0),  max($staffPercent, 0), max($agentPercent, 0), max($divisionPercent, 0)];
+        return [max($storeBrokerageOne, 0), max($storeBrokerageTwo, 0), max($staffPercent, 0), max($agentPercent, 0), max($divisionPercent, 0)];
     }
 }

+ 1 - 2
crmeb/app/services/article/ArticleCategoryServices.php

@@ -48,8 +48,7 @@ class ArticleCategoryServices extends BaseServices
     {
         $list = $this->dao->getList($where);
         $list = get_tree_children($list);
-        $count = $this->dao->count($where);
-        return compact('list', 'count');
+        return compact('list');
     }
 
     /**

+ 5 - 2
crmeb/app/services/message/MessageSystemServices.php

@@ -55,7 +55,7 @@ class MessageSystemServices extends BaseServices
         if (!$list) return ['list' => [], 'count' => 0];
         foreach ($list as &$item) {
             $item['add_time'] = time_tran($item['add_time']);
-            if ($item['data'] != '') {
+            if ($item['data'] != '' && $this->getMsgCode($item['mark']) != 000000) {
                 $item['content'] = getLang($this->getMsgCode($item['mark']), json_decode($item['data'], true));
             }
         }
@@ -80,7 +80,7 @@ class MessageSystemServices extends BaseServices
         if ($info['look'] == 0) {
             $this->update($info['id'], ['look' => 1]);
         }
-        if ($info['data'] != '') {
+        if ($info['data'] != '' && $this->getMsgCode($info['mark']) != 000000) {
             $info['content'] = getLang($this->getMsgCode($info['mark']), json_decode($info['data'], true));
         }
         $info['add_time'] = time_tran($info['add_time']);
@@ -163,6 +163,9 @@ class MessageSystemServices extends BaseServices
             case 'order_pay_false':
                 $code = 500027;
                 break;
+            default:
+                $code = 000000;
+                break;
         }
         return $code;
     }

+ 308 - 0
crmeb/app/services/message/SystemNotificationServices.php

@@ -15,6 +15,8 @@ namespace app\services\message;
 use app\dao\system\SystemNotificationDao;
 use app\services\BaseServices;
 use crmeb\exceptions\AdminException;
+use crmeb\services\FormBuilder as Form;
+use think\facade\Route as Url;
 
 /**
  * 消息管理类
@@ -25,6 +27,217 @@ use crmeb\exceptions\AdminException;
 class SystemNotificationServices extends BaseServices
 {
 
+    protected $messageData = [
+
+        //短信验证码
+        'verify_code' => [
+            ['label' => '验证码', 'value' => 'code'],
+            ['label' => '有效时间', 'value' => 'time'],
+        ],
+
+        //用户登录
+        'login_success' => [
+            ['label' => '用户昵称', 'value' => 'nickname'],
+            ['label' => '用户电话', 'value' => 'phone'],
+            ['label' => '上次登录时间', 'value' => 'last_time'],
+            ['label' => '用户余额', 'value' => 'now_money'],
+            ['label' => '用户佣金', 'value' => 'brokerage_price'],
+            ['label' => '用户积分', 'value' => 'integral'],
+            ['label' => '用户经验', 'value' => 'exp'],
+            ['label' => '登录时间', 'value' => 'time'],
+        ],
+
+        //用户绑定关系
+        'spread_success' => [
+            ['label' => '用户昵称', 'value' => 'nickname'],
+            ['label' => '绑定时间', 'value' => 'time'],
+        ],
+
+        //未支付订单修改金额
+        'price_change_price' => [
+            ['label' => '订单order_id', 'value' => 'order_id'],
+            ['label' => '订单原金额', 'value' => 'pay_price'],
+            ['label' => '修改后金额', 'value' => 'change_price'],
+        ],
+
+        //订单支付成功
+        'order_pay_success' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '订单order_id', 'value' => 'order_id'],
+            ['label' => '用户名称', 'value' => 'real_name'],
+            ['label' => '用户电话', 'value' => 'user_phone'],
+            ['label' => '用户地址', 'value' => 'user_address'],
+            ['label' => '商品总数', 'value' => 'total_num'],
+            ['label' => '支付金额', 'value' => 'pay_price'],
+            ['label' => '支付邮费', 'value' => 'pay_postage'],
+            ['label' => '积分抵扣金额', 'value' => 'deduction_price'],
+            ['label' => '优惠券抵扣金额', 'value' => 'coupon_price'],
+            ['label' => '支付类型', 'value' => 'pay_type'],
+            ['label' => '商品名称', 'value' => 'storeName'],
+            ['label' => '下单时间', 'value' => 'time'],
+        ],
+
+        //订单快递发货
+        'order_express_success' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '订单order_id', 'value' => 'order_id'],
+            ['label' => '用户名称', 'value' => 'real_name'],
+            ['label' => '用户电话', 'value' => 'user_phone'],
+            ['label' => '用户地址', 'value' => 'user_address'],
+            ['label' => '商品总数', 'value' => 'total_num'],
+            ['label' => '支付金额', 'value' => 'pay_price'],
+            ['label' => '支付邮费', 'value' => 'pay_postage'],
+            ['label' => '积分抵扣金额', 'value' => 'deduction_price'],
+            ['label' => '优惠券抵扣金额', 'value' => 'coupon_price'],
+            ['label' => '支付类型', 'value' => 'pay_type'],
+            ['label' => '商品名称', 'value' => 'storeName'],
+            ['label' => '快递公司', 'value' => 'delivery_name'],
+            ['label' => '快递单号', 'value' => 'delivery_id'],
+            ['label' => '发货时间', 'value' => 'time'],
+        ],
+
+        //订单配送员送货
+        'order_send_success' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '订单order_id', 'value' => 'order_id'],
+            ['label' => '用户名称', 'value' => 'real_name'],
+            ['label' => '用户电话', 'value' => 'user_phone'],
+            ['label' => '用户地址', 'value' => 'user_address'],
+            ['label' => '商品总数', 'value' => 'total_num'],
+            ['label' => '支付金额', 'value' => 'pay_price'],
+            ['label' => '支付邮费', 'value' => 'pay_postage'],
+            ['label' => '积分抵扣金额', 'value' => 'deduction_price'],
+            ['label' => '优惠券抵扣金额', 'value' => 'coupon_price'],
+            ['label' => '支付类型', 'value' => 'pay_type'],
+            ['label' => '商品名称', 'value' => 'storeName'],
+            ['label' => '配送员姓名', 'value' => 'delivery_name'],
+            ['label' => '配送员电话', 'value' => 'delivery_id'],
+            ['label' => '送货时间', 'value' => 'time'],
+
+        ],
+
+        //订单收货
+        'order_take' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '订单order_id', 'value' => 'order_id'],
+            ['label' => '用户名称', 'value' => 'real_name'],
+            ['label' => '用户电话', 'value' => 'user_phone'],
+            ['label' => '用户地址', 'value' => 'user_address'],
+            ['label' => '商品总数', 'value' => 'total_num'],
+            ['label' => '支付金额', 'value' => 'pay_price'],
+            ['label' => '支付邮费', 'value' => 'pay_postage'],
+            ['label' => '积分抵扣金额', 'value' => 'deduction_price'],
+            ['label' => '优惠券抵扣金额', 'value' => 'coupon_price'],
+            ['label' => '支付类型', 'value' => 'pay_type'],
+            ['label' => '商品名称', 'value' => 'storeTitle'],
+            ['label' => '配送员姓名', 'value' => 'delivery_name'],
+            ['label' => '配送员电话', 'value' => 'delivery_id'],
+            ['label' => '签收时间', 'value' => 'time'],
+        ],
+
+        //订单发起退款
+//        'order_initiated_refund' => [
+//            ['label' => '用户uid', 'value' => 'uid'],
+//            ['label' => '订单order_id', 'value' => 'order_id'],
+//            ['label' => '用户名称', 'value' => 'real_name'],
+//            ['label' => '用户电话', 'value' => 'user_phone'],
+//            ['label' => '用户地址', 'value' => 'user_address'],
+//            ['label' => '商品总数', 'value' => 'total_num'],
+//            ['label' => '支付金额', 'value' => 'pay_price'],
+//            ['label' => '支付邮费', 'value' => 'pay_postage'],
+//            ['label' => '积分抵扣金额', 'value' => 'deduction_price'],
+//            ['label' => '优惠券抵扣金额', 'value' => 'coupon_price'],
+//            ['label' => '支付类型', 'value' => 'pay_type'],
+//        ],
+
+        //订单成功退款
+        'order_refund_success' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '订单order_id', 'value' => 'order_id'],
+            ['label' => '用户名称', 'value' => 'real_name'],
+            ['label' => '用户电话', 'value' => 'user_phone'],
+            ['label' => '用户地址', 'value' => 'user_address'],
+            ['label' => '商品总数', 'value' => 'total_num'],
+            ['label' => '支付金额', 'value' => 'pay_price'],
+            ['label' => '支付邮费', 'value' => 'pay_postage'],
+            ['label' => '积分抵扣金额', 'value' => 'deduction_price'],
+            ['label' => '优惠券抵扣金额', 'value' => 'coupon_price'],
+            ['label' => '支付类型', 'value' => 'pay_type'],
+            ['label' => '退款理由类型', 'value' => 'refund_reason_wap'],
+            ['label' => '退款理由', 'value' => 'refund_reason_wap_explain'],
+            ['label' => '实际退款金额', 'value' => 'refund_price'],
+        ],
+
+        //订单拒绝退款
+        'order_refund_fail' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '退款金额', 'value' => 'refund_price'],
+            ['label' => '拒绝退款理由', 'value' => 'refuse_reason'],
+            ['label' => '拒绝时间', 'value' => 'time'],
+        ],
+
+        //用户充值
+        'recharge_success' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '用户昵称', 'value' => 'nickname'],
+            ['label' => '用户电话', 'value' => 'phone'],
+            ['label' => '充值金额', 'value' => 'price'],
+            ['label' => '赠送金额', 'value' => 'give_price'],
+            ['label' => '充值后用户余额', 'value' => 'now_money'],
+            ['label' => '充值时间', 'value' => 'time'],
+        ],
+
+        //用户充值退款
+        'recharge_refund' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '用户昵称', 'value' => 'nickname'],
+            ['label' => '用户电话', 'value' => 'phone'],
+            ['label' => '退款金额', 'value' => 'price'],
+            ['label' => '退款后用户余额', 'value' => 'now_money'],
+            ['label' => '退款时间', 'value' => 'time'],
+        ],
+
+        //用户提现通过
+        'extract_success' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '用户昵称', 'value' => 'nickname'],
+            ['label' => '用户电话', 'value' => 'phone'],
+            ['label' => '提现金额', 'value' => 'price'],
+            ['label' => '提现时间', 'value' => 'time'],
+        ],
+
+        //用户提现失败
+        'extract_fail' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '用户昵称', 'value' => 'nickname'],
+            ['label' => '失败理由', 'value' => 'message'],
+            ['label' => '提现金额', 'value' => 'price'],
+            ['label' => '失败时间', 'value' => 'time'],
+        ],
+
+        //佣金到账
+        'brokerage_received' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '用户电话', 'value' => 'phone'],
+            ['label' => '到账金额', 'value' => 'brokeragePrice'],
+            ['label' => '商品名称', 'value' => 'goodsName'],
+            ['label' => '商品金额', 'value' => 'goodsPrice'],
+            ['label' => '到账时间', 'value' => 'time'],
+        ],
+
+        //积分到账
+        'point_received' => [
+            ['label' => '用户uid', 'value' => 'uid'],
+            ['label' => '用户电话', 'value' => 'phone'],
+            ['label' => '积分数量', 'value' => 'give_integral'],
+            ['label' => '商品名称', 'value' => 'storeTitle'],
+            ['label' => '积分总数', 'value' => 'integral'],
+            ['label' => '到账时间', 'value' => 'time'],
+        ],
+
+
+    ];
+
     /**
      * SystemNotificationServices constructor.
      * @param SystemNotificationDao $dao
@@ -60,6 +273,78 @@ class SystemNotificationServices extends BaseServices
         return $this->dao->getList($where);
     }
 
+    /**
+     * 添加自定义消息表单
+     * @return array
+     * @throws \FormBuilder\Exception\FormBuilderException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/2/19
+     */
+    public function getNotForm($id = 0)
+    {
+        if ($id) {
+            $info = $this->dao->get($id);
+            if ($info) $info = $info->toArray();
+        } else {
+            $info = [];
+        }
+        $data = [
+            ['value' => 'login_success', 'label' => '用户登录成功场景'],
+            ['value' => 'spread_success', 'label' => '绑定推广关系成功场景'],
+            ['value' => 'price_change_price', 'label' => '未支付订单修改价格场景'],
+            ['value' => 'order_pay_success', 'label' => '订单支付成功场景'],
+            ['value' => 'order_express_success', 'label' => '订单快递发货成功场景'],
+            ['value' => 'order_send_success', 'label' => '订单配送员开始送货场景'],
+            ['value' => 'order_take', 'label' => '订单成功收货场景'],
+            ['value' => 'order_initiated_refund', 'label' => '订单发起退款场景'],
+            ['value' => 'order_refund_success', 'label' => '订单退款成功场景'],
+            ['value' => 'order_refund_fail', 'label' => '订单退款失败场景'],
+            ['value' => 'recharge_success', 'label' => '充值成功场景'],
+            ['value' => 'recharge_refund', 'label' => '充值退款场景'],
+            ['value' => 'extract_success', 'label' => '提现成功场景'],
+            ['value' => 'extract_fail', 'label' => '提现失败场景'],
+            ['value' => 'brokerage_received', 'label' => '佣金到账场景'],
+            ['value' => 'point_received', 'label' => '积分到账场景'],
+//            ['value' => 'login_success', 'label' => '砍价成功场景'],
+//            ['value' => 'login_success', 'label' => '开团成功场景'],
+//            ['value' => 'login_success', 'label' => '参团成功场景'],
+//            ['value' => 'login_success', 'label' => '拼团成功场景'],
+//            ['value' => 'login_success', 'label' => '拼团失败场景'],
+//            ['value' => 'login_success', 'label' => '取消拼团场景'],
+        ];
+        $field = [];
+        $field[] = Form::select('custom_trigger', '触发位置', $info['custom_trigger'] ?? '')->options($data);
+        $field[] = Form::input('name', '名称', $info['name'] ?? '')->placeholder('请填写消息名称,例:支付成功消息');
+        $field[] = Form::input('mark', '标识', $info['mark'] ?? '')->placeholder('请填写消息标识,使用英文和下划线,例:order_pay_success');
+        return create_form('添加消息', $field, Url::buildUrl('/setting/notification/not_form_save/' . $id), 'POST');
+    }
+
+    /**
+     * 保存自定义消息
+     * @param $id
+     * @param $data
+     * @return bool
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/2/20
+     */
+    public function notFormSave($id, $data)
+    {
+        if ($id) {
+            $data['title'] = $data['name'];
+            $res = $this->dao->update($id, $data);
+        } else {
+            $data['type'] = 3;
+            $data['title'] = $data['name'];
+            $data['is_system'] = $data['is_wechat'] = $data['is_routine'] = $data['is_sms'] = $data['is_ent_wechat'] = 2;
+            $data['add_time'] = time();
+            $res = $this->dao->save($data);
+        }
+        if ($res) return true;
+        throw new AdminException(100006);
+    }
+
     /**
      * 获取单条数据
      * @param array $where
@@ -86,13 +371,26 @@ class SystemNotificationServices extends BaseServices
                 $info['tempkey'] = $info['wechat_tempkey'] ?? '';
                 $info['tempid'] = $info['wechat_tempid'] ?? '';
                 $info['content'] = $info['wechat_content'] ?? '';
+                $info['key_list'] = json_decode($info['wechat_data'], true) ?? [];
                 break;
             case 'is_routine':
                 $info['tempkey'] = $info['routine_tempkey'] ?? '';
                 $info['tempid'] = $info['routine_tempid'] ?? '';
                 $info['content'] = $info['routine_content'] ?? '';
+                $info['key_list'] = json_decode($info['routine_data'], true) ?? [];
+                break;
+            case 'is_ent_wechat':
+                $info['content'] = $info['ent_wechat_text'];
                 break;
         }
+        if ($info['type'] == 3) {
+            $info['custom_variable'] = $this->messageData[$info['custom_trigger']];
+            if (in_array($type, ['is_system', 'is_sms', 'is_ent_wechat'])) {
+                foreach ($info['custom_variable'] as &$item) {
+                    $item['value'] = '{' . $item['value'] . '}';
+                }
+            }
+        }
         return $info;
     }
 
@@ -130,16 +428,26 @@ class SystemNotificationServices extends BaseServices
                 $update['title'] = $data['title'];
                 $update['is_sms'] = $data['is_sms'];
                 $update['sms_id'] = $data['sms_id'];
+                $update['sms_text'] = $data['sms_text'];
                 $res = $this->dao->update((int)$id, $update);
                 break;
             case 'is_wechat':
                 $update['is_wechat'] = $data['is_wechat'];
                 $update['wechat_tempid'] = $data['tempid'];
+                $update['wechat_tempkey'] = $data['tempkey'];
+                $update['wechat_content'] = $data['content'];
+                $update['wechat_link'] = $data['wechat_link'];
+                $update['wechat_to_routine'] = $data['wechat_to_routine'];
+                $update['wechat_data'] = json_encode($data['key_list']);
                 $res = $this->dao->update((int)$id, $update);
                 break;
             case 'is_routine':
                 $update['is_routine'] = $data['is_routine'];
                 $update['routine_tempid'] = $data['tempid'];
+                $update['routine_tempkey'] = $data['tempkey'];
+                $update['routine_content'] = $data['content'];
+                $update['routine_data'] = json_encode($data['key_list']);
+                $update['routine_link'] = $data['routine_link'];
                 $res = $this->dao->update((int)$id, $update);
                 break;
             case 'is_ent_wechat':

+ 2 - 2
crmeb/app/services/order/OtherOrderServices.php

@@ -342,8 +342,8 @@ class OtherOrderServices extends BaseServices
             $spread_two = sys_config('brokerage_level', 2) == 2 ? $userServices->getSpreadUid($spread_one) : 0;
             $spread_one_price = bcmul((string)$orderInfo['pay_price'], (string)bcdiv((string)sys_config('store_brokerage_ratio', 0), '100', 4), 2);
             $spread_two_price = bcmul((string)$orderInfo['pay_price'], (string)bcdiv((string)sys_config('store_brokerage_two', 0), '100', 4), 2);
-            if ($spread_one && $spread_one_price > 0) $this->memberBrokerage($spread_one, $spread_one_price, sys_config('is_self_brokerage') ? 'get_self_member_brokerage' : 'get_member_brokerage', $orderInfo);
-            if ($spread_two && $spread_two_price > 0) $this->memberBrokerage($spread_two, $spread_two_price, 'get_two_member_brokerage', $orderInfo);
+            if ($spread_one && $spread_one_price > 0 && $userServices->checkUserPromoter($spread_one)) $this->memberBrokerage($spread_one, $spread_one_price, sys_config('is_self_brokerage') ? 'get_self_member_brokerage' : 'get_member_brokerage', $orderInfo);
+            if ($spread_two && $spread_two_price > 0 && $userServices->checkUserPromoter($spread_two)) $this->memberBrokerage($spread_two, $spread_two_price, 'get_two_member_brokerage', $orderInfo);
         }
 
         $orderInfo['pay_type'] = $paytype;

+ 1 - 0
crmeb/app/services/order/OutStoreOrderRefundServices.php

@@ -220,6 +220,7 @@ class OutStoreOrderRefundServices extends BaseServices
             mt_srand();
             $refundData['refund_id'] = $order['order_id'] . rand(100, 999);
         }
+        $refundData['order_id'] = $orderId;
         //修改订单退款状态
         if ($refundServices->agreeRefund((int)$orderRefund['id'], $refundData)) {
             $refundServices->update((int)$orderRefund['id'], $data);

+ 16 - 0
crmeb/app/services/order/StoreOrderDeliveryServices.php

@@ -455,10 +455,26 @@ class StoreOrderDeliveryServices extends BaseServices
             case 1://快递发货
                 $res = $this->orderDeliverGoods($id, $data, $orderInfo, $storeName);
                 event('NoticeListener', [['orderInfo' => $orderInfo, 'storeName' => $storeName, 'data' => $data], 'order_postage_success']);
+
+                //自定义消息-快递发货
+                $orderInfo['storeName'] = $storeName;
+                $orderInfo['delivery_name'] = $data['delivery_name'];
+                $orderInfo['delivery_id'] = $data['delivery_id'];
+                $orderInfo['time'] = date('Y-m-d H:i:s');
+                $orderInfo['phone'] = $orderInfo['user_phone'];
+                event('CustomNoticeListener', [$orderInfo['uid'], $orderInfo, 'order_express_success']);
                 break;
             case 2://配送
                 $this->orderDelivery($id, $data, $orderInfo, $storeName);
                 event('NoticeListener', [['orderInfo' => $orderInfo, 'storeName' => $storeName, 'data' => $data], 'order_deliver_success']);
+
+                //自定义消息-配送员配送
+                $orderInfo['storeName'] = $storeName;
+                $orderInfo['delivery_name'] = $data['delivery_name'];
+                $orderInfo['delivery_id'] = $data['delivery_id'];
+                $orderInfo['time'] = date('Y-m-d H:i:s');
+                $orderInfo['phone'] = $orderInfo['user_phone'];
+                event('CustomNoticeListener', [$orderInfo['uid'], $orderInfo, 'order_send_success']);
                 break;
             case 3://虚拟发货
                 $this->orderVirtualDelivery($id, $data, $orderInfo, $storeName);

+ 24 - 5
crmeb/app/services/order/StoreOrderRefundServices.php

@@ -280,6 +280,11 @@ class StoreOrderRefundServices extends BaseServices
         //订单退款记录
         ProductLogJob::dispatch(['refund', ['uid' => $order['uid'], 'order_id' => $order['id']]]);
         event('NoticeListener', [['data' => $refundData, 'order' => $order], 'order_refund']);
+
+        //自定义消息-退款成功
+        $order['phone'] = $order['user_phone'];
+        event('CustomNoticeListener', [$order['uid'], $order, 'order_refund_success']);
+
         return true;
     }
 
@@ -504,9 +509,15 @@ class StoreOrderRefundServices extends BaseServices
             //判断订单是否已经回退积分
             $count = $userBillServices->count(['category' => 'integral', 'type' => 'integral_refund', 'link_id' => $order['id']]);
             if (!$count) {
-                $res1 = $userServices->bcDec($order['uid'], 'integral', $give_integral);
-                //记录赠送积分收回
-                $integral = $integral - $give_integral;
+                if ($integral > $give_integral) {
+                    $res1 = $userServices->bcDec($order['uid'], 'integral', $give_integral);
+                    //记录赠送积分收回
+                    $integral = $integral - $give_integral;
+                } else {
+                    $res1 = $userServices->update($order['uid'], ['integral' => 0]);
+                    //记录赠送积分收回
+                    $integral = 0;
+                }
                 $res2 = $userBillServices->income('integral_refund', $order['uid'], $give_integral, $integral, $order['id']);
                 //清除积分冻结
                 $userBillServices->update(['link_id' => $order['id']], ['frozen_time' => 0]);
@@ -689,19 +700,23 @@ class StoreOrderRefundServices extends BaseServices
 
             $storeOrderServices->update($oid, ['refund_status' => 0, 'refund_type' => 3]);
             //处理订单商品cart_info
-            $this->cancelOrderRefundCartInfo($id, $oid, $orderRefundInfo, '不退款原因:' . ($data['refund_reason'] ?? ''));
+            $this->cancelOrderRefundCartInfo($id, $oid, $orderRefundInfo, '不退款原因:' . ($data['refuse_reason'] ?? ''));
             //记录
             /** @var StoreOrderStatusServices $statusService */
             $statusService = app()->make(StoreOrderStatusServices::class);
             $statusService->save([
                 'oid' => $id,
                 'change_type' => 'refund_n',
-                'change_message' => '不退款原因:' . ($data['refund_reason'] ?? ''),
+                'change_message' => '不退款原因:' . ($data['refuse_reason'] ?? ''),
                 'change_time' => time()
             ]);
         });
         $orderRefundInfo['refuse_reason'] = $data['refuse_reason'];
         event('NoticeListener', [['orderInfo' => $orderRefundInfo], 'send_order_refund_no_status']);
+
+        //自定义消息-退款失败
+        event('CustomNoticeListener', [$orderRefundInfo['uid'], $orderRefundInfo->toArray(), 'order_refund_fail']);
+
         return true;
     }
 
@@ -1025,6 +1040,10 @@ class StoreOrderRefundServices extends BaseServices
         event('NoticeListener', [['order' => $order], 'send_order_apply_refund']);
         //推送订单
         event('OutPushListener', ['refund_create_push', ['order_id' => (int)$order['id']]]);
+
+        //自定义消息-订单发起退款
+//        event('CustomNoticeListener', [$order['uid'], $order->toArray(), 'order_initiated_refund']);
+
         try {
             ChannelService::instance()->send('NEW_REFUND_ORDER', ['order_id' => $order['order_id']]);
         } catch (\Exception $e) {

+ 33 - 30
crmeb/app/services/order/StoreOrderServices.php

@@ -110,6 +110,7 @@ class StoreOrderServices extends BaseServices
             }
             $item['total_price'] = bcadd($item['total_price'], $vipTruePrice, 2);
             $item['is_all_refund'] = $refund_num == $cart_num;
+            $item['pay_price'] = (float)$item['pay_price'];
         }
         return compact('data', 'count');
     }
@@ -476,25 +477,25 @@ class StoreOrderServices extends BaseServices
                         break;
                     default:
                         $item['pink_name'] = '[拼团订单]历史订单';
-                        $item['color'] = '#457856';
+                        $item['color'] = '#FF7D00';
                         break;
                 }
             } elseif ($item['combination_id']) {
                 $item['pink_name'] = '[拼团订单]';
-                $item['color'] = '#32c5e9';
+                $item['color'] = '#FF7D00';
             } elseif ($item['seckill_id']) {
                 $item['pink_name'] = '[秒杀订单]';
-                $item['color'] = '#32c5e9';
+                $item['color'] = '#3491FA';
             } elseif ($item['bargain_id']) {
                 $item['pink_name'] = '[砍价订单]';
-                $item['color'] = '#12c5e9';
+                $item['color'] = '#F7BA1E';
             } elseif ($item['advance_id']) {
                 $item['pink_name'] = '[预售订单]';
-                $item['color'] = '#12c5e9';
+                $item['color'] = '#B27FEB';
             } else {
                 if ($item['shipping_type'] == 1) {
                     $item['pink_name'] = '[普通订单]';
-                    $item['color'] = '#895612';
+                    $item['color'] = '#333';
                 } else if ($item['shipping_type'] == 2) {
                     $item['pink_name'] = '[核销订单]';
                     $item['color'] = '#8956E8';
@@ -733,15 +734,20 @@ HTML;
      * @param array $where
      * @return mixed
      */
+    /**
+     * @param array $where
+     * @return array
+     * @throws \ReflectionException
+     * @author wuhaotian
+     * @email 442384644@qq.com
+     * @date 2024/3/14
+     */
     public function orderCount(array $where)
     {
-        $where_one = ['time' => $where['time'], 'is_system_del' => 0, 'pid' => 0, 'status' => 1, 'shipping_type' => 1];
-        $data['all'] = (string)$this->dao->count($where_one);
-        $data['general'] = (string)$this->dao->count($where_one + ['type' => 1]);
-        $data['pink'] = (string)$this->dao->count($where_one + ['type' => 2]);
-        $data['seckill'] = (string)$this->dao->count($where_one + ['type' => 3]);
-        $data['bargain'] = (string)$this->dao->count($where_one + ['type' => 4]);
-        $data['advance'] = (string)$this->dao->count($where_one + ['type' => 5]);
+        $where['is_system_del'] = 0;
+        $where['pid'] = 0;
+        $data['un_paid'] = $this->dao->count($where + ['status' => 0], false);
+        $data['un_send'] = $this->dao->count($where + ['status' => 1], false);
         return $data;
     }
 
@@ -759,8 +765,8 @@ HTML;
         }
         $f = [];
         $f[] = Form::input('order_id', '订单编号', $product->getData('order_id'))->disabled(true);
-        $f[] = Form::number('total_price', '商品总价', (float)$product->getData('total_price'))->min(0)->disabled(true);
-        $f[] = Form::number('pay_postage', '支付邮费', (float)$product->getData('pay_postage') ?: 0)->disabled(true);
+        $f[] = Form::hidden('total_price', (float)$product->getData('total_price'));
+        $f[] = Form::hidden('pay_postage', (float)$product->getData('pay_postage') ?: 0);
         $f[] = Form::number('pay_price', '实际支付金额', (float)$product->getData('pay_price'))->min(0);
         $f[] = Form::number('gain_integral', '赠送积分', (float)$product->getData('gain_integral') ?: 0)->min(0);
         return create_form('修改订单', $f, $this->url('/order/update/' . $id), 'PUT');
@@ -813,6 +819,9 @@ HTML;
                 $order = $this->dao->getOne(['id' => $id, 'is_del' => 0]);
                 //改价短信提醒
                 event('NoticeListener', [['order' => $order, 'pay_price' => $data['pay_price']], 'price_revision']);
+                //自定义消息-订单改价
+                $order['change_price'] = $data['pay_price'];
+                event('NoticeListener', [$order['uid'], $order, 'price_change_price']);
                 return $data['order_id'];
             } else {
                 throw new AdminException(100007);
@@ -1635,19 +1644,6 @@ HTML;
         $data = [];
         $data['storeFreePostage'] = $storeFreePostage = floatval(sys_config('store_free_postage')) ?: 0;//满额包邮金额
         $validCartInfo = $cartGroup['valid'];
-        if (count($validCartInfo)) {
-            if (isset($validCartInfo[0]['productInfo']['is_virtual']) && $validCartInfo[0]['productInfo']['is_virtual']) {
-                $data['virtual_type'] = 1;
-                $data['deduction'] = true;
-            } else {
-                if ($validCartInfo[0]['productInfo']['virtual_type'] == 3) {
-                    $data['virtual_type'] = 1;
-                    $data['deduction'] = true;
-                } else {
-                    $data['virtual_type'] = 0;
-                }
-            }
-        }
         /** @var StoreOrderComputedServices $computedServices */
         $computedServices = app()->make(StoreOrderComputedServices::class);
         $priceGroup = $computedServices->getOrderPriceGroup($storeFreePostage, $validCartInfo, $addr, $user, $shipping_type);
@@ -1668,6 +1664,7 @@ HTML;
             $advance_id = $cartGroup['deduction']['advance_id'] ?? 0;
         }
         $data['valid_count'] = count($validCartInfo);
+        $data['virtual_type'] = $data['valid_count'] ? (int)$validCartInfo[0]['productInfo']['virtual_type'] > 0 : 0;
         $data['deduction'] = $seckill_id || $combination_id || $bargain_id || $advance_id;
         $data['addressInfo'] = $addr;
         $data['seckill_id'] = $seckill_id;
@@ -2003,8 +2000,14 @@ HTML;
                 case 2:
                     $where_data['spread_two_uid'] = $uid;
                     break;
+                case 3:
+                    $where_data['division_id'] = $uid;
+                    break;
+                case 4:
+                    $where_data['agent_id'] = $uid;
+                    break;
                 default:
-                    $where_data['spread_or_uid'] = $uid;
+                    $where_data['all_spread'] = $uid;
                     break;
             }
         }
@@ -2553,7 +2556,7 @@ HTML;
         }
         // 判断是否开启小程序订单管理
         $orderData['order_shipping_open'] = false;
-        if (sys_config('order_shipping_open', 0) && MiniOrderService::isManaged() && $order['is_channel'] == 1 && $order['pay_type'] == 'weixin') {
+        if (sys_config('order_shipping_open', 0) && $order['is_channel'] == 1 && $order['pay_type'] == 'weixin' && MiniOrderService::isManaged()) {
             // 判断是否存在子未收货子订单
             if ($order['pid'] > 0) {
                 if ($this->checkSubOrderNotTake((int)$order['pid'], (int)$order['id'])) {

+ 5 - 0
crmeb/app/services/order/StoreOrderSuccessServices.php

@@ -101,6 +101,11 @@ class StoreOrderSuccessServices extends BaseServices
         // 推送订单
         event('OutPushListener', ['order_pay_push', ['order_id' => (int)$orderInfo['id']]]);
 
+        //自定义消息-订单支付成功
+        $orderInfo['time'] = date('Y-m-d H:i:s');
+        $orderInfo['phone'] = $orderInfo['user_phone'];
+        event('CustomNoticeListener', [$orderInfo['uid'], $orderInfo, 'order_pay_success']);
+
         // 小程序订单管理 (自提商品)
         if ($orderInfo['shipping_type'] == 2) {
             event('OrderShipping', ['product', $orderInfo, 4, '', '']);

+ 31 - 3
crmeb/app/services/order/StoreOrderTakeServices.php

@@ -144,6 +144,11 @@ class StoreOrderTakeServices extends BaseServices
                 event('NoticeListener', [['order' => $order, 'storeTitle' => $storeTitle], 'order_take']);
                 //收货给客服发送消息
                 event('NoticeListener', [['order' => $order, 'storeTitle' => $storeTitle], 'send_admin_confirm_take_over']);
+                //自定义消息-订单收货
+                $order['storeTitle'] = $storeTitle;
+                $order['time'] = date('Y-m-d H:i:s');
+                $order['phone'] = $order['user_phone'];
+                event('CustomNoticeListener', [$order['uid'], $order, 'order_take']);
             } catch (\Throwable $exception) {
 
             }
@@ -216,6 +221,17 @@ class StoreOrderTakeServices extends BaseServices
             $orderServices = app()->make(StoreOrderServices::class);
             $orderServices->update($order['id'], ['gain_integral' => $give_integral], 'id');
             event('NoticeListener', [['order' => $order, 'storeTitle' => $storeTitle, 'give_integral' => $give_integral, 'integral' => $integral], 'integral_accout']);
+
+            //自定义消息-积分到账
+            event('CustomNoticeListener', [$order['uid'], [
+                'uid' => $order['uid'],
+                'phone' => app()->make(UserServices::class)->value($order['uid'], 'phone'),
+                'storeTitle' => $storeTitle,
+                'give_integral' => $give_integral,
+                'integral' => $integral,
+                'time' => date('Y-m-d H:i:s'),
+            ], 'point_received']);
+
             return true;
         }
         return true;
@@ -392,8 +408,7 @@ class StoreOrderTakeServices extends BaseServices
         $res1 = $userServices->bcInc($one_spread_uid, 'brokerage_price', $brokeragePrice, 'uid');
         if ($res1) {
             //冻结时间
-            $broken_time = intval(sys_config('extract_time'));
-            $frozen_time = time() + $broken_time * 86400;
+            $frozen_time = time() + intval(sys_config('extract_time')) * 86400;
             // 添加佣金记录
             /** @var UserBrokerageServices $userBrokerageServices */
             $userBrokerageServices = app()->make(UserBrokerageServices::class);
@@ -463,6 +478,8 @@ class StoreOrderTakeServices extends BaseServices
         // 添加佣金记录
         /** @var UserBrokerageServices $userBrokerageServices */
         $userBrokerageServices = app()->make(UserBrokerageServices::class);
+        //冻结时间
+        $frozenTime = time() + intval(sys_config('extract_time')) * 86400;
         $res1 = $userBrokerageServices->income('get_two_brokerage', $spread_two_uid, [
             'nickname' => $userInfo['nickname'],
             'pay_price' => floatval($orderInfo['pay_price']),
@@ -509,6 +526,17 @@ class StoreOrderTakeServices extends BaseServices
         }
         //提醒推送
         event('NoticeListener', [['spread_uid' => $spread_uid, 'userType' => $userType, 'brokeragePrice' => $brokeragePrice, 'goodsName' => $goodsName, 'goodsPrice' => $goodsPrice, 'add_time' => $orderInfo['add_time'] ?? time()], 'order_brokerage']);
+
+        //自定义消息-佣金到账
+        event('CustomNoticeListener', [$spread_uid, [
+            'uid' => $spread_uid,
+            'phone' => app()->make(UserServices::class)->value($spread_uid, 'phone'),
+            'brokeragePrice' => $brokeragePrice,
+            'goodsName' => $goodsName,
+            'goodsPrice' => $goodsPrice,
+            'time' => date('Y-m-d H:i:s')
+        ], 'brokerage_received']);
+
     }
 
 
@@ -599,7 +627,7 @@ class StoreOrderTakeServices extends BaseServices
                     }
                 });
             } catch (\Throwable $e) {
-                Log::error('自动收货失败,失败原因:' . $e->getMessage());
+                Log::error('自动收货失败,失败原因:' . $e->getMessage() . '|' . $e->getFile() . '|' . $e->getLine());
             }
 
         }

+ 4 - 0
crmeb/app/services/order/StoreOrderWriteOffServices.php

@@ -130,6 +130,10 @@ class StoreOrderWriteOffServices extends BaseServices
             if (!$re) {
                 throw new ApiException(410272);
             }
+            // 小程序订单管理
+            if ($orderInfo['shipping_type'] == 2) {
+                event('OrderShipping', ['product', $orderInfo, 1, '123456', '中通快递']);
+            }
             return $orderInfo->toArray();
         } else {
             throw new ApiException(410272);

+ 3 - 0
crmeb/app/services/out/OutInterfaceServices.php

@@ -31,6 +31,9 @@ class OutInterfaceServices extends BaseServices
         foreach ($authList as $item) {
             $rolesAuth[trim(strtolower($item['method']))][] = trim(strtolower(str_replace(' ', '', $item['path'])));
         }
+        if (in_array($rule, $rolesAuth[$method])) {
+            return true;
+        }
         $rule = str_replace('<', '{', $rule);
         $rule = str_replace('>', '}', $rule);
         if (in_array($rule, $rolesAuth[$method])) {

+ 1 - 1
crmeb/app/services/product/product/CopyTaobaoServices.php

@@ -315,7 +315,7 @@ class CopyTaobaoServices extends BaseServices
                 $content = ob_get_contents();
                 ob_end_clean();
             } catch (\Exception $e) {
-                return '';
+                throw new AdminException($e->getMessage());
             }
         }
         $size = strlen(trim($content));

+ 4 - 0
crmeb/app/services/statistic/OrderStatisticServices.php

@@ -158,9 +158,13 @@ class OrderStatisticServices extends BaseServices
         $orderService = app()->make(StoreOrderServices::class);
 
         $bing_xdata = ['普通订单', '秒杀订单', '砍价订单', '拼团订单', '预售订单'];
+        $model_checkbox = sys_config('model_checkbox', ['seckill', 'bargain', 'combination']);
         $color = ['#64a1f4', '#3edeb5', '#70869f', '#ffc653', '#fc7d6a'];
         $bing_data = [];
         foreach ($bing_xdata as $key => $item) {
+            if (!in_array('seckill', $model_checkbox) && $key == 1) continue;
+            if (!in_array('bargain', $model_checkbox) && $key == 2) continue;
+            if (!in_array('combination', $model_checkbox) && $key == 3) continue;
             $bing_data[] = [
                 'name' => $item,
                 'value' => $orderService->together(['paid' => 1, 'pid' => 0, 'activity_type' => $key, 'time' => $where['time']], 'pay_price', 'sum'),

+ 1 - 1
crmeb/app/services/system/SystemMenusServices.php

@@ -120,7 +120,7 @@ class SystemMenusServices extends BaseServices
      * @throws \think\db\exception\DbException
      * @throws \think\db\exception\ModelNotFoundException
      */
-    protected function getFormCascaderMenus(int $value = 0, $auth_type = 0)
+    public function getFormCascaderMenus(int $value = 0, $auth_type = 0)
     {
         $where = ['is_del' => 0];
         $menuList = $this->dao->getMenusRoule($where, ['id as value', 'pid', 'menu_name as label']);

+ 1 - 0
crmeb/app/services/system/admin/SystemAdminServices.php

@@ -147,6 +147,7 @@ class SystemAdminServices extends BaseServices
             'queue' => $queue ?? true,
             'timer' => $timer ?? true,
             'site_name' => sys_config('site_name'),
+            'site_func' => sys_config('model_checkbox', ['seckill', 'bargain', 'combination']),
         ];
     }
 

+ 2 - 1
crmeb/app/services/system/config/SystemConfigServices.php

@@ -204,7 +204,8 @@ class SystemConfigServices extends BaseServices
         ],
         'mer_type' => [
             'son_type' => [
-                'pay_sub_merchant_id' => ''
+                'pay_sub_merchant_id' => '',
+                'sp_appid' => ''
             ],
             'show_value' => 1
         ],

+ 5 - 2
crmeb/app/services/system/config/SystemConfigTabServices.php

@@ -14,6 +14,7 @@ namespace app\services\system\config;
 
 use app\dao\system\config\SystemConfigTabDao;
 use app\services\BaseServices;
+use app\services\system\SystemMenusServices;
 use crmeb\exceptions\AdminException;
 use crmeb\services\FormBuilder as Form;
 
@@ -124,8 +125,8 @@ class SystemConfigTabServices extends BaseServices
      */
     public function createConfigTabForm(array $formData = [])
     {
-        [$configTabList, $data] = $this->getConfigTabListForm((int)($formData['pid'] ?? 0), 3);
-        $form[] = Form::cascader('pid', '父级分类', $data)->options($configTabList)->filterable(true)->props(['props' => ['multiple' => false, 'checkStrictly' => true, 'emitPath' => false]])->style(['width'=>'100%']);
+        [$configTabList, $data1] = $this->getConfigTabListForm((int)($formData['pid'] ?? 0), 3);
+        $form[] = Form::cascader('pid', '父级分类', $data1)->options($configTabList)->filterable(true)->props(['props' => ['multiple' => false, 'checkStrictly' => true, 'emitPath' => false]])->style(['width'=>'100%']);
         $form[] = Form::input('title', '分类名称', $formData['title'] ?? '');
         $form[] = Form::input('eng_title', '分类字段英文', $formData['eng_title'] ?? '');
         $form[] = Form::frameInput('icon', '图标', $this->url(config('app.admin_prefix', 'admin') . '/widget.widgets/icon', ['fodder' => 'icon'], true), $formData['icon'] ?? '')->icon('el-icon-picture-outline')->height('560px')->props(['footer' => false]);
@@ -133,6 +134,8 @@ class SystemConfigTabServices extends BaseServices
             ['value' => 0, 'label' => '系统'],
             ['value' => 3, 'label' => '其它']
         ]);
+        [$menusList, $data2] = app()->make(SystemMenusServices::class)->getFormCascaderMenus((int)($formData['menus_id'] ?? 0));
+        $form[] = Form::cascader('menus_id', '关联菜单', $data2)->options($menusList)->filterable(true)->props(['props' => ['multiple' => false, 'checkStrictly' => true, 'emitPath' => false]])->style(['width'=>'100%']);
         $form[] = Form::radio('status', '状态', $formData['status'] ?? 1)->options([['value' => 1, 'label' => '显示'], ['value' => 2, 'label' => '隐藏']]);
         $form[] = Form::number('sort', '排序', (int)($formData['sort'] ?? 0))->precision(0)->controls(false);
         return $form;

+ 14 - 0
crmeb/app/services/system/lang/LangCodeServices.php

@@ -3,6 +3,7 @@
 namespace app\services\system\lang;
 
 use app\dao\system\lang\LangCodeDao;
+use app\jobs\TranslateJob;
 use app\services\BaseServices;
 use crmeb\exceptions\AdminException;
 use crmeb\services\CacheService;
@@ -186,4 +187,17 @@ class LangCodeServices extends BaseServices
             ];
         });
     }
+
+
+    public function BatchTranslation($typeId, $langType)
+    {
+        $list = $this->dao->selectList(['type_id' => $typeId], 'id,remarks')->toArray();
+        $list = array_chunk($list, 100);
+        $time = 1;
+        foreach ($list as $item) {
+            TranslateJob::dispatchSecs($time, 'doJob', [$item, $langType]);
+            $time++;
+        }
+        return true;
+    }
 }

+ 3 - 1
crmeb/app/services/system/lang/LangTypeServices.php

@@ -81,6 +81,8 @@ class LangTypeServices extends BaseServices
                     $list[$key]['type_id'] = $res->id;
                 }
                 $codeServices->saveAll($list);
+                $codeServices->BatchTranslation($res->id, $data['file_name']);
+                app()->make(LangCountryServices::class)->update(['code' => $data['file_name']], ['type_id' => $res->id]);
             } else {
                 throw new AdminException(100006);
             }
@@ -100,8 +102,8 @@ class LangTypeServices extends BaseServices
     public function setDefaultLangName()
     {
         $fileName = $this->dao->value(['is_default' => 1], 'file_name');
-        CacheService::set('range_name', $fileName);
         CacheService::clear();
+        CacheService::set('range_name', $fileName);
     }
 
     /**

+ 1 - 1
crmeb/app/services/system/log/ClearServices.php

@@ -51,7 +51,7 @@ class ClearServices extends BaseServices
     /**
      * 刷新数据缓存
      */
-    public function refreshCache()
+    public function refresCache()
     {
         $root = app()->getRootPath() . 'runtime' . DS;
         $adminRoute = $root . 'admin';

+ 3 - 15
crmeb/app/services/user/LoginServices.php

@@ -105,7 +105,8 @@ class LoginServices extends BaseServices
             $data['division_end_time'] = $spreadInfo->division_end_time;
             //如果店员切换代理商,则店员在之前代理商下推广的用户,他们的直接上级从当前店员变为之前代理商
             if ($userInfo->agent_id != 0 && $userInfo->agent_id != $spreadInfo->agent_id) {
-                $this->dao->update($userInfo['uid'], ['spread_uid' => $userInfo->agent_id], 'spread_uid');
+                $this->dao->update(['staff_id' => $userInfo['uid'], 'spread_uid' => $userInfo['uid']], ['spread_uid' => $spreadInfo['agent_id'], 'staff_id' => 0]);
+                $this->dao->update(['staff_id' => $userInfo['uid'], 'not_spread_uid' => $userInfo['uid']], ['staff_id' => 0]);
             }
         }
         if ($is_new) {
@@ -156,24 +157,11 @@ class LoginServices extends BaseServices
         return true;
     }
 
-    public function verify(SmsService $services, $phone, $type, $time, $ip)
+    public function verify(SmsService $services, $phone, $type, $time)
     {
         if ($this->dao->getOne(['account' => $phone, 'is_del' => 0]) && $type == 'register') {
             throw new ApiException(410028);
         }
-        $default = Config::get('sms.default', 'yihaotong');
-        $defaultMaxPhoneCount = Config::get('sms.maxPhoneCount', 10);
-        $defaultMaxIpCount = Config::get('sms.maxIpCount', 50);
-        $maxPhoneCount = Config::get('sms.stores.' . $default . '.maxPhoneCount', $defaultMaxPhoneCount);
-        $maxIpCount = Config::get('sms.stores.' . $default . '.maxIpCount', $defaultMaxIpCount);
-        /** @var SmsRecordServices $smsRecord */
-        $smsRecord = app()->make(SmsRecordServices::class);
-        if ($smsRecord->count(['phone' => $phone, 'add_ip' => $ip, 'time' => 'today']) >= $maxPhoneCount) {
-            throw new ApiException(410029);
-        }
-        if ($smsRecord->count(['add_ip' => $ip, 'time' => 'today']) >= $maxIpCount) {
-            throw new ApiException(410030);
-        }
         $code = rand(100000, 999999);
         $data['code'] = $code;
         $data['time'] = $time;

+ 0 - 0
crmeb/app/services/user/UserBillServices.php


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio