瀏覽代碼

first commit

jun 1 月之前
當前提交
8c4644f2bb
共有 100 個文件被更改,包括 8233 次插入0 次删除
  1. 5 0
      .gitattributes
  2. 20 0
      .gitignore
  3. 1 0
      .htaccess
  4. 13 0
      .styleci.yml
  5. 1 0
      .user.ini
  6. 7 0
      404.html
  7. 0 0
      README.md
  8. 412 0
      app/Console/Commands/Export/CreateFolderAndFileCommand.php
  9. 379 0
      app/Console/Commands/Export/ExportQuestionBankDataCommand.php
  10. 260 0
      app/Console/Commands/Export/ExportRealTopicExchangeCommand.php
  11. 367 0
      app/Console/Commands/Export/ExportTopicExchangeCommand.php
  12. 60 0
      app/Console/Commands/Export/ExportTrait.php
  13. 39 0
      app/Console/Commands/HistorySyncDrugReportCommand.php
  14. 35 0
      app/Console/Commands/IncrementSyncDrugReportAssCommand.php
  15. 37 0
      app/Console/Commands/IncrementSyncDrugReportCommand.php
  16. 34 0
      app/Console/Commands/SignedSyncDrugReportCommand.php
  17. 277 0
      app/Console/Commands/SyncDatabaseCommand.php
  18. 35 0
      app/Console/Commands/SyncDrugReportAssCommand.php
  19. 37 0
      app/Console/Commands/SyncDrugReportCommand.php
  20. 42 0
      app/Console/Commands/UpdateDrugReportAssCommand.php
  21. 77 0
      app/Console/Commands/UpdateTaskCommand.php
  22. 286 0
      app/Console/Commands/WashData/InitQuestionChapterCommand.php
  23. 230 0
      app/Console/Commands/WashData/InitQuestionPointCommand.php
  24. 275 0
      app/Console/Commands/WashData/InitQuestionRealTopicCommand.php
  25. 374 0
      app/Console/Commands/WashData/InitQuestionTopicCommand.php
  26. 184 0
      app/Console/Commands/WashData/InitQuestionTopicPointCommand.php
  27. 333 0
      app/Console/Commands/WashData/UpdateQuestionTopicCommand.php
  28. 78 0
      app/Console/Commands/WashData/WashDataSetQuestionPointStatusCommand.php
  29. 55 0
      app/Console/Kernel.php
  30. 112 0
      app/Console/开邻数据迁移到Saas.md
  31. 68 0
      app/Events/ExternalAndFollowOperateEvent.php
  32. 93 0
      app/Events/ExternalAndGroupOperateEvent.php
  33. 64 0
      app/Exceptions/Api/ApiException.php
  34. 11 0
      app/Exceptions/Api/LoginException.php
  35. 15 0
      app/Exceptions/Api/ValidException.php
  36. 15 0
      app/Exceptions/Api/VerifySignException.php
  37. 13 0
      app/Exceptions/Company/ShopException.php
  38. 80 0
      app/Exceptions/Handler.php
  39. 21 0
      app/Facades/Servers/Aliyun/IpLocation.php
  40. 27 0
      app/Facades/Servers/Aliyun/Sms.php
  41. 28 0
      app/Facades/Servers/Encrypts/AccessToken.php
  42. 26 0
      app/Facades/Servers/Encrypts/ApiSign.php
  43. 24 0
      app/Facades/Servers/Encrypts/Simple.php
  44. 29 0
      app/Facades/Servers/Logs/Log.php
  45. 33 0
      app/Facades/Servers/Redis/Redis.php
  46. 23 0
      app/Facades/Servers/Redis/RedisLock.php
  47. 27 0
      app/Facades/Servers/Safe/TextModeration.php
  48. 26 0
      app/Facades/Servers/ShortUrl/Client.php
  49. 28 0
      app/Facades/Servers/Sms/GuoDu.php
  50. 28 0
      app/Facades/Servers/Sms/VerifyCode.php
  51. 28 0
      app/Facades/Servers/Tencent/Cos.php
  52. 26 0
      app/Facades/Servers/Tencent/Sms.php
  53. 31 0
      app/Facades/Servers/Wechat/Mini.php
  54. 31 0
      app/Facades/Servers/Wechat/Official.php
  55. 50 0
      app/Facades/Servers/Wechat/OpenPlat.php
  56. 30 0
      app/Facades/Servers/Wechat/OpenWork.php
  57. 30 0
      app/Facades/Servers/Wechat/OpenWorkWeb.php
  58. 31 0
      app/Facades/Servers/Wechat/QuestionMini.php
  59. 32 0
      app/Facades/Servers/WeiBan/OpenApi.php
  60. 402 0
      app/Helpers/functions.php
  61. 201 0
      app/Http/Controllers/Api/Api.php
  62. 236 0
      app/Http/Controllers/Api/DrugReportInfoController.php
  63. 47 0
      app/Http/Controllers/Api/Login.php
  64. 181 0
      app/Http/Controllers/Api/QueryDrugReportController.php
  65. 57 0
      app/Http/Controllers/Controller.php
  66. 64 0
      app/Http/Kernel.php
  67. 80 0
      app/Http/Middleware/Api/SwitchCompanyDb.php
  68. 111 0
      app/Http/Middleware/Company/AccessAuth.php
  69. 69 0
      app/Http/Middleware/Company/Login.php
  70. 22 0
      app/Http/Middleware/EncryptCookies.php
  71. 69 0
      app/Http/Middleware/LoadLocale.php
  72. 17 0
      app/Http/Middleware/PreventRequestsDuringMaintenance.php
  73. 32 0
      app/Http/Middleware/RedirectIfAuthenticated.php
  74. 23 0
      app/Http/Middleware/TrimStrings.php
  75. 20 0
      app/Http/Middleware/TrustHosts.php
  76. 24 0
      app/Http/Middleware/TrustProxies.php
  77. 52 0
      app/Http/Requests/Api/Coupon.php
  78. 45 0
      app/Http/Requests/Api/Coupon/Active.php
  79. 45 0
      app/Http/Requests/Api/Coupon/Custom.php
  80. 45 0
      app/Http/Requests/Api/Custom.php
  81. 70 0
      app/Http/Requests/Api/CustomAddr.php
  82. 64 0
      app/Http/Requests/Api/CustomCompany.php
  83. 71 0
      app/Http/Requests/Api/CustomShopResource.php
  84. 69 0
      app/Http/Requests/Api/CustomerForm/Company/CustomerForm.php
  85. 176 0
      app/Http/Requests/Api/DynamicQrcode/Templates.php
  86. 52 0
      app/Http/Requests/Api/LiveReward/Order.php
  87. 46 0
      app/Http/Requests/Api/Login.php
  88. 39 0
      app/Http/Requests/Api/Login/Phone.php
  89. 67 0
      app/Http/Requests/Api/Lottery/LiveRecord.php
  90. 56 0
      app/Http/Requests/Api/Lottery/OrderRecord.php
  91. 56 0
      app/Http/Requests/Api/Lottery/ScoreRecord.php
  92. 58 0
      app/Http/Requests/Api/Orders.php
  93. 50 0
      app/Http/Requests/Api/Orders/Receipt.php
  94. 62 0
      app/Http/Requests/Api/Product.php
  95. 71 0
      app/Http/Requests/Api/Promoter/ApplicationForm.php
  96. 69 0
      app/Http/Requests/Api/Promoter/CommonProduct.php
  97. 74 0
      app/Http/Requests/Api/Promoter/Configuration.php
  98. 69 0
      app/Http/Requests/Api/Promoter/Customers.php
  99. 70 0
      app/Http/Requests/Api/Promoter/EarningsRecord.php
  100. 69 0
      app/Http/Requests/Api/Promoter/Product.php

+ 5 - 0
.gitattributes

@@ -0,0 +1,5 @@
+* text=auto
+*.css linguist-vendored
+*.scss linguist-vendored
+*.js linguist-vendored
+CHANGELOG.md export-ignore

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+/.idea
+/.vscode
+/.well-known
+/bootstrap/cache/*
+/storage/framework/cache/data/*
+/storage/framework/sessions/*
+/storage/framework/testing/*
+/storage/framework/views/*
+/storage/exports/*
+/elkconf
+/vendor
+/public
+*.DS_Store
+*.log
+.env
+.env.backup
+.phpunit.result.cache
+docker-compose.override.yml
+Homestead.json
+Homestead.yaml

+ 1 - 0
.htaccess

@@ -0,0 +1 @@
+ 

+ 13 - 0
.styleci.yml

@@ -0,0 +1,13 @@
+php:
+  preset: laravel
+  disabled:
+    - no_unused_imports
+  finder:
+    not-name:
+      - index.php
+      - server.php
+js:
+  finder:
+    not-name:
+      - webpack.mix.js
+css: true

+ 1 - 0
.user.ini

@@ -0,0 +1 @@
+open_basedir=/www/wwwroot/drug_report/:/tmp/

+ 7 - 0
404.html

@@ -0,0 +1,7 @@
+<html>
+<head><title>404 Not Found</title></head>
+<body>
+<center><h1>404 Not Found</h1></center>
+<hr><center>nginx</center>
+</body>
+</html>

+ 0 - 0
README.md


+ 412 - 0
app/Console/Commands/Export/CreateFolderAndFileCommand.php

@@ -0,0 +1,412 @@
+<?php
+
+
+namespace App\Console\Commands\Export;
+
+
+
+
+use Illuminate\Console\Command;
+
+class CreateFolderAndFileCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'create:folder_file';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '在指定目录下创建文件夹和文件';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $directory = "E:\\美团数据抓取\\三九胃泰颗粒"; // 指定目录路径
+        $this->createFileEx($directory);
+
+        //$directory = "E:\\鸭题库数据初始化历年真题"; // 指定目录路径
+        //$this->createFile($directory);
+        //$this->deleteTxtFilesRecursively($directory);
+
+        $directory = "E:\\鸭题库数据初始化考点_第二次\\中药二"; // 指定目录路径
+        //$this->clearTxtFilesContent($directory);
+//        $this->deleteTxtFilesRecursively($directory);
+
+
+        $baseDirectory = "E:\\鸭题库数据\\中药二";
+        $firstFolderName = [
+            '第一节 中药药物警戒',
+            '第二节 中药不良反应',
+            '第三节 中药用药错误',
+            '第四节 医疗用毒性中药的中毒反应和基本救治原则',
+        ];
+
+        //$this->createFirstFolder($firstFolderName, $baseDirectory);
+
+        $firstAndSecondFolderNames = [
+            [
+                'firstName' => '历年真题',
+                'secondNames' => [
+                    '2015',
+                    '2016',
+                    '2017',
+                    '2018',
+                    '2019',
+                    '2020',
+                    '2021',
+                    '2022-1',
+                    '2022-2',
+                    '2023',
+                    '2024',
+                ]
+            ],
+//            [
+//                'firstName' => '第二章  处方审核与调剂',
+//                'secondNames' => []
+//            ],
+//            [
+//                'firstName' => '第三章 用药咨询与药物治疗管理',
+//                'secondNames' => [
+//                    '第一节 药学信息咨询服务',
+//                    '第二节 疾病管理与健康宣教',
+//                    '第三节 药物治疗管理',
+//                    '第四节 常用医学检查',
+//                ]
+//            ],
+//            [
+//                'firstName' => '第四章 用药安全',
+//                'secondNames' => [
+//                    '第一节 药物警戒',
+//                    '第二节 药品不良反应',
+//                    '第三节 药源性疾病',
+//                    '第四节 用药错误',
+//                    '第五节 特殊人群用药'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第五章 急救、中毒解救及职业防护',
+//                'secondNames' => [
+//                    '第一节 急救的意义与原则',
+//                    '第二节 常见急症及处置',
+//                    '第三节 中毒解救',
+//                    '第四节 细胞毒性药物的职业防护'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第六章 常见病症的健康管理',
+//                'secondNames' => [
+//                    '第一节 发热',
+//                    '第二节 疼痛',
+//                    '第三节 咳嗽',
+//                    '第四节 感冒',
+//                    '第五节 急性扁桃体炎',
+//                    '第六节  鼻窦炎',
+//                    '第七节 过敏性鼻炎',
+//                    '第八节  急性气管-支气管炎',
+//                    '第九节 社区获得性肺炎',
+//                    '第十节 口腔溃疡',
+//                    '第十一节 消化不良',
+//                    '第十二节 胆石症与胆囊炎',
+//                    '第十三节 便秘',
+//                    '第十四节 腹泻',
+//                    '第十五节 痔疮',
+//                    '第十六节 阴道炎',
+//                    '第十七节  乳腺炎',
+//                    '第十八节 尿路感染',
+//                    '第十九节 男性性功能障碍',
+//                    '第二十节 尿失禁',
+//                    '第二十一节 绝经综合征',
+//                    '第二十二节 多囊卵巢综合征',
+//                    '第二十三节 避孕',
+//                    '第二十四节 昆虫叮咬',
+//                    '第二十五节 烫伤',
+//                    '第二十六节 带状疱疹',
+//                    '第二十七节 单纯疱疹',
+//                    '第二十八节 冻伤 ( 疮 )',
+//                    '第二十九节  白癜风',
+//                    '第三十节  银屑病',
+//                    '第三十一节 痤疮',
+//                    '第三十二节 荨麻疹',
+//                    '第三十三节 湿疹',
+//                    '第三十四节  甲沟炎',
+//                    '第三十五节  特应性皮炎',
+//                    '第三十六节 真菌感染性皮肤病',
+//                    '第三十七节  雄激素性脱发',
+//                    '第三十八节  疖痈',
+//                    '第三十九节 视疲劳',
+//                    '第四十节 急性结膜炎',
+//                    '第四十一节 干眼症',
+//                    '第四十二节  沙眼',
+//                    '第四十三节  麦粒肿',
+//                    '第四十四节 贫血',
+//                    '第四十五节 超重和肥胖',
+//                    '第四十六节  晕动病'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第七章 慢性病管理',
+//                'secondNames' => [
+//                ]
+//            ],
+//            [
+//                'firstName' => '第八章 呼吸系统常见疾病',
+//                'secondNames' => [
+//                    '第一节 哮喘',
+//                    '第二节 慢性阻塞性肺疾病'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第九章 心血管系统常见疾病',
+//                'secondNames' => [
+//                    '第一节 高血压',
+//                    '第二节 血脂异常',
+//                    '第三节 冠状动脉粥样硬化',
+//                    '第四节 心房颤动'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第十章 神经精神系统常见疾病',
+//                'secondNames' => [
+//                    '第一节 焦虑抑郁',
+//                    '第二节 睡眠障碍',
+//                    '第三节 脑卒中',
+//                    '第四节 帕金森病',
+//                    '第五节 癫痫',
+//                    '第六节 痴呆'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第十一章 消化系统常见疾病',
+//                'secondNames' => [
+//                    '第一节 胃食管反流病',
+//                    '第二节 消化性溃疡',
+//                    '第三节 溃疡性结肠炎',
+//                    '第四节 慢性病毒性肝炎'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第十二章 内分泌系统常见疾病',
+//                'secondNames' => [
+//                    '第一节 甲状腺功能亢进症',
+//                    '第二节 甲状腺功能减退症',
+//                    '第三节 糖尿病',
+//                    '第四节 骨质疏松症',
+//                    '第五节 高尿酸血症与痛风'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第十三章 免疫系统常见疾病',
+//                'secondNames' => [
+//                    '第一节 类风湿关节炎',
+//                    '第二节 系统性红斑狼疮'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第十四章 泌尿系统常见疾病',
+//                'secondNames' => [
+//                    '第一节 良性前列腺增生症',
+//                    '第二节 慢性肾脏病'
+//                ]
+//            ],
+//            [
+//                'firstName' => '第十五章 肿瘤',
+//                'secondNames' => [
+//                    '第一节 肿瘤的临床基础',
+//                    '第二节 肿瘤的治疗与预防'
+//                ]
+//            ],
+
+        ];
+
+        //$this->createFirstAndSecondFolder($firstAndSecondFolderNames, $baseDirectory);
+
+        $this->info('success!');
+
+
+    }
+
+    public function createFile($directory)
+    {
+        $files = scandir($directory);
+
+        $folders = [];
+        foreach ($files as $key=> $file) {
+            if ($file != "." && $file != "..") { // 排除当前目录"."和上级目录".."
+                $fullPath = $directory . '/' . $file;
+                if (is_dir($fullPath)) {
+                    $folders[] = $file;
+                    $tempFilePath = $directory . '\\' . $file . '\\' . $file . '.txt';
+                    //dd($tempFilePath);
+                    //在目录下创建文件
+                    $tempFile = fopen($tempFilePath, "w");
+                    fclose($tempFile);
+                }
+            }
+        }
+        dd($folders);
+
+    }
+
+    public function createFileEx($baseDirectory)
+    {
+        if (!is_dir($baseDirectory)) {
+            mkdir($baseDirectory, 0777, true); // 创建文件夹,0777 是权限设置,true 表示递归创建
+        }
+
+        for ($i = 11; $i <= 100; $i++) {
+            $fileName = $baseDirectory . '/' . 'file_' . $i . '.txt'; // 文件名格式为 file_1.txt, file_2.txt, ...
+            $file = fopen($fileName, 'w'); // 打开文件用于写入,如果文件不存在会自动创建
+            if ($file) {
+                fwrite($file, ''); // 写入内容,这里写入空内容
+                fclose($file); // 关闭文件
+                echo "文件 $fileName 创建成功!<br>";
+            } else {
+                echo "文件 $fileName 创建失败!<br>";
+            }
+        }
+
+    }
+
+    public function createFirstFolder($fileNames, $baseDirectory) {
+
+        foreach ($fileNames as $key => $fileName) {
+            $realKey = $key + 1;
+            $dirPath = $baseDirectory . '\\' . $realKey . $fileName;
+            if (!is_dir($dirPath)) {
+                mkdir($dirPath, 0x777, true);
+            }
+
+            //再创建txt文件
+            $filePath = $dirPath . '\\' . $realKey . $fileName . '.txt';
+            $fd = fopen($filePath, 'w+');
+            fclose($fd);
+
+        }
+    }
+
+    public function createFirstAndSecondFolder($firstAndSecondFolderNames, $baseDirectory)
+    {
+        foreach ($firstAndSecondFolderNames as $key => $firstAndSecondFolderName) {
+            $firstKey = $key + 1;
+            $firstDirPath = $baseDirectory . '\\' . $firstKey . $firstAndSecondFolderName['firstName'];
+            if (!is_dir($firstDirPath)) {
+                mkdir($firstDirPath, 0x777, true);
+            }
+
+            //创建二级文件夹
+            if (!empty($firstAndSecondFolderName['secondNames'])) {
+                foreach ($firstAndSecondFolderName['secondNames'] as $k => $item) {
+                    $secondKey = $k + 1;
+                    $secondDirPath = $firstDirPath . '\\' . $secondKey . $item;
+                    if (!is_dir($secondDirPath)) {
+                        mkdir($secondDirPath, 0x777, true);
+                    }
+                    //再创建txt文件
+                    $filePath = $secondDirPath . '\\' . $secondKey . $item . '.txt';
+                    $fd = fopen($filePath, 'w+');
+                    fclose($fd);
+                }
+            } else {
+                //创建txt文件
+                $filePath = $firstDirPath . '\\' . $firstKey . $firstAndSecondFolderName['firstName'] . '.txt';
+                $fd = fopen($filePath, 'w+');
+                fclose($fd);
+            }
+
+        }
+    }
+
+    function deleteTxtFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 检查文件扩展名是否为 .txt
+                if (pathinfo($filePath, PATHINFO_EXTENSION) === 'xlsx') {
+                    // 删除文件
+                    if (unlink($filePath)) {
+                        echo "已删除文件: $filePath\n";
+                    } else {
+                        echo "无法删除文件: $filePath\n";
+                    }
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $this->deleteTxtFilesRecursively($filePath);
+            }
+        }
+    }
+
+    public function clearTxtFilesContent($directory)
+    {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 检查文件扩展名是否为 .txt
+                if (pathinfo($filePath, PATHINFO_EXTENSION) === 'txt') {
+
+                    file_put_contents($filePath, '');
+                    echo "清空文件内容:$filePath\n";
+                    // 删除文件
+//                    if (unlink($filePath)) {
+//                        echo "已删除文件: $filePath\n";
+//                    } else {
+//                        echo "无法删除文件: $filePath\n";
+//                    }
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $this->clearTxtFilesContent($filePath);
+            }
+        }
+
+    }
+
+}

+ 379 - 0
app/Console/Commands/Export/ExportQuestionBankDataCommand.php

@@ -0,0 +1,379 @@
+<?php
+
+
+namespace App\Console\Commands\Export;
+
+
+use App\Servers\QuestionBank\QuestionChapterService;
+use Illuminate\Console\Command;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+
+class ExportQuestionBankDataCommand extends Command
+{
+    use ExportTrait;
+
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'export:questionBankData';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '题库数据导出';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+
+        $head = [
+            '用户id',
+            '手机号',
+            '注册时间',
+            '第一次学习时间',
+            '第一天做题数量',
+            '学习进度',
+        ];
+
+        //正式
+        $dataBaseInfos = [
+            //saas-company
+            'company' => [
+                'host' => '120.24.49.2',
+                'port' => 3306,
+                'db_name' => 'company_61',
+                'db_username' => 'company_61',
+                'db_password' => 'NKD4saLKN7zWKrzz'
+            ]
+        ];
+
+        $companyConnectionConfig = [
+            'driver' => 'mysql',
+            'host' => $dataBaseInfos['company']['host'],
+            'port' => $dataBaseInfos['company']['port'],
+            'database' => $dataBaseInfos['company']['db_name'],
+            'username' => $dataBaseInfos['company']['db_username'],
+            'password' => $dataBaseInfos['company']['db_password'],
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_unicode_ci',
+            'unix_socket' => env('DB_SOCKET', ''),
+            'prefix' => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+        $tableNameCustomer = 'kailin_custom';
+
+        $tableStudyProc    = 'kailin_question_point_study_progress';
+        $tableAnswerTopic  = 'kailin_question_user_topic_answer_statistics';
+        //$tableAnswerTopic  = 'kailin_question_user_topic_answer_record';
+        $tableAnswerRealTopic  = 'kailin_question_user_real_topic_answer_record';
+
+        $tableNameChapter = 'kailin_question_chapter';
+
+
+
+        $directory = storage_path('exports/');
+        $fileName  = '统计-'. Carbon::today()->toDateString();
+
+        $this->createdFile($directory, $fileName);
+
+        $this->putRow($head);
+
+        $allUserInfos = DB::connection('company')->table($tableNameCustomer)->get(['uid', 'phone', 'insert_time'])->toArray();
+
+
+        $dataCount = count($allUserInfos);
+        $this->info('开始处理');
+        $this->info("总共有:{$dataCount} 用户需要处理");
+        $bar = $this->output->createProgressBar($dataCount);
+
+
+        foreach ($allUserInfos as $allUserInfo) {
+
+            $uid = $allUserInfo['uid'];
+            //通过uid获取第一次的学习时间
+
+            $firstStudyTime = '';
+
+            $studyInfo =  DB::connection('company')->table($tableStudyProc)->where('user_id', $uid)->first();
+
+            if ($studyInfo) {
+                $firstStudyTime = $studyInfo['insert_time'];
+            }
+
+            $regDate = date("Y-m-d", $allUserInfo['insert_time']);
+
+            $startTime = strtotime($regDate . " 00:00:00"); // 获取指定日期的开始时间戳
+            $endTime = strtotime($regDate . " 23:59:59"); // 获取指定日期的结束时间戳
+
+            //通过uid获取学员注册当天的答题数量
+
+            $answerNum  = DB::connection('company')->table($tableAnswerTopic)
+                ->where('user_id', $uid)
+                ->where('insert_time', '>=', $startTime)
+                ->where('insert_time', '<=', $endTime)
+                ->count();
+
+            //获取学员学习了考点的章节id
+            $chapters = DB::connection('company')->table($tableNameChapter)->get(['id', 'parent_id'])->toArray();
+            $questionChapterService = new QuestionChapterService();
+
+//            $allSonChapterIds       = $questionChapterService->getSonChapters($chapters, $chapterId);
+
+
+            $faguiChapterIds = $questionChapterService->getSonChapters($chapters, 3);
+            array_unshift($faguiChapterIds, 3);//放到数组前面
+
+            $y1ChapterIds = $questionChapterService->getSonChapters($chapters, 4);
+            array_unshift($y1ChapterIds, 4);//放到数组前面
+
+            $y2ChapterIds = $questionChapterService->getSonChapters($chapters, 5);
+            array_unshift($y2ChapterIds, 5);//放到数组前面
+
+            $yzChapterIds = $questionChapterService->getSonChapters($chapters, 6);
+            array_unshift($yzChapterIds, 6);//放到数组前面
+
+            $zy1ChapterIds = $questionChapterService->getSonChapters($chapters, 7);
+            array_unshift($zy1ChapterIds, 7);//放到数组前面
+
+            $zy2ChapterIds = $questionChapterService->getSonChapters($chapters, 8);
+            array_unshift($zy2ChapterIds, 8);//放到数组前面
+
+            $zyzChapterIds = $questionChapterService->getSonChapters($chapters, 9);
+            array_unshift($zyzChapterIds, 9);//放到数组前面
+
+
+
+
+            $resultChapterIds =  DB::connection('company')->table($tableStudyProc)->where('user_id', $uid)->orderBy('chapter_id', 'asc')->pluck('chapter_id')->toArray();
+
+            $resultChapterIds = array_values(array_unique($resultChapterIds));
+
+            $needExecuteFgChapterIds = [];
+            $needExecuteY1ChapterIds = [];
+            $needExecuteY2ChapterIds = [];
+            $needExecuteYzChapterIds = [];
+            $needExecuteZy1ChapterIds = [];
+            $needExecuteZy2ChapterIds = [];
+            $needExecuteZyzChapterIds = [];
+
+            if (!empty($resultChapterIds)) {
+
+                foreach ($resultChapterIds as $resultChapterId) {
+                    if (in_array($resultChapterId, $faguiChapterIds)) {
+                        $needExecuteFgChapterIds [] =  $resultChapterId;
+                    } elseif (in_array($resultChapterId, $y1ChapterIds)) {
+                        $needExecuteY1ChapterIds [] = $resultChapterId;
+                    } elseif (in_array($resultChapterId, $y2ChapterIds)) {
+                        $needExecuteY2ChapterIds [] = $resultChapterId;
+                    } elseif (in_array($resultChapterId, $yzChapterIds)) {
+                        $needExecuteYzChapterIds [] = $resultChapterId;
+                    } elseif (in_array($resultChapterId, $zy1ChapterIds)) {
+                        $needExecuteZy1ChapterIds [] = $resultChapterId;
+                    } elseif (in_array($resultChapterId, $zy2ChapterIds)) {
+                        $needExecuteZy2ChapterIds [] = $resultChapterId;
+                    } elseif (in_array($resultChapterId, $zyzChapterIds)) {
+                        $needExecuteZyzChapterIds [] = $resultChapterId;
+                    }
+                }
+
+                $study_proc = '';
+
+                if (!empty($needExecuteFgChapterIds)) {
+                    $needChapterFgId = end($needExecuteFgChapterIds);
+                    $needChapterFgId = $this->getChapterName($needChapterFgId, $tableNameChapter);
+                    $study_proc = $needChapterFgId;
+                }
+
+                if (!empty($needExecuteY1ChapterIds)) {
+                    $needChapterY1Id = end($needExecuteY1ChapterIds);
+                    $needChapterY1Id = $this->getChapterName($needChapterY1Id, $tableNameChapter);
+                    if (!empty($study_proc)) {
+                        $study_proc = $study_proc . ','. $needChapterY1Id;
+                    } else {
+                        $study_proc = $needChapterY1Id;
+                    }
+                }
+
+                if (!empty($needExecuteY2ChapterIds)) {
+                    $needChapterY2Id = end($needExecuteY2ChapterIds);
+                    $needChapterY2Id = $this->getChapterName($needChapterY2Id, $tableNameChapter);
+                    if (!empty($study_proc)) {
+                        $study_proc = $study_proc . ','. $needChapterY2Id;
+                    } else {
+                        $study_proc = $needChapterY2Id;
+                    }
+                }
+
+                if (!empty($needExecuteYzChapterIds)) {
+                    $needChapterYzId = end($needExecuteYzChapterIds);
+                    $needChapterYzId = $this->getChapterName($needChapterYzId, $tableNameChapter);
+                    if (!empty($study_proc)) {
+                        $study_proc = $study_proc . ','. $needChapterYzId;
+                    } else {
+                        $study_proc = $needChapterYzId;
+                    }
+                }
+
+                if (!empty($needExecuteZy1ChapterIds)) {
+                    $needChapterZy1Id = end($needExecuteZy1ChapterIds);
+                    $needChapterZy1Id = $this->getChapterName($needChapterZy1Id, $tableNameChapter);
+                    if (!empty($study_proc)) {
+                        $study_proc = $study_proc . ','. $needChapterZy1Id;
+                    } else {
+                        $study_proc = $needChapterZy1Id;
+                    }
+                }
+
+                if (!empty($needExecuteZy2ChapterIds)) {
+                    $needChapterZy2Id = end($needExecuteZy2ChapterIds);
+                    $needChapterZy2Id = $this->getChapterName($needChapterZy2Id, $tableNameChapter);
+                    if (!empty($study_proc)) {
+                        $study_proc = $study_proc . ','. $needChapterZy2Id;
+                    } else {
+                        $study_proc = $needChapterZy2Id;
+                    }
+                }
+
+                if (!empty($needExecuteZy2ChapterIds)) {
+                    $needChapterZyzId = end($needExecuteZy2ChapterIds);
+                    $needChapterZyzId = $this->getChapterName($needChapterZyzId, $tableNameChapter);
+                    if (!empty($study_proc)) {
+                        $study_proc = $study_proc . ','. $needChapterZyzId;
+                    } else {
+                        $study_proc = $needChapterZyzId;
+                    }
+                }
+
+
+                //$study_proc = implode(',', $resultChapterIds);
+            } else {
+                $study_proc = '';
+            }
+
+
+
+            $exportData = [];
+            $exportData['uid'] = $uid;
+            $exportData['phone'] = $allUserInfo['phone'];
+            $exportData['reg_date'] = date("Y-m-d H:i:s", $allUserInfo['insert_time']) ;
+            $exportData['first_study_date'] = !empty($firstStudyTime) ? date("Y-m-d H:i:s", $firstStudyTime) : '';
+            $exportData['first_answer_num'] = $answerNum;
+            $exportData['study_proc'] = $study_proc;
+
+            $this->putRow($exportData);
+
+            $bar->advance();
+        }
+
+        $bar->finish();
+
+        $this->info('');
+        $this->info('导出完成');
+
+
+    }
+
+    public function getChapterName($chapterId, $tableNameChapter)
+    {
+        $infos = DB::connection('company')->table($tableNameChapter)->where('id', $chapterId)->first();
+
+        $baseName = $infos['name'];
+        $level    = $infos['level'];
+
+        if ($level == 3) {
+            //父级id也就是第二级的id
+            $thirdChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $chapterId)->value('parent_id');
+            $secondLevelName = DB::connection('company')->table($tableNameChapter)->where('id', $thirdChapterParentId)->value('name');
+            //dd($secondLevelName . '-' . $baseName);
+            return $secondLevelName . '->' . $baseName;
+        } elseif ($level == 4) {
+            //父级id也就是第三级的id
+            $fourthLevelChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $chapterId)->value('parent_id');
+            $thirdLevelInfo = DB::connection('company')->table($tableNameChapter)->where('id', $fourthLevelChapterParentId)->first();
+            $thirdLevelName = $thirdLevelInfo['name'];
+            $thirdLevelId = $thirdLevelInfo['id'];
+            //父级id也就是第二级的id
+            $thirdLevelChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $thirdLevelId)->value('parent_id');
+
+            $secondLevelName = DB::connection('company')->table($tableNameChapter)->where('id', $thirdLevelChapterParentId)->value('name');
+            //dd($secondLevelName . '--' . $thirdLevelName .'--'. $baseName);
+            return $secondLevelName . '->' . $thirdLevelName .'->'. $baseName;
+
+        } elseif ($level == 5) {
+            $fifthChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $chapterId)->value('parent_id');
+            $fourthLevelInfo = DB::connection('company')->table($tableNameChapter)->where('id', $fifthChapterParentId)->first();
+            $fourthLevelName = $fourthLevelInfo['name'];
+            $fourthLevelId = $fourthLevelInfo['id'];
+
+            $fourthChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $fourthLevelId)->value('parent_id');
+
+            $thirdLevelInfo = DB::connection('company')->table($tableNameChapter)->where('id', $fourthChapterParentId)->first();
+            $thirdLevelName = $thirdLevelInfo['name'];
+            $thirdLevelId = $thirdLevelInfo['id'];
+
+            $thirdChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $thirdLevelId)->value('parent_id');
+
+            $secondLevelName = DB::connection('company')->table($tableNameChapter)->where('id', $thirdChapterParentId)->value('name');
+            //dd($secondLevelName . '-' . $thirdLevelName . '-'. $fourthLevelName . '-' .  $baseName);
+            return $secondLevelName . '->' . $thirdLevelName . '->'. $fourthLevelName . '->' .  $baseName;
+        } elseif ($level == 6) {
+            $sixChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $chapterId)->value('parent_id');
+
+            $fifthLevelInfo = DB::connection('company')->table($tableNameChapter)->where('id', $sixChapterParentId)->first();
+
+            $fifthLevelName = $fifthLevelInfo['name'];
+            $fifthLevelId = $fifthLevelInfo['id'];
+
+            $fifthChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $fifthLevelId)->value('parent_id');
+
+            $fourthLevelInfo = DB::connection('company')->table($tableNameChapter)->where('id', $fifthChapterParentId)->first();
+            $fourthLevelName = $fourthLevelInfo['name'];
+            $fourthLevelId = $fourthLevelInfo['id'];
+
+            $fourthChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $fourthLevelId)->value('parent_id');
+
+            $thirdLevelInfo = DB::connection('company')->table($tableNameChapter)->where('parent_id', $fourthChapterParentId)->first();
+            $thirdLevelName = $thirdLevelInfo['name'];
+            $thirdLevelId = $thirdLevelInfo['id'];
+            $thirdChapterParentId = DB::connection('company')->table($tableNameChapter)->where('id', $thirdLevelId)->value('parent_id');
+            $secondLevelName = DB::connection('company')->table($tableNameChapter)->where('id', $thirdChapterParentId)->value('name');
+//dd($secondLevelName . '-' . $thirdLevelName . '-'. $fourthLevelName . '-' . $fifthLevelName . '-' . $baseName);
+            return $secondLevelName . '->' . $thirdLevelName . '->'. $fourthLevelName . '->' . $fifthLevelName . '->' . $baseName;
+        }
+
+
+    }
+
+}

+ 260 - 0
app/Console/Commands/Export/ExportRealTopicExchangeCommand.php

@@ -0,0 +1,260 @@
+<?php
+
+
+namespace App\Console\Commands\Export;
+
+
+use Illuminate\Console\Command;
+
+class ExportRealTopicExchangeCommand extends Command
+{
+    use ExportTrait;
+
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'export:exchangeRealTopic';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '题库解析';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $head = [
+            '题组名称',
+            '题目类型',
+            '题目',
+            '选项A',
+            '选项B',
+            '选项c',
+            '选项D',
+            '选项E',
+            '正确答案',
+            '解析',
+            '正确率',
+            '考点'
+        ];
+
+        $directory = "E:\\鸭题库数据\\药二\\历年真题";
+
+
+        $needExchangeTopics = $this->getFilesRecursively($directory);
+//dd($needExchangeTopics);
+        $dataCount = count($needExchangeTopics);
+
+        $this->info('开始转换');
+        $this->info("总共有:{$dataCount} 套题需要转换");
+        $bar = $this->output->createProgressBar($dataCount);
+
+        foreach ($needExchangeTopics as $needExchangeTopic) {
+            $this->createdFile($needExchangeTopic['directory'], $needExchangeTopic['fileName']);
+            $this->putRow($head);
+
+            $data = file_get_contents($needExchangeTopic['data']);
+
+            $data = json_decode($data, true);
+
+            foreach ($data['data'] as $item) {
+                //真题组名称:
+                $groupName = $item['groupName'];
+
+                $paperQuestions = $item['paperQuestionVos'];
+
+                foreach ($paperQuestions as $value) {
+
+                    //题目类型
+                    $style = $value['style'];
+                    $styleName = $this->getStyleName($style);
+                    //dd($styleName);
+                    //题目
+                    $title = '';
+                    if (!empty($value['exerciseQuestionsTopicPo'])) {
+                        $title = $value['exerciseQuestionsTopicPo']['content'] . "\r\n";
+                    }
+                    $title = $title . $value['title'];
+
+                    //解析
+                    $explain = $value['explain'];
+
+                    //准确率
+                    $accuracy = $value['accuracy'];
+
+                    //
+                    $correctAnswerStr = ''; //正确选项Str
+                    $correctAnswers = [];  //正确答案数据
+                    $selectAContent = '';
+                    $selectBContent = '';
+                    $selectCContent = '';
+                    $selectDContent = '';
+                    $selectEContent = '';
+
+                    //考点
+                    $examPointStr = '';
+                    $examPointNames = [];
+                    $examPoints = $value['points'];
+                    foreach ($examPoints as $examPoint) {
+                        $examPointNames [] = $examPoint['name'];
+                    }
+                    $examPointStr = implode(',', $examPointNames);//考点
+                    //dd($examPointStr);
+
+
+                    //$answerFlag = false;
+
+//                $answer = $data['answer'];
+//
+//                if (in_array($answer, $answerArray)) {
+//                    $answerFlag = true; //获取answer成功
+//                }
+
+
+                    $questionCount = $value['questionCount']; //选项数量
+                    //获取选项:
+                    $questionsOptionsPos = $value['questionsOptionsPos'];
+
+                    foreach ($questionsOptionsPos as $key => $item) {
+                        if ($key == 0 && $key <= $questionCount - 1) {
+                            $selectAContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'A';
+                            }
+                        } elseif ($key == 1 && $key <= $questionCount - 1) {
+                            $selectBContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'B';
+                            }
+                        } elseif ($key == 2 && $key <= $questionCount - 1) {
+                            $selectCContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'C';
+                            }
+                        } elseif ($key == 3 && $key <= $questionCount - 1) {
+                            $selectDContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'D';
+                            }
+                        } elseif ($key == 4 && $key <= $questionCount - 1) {
+                            $selectEContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'E';
+                            }
+                        }
+                    }
+                    $correctAnswerStr = implode(',', $correctAnswers); //正确答案
+                    //dd($correctAnswerStr);
+
+
+                    $exportData = [];
+                    $exportData['groupName'] = $groupName;
+                    $exportData['style'] = $styleName;
+                    $exportData['title'] = $title;
+
+                    $exportData['selectA'] = $selectAContent;
+                    $exportData['selectB'] = $selectBContent;
+                    $exportData['selectC'] = $selectCContent;
+                    $exportData['selectD'] = $selectDContent;
+                    $exportData['selectE'] = $selectEContent;
+                    $exportData['answer'] = $correctAnswerStr;
+                    $exportData['explain'] = $explain;
+                    $exportData['accuracy'] = $accuracy;
+                    $exportData['point'] = $examPointStr;
+                    $this->putRow($exportData);
+                }
+
+
+            }
+
+            $this->closeFile();
+
+            $bar->advance();
+        }
+
+        $bar->finish();
+
+        $this->info('');
+        $this->info('导出完成');
+
+    }
+
+    public function getStyleName($style)
+    {
+        if ($style == 6) {
+            $styleName = '配伍题';
+        } elseif ($style == 2) {
+            $styleName = '单选题';
+        } elseif ($style == 3) {
+            $styleName = '多选题';
+        } else {
+            $styleName = $style;
+        }
+
+        return $styleName;
+    }
+
+
+    public function getFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        $result = [];
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 过滤掉文件名中包含“数据源”的文件
+                if (strpos($file, '数据源') === false) {
+                    // 获取文件名的主名称(去除.txt后缀)
+                    $fileNameWithoutExtension = pathinfo($file, PATHINFO_FILENAME);
+                    // 获取文件所在的目录路径
+                    $fileDir = dirname($filePath);
+                    // 获取文件的大小
+                    $fileSize = filesize($filePath);
+                    $result[] = [
+                        'fileName' => $fileNameWithoutExtension, // 文件名(去除.txt后缀)
+                        'data' => $filePath, // 完整路径
+                        'directory' => $fileDir, // 文件所在目录
+                        'size' => $fileSize // 文件大小
+                    ];
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $result = array_merge($result, $this->getFilesRecursively($filePath));
+            }
+        }
+
+        return $result;
+    }
+
+
+}

+ 367 - 0
app/Console/Commands/Export/ExportTopicExchangeCommand.php

@@ -0,0 +1,367 @@
+<?php
+
+
+namespace App\Console\Commands\Export;
+
+
+use Illuminate\Console\Command;
+
+class ExportTopicExchangeCommand extends Command
+{
+    use ExportTrait;
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'export:exchangeTopic';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '题库解析';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $head = [
+            '题目类型',
+            '题目',
+            '选项A',
+            '选项B',
+            '选项c',
+            '选项D',
+            '选项E',
+            '正确答案',
+            '解析',
+            '正确率',
+            '考点'
+        ];
+
+        //$dataTest = file_get_contents();
+        //$this->createdFile('');
+        //$this->putRow($head);
+
+//        $needExchangeTopics = [
+//            [
+//                'fileName' => '1第一节 感冒',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\1第一节 感冒\\1第一节 感冒.txt"
+//            ],
+//            [
+//                'fileName' => '2第二节 咳嗽',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\2第二节 咳嗽\\2第二节 咳嗽.txt"
+//            ],
+//            [
+//                'fileName' => '3第三节 喘证',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\3第三节 喘证\\3第三节 喘证.txt"
+//            ],
+//            [
+//                'fileName' => '4第四节 肺胀',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\4第四节 肺胀\\4第四节 肺胀.txt"
+//            ],
+//            [
+//                'fileName' => '5第五节 心悸',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\5第五节 心悸\\5第五节 心悸.txt"
+//            ],
+//            [
+//                'fileName' => '6第六节 胸痹',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\6第六节 胸痹\\6第六节 胸痹.txt"
+//            ],
+//            [
+//                'fileName' => '7第七节 不寐',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\7第七节 不寐\\7第七节 不寐.txt"
+//            ],
+//            [
+//                'fileName' => '8第八节 胃痛',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\8第八节 胃痛\\8第八节 胃痛.txt"
+//            ],
+//            [
+//                'fileName' => '9第九节 泄泻',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\9第九节 泄泻\\9第九节 泄泻.txt"
+//            ],
+//            [
+//                'fileName' => '10第十节 便秘',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\10第十节 便秘\\10第十节 便秘.txt"
+//            ],
+//            [
+//                'fileName' => '11第十一节 头痛',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\11第十一节 头痛\\11第十一节 头痛.txt"
+//            ],
+//            [
+//                'fileName' => '12第十二节 眩晕',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\12第十二节 眩晕\\12第十二节 眩晕.txt"
+//            ],
+//            [
+//                'fileName' => '13第十三节 胁痛',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\13第十三节 胁痛\\13第十三节 胁痛.txt"
+//            ],
+//            [
+//                'fileName' => '14第十四节 中风',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\14第十四节 中风\\14第十四节 中风.txt"
+//            ],
+//            [
+//                'fileName' => '15第十五节 汗证',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\15第十五节 汗证\\15第十五节 汗证.txt"
+//            ],
+//            [
+//                'fileName' => '4第四节 肝功能检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\4第四节 肝功能检查\\4第四节 肝功能检查.txt"
+//            ],
+//            [
+//                'fileName' => '1第一节 血常规检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\1第一节 血常规检查\\1第一节 血常规检查.txt"
+//            ],
+//            [
+//                'fileName' => '2第二节 尿常规检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\2第二节 尿常规检查\\2第二节 尿常规检查.txt"
+//            ],
+//            [
+//                'fileName' => '3第三节 粪常规检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\3第三节 粪常规检查\\3第三节 粪常规检查.txt"
+//            ],
+//            [
+//                'fileName' => '4第四节 肝功能检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\4第四节 肝功能检查\\4第四节 肝功能检查.txt"
+//            ],
+//            [
+//                'fileName' => '1第一节 血常规检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\1第一节 血常规检查\\1第一节 血常规检查.txt"
+//            ],
+//            [
+//                'fileName' => '2第二节 尿常规检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\2第二节 尿常规检查\\2第二节 尿常规检查.txt"
+//            ],
+//            [
+//                'fileName' => '3第三节 粪常规检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\3第三节 粪常规检查\\3第三节 粪常规检查.txt"
+//            ],
+//            [
+//                'fileName' => '4第四节 肝功能检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\4第四节 肝功能检查\\4第四节 肝功能检查.txt"
+//            ],
+//            [
+//                'fileName' => '3第三节 粪常规检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\3第三节 粪常规检查\\3第三节 粪常规检查.txt"
+//            ],
+//            [
+//                'fileName' => '4第四节 肝功能检查',
+//                'data'     => "E:\\鸭题库数据\中药综\\5第五章 中医内科常见病的辨证论治\\4第四节 肝功能检查\\4第四节 肝功能检查.txt"
+//            ],
+//
+//        ];
+
+        $directory = "E:\\鸭题库数据\\药综\\6第六章 常见病症的健康管理";
+
+
+        $needExchangeTopics = $this->getFilesRecursively($directory);
+//dd($needExchangeTopics);
+
+
+        $dataCount = count($needExchangeTopics);
+
+        $this->info('开始转换');
+        $this->info("总共有:{$dataCount} 节");
+        $bar = $this->output->createProgressBar($dataCount);
+        //$answerArray = ['A', 'B', 'C', 'D', 'E'];
+
+        foreach ($needExchangeTopics as $needExchangeTopic) {
+            $this->createdFile($needExchangeTopic['directory'], $needExchangeTopic['fileName']);
+            $this->putRow($head);
+
+            $data = file_get_contents($needExchangeTopic['data']);
+
+            $data = json_decode($data, true);
+
+            foreach ($data['data'] as $value) {
+
+                //题目类型
+                $style = $value['style'];
+                $styleName = $this->getStyleName($style);
+                //dd($styleName);
+                //题目
+                $title = '';
+                if (!empty($value['exerciseQuestionsTopicPo'])) {
+                    $title  = $value['exerciseQuestionsTopicPo']['content'] . "\r\n";
+                }
+                $title = $title  . $value['title'];
+
+                //解析
+                $explain = $value['explain'];
+
+                //准确率
+                $accuracy = $value['accuracy'];
+
+                //
+                $correctAnswerStr = ''; //正确选项Str
+                $correctAnswers = [];  //正确答案数据
+                $selectAContent = '';
+                $selectBContent = '';
+                $selectCContent = '';
+                $selectDContent = '';
+                $selectEContent = '';
+
+                //考点
+                $examPointStr = '';
+                $examPointNames = [];
+                $examPoints = $value['points'];
+                foreach ($examPoints as $examPoint) {
+                    $examPointNames [] = $examPoint['name'];
+                }
+                $examPointStr = implode(',', $examPointNames);//考点
+                //dd($examPointStr);
+
+
+                //$answerFlag = false;
+
+//                $answer = $data['answer'];
+//
+//                if (in_array($answer, $answerArray)) {
+//                    $answerFlag = true; //获取answer成功
+//                }
+
+
+                $questionCount = $value['questionCount'] ; //选项数量
+                //获取选项:
+                $questionsOptionsPos = $value['questionsOptionsPos'];
+
+                foreach ($questionsOptionsPos as $key => $item) {
+                    if ($key == 0 && $key <=  $questionCount-1) {
+                        $selectAContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'A';
+                        }
+                    } elseif ($key == 1 && $key <=  $questionCount-1) {
+                        $selectBContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'B';
+                        }
+                    } elseif ($key == 2 && $key <=  $questionCount-1) {
+                        $selectCContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'C';
+                        }
+                    } elseif ($key == 3 && $key <=  $questionCount-1) {
+                        $selectDContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'D';
+                        }
+                    } elseif ($key == 4 && $key <=  $questionCount-1) {
+                        $selectEContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'E';
+                        }
+                    }
+                }
+                $correctAnswerStr = implode(',', $correctAnswers); //正确答案
+                //dd($correctAnswerStr);
+
+
+
+                $exportData = [];
+
+                $exportData['style'] = $styleName;
+                $exportData['title'] = $title;
+
+                $exportData['selectA'] =  $selectAContent;
+                $exportData['selectB'] =  $selectBContent;
+                $exportData['selectC'] =  $selectCContent;
+                $exportData['selectD'] =  $selectDContent;
+                $exportData['selectE'] =  $selectEContent;
+                $exportData['answer']  = $correctAnswerStr;
+                $exportData['explain'] = $explain;
+                $exportData['accuracy'] = $accuracy;
+                $exportData['point']    = $examPointStr;
+                $this->putRow($exportData);
+
+            }
+
+            $this->closeFile();
+
+            $bar->advance();
+        }
+
+        $bar->finish();
+
+        $this->info('');
+        $this->info('导出完成');
+
+
+
+    }
+
+    public function getStyleName($style)
+    {
+        if ($style == 6) {
+            $styleName = '配伍题';
+        } elseif ($style == 2) {
+            $styleName = '单选题';
+        } elseif ($style == 3) {
+            $styleName = '多选题';
+        } else {
+            $styleName = $style;
+        }
+
+        return $styleName;
+    }
+
+    public function getFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        $result = [];
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 过滤掉文件名中包含“数据源”的文件
+                if (strpos($file, '数据源') === false) {
+                    // 获取文件名的主名称(去除.txt后缀)
+                    $fileNameWithoutExtension = pathinfo($file, PATHINFO_FILENAME);
+                    // 获取文件所在的目录路径
+                    $fileDir = dirname($filePath);
+                    // 获取文件的大小
+                    $fileSize = filesize($filePath);
+                    $result[] = [
+                        'fileName' => $fileNameWithoutExtension, // 文件名(去除.txt后缀)
+                        'data' => $filePath, // 完整路径
+                        'directory' => $fileDir, // 文件所在目录
+                        'size' => $fileSize // 文件大小
+                    ];
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $result = array_merge($result, $this->getFilesRecursively($filePath));
+            }
+        }
+
+        return $result;
+    }
+
+
+}

+ 60 - 0
app/Console/Commands/Export/ExportTrait.php

@@ -0,0 +1,60 @@
+<?php
+
+
+namespace App\Console\Commands\Export;
+
+use Carbon\Carbon;
+use function EasyWeChat\Kernel\Support\str_random;
+
+trait ExportTrait
+{
+    public $filename;
+
+    public $path;
+
+    public $fd;
+
+    public function createdFile($directory, $name){
+        //$this->filename = Carbon::today()->toDateString().'-'.str_random('5').'-'.$name.'.xlsx';
+        $this->filename = $name.'.xlsx';
+        //$this->path = storage_path('exports/' . $this->filename);
+        $this->path =  $directory . '/' . $this->filename;// storage_path('exports/' . $this->filename);
+        //$this->path = $path;
+        $dirPath = dirname($this->path);
+
+        // 确保目录存在
+//        if (!is_dir($dirPath)) {
+//            mkdir($dirPath, 0777, true);
+//        }
+
+        if (is_dir($dirPath)) {
+            $this->fd = fopen($this->path, 'w+');
+            if ($this->fd) {
+                //add BOM to fix UTF-8 in Excel
+                fputs($this->fd, $bom = (chr(0xEF) . chr(0xBB) . chr(0xBF)));
+                /* $table = $data;
+                 foreach ($table as $fields) {
+                     fputcsv($fd, $fields);
+                 }*/
+                //fclose($fd);
+            }
+        }
+    }
+
+    public function putRow($row)
+    {
+        if (!$this->fd) {
+            $this->fd = fopen($this->path, 'a+');
+        }
+        return fputcsv($this->fd, $row);
+    }
+
+    public function closeFile()
+    {
+        if ($this->fd) {
+            fclose($this->fd);
+            $this->fd = null;
+        }
+    }
+
+}

+ 39 - 0
app/Console/Commands/HistorySyncDrugReportCommand.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\DrugReportInfo;
+use App\Servers\HistoryDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+use App\Facades\Servers\Logs\Log;
+
+class HistorySyncDrugReportCommand extends Command
+{
+    protected $signature = 'drug-report-history';
+
+    protected $description = '操作日志同步更新药检报告数据';
+
+    public function handle(HistoryDrugReportService $service): int
+    {
+        Log::info('drug-report-history','操作历史同步开始');
+        $time           = time();
+        $lastTime      = cache('drug:report:history:time');
+        $lastTime       = 0;
+        if (empty($lastTime)){
+            $startTime = Carbon::yesterday()->startOfDay()->format('Y-m-d');
+        }else{
+            $startTime = date('Y-m-d', $lastTime);
+        }
+        $endTime = date('Y-m-d', $time);
+        $page = 1;
+        $pageSize = 20;
+        Log::info('drug-report-history','操作历史同步开始', ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        $count = $service->syncDrugReport( $startTime, $endTime, $page, $pageSize, $lastTime);
+        cache(['drug:report:history:time'=>$time]);
+        Log::info('drug-report-history','操作历史同步结束:'.$count, ['startTime'=>$startTime, 'endTime'=>$endTime]);
+
+        return 0;
+    }
+
+}

+ 35 - 0
app/Console/Commands/IncrementSyncDrugReportAssCommand.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\DrugReportInfo;
+use App\Servers\IncrementDrugReportAssService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+use App\Facades\Servers\Logs\Log;
+
+class IncrementSyncDrugReportAssCommand extends Command
+{
+    protected $signature = 'increment-drug-report-ass';
+
+    protected $description = '增量同步上游出库单数据';
+
+    public function handle(IncrementDrugReportAssService $service): int
+    {
+        Log::info('increment_drug','增量同步开始');
+        $startTime      = cache('increment:drug:ass:time');
+        if (empty($startTime)){
+            $startTime = Carbon::now()->startOfDay();
+        }
+        $source = 'querydrugreportass';
+        $endTime = Carbon::now();
+        Log::info('increment_drug','增量同步开始', ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        $page = 1;
+        $pageSize = 20;
+        $data = $service->syncDrugReport($source, $startTime, $endTime, $page, $pageSize);
+        Log::info('increment_drug','增量同步结束'. ",共{$data['total_num']},处理 {$data['total']} 条记录!", ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        cache(['increment:drug:ass:time'=>$endTime]);
+        return 0;
+    }
+
+}

+ 37 - 0
app/Console/Commands/IncrementSyncDrugReportCommand.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\DrugReportInfo;
+use App\Servers\IncrementDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+use App\Facades\Servers\Logs\Log;
+
+class IncrementSyncDrugReportCommand extends Command
+{
+    protected $signature = 'increment-drug-report';
+
+    protected $description = '增量同步药检报告数据';
+
+    public function handle(IncrementDrugReportService $service): int
+    {
+        Log::info('increment_drug_report','增量同步开始');
+        $startTime      = cache('increment:drug:report:time');
+        if (empty($startTime)){
+            $startTime = Carbon::now()->startOfDay();
+        }
+        $source = 'querydrugreport';
+        $endTime = Carbon::now();
+        Log::info('increment_drug_report','增量同步开始', ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        $page = 1;
+        $pageSize = 20;
+        $data = $service->syncDrugReport($source, $startTime, $endTime, $page, $pageSize,2);
+        $sealData = $service->syncDrugReport($source, $startTime, $endTime, $page, $pageSize,1);
+        Log::info('increment_drug_report','增量同步已盖章结束'. ",共{$data['total_num']},处理 {$data['total']} 条记录!", ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        Log::info('increment_drug_report','增量同步未盖章结束'. ",共{$sealData['total_num']},处理 {$sealData['total']} 条记录!", ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        cache(['increment:drug:report:time'=>$endTime]);
+        return 0;
+    }
+
+}

+ 34 - 0
app/Console/Commands/SignedSyncDrugReportCommand.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Servers\SignedDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+use App\Facades\Servers\Logs\Log;
+
+class SignedSyncDrugReportCommand extends Command
+{
+    protected $signature = 'drug-report-signed';
+
+    protected $description = '查询一段时间内已经签收的单据 同步更新药检报告数据';
+
+    public function handle(SignedDrugReportService $service): int
+    {
+        Log::info('drug-report-signed','已经签收同步开始');
+        $startTime      = cache('drug:report:signed:time');
+        if (empty($startTime)){
+            $startTime = Carbon::now()->startOfDay();
+        }
+        $endTime = date('Y-m-d H:i:s', time());
+        $page = 1;
+        $pageSize = 20;
+        Log::info('drug-report-signed','已经签收同步开始', ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        $count = $service->syncDrugReport( $startTime, $endTime, $page, $pageSize);
+        cache(['drug:report:signed:time'=>$endTime]);
+        Log::info('drug-report-signed','已经签收同步结束:'.$count, ['startTime'=>$startTime, 'endTime'=>$endTime]);
+
+        return 0;
+    }
+
+}

+ 277 - 0
app/Console/Commands/SyncDatabaseCommand.php

@@ -0,0 +1,277 @@
+<?php
+
+
+namespace App\Console\Commands;
+
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+use App\Facades\Servers\Logs\Log;
+
+class SyncDatabaseCommand extends Command
+{
+    /**
+     * 任务名称
+     *
+     * @var string
+     */
+    protected $signature = 'sync:database';
+
+    /**
+     * 任务描述
+     *
+     * @var string
+     */
+    protected $description = '开邻的数据复制到Saas';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    public function handle()
+    {
+        $dataBaseInfos = [
+            //kailin
+            'remote' => [
+                'host'          => '39.108.116.125',
+                'port'          => 3306,
+                'db_name'       => 'kailin_migrate_test',
+                'db_username'   => 'kailin_migrate_test',
+                'db_password'   => '4bCtNLjfsLJ5Fhbw'
+            ],
+
+            //saas-system
+            'system' => [
+                'host'          => '120.24.49.2',
+                'port'          => 3306,
+                'db_name'       => 'system',
+                'db_username'   => 'system',
+                'db_password'   => 'tCDnjKmRRPsHikZw'
+            ],
+
+            //saas-company
+            'company' => [
+                'host'          => '120.24.49.2',
+                'port'          => 3306,
+                'db_name'       => 'company',
+                'db_username'   => 'company',
+                'db_password'   => 'ifAJ7XaDGBZBpLdc'
+            ]
+
+            //测试用 start
+//            'system' => [
+//                //'host'          => '127.0.0.1',
+//                'host'          => '39.108.116.125',
+//                'port'          => 3306,
+//                'db_name'       => 'system_migrate_test',
+//                'db_username'   => 'system_migrate_test',
+//                'db_password'   => 'wwaxK4AxTkZe3tBa'
+//            ],
+//
+//            'company' => [
+//                'host'          => '39.108.116.125',
+//                'port'          => 3306,
+//                'db_name'       => 'company_migrate_0620',
+//                'db_username'   => 'company_migrate_0620',
+//                'db_password'   => 'JhbycESDw7HDMfh2'
+//            ]
+
+            //测试用 end
+
+
+
+
+        ];
+
+        $remoteConnectionConfig = [
+            'driver'        => 'mysql',
+            'host'          => $dataBaseInfos['remote']['host'],
+            'port'          => $dataBaseInfos['remote']['port'],
+            'database'      => $dataBaseInfos['remote']['db_name'],
+            'username'      => $dataBaseInfos['remote']['db_username'],
+            'password'      => $dataBaseInfos['remote']['db_password'],
+            'charset'       => 'utf8mb4',
+            'collation'     => 'utf8mb4_unicode_ci',
+            'unix_socket'   => env('DB_SOCKET', ''),
+            'prefix'        => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.remote", $remoteConnectionConfig);
+
+        $systemConnectionConfig = [
+            'driver'        => 'mysql',
+            'host'          => $dataBaseInfos['system']['host'],
+            'port'          => $dataBaseInfos['system']['port'],
+            'database'      => $dataBaseInfos['system']['db_name'],
+            'username'      => $dataBaseInfos['system']['db_username'],
+            'password'      => $dataBaseInfos['system']['db_password'],
+            'charset'       => 'utf8mb4',
+            'collation'     => 'utf8mb4_unicode_ci',
+            'unix_socket'   => env('DB_SOCKET', ''),
+            'prefix'        => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.system", $systemConnectionConfig);
+
+        $companyConnectionConfig = [
+            'driver'        => 'mysql',
+            'host'          => $dataBaseInfos['company']['host'],
+            'port'          => $dataBaseInfos['company']['port'],
+            'database'      => $dataBaseInfos['company']['db_name'],
+            'username'      => $dataBaseInfos['company']['db_username'],
+            'password'      => $dataBaseInfos['company']['db_password'],
+            'charset'       => 'utf8mb4',
+            'collation'     => 'utf8mb4_unicode_ci',
+            'unix_socket'   => env('DB_SOCKET', ''),
+            'prefix'        => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+        // 获取远程数据库的表名
+        $tables = DB::connection('remote')->select('SHOW TABLES');
+
+        $systemTables = ['kailin_business']; // 远端是kailin_business 在系统库中是kailin_shop
+        $notExecuteTables = [
+            //'kailin_article', 'kailin_article_event',
+            //'kailin_lottery_recruitment', 'kailin_lottery_recruitment_record',
+            //'kailin_lottery_recruitment_reward', 'kailin_redpacket_active', 'kailin_redpacket_active_record',
+            //'kailin_redpacket_active_reward',
+            'kailin_short_link', 'kailin_video_answer',
+            'kailin_video_course', 'kailin_video_course_service', 'kailin_video_course_type',
+            'kailin_video_exam_answer', 'kailin_video_exam_question', 'kailin_video_exam_record',
+            'kailin_video_learn_answer', 'kailin_video_learn_question', 'kailin_video_learn_record',
+            'kailin_video_question', 'kailin_video_vip', 'kailin_weiban_external',
+            'kailin_work_external', 'kailin_weiban_follow', 'kailin_weiban_qrcode',
+            'kailin_weiban_sync', 'kailin_weiban_taglist', 'kailin_weiban_tags',
+            'kailin_work_state', 'kailin_work_sync', 'kailin_work_tag',
+            'kailin_work_user',
+            'kailin_admin', 'kailin_admin_history', 'kailin_admin_rule',
+            'kailin_auth_group', 'kailin_auth_group_access', 'kailin_auth_rule',
+            'kailin_city', 'kailin_company', 'kailin_menu',
+            'kailin_score_orders_product', 'kailin_pay_city'
+        ];
+
+        $dataCount = count($tables);
+
+        $this->info("总共有数据表:{$dataCount} 个");
+
+        $this->info('开始处理...');
+
+        $bar = $this->output->createProgressBar($dataCount);
+
+        foreach ($tables as $table) {
+            $tableName = (array) $table;
+
+            $tableName = array_values($tableName)[0];
+            //测试代码start
+//            if ($tableName == 'kailin_business') {
+//                // 获取远程表的数据
+//                $data = DB::connection('remote')->table($tableName)->get();
+//
+//                foreach ($data as $row) {
+//                    $insertData = (array)$row;
+//                    $insertData['id'] = $row['id'];
+//
+//                    DB::connection('system')->table('kailin_shop')->updateOrInsert(
+//                        ['id' => $row['id']], // 检查是否存在的条件
+//                        $insertData         // 需要插入或更新的数据
+//                    );
+//                    Log::info('sync_database', "Synced data from remote table {$tableName} to local system database: " . json_encode($row));
+//                }
+//            } else {
+//                $bar->advance();
+//            }
+            //测试代码end
+
+            if (!in_array($tableName, $notExecuteTables)) {
+                // 获取远程表的数据
+                $data = DB::connection('remote')->table($tableName)->get();
+                $this->info("\n");
+                $this->info("当前处理的数据表:{$tableName} ");
+                // 删除本地数据库中的数据
+                if (in_array($tableName, $systemTables)) {
+                    if ($tableName == 'kailin_business') {
+                        //kailin_shop表的数据不删除
+                        //$this->truncateTable('kailin_shop', 'system');
+                    } else {
+                        $this->truncateTable($tableName, 'system');
+                    }
+                } else {
+                    $this->truncateTable($tableName, 'company');
+                }
+
+                // 同步到本地数据库
+                $tableCountData = count($data);
+                $this->info("当前数据表的数据条数:{$tableCountData} ");
+                $bar1 = $this->output->createProgressBar($tableCountData);
+                foreach ($data as $row) {
+                    try {
+                        if (in_array($tableName, $systemTables)) {
+                            if ($tableName == 'kailin_business') {
+                                //DB::connection('system')->table('kailin_shop')->insert((array)$row);
+                                $insertData = (array)$row;
+                                $insertData['id'] = $row['id'];
+                                DB::connection('system')->table('kailin_shop')->updateOrInsert(
+                                    ['id' => $row['id']], // 检查是否存在的条件
+                                    $insertData         // 需要插入或更新的数据
+                                );
+                            } else {
+                                DB::connection('system')->table($tableName)->insert((array)$row);
+                            }
+                            Log::info('sync_database', "Synced data from remote table {$tableName} to local system database: " . json_encode($row));
+                        } else {
+                            DB::connection('company')->table($tableName)->insert((array) $row);
+                            Log::info('sync_database',"Synced data from remote table {$tableName} to local company database: " . json_encode($row));
+                        }
+
+                    } catch (\Exception $e) {
+                        Log::error('sync_database_error',"Failed to sync data from remote table {$tableName}: " . $e->getMessage());
+                    }
+                    $bar1->advance();
+                }
+                $bar1->finish();
+                $bar->advance();
+            } else {
+                $bar->advance();
+            }
+        }
+
+        $bar->finish();
+        $this->info('');
+        $this->info('处理完成');
+
+    }
+
+
+    protected function truncateTable($tableName, $connection)
+    {
+        try {
+            DB::connection($connection)->table($tableName)->truncate();
+            Log::info('sync_database',"Truncated table {$tableName} in {$connection} database.");
+        } catch (\Exception $e) {
+            Log::error('sync_database',"Failed to truncate table {$tableName} in {$connection} database: " . $e->getMessage());
+        }
+    }
+
+
+}

+ 35 - 0
app/Console/Commands/SyncDrugReportAssCommand.php

@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\DrugReportInfo;
+use App\Servers\SyncDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+
+class SyncDrugReportAssCommand extends Command
+{
+    protected $signature = 'sync-drug-report-ass';
+
+    protected $description = '同步药检报告数据';
+    public function __construct()
+    {
+        parent::__construct();
+    }
+    public function handle(SyncDrugReportService $service): int
+    {
+        echo "开始同步药检报告数据...", PHP_EOL;
+        $isFull     = true;
+        $source     = 'querydrugreportass';
+        $startTime  = '2025-12-01 00:00:00';
+        $endTime    = '2025-12-17 00:00:00';
+        $page       = 1;
+        $pageSize   = 20;
+        $data       = $service->syncDrugReport($source, $isFull, $startTime, $endTime, $page, $pageSize);
+
+        echo $isFull ? "全量同步结束" : "增量同步结束", ",共{$data['count']},处理 {$data['total']} 条记录!", PHP_EOL;
+
+        return 0;
+    }
+
+}

+ 37 - 0
app/Console/Commands/SyncDrugReportCommand.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\DrugReportInfo;
+use App\Servers\SyncDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+
+class SyncDrugReportCommand extends Command
+{
+    protected $signature = 'sync-drug-report';
+
+    protected $description = '同步药检报告数据';
+    public function __construct()
+    {
+        parent::__construct();
+    }
+    public function handle(SyncDrugReportService $service): int
+    {
+        echo "开始同步药检报告数据...", PHP_EOL;
+        $isFull     = true;
+        $source     = 'querysealdrugreport';
+        $startTime  = '2024-01-01 00:00:00';
+        $endTime    = '';
+        $page       = 1;
+        $pageSize   = 20;
+        $data       = $service->syncDrugReport($source, $isFull, $startTime, $endTime, $page, $pageSize,1);
+        $SealData   = $service->syncDrugReport($source, $isFull, $startTime, $endTime, $page, $pageSize,2);
+
+        echo $isFull ? "全量同步结束" : "增量同步结束", ",共{$data['count']},处理 {$data['total']} 条记录!", PHP_EOL;
+        echo $isFull ? "全量同步结束" : "增量同步结束", ",共{$SealData['count']},处理 {$SealData['total']} 条记录!", PHP_EOL;
+
+        return 0;
+    }
+
+}

+ 42 - 0
app/Console/Commands/UpdateDrugReportAssCommand.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Facades\Servers\Logs\Log;
+use App\Models\DrugReportInfo;
+use App\Servers\UpdateDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+
+class UpdateDrugReportAssCommand extends Command
+{
+    protected $signature = 'update-drug-report-ass';
+
+    protected $description = '同步药检报告数据';
+    public function __construct()
+    {
+        parent::__construct();
+    }
+    public function handle(UpdateDrugReportService $service): int
+    {
+        echo "开始同步药检报告数据...", PHP_EOL;
+        $endDateTime    = cache('increment:drug:ass:time');
+        if (!$endDateTime){
+            $endTime = time();
+        }else{
+            $endTime = strtotime($endDateTime);
+        }
+        $startTime  = ($endTime - 86400 * 30);
+        $startTime  = date('Y-m-d H:i:s', $startTime);
+        $endTime    = date('Y-m-d H:i:s', $endTime);
+        $page       = 1;
+        $pageSize   = 20;
+        Log::info('update-drug-report-ass','更新同步开始', ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        $data       = $service->syncDrugReport($startTime, $endTime, $page, $pageSize);
+        Log::info('update-drug-report-ass','更新同步结束'. ",共{$data['count']},处理 {$data['total']} 条记录!", ['startTime'=>$startTime, 'endTime'=>$endTime]);
+        echo "共{$data['count']},处理 {$data['total']} 条记录!", PHP_EOL;
+
+        return 0;
+    }
+
+}

+ 77 - 0
app/Console/Commands/UpdateTaskCommand.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\DrugReportInfo;
+use App\Services\SyncDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+
+class UpdateTaskCommand extends Command
+{
+    protected $signature = 'update-task';
+
+    protected $description = '批量更新药检报告域名';
+    public function __construct()
+    {
+        parent::__construct();
+    }
+    public function handle()
+    {
+        $oldDomain = 'https://drugreport.oss-cn-shenzhen.aliyuncs.com';
+        $newDomain = 'https://file.snowkirin.com.cn';
+        $batchSize = 5000; // 每批 5000 条
+        $totalUpdated = 0;
+
+        // 先获取符合条件的总记录数
+        $totalCount = DB::table('drug_report_ass')
+            ->where('report_url', 'like', "{$oldDomain}%")
+            ->count();
+        var_dump($totalCount);exit();
+        if ($totalCount === 0) {
+            $this->info('没有需要更新的记录');
+            return 0;
+        }
+
+        $this->info("共发现 {$totalCount} 条需要更新的记录");
+        $this->info("开始分批更新,每批 {$batchSize} 条...");
+
+        // 创建进度条
+        $bar = $this->output->createProgressBar($totalCount);
+        $bar->start();
+
+        try {
+            // 使用分块更新,避免一次性锁表
+            do {
+                $affected = DB::table('drug_report_ass')
+                    ->where('report_url', 'like', "{$oldDomain}%")
+                    ->limit($batchSize)
+                    ->update([
+                        'report_url' => DB::raw(
+                            "REPLACE(report_url, '{$oldDomain}', '{$newDomain}')"
+                        )
+                    ]);
+
+                $totalUpdated += $affected;
+                $bar->advance($affected);
+
+                // 可选:每批之间短暂休眠,减轻数据库压力
+                // usleep(100000); // 0.1 秒
+
+            } while ($affected > 0);
+
+            $bar->finish();
+            $this->newLine();
+            $this->info("✅ 更新完成,共处理 {$totalUpdated} 条记录");
+
+        } catch (\Exception $e) {
+            $bar->finish();
+            $this->error("❌ 更新失败:{$e->getMessage()}");
+            return 1;
+        }
+
+        return 0;
+    }
+
+}

+ 286 - 0
app/Console/Commands/WashData/InitQuestionChapterCommand.php

@@ -0,0 +1,286 @@
+<?php
+
+
+namespace App\Console\Commands\WashData;
+
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+
+class InitQuestionChapterCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'washdata:initChapter';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '题库解析';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+
+        //连接数据库
+
+        $dataBaseInfos = [
+            //saas-company
+            'company' => [
+                'host'          => '39.108.116.125',
+                'port'          => 3306,
+                'db_name'       => 'company_migrate_test',
+                'db_username'   => 'company_migrate_test',
+                'db_password'   => '7dSTAAxa64AkHB8R'
+            ]
+        ];
+
+        $companyConnectionConfig = [
+            'driver'        => 'mysql',
+            'host'          => $dataBaseInfos['company']['host'],
+            'port'          => $dataBaseInfos['company']['port'],
+            'database'      => $dataBaseInfos['company']['db_name'],
+            'username'      => $dataBaseInfos['company']['db_username'],
+            'password'      => $dataBaseInfos['company']['db_password'],
+            'charset'       => 'utf8mb4',
+            'collation'     => 'utf8mb4_unicode_ci',
+            'unix_socket'   => env('DB_SOCKET', ''),
+            'prefix'        => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+//        $dataCount = count($tables);
+//
+//        $this->info("总共有数据表:{$dataCount} 个");
+//
+//        $this->info('开始处理...');
+//
+//        $bar = $this->output->createProgressBar($dataCount);
+
+        $tableName = 'kailin_question_chapter';
+
+        $insertDataArray = [
+            [
+                'name'          => '法规',
+                'parent_id'     => 1,
+                'status'        => 0,
+                'weight'        => 0,
+                'level'         => 1,
+                'created_id'    => 0,
+                'updated_id'    => 0,
+                'insert_time'   => time(),
+                'update_time'   => time(),
+            ]
+        ];
+
+        $needWashDataArray = [
+            [
+                'chapterName' => '中药综',
+                'filePath'    => 'E:\\鸭题库数据初始化目录\\中药综'
+            ]
+
+        ];
+
+        foreach ($needWashDataArray as $value) {
+            $parentId = DB::connection('company')->table($tableName)->where('name', $value['chapterName'])->value('id');
+            //获取路径下的子级文件夹名称
+            $returnFiles = [];
+            $this->getSubdirectories($value['filePath'], 0, $value['chapterName'], $returnFiles);
+//dd($returnFiles);
+            foreach ($returnFiles as $returnFile) {
+                if ($returnFile['level'] == 1) {
+                    if ($returnFile['parent'] == $value['chapterName']) {
+                        $insertData = [
+                            'name'          => $returnFile['name'],
+                            'parent_id'     => $parentId,
+                            'status'        => 0,
+                            'weight'        => 0,
+                            'level'         => $returnFile['level'] + 1,
+                            'created_id'    => 0,
+                            'updated_id'    => 0,
+                            'insert_time'   => time(),
+                            'update_time'   => time(),
+                        ];
+                        if (!DB::connection('company')->table($tableName)->where('name', $returnFile['name'])->exists()) {
+                            DB::connection('company')->table($tableName)->insert($insertData);
+                        }
+                    }
+                }
+
+                $secondParentId = DB::connection('company')->table($tableName)->where('name', $returnFile['parent'])->value('id');
+                if ($returnFile['level'] == 2) {
+                    $insertData = [
+                        'name'          => $returnFile['name'],
+                        'parent_id'     => $secondParentId,
+                        'status'        => 0,
+                        'weight'        => 0,
+                        'level'         => $returnFile['level'] + 1,
+                        'created_id'    => 0,
+                        'updated_id'    => 0,
+                        'insert_time'   => time(),
+                        'update_time'   => time(),
+                    ];
+                    DB::connection('company')->table($tableName)->insert($insertData);
+                }
+
+                $thirdParentId = DB::connection('company')->table($tableName)->where('name', $returnFile['parent'])->value('id');
+                if ($returnFile['level'] == 3) {
+                    $insertData = [
+                        'name'          => $returnFile['name'],
+                        'parent_id'     => $thirdParentId,
+                        'status'        => 0,
+                        'weight'        => 0,
+                        'level'         => $returnFile['level'] + 1,
+                        'created_id'    => 0,
+                        'updated_id'    => 0,
+                        'insert_time'   => time(),
+                        'update_time'   => time(),
+                    ];
+                    DB::connection('company')->table($tableName)->insert($insertData);
+                }
+
+            }
+
+            //dd($returnFiles);
+        }
+
+
+
+//        $parentId = DB::connection('company')->table($tableName)->where('level', 1)->pluck('id')->toArray();
+//
+//        dd($parentId);
+
+        //DB::connection('company')->table($tableName)->insert($insertDataArray);
+
+        $this->info('success~');
+
+    }
+
+
+//    function getSubdirectories($directory, $level = 0, &$result = []) {
+//        // 打开目录
+//        if (!is_dir($directory)) {
+//            return; // 如果不是目录,直接返回
+//        }
+//
+//        // 获取目录中的所有条目
+//        $entries = scandir($directory);
+//
+//        // 遍历目录中的每个条目
+//        foreach ($entries as $entry) {
+//            // 跳过当前目录(.)和上级目录(..)
+//            if ($entry === '.' || $entry === '..') {
+//                continue;
+//            }
+//
+//            // 构造完整的子目录路径
+//            $subdir = $directory . DIRECTORY_SEPARATOR . $entry;
+//
+//            // 检查是否是目录
+//            if (is_dir($subdir)) {
+//                // 将目录名称和级数存储到结果数组中
+//                $result[] = ['name' => $entry, 'level' => $level + 1];
+//
+//                // 递归调用,进入下一级目录
+//                $this->getSubdirectories($subdir, $level + 1, $result);
+//            }
+//        }
+//    }
+
+//    function getSubdirectories($directory, $level = 0, $parentName = '', &$result = []) {
+//        // 打开目录
+//        if (!is_dir($directory)) {
+//            return; // 如果不是目录,直接返回
+//        }
+//
+//        // 获取目录中的所有条目
+//        $entries = scandir($directory);
+//
+//        // 遍历目录中的每个条目
+//        foreach ($entries as $entry) {
+//            // 跳过当前目录(.)和上级目录(..)
+//            if ($entry === '.' || $entry === '..') {
+//                continue;
+//            }
+//
+//            // 构造完整的子目录路径
+//            $subdir = $directory . DIRECTORY_SEPARATOR . $entry;
+//
+//            // 检查是否是目录
+//            if (is_dir($subdir)) {
+//                // 将目录名称、级数和父级目录名称存储到结果数组中
+//                $result[] = [
+//                    'name' => $entry,
+//                    'level' => $level + 1,
+//                    'parent' => $parentName
+//                ];
+//
+//                // 递归调用,进入下一级目录
+//                $this->getSubdirectories($subdir, $level + 1, $entry, $result);
+//            }
+//        }
+//    }
+    function getSubdirectories($directory, $level = 0, $parentName = '', &$result = []) {
+        // 打开目录
+        if (!is_dir($directory)) {
+            return; // 如果不是目录,直接返回
+        }
+
+        // 获取目录中的所有条目
+        $entries = scandir($directory);
+
+        // 遍历目录中的每个条目
+        foreach ($entries as $entry) {
+            // 跳过当前目录(.)和上级目录(..)
+            if ($entry === '.' || $entry === '..') {
+                continue;
+            }
+
+            // 构造完整的子目录路径
+            $subdir = $directory . DIRECTORY_SEPARATOR . $entry;
+
+            // 检查是否是目录
+            if (is_dir($subdir)) {
+                // 过滤掉目录名称中的数字
+                $filteredName = preg_replace('/\d+/', '', $entry);
+
+                // 将目录名称、级数和父级目录名称存储到结果数组中
+                $result[] = [
+                    'name' => $filteredName,
+                    'level' => $level + 1,
+                    'parent' => $parentName
+                ];
+
+                // 递归调用,进入下一级目录
+                $this->getSubdirectories($subdir, $level + 1, $filteredName, $result);
+            }
+        }
+    }
+
+
+}

+ 230 - 0
app/Console/Commands/WashData/InitQuestionPointCommand.php

@@ -0,0 +1,230 @@
+<?php
+
+
+namespace App\Console\Commands\WashData;
+
+
+use GuzzleHttp\Client as HttpClient;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Schema;
+
+class InitQuestionPointCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'washdata:initPoint';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '考点数据初始化';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        //连接数据库
+
+        $dataBaseInfos = [
+            //saas-company
+            'company' => [
+                'host' => '39.108.116.125',
+                'port' => 3306,
+                'db_name' => 'company_61',
+                'db_username' => 'company_61',
+                'db_password' => 'N35fYCPHziAAGd4L'
+            ]
+        ];
+
+        $companyConnectionConfig = [
+            'driver' => 'mysql',
+            'host' => $dataBaseInfos['company']['host'],
+            'port' => $dataBaseInfos['company']['port'],
+            'database' => $dataBaseInfos['company']['db_name'],
+            'username' => $dataBaseInfos['company']['db_username'],
+            'password' => $dataBaseInfos['company']['db_password'],
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_unicode_ci',
+            'unix_socket' => env('DB_SOCKET', ''),
+            'prefix' => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+        $tableName                  = 'kailin_question_point';
+        $tableNameChapterPoint      = 'kailin_question_chapter_point';
+        $tableNameChapter           = 'kailin_question_chapter';
+
+
+
+        $directory = "E:\\鸭题库数据初始化考点\\中药综\\13第十三章 中药用药安全";
+
+        $needExchangeTopics = $this->getFilesRecursively($directory);
+//dd($needExchangeTopics);
+
+        $dataCount = count($needExchangeTopics);
+
+        $this->info('开始转换');
+        $this->info("总共有:{$dataCount} 节");
+        $bar = $this->output->createProgressBar($dataCount);
+
+        foreach ($needExchangeTopics as $needExchangeTopic) {
+            $chapterName =  $needExchangeTopic['fileName'];
+            $chapterId  = DB::connection('company')->table($tableNameChapter)->where('name', $chapterName)->value('id');
+
+            $this->info('获取章节id =' . $chapterId);
+            if (empty($chapterId)) {
+                $this->info('获取章节id失败!');
+                $this->info('章节名称='.$chapterName);
+                return ;
+            }
+
+            $data = file_get_contents($needExchangeTopic['data']);
+
+            $data = json_decode($data, true);
+
+            if (empty($data['data'])) {
+                continue;
+            }
+
+            $pointIds = [];
+
+            foreach ($data['data'] as $value) {
+                $pointIds [] = $value['pointId'];
+            }
+            //dd($pointIds);
+
+            foreach ($pointIds as $pointId) {
+                $http = new HttpClient(['verify' => false]);
+                //https://app.yatiku.com/youxue/api/v2/back/exercise/getPointInfo?pointId=898
+                $pointApi = 'https://app.yatiku.com/youxue/api/v2/back/exercise/getPointInfo?' . http_build_query([
+                        'pointId' => $pointId
+                    ]);
+
+                //获取考点信息:
+                $pointResult = $http->get($pointApi);
+
+                $pointInfos = json_decode($pointResult->getBody()->getContents(), JSON_UNESCAPED_UNICODE);
+
+                $pointInfos = $pointInfos['data'];
+
+                $stars = 0;
+                $difficultyStars = 0;
+
+                if (isset($pointInfos['stars']) && !empty($pointInfos['stars'])) {
+                    $stars = $pointInfos['stars'];
+                }
+
+                if (isset($pointInfos['difficultyStars']) && !empty($pointInfos['difficultyStars'])) {
+                    $difficultyStars = $pointInfos['difficultyStars'];
+                }
+
+//dd($pointInfos['difficultyStars']);
+                $insertData['name']                 = isset($pointInfos['name']) ? $pointInfos['name'] : '' ;
+                $insertData['describe']             = isset($pointInfos['describe']) ? $pointInfos['describe'] : '';
+                $insertData['detail']               = isset($pointInfos['detail']) ? $pointInfos['detail'] : '';
+                $insertData['video']                = !empty($pointInfos['video']) ? $pointInfos['video'] : '';
+                $insertData['image']                = '';
+                $insertData['voice']                = '';
+                $insertData['status']               = 0;
+                $insertData['stars']                = $stars;
+                $insertData['difficultyStars']      = $difficultyStars;
+                $insertData['created_id']           = '';
+                $insertData['updated_id']           = '';
+                $insertData['insert_time']          = time();
+                $insertData['update_time']          = time();
+//dd($insertData);
+                $returnId = DB::connection('company')->table($tableName)->insertGetId($insertData);
+
+
+                $insertChapterPoint['point_id']     = $returnId;
+                $insertChapterPoint['chapter_id']   = $chapterId;
+                $insertChapterPoint['insert_time']  = time();
+                $insertChapterPoint['update_time']  = time();
+
+                //$tableNameChapterPoint
+
+                DB::connection('company')->table($tableNameChapterPoint)->insert($insertChapterPoint);
+
+                //dd($insertData);
+            }
+
+            $bar->advance();
+        }
+
+        $bar->finish();
+        $this->info('success!');
+    }
+
+    public function getFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        $result = [];
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 过滤掉文件名中包含“数据源”的文件
+                if (strpos($file, '数据源') === false) {
+                    // 获取文件名的主名称(去除.txt后缀)
+                    $fileNameWithoutExtension = pathinfo($file, PATHINFO_FILENAME);
+                    // 获取文件所在的目录路径
+                    $fileDir = dirname($filePath);
+
+                    // 去除文件名中的数字(如1, 2, 3...10, 11, 12等)
+                    // 去除文件名中的数字和连字符(-)
+                    $fileNameWithoutNumbers = preg_replace('/\d+/', '', $fileNameWithoutExtension);
+                    // 获取文件的大小
+                    $fileSize = filesize($filePath);
+                    $result[] = [
+                        'fileName' => $fileNameWithoutNumbers, // 文件名(去除.txt后缀)
+                        'data' => $filePath, // 完整路径
+                        'directory' => $fileDir, // 文件所在目录
+                        'size' => $fileSize // 文件大小
+                    ];
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $result = array_merge($result, $this->getFilesRecursively($filePath));
+            }
+        }
+
+        return $result;
+    }
+
+}

+ 275 - 0
app/Console/Commands/WashData/InitQuestionRealTopicCommand.php

@@ -0,0 +1,275 @@
+<?php
+
+
+namespace App\Console\Commands\WashData;
+
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+
+class InitQuestionRealTopicCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'washdata:initRealTopic';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '历年真题解析';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        //连接数据库
+
+        $dataBaseInfos = [
+            //saas-company
+            'company' => [
+                'host' => '39.108.116.125',
+                'port' => 3306,
+                'db_name' => 'company_61',
+                'db_username' => 'company_61',
+                'db_password' => 'N35fYCPHziAAGd4L'
+            ]
+        ];
+
+        $companyConnectionConfig = [
+            'driver' => 'mysql',
+            'host' => $dataBaseInfos['company']['host'],
+            'port' => $dataBaseInfos['company']['port'],
+            'database' => $dataBaseInfos['company']['db_name'],
+            'username' => $dataBaseInfos['company']['db_username'],
+            'password' => $dataBaseInfos['company']['db_password'],
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_unicode_ci',
+            'unix_socket' => env('DB_SOCKET', ''),
+            'prefix' => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+        $tableName = 'kailin_question_real_topic';
+
+        $tableNameCatalogue = 'kailin_question_past_real_catalogue';
+
+        $directory = "E:\\鸭题库数据初始化历年真题\\中药综\\历年真题";
+
+        $needExchangeTopics = $this->getFilesRecursively($directory);
+//dd($needExchangeTopics);
+
+        $dataCount = count($needExchangeTopics);
+
+        $this->info('开始转换');
+        $this->info("总共有:{$dataCount} 年的真题");
+        $bar = $this->output->createProgressBar($dataCount);
+
+        foreach ($needExchangeTopics as $needExchangeTopic) {
+
+            $year = $needExchangeTopic['fileName'];
+
+            //法规
+            $catalogueId  = DB::connection('company')->table($tableNameCatalogue)->where('chapter_id', 9)->where('year', $year)->value('id');
+
+            $this->info('获取目录id =' . $catalogueId);
+            if (empty($catalogueId)) {
+                $this->info('获取目录id失败!');
+                $this->info('目录年份='.$year);
+                return ;
+            }
+
+            $data = file_get_contents($needExchangeTopic['data']);
+
+            $data = json_decode($data, true);
+            foreach ($data['data'] as $item) {
+                //真题组名称:
+                $groupName = $item['groupName'];
+
+                $paperQuestions = $item['paperQuestionVos'];
+
+                foreach ($paperQuestions as $value) {
+                    //题目类型
+                    $style = $value['style'];
+                    //$styleName = $this->getStyleName($style);
+
+
+                    //题目
+                    $title  = $value['title'];
+                    $questions_ex = null;
+                    if (!empty($value['exerciseQuestionsTopicPo'])) {
+                        $questions_ex = $value['exerciseQuestionsTopicPo']['content'];
+                    }
+
+
+                    //解析
+                    $explain = $value['explain'];
+
+                    //准确率
+                    $accuracy = $value['accuracy'];
+
+                    //
+                    $correctAnswerStr = ''; //正确选项Str
+                    $correctAnswers = [];  //正确答案数据
+                    $selectAContent = '';
+                    $selectBContent = '';
+                    $selectCContent = '';
+                    $selectDContent = '';
+                    $selectEContent = '';
+
+                    //考点
+                    $examPointStr = '';
+                    $examPointNames = [];
+                    $examPoints = $value['points'];
+                    foreach ($examPoints as $examPoint) {
+                        $examPointNames [] = $examPoint['name'];
+                    }
+                    $examPointStr = implode(',', $examPointNames);//考点
+                    //dd($examPointStr);
+
+                    $questionCount = $value['questionCount']; //选项数量
+                    //获取选项:
+                    $questionsOptionsPos = $value['questionsOptionsPos'];
+
+                    foreach ($questionsOptionsPos as $key => $item) {
+                        if ($key == 0 && $key <= $questionCount - 1) {
+                            $selectAContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'A';
+                            }
+                        } elseif ($key == 1 && $key <= $questionCount - 1) {
+                            $selectBContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'B';
+                            }
+                        } elseif ($key == 2 && $key <= $questionCount - 1) {
+                            $selectCContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'C';
+                            }
+                        } elseif ($key == 3 && $key <= $questionCount - 1) {
+                            $selectDContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'D';
+                            }
+                        } elseif ($key == 4 && $key <= $questionCount - 1) {
+                            $selectEContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'E';
+                            }
+                        }
+                    }
+                    $correctAnswerStr = implode(',', $correctAnswers); //正确答案
+
+
+                    if ($style == 6) {
+                        $selectAContent = '';
+                        $selectBContent = '';
+                        $selectCContent = '';
+                        $selectDContent = '';
+                        $selectEContent = '';
+                    }
+
+                    $insertData = [
+                        'title'             => $title,
+                        'style'             => $style,
+                        'group_name'        => $groupName,
+                        'select_a'          => $selectAContent,
+                        'select_b'          => $selectBContent,
+                        'select_c'          => $selectCContent,
+                        'select_d'          => $selectDContent,
+                        'select_e'          => $selectEContent,
+                        'question_count'    => $questionCount,
+                        'questions_ex'      => $questions_ex,
+                        'explain'           => $explain,
+                        'correct_answer'    => $correctAnswerStr,
+                        'accuracy'          => $accuracy,
+                        'catalogue_id'      => $catalogueId,
+                        'insert_time'       => time(),
+                        'update_time'       => time()
+                    ];
+
+                    DB::connection('company')->table($tableName)->insert($insertData);
+
+                }
+            }
+
+            $bar->advance();
+
+
+        }
+
+    }
+
+    public function getFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        $result = [];
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 过滤掉文件名中包含“数据源”的文件
+                if (strpos($file, '数据源') === false) {
+                    // 获取文件名的主名称(去除.txt后缀)
+                    $fileNameWithoutExtension = pathinfo($file, PATHINFO_FILENAME);
+                    // 获取文件所在的目录路径
+                    $fileDir = dirname($filePath);
+
+                    // 去除文件名中的数字(如1, 2, 3...10, 11, 12等)
+                    // 去除文件名中的数字和连字符(-)
+                    $fileNameWithoutNumbers = preg_replace('/\d+/', '', $fileNameWithoutExtension);
+                    // 获取文件的大小
+                    $fileSize = filesize($filePath);
+                    $result[] = [
+                        'fileName' => $fileNameWithoutExtension, // 文件名(去除.txt后缀)
+                        'data' => $filePath, // 完整路径
+                        'directory' => $fileDir, // 文件所在目录
+                        'size' => $fileSize // 文件大小
+                    ];
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $result = array_merge($result, $this->getFilesRecursively($filePath));
+            }
+        }
+
+        return $result;
+    }
+
+
+}

+ 374 - 0
app/Console/Commands/WashData/InitQuestionTopicCommand.php

@@ -0,0 +1,374 @@
+<?php
+
+
+namespace App\Console\Commands\WashData;
+
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+
+class InitQuestionTopicCommand extends Command
+{
+
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'washdata:initTopic';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '题库解析';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        //连接数据库
+
+        $dataBaseInfos = [
+            //saas-company
+            'company' => [
+                'host'          => '39.108.116.125',
+                'port'          => 3306,
+                'db_name'       => 'company_61',
+                'db_username'   => 'company_61',
+                'db_password'   => 'N35fYCPHziAAGd4L'
+            ]
+        ];
+
+        $companyConnectionConfig = [
+            'driver'        => 'mysql',
+            'host'          => $dataBaseInfos['company']['host'],
+            'port'          => $dataBaseInfos['company']['port'],
+            'database'      => $dataBaseInfos['company']['db_name'],
+            'username'      => $dataBaseInfos['company']['db_username'],
+            'password'      => $dataBaseInfos['company']['db_password'],
+            'charset'       => 'utf8mb4',
+            'collation'     => 'utf8mb4_unicode_ci',
+            'unix_socket'   => env('DB_SOCKET', ''),
+            'prefix'        => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+        $tableName = 'kailin_question_topic';
+
+        $tableNameChapter = 'kailin_question_chapter';
+
+        $directory = "E:\\鸭题库数据初始化题目\\中药综\\13第十三章 中药用药安全";
+
+        $needExchangeTopics = $this->getFilesRecursively($directory);
+//dd($needExchangeTopics);
+
+        $dataCount = count($needExchangeTopics);
+
+        $this->info('开始转换');
+        $this->info("总共有:{$dataCount} 节");
+        $bar = $this->output->createProgressBar($dataCount);
+
+        foreach ($needExchangeTopics as $needExchangeTopic) {
+
+            $chapterName =  $needExchangeTopic['fileName'];
+
+//            if (strpos($chapterName, '节')) {
+//                $chapterName = explode('节', $chapterName)[1];
+//                $chapterName = trim($chapterName);
+//                //通过chapterName获取chapterId
+//                $chapterId  = DB::connection('company')->table($tableNameChapter)->where('name', 'like', '%' . $chapterName . '%')->value('id');
+//            } else {
+//                //通过chapterName获取chapterId
+//                $chapterId  = DB::connection('company')->table($tableNameChapter)->where('name', $chapterName)->value('id');
+//            }
+
+            $chapterId  = DB::connection('company')->table($tableNameChapter)->where('name', $chapterName)->value('id');
+
+            $this->info('获取章节id =' . $chapterId);
+            if (empty($chapterId)) {
+                $this->info('获取章节id失败!');
+                $this->info('章节名称='.$chapterName);
+                return ;
+            }
+
+            $data = file_get_contents($needExchangeTopic['data']);
+
+            $data = json_decode($data, true);
+
+            foreach ($data['data'] as $value) {
+
+                //题目类型
+                $style = $value['style'];
+                //$styleName = $this->getStyleName($style);
+                //dd($styleName);
+                //题目
+//                $title = '';
+//                if (!empty($value['exerciseQuestionsTopicPo'])) {
+//                    $title  = $value['exerciseQuestionsTopicPo']['content'] . "\r\n";
+//                }
+//                $title = $title  . $value['title'];
+                $title  = $value['title'];
+                $questions_ex = null;
+                if (!empty($value['exerciseQuestionsTopicPo'])) {
+                    $questions_ex = $value['exerciseQuestionsTopicPo']['content'];
+                }
+
+                //解析
+                $explain = $value['explain'];
+
+                //准确率
+                $accuracy = $value['accuracy'];
+
+                //
+                $correctAnswerStr = ''; //正确选项Str
+                $correctAnswers = [];  //正确答案数据
+                $selectAContent = '';
+                $selectBContent = '';
+                $selectCContent = '';
+                $selectDContent = '';
+                $selectEContent = '';
+
+                //考点
+                $examPointStr = '';
+                $examPointNames = [];
+                $examPoints = $value['points'];
+                foreach ($examPoints as $examPoint) {
+                    $examPointNames [] = $examPoint['name'];
+                }
+                $examPointStr = implode(',', $examPointNames);//考点
+                //dd($examPointStr);
+
+
+                //$answerFlag = false;
+
+//                $answer = $data['answer'];
+//
+//                if (in_array($answer, $answerArray)) {
+//                    $answerFlag = true; //获取answer成功
+//                }
+
+
+                $questionCount = $value['questionCount'] ; //选项数量
+                //获取选项:
+                $questionsOptionsPos = $value['questionsOptionsPos'];
+
+                foreach ($questionsOptionsPos as $key => $item) {
+                    //配伍题的选项在$questions_ex 答案不能混淆
+                    //解析中包含A、B、C、D、E的时候也不能混淆
+                    if (strpos($explain, 'A') || strpos($explain, 'B') || strpos($explain, 'C') || strpos($explain, 'D') || strpos($explain, 'E')) {
+                        if ($key == 0 && $key <= $questionCount - 1) {
+                            $selectAContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'A';
+                            }
+                        } elseif ($key == 1 && $key <= $questionCount - 1) {
+                            $selectBContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'B';
+                            }
+                        } elseif ($key == 2 && $key <= $questionCount - 1) {
+                            $selectCContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'C';
+                            }
+                        } elseif ($key == 3 && $key <= $questionCount - 1) {
+                            $selectDContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'D';
+                            }
+                        } elseif ($key == 4 && $key <= $questionCount - 1) {
+                            $selectEContent = $item['option'];
+                            if ($item['isTrue'] == true) {
+                                $correctAnswers [] = 'E';
+                            }
+                        }
+                    } else {
+                        if ($style == 6) {
+                            if ($key == 0 && $key <= $questionCount - 1) {
+                                $selectAContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'A';
+                                }
+                            } elseif ($key == 1 && $key <= $questionCount - 1) {
+                                $selectBContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'B';
+                                }
+                            } elseif ($key == 2 && $key <= $questionCount - 1) {
+                                $selectCContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'C';
+                                }
+                            } elseif ($key == 3 && $key <= $questionCount - 1) {
+                                $selectDContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'D';
+                                }
+                            } elseif ($key == 4 && $key <= $questionCount - 1) {
+                                $selectEContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'E';
+                                }
+                            }
+                        } else {
+
+                            if ($key == 0 && $key <= $questionCount - 1) {
+                                $selectCContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'C';
+                                }
+                            } elseif ($key == 1 && $key <= $questionCount - 1) {
+                                $selectDContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'D';
+                                }
+                            } elseif ($key == 2 && $key <= $questionCount - 1) {
+                                $selectAContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'A';
+                                }
+                            } elseif ($key == 3 && $key <= $questionCount - 1) {
+                                $selectBContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'B';
+                                }
+                            } elseif ($key == 4 && $key <= $questionCount - 1) {
+                                $selectEContent = $item['option'];
+                                if ($item['isTrue'] == true) {
+                                    $correctAnswers [] = 'E';
+                                }
+                            }
+                        }
+                    }
+                }
+                sort($correctAnswers);
+                $correctAnswerStr = implode(',', $correctAnswers); //正确答案
+                //dd($correctAnswerStr);
+
+                if ($style == 6) {
+                    $selectAContent = '';
+                    $selectBContent = '';
+                    $selectCContent = '';
+                    $selectDContent = '';
+                    $selectEContent = '';
+                }
+
+                $insertData = [
+                    'title'     => $title,
+                    'style'     => $style,
+                    'select_a'  => $selectAContent,
+                    'select_b'  => $selectBContent,
+                    'select_c'  => $selectCContent,
+                    'select_d'  => $selectDContent,
+                    'select_e'  => $selectEContent,
+                    'question_count'    => $questionCount,
+                    'questions_ex'      => $questions_ex,
+                    'explain'           => $explain,
+                    'correct_answer'    => $correctAnswerStr,
+                    'accuracy'          => $accuracy,
+                    'chapter_id'        => $chapterId,
+                    'point_id'          => 0,
+                    'insert_time'       => time(),
+                    'update_time'       => time()
+                ];
+
+                DB::connection('company')->table($tableName)->insert($insertData);
+
+//                $exportData = [];
+//
+//                $exportData['style'] = $styleName;
+//                $exportData['title'] = $title;
+//
+//                $exportData['selectA'] =  $selectAContent;
+//                $exportData['selectB'] =  $selectBContent;
+//                $exportData['selectC'] =  $selectCContent;
+//                $exportData['selectD'] =  $selectDContent;
+//                $exportData['selectE'] =  $selectEContent;
+//                $exportData['answer']  = $correctAnswerStr;
+//                $exportData['explain'] = $explain;
+//                $exportData['accuracy'] = $accuracy;
+//                $exportData['point']    = $examPointStr;
+//                $this->putRow($exportData);
+
+            }
+
+
+            $bar->advance();
+        }
+
+        $bar->finish();
+
+        $this->info('');
+        $this->info('导出完成');
+
+    }
+
+    public function getFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        $result = [];
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 过滤掉文件名中包含“数据源”的文件
+                if (strpos($file, '数据源') === false) {
+                    // 获取文件名的主名称(去除.txt后缀)
+                    $fileNameWithoutExtension = pathinfo($file, PATHINFO_FILENAME);
+                    // 获取文件所在的目录路径
+                    $fileDir = dirname($filePath);
+
+                    // 去除文件名中的数字(如1, 2, 3...10, 11, 12等)
+                    // 去除文件名中的数字和连字符(-)
+                    $fileNameWithoutNumbers = preg_replace('/\d+/', '', $fileNameWithoutExtension);
+                    // 获取文件的大小
+                    $fileSize = filesize($filePath);
+                    $result[] = [
+                        'fileName' => $fileNameWithoutNumbers, // 文件名(去除.txt后缀)
+                        'data' => $filePath, // 完整路径
+                        'directory' => $fileDir, // 文件所在目录
+                        'size' => $fileSize // 文件大小
+                    ];
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $result = array_merge($result, $this->getFilesRecursively($filePath));
+            }
+        }
+
+        return $result;
+    }
+
+}

+ 184 - 0
app/Console/Commands/WashData/InitQuestionTopicPointCommand.php

@@ -0,0 +1,184 @@
+<?php
+
+
+namespace App\Console\Commands\WashData;
+
+
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+
+class InitQuestionTopicPointCommand extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'washdata:initTopicPoint';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '题库关联考点初始化';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        //连接数据库
+
+        $dataBaseInfos = [
+            //saas-company
+            'company' => [
+                'host' => '39.108.116.125',
+                'port' => 3306,
+                'db_name' => 'company_61',
+                'db_username' => 'company_61',
+                'db_password' => 'N35fYCPHziAAGd4L'
+            ]
+        ];
+
+        $companyConnectionConfig = [
+            'driver' => 'mysql',
+            'host' => $dataBaseInfos['company']['host'],
+            'port' => $dataBaseInfos['company']['port'],
+            'database' => $dataBaseInfos['company']['db_name'],
+            'username' => $dataBaseInfos['company']['db_username'],
+            'password' => $dataBaseInfos['company']['db_password'],
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_unicode_ci',
+            'unix_socket' => env('DB_SOCKET', ''),
+            'prefix' => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+        $tableName = 'kailin_question_topic';
+
+        $tableNamePoint = 'kailin_question_point';
+
+        $directory = "E:\\鸭题库数据初始化题目\\中药综";
+
+        $needExchangeTopics = $this->getFilesRecursively($directory);
+//dd($needExchangeTopics);
+
+        $dataCount = count($needExchangeTopics);
+
+        $this->info('开始转换');
+        $this->info("总共有:{$dataCount} 节");
+        $bar = $this->output->createProgressBar($dataCount);
+
+        foreach ($needExchangeTopics as $needExchangeTopic) {
+
+            $data = file_get_contents($needExchangeTopic['data']);
+
+            $data = json_decode($data, true);
+
+            foreach ($data['data'] as $value) {
+                $title  = $value['title'];
+
+                $topicId    = DB::connection('company')->table($tableName)->where('title', $title)->value('id');
+
+                if ($topicId) {
+                    $examPointNames = [];
+                    $examPoints = $value['points'];
+                    foreach ($examPoints as $examPoint) {
+                        $examPointNames [] = $examPoint['name'];
+                    }
+                    $pointIds = [];
+                    foreach ($examPointNames as $examPointName) {
+                        //通过考点名称,获取考点的id
+                        $pointId = DB::connection('company')->table($tableNamePoint)->where('name', $examPointName)->value('id');
+
+                        if (!empty($pointId)) {
+                            $pointIds [] = $pointId;
+                        }
+                    }
+
+                    $pointStr = implode(',', $pointIds);
+
+                    DB::connection('company')->table($tableName)->where('id', $topicId)->update(['point_id' => $pointStr]);
+
+                }
+
+                //通过title获取到kailin_question_topic的id
+            }
+
+            $bar->advance();
+
+        }
+
+        $bar->finish();
+
+        $this->info('');
+        $this->info('初始化完成');
+    }
+
+    public function getFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        $result = [];
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 过滤掉文件名中包含“数据源”的文件
+                if (strpos($file, '数据源') === false) {
+                    // 获取文件名的主名称(去除.txt后缀)
+                    $fileNameWithoutExtension = pathinfo($file, PATHINFO_FILENAME);
+                    // 获取文件所在的目录路径
+                    $fileDir = dirname($filePath);
+
+                    // 去除文件名中的数字(如1, 2, 3...10, 11, 12等)
+                    // 去除文件名中的数字和连字符(-)
+                    $fileNameWithoutNumbers = preg_replace('/\d+/', '', $fileNameWithoutExtension);
+                    // 获取文件的大小
+                    $fileSize = filesize($filePath);
+                    $result[] = [
+                        'fileName' => $fileNameWithoutNumbers, // 文件名(去除.txt后缀)
+                        'data' => $filePath, // 完整路径
+                        'directory' => $fileDir, // 文件所在目录
+                        'size' => $fileSize // 文件大小
+                    ];
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $result = array_merge($result, $this->getFilesRecursively($filePath));
+            }
+        }
+
+        return $result;
+    }
+
+}

+ 333 - 0
app/Console/Commands/WashData/UpdateQuestionTopicCommand.php

@@ -0,0 +1,333 @@
+<?php
+
+
+namespace App\Console\Commands\WashData;
+
+
+use App\Console\Commands\Export\ExportTrait;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\DB;
+
+class UpdateQuestionTopicCommand extends Command
+{
+    use ExportTrait;
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'washdata:updateTopic';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = '更新题库的题目';
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+
+        $head = [
+            '题目id',
+            '题目类型',
+            '题目',
+            '选项A',
+            '选项B',
+            '选项c',
+            '选项D',
+            '选项E',
+            '正确答案',
+            '解析',
+            '正确率',
+            '题目扩展',
+            '章节名称'
+        ];
+
+        //连接数据库
+        //测试
+//        $dataBaseInfos = [
+//            //saas-company
+//            'company' => [
+//                'host' => '39.108.116.125',
+//                'port' => 3306,
+//                'db_name' => 'company_61',
+//                'db_username' => 'company_61',
+//                'db_password' => 'N35fYCPHziAAGd4L'
+//            ]
+//        ];
+        //正式
+        $dataBaseInfos = [
+            //saas-company
+            'company' => [
+                'host' => '120.24.49.2',
+                'port' => 3306,
+                'db_name' => 'company_61',
+                'db_username' => 'company_61',
+                'db_password' => 'NKD4saLKN7zWKrzz'
+            ]
+        ];
+
+        $companyConnectionConfig = [
+            'driver' => 'mysql',
+            'host' => $dataBaseInfos['company']['host'],
+            'port' => $dataBaseInfos['company']['port'],
+            'database' => $dataBaseInfos['company']['db_name'],
+            'username' => $dataBaseInfos['company']['db_username'],
+            'password' => $dataBaseInfos['company']['db_password'],
+            'charset' => 'utf8mb4',
+            'collation' => 'utf8mb4_unicode_ci',
+            'unix_socket' => env('DB_SOCKET', ''),
+            'prefix' => env('DB_PREFIX', ''),
+            'strict' => false,
+            'sticky' => true,
+            'engine' => null,
+            'options' => [],
+        ];
+
+        Config::set("database.connections.company", $companyConnectionConfig);
+
+        $tableNameTopic = 'kailin_question_topic';
+
+        $tableNameChapter = 'kailin_question_chapter';
+
+        $directory = "E:\\鸭题库数据初始化考点_第二次\\中药二\\28第二十八章 骨伤科常用中成药";
+
+        $needExchangeTopics = $this->getFilesRecursively($directory);
+//dd($needExchangeTopics);
+        $dataCount = count($needExchangeTopics);
+
+        $this->info('开始转换');
+        $this->info("总共有:{$dataCount} 节");
+        $bar = $this->output->createProgressBar($dataCount);
+
+        foreach ($needExchangeTopics as $needExchangeTopic) {
+
+            $chapterName =  $needExchangeTopic['fileName'];
+
+            $chapterId  = DB::connection('company')->table($tableNameChapter)->where('name', $chapterName)->value('id');
+
+            $this->info('获取章节id =' . $chapterId);
+            if (empty($chapterId)) {
+                $this->info('获取章节id失败!');
+                $this->info('章节名称='.$chapterName);
+                return ;
+            }
+
+            //通过章节获取所有的题目id
+            //$topicIds = DB::connection('company')->table($tableNameTopic)->where('chapter_id', $chapterId)
+
+            $data = file_get_contents($needExchangeTopic['data']);
+            $data = json_decode($data, true);
+            $changeTopicIds = [];
+            foreach ($data['data'] as $value) {
+                $title  = $value['title'];
+                $topicId = DB::connection('company')->table($tableNameTopic)->where('chapter_id', $chapterId)->where('title', $title)->value('id');
+
+                if (!$topicId) {
+                    continue;
+                }
+
+                $changeTopicIds [] = $topicId;
+
+                $questions_ex = null;
+                if (!empty($value['exerciseQuestionsTopicPo'])) {
+                    $questions_ex = $value['exerciseQuestionsTopicPo']['content'];
+                }
+
+                //题目类型
+                $style = $value['style'];
+
+                //解析
+                $explain = $value['explain'];
+
+                //准确率
+                $accuracy = $value['accuracy'];
+
+                $correctAnswers = [];  //正确答案数据
+                $selectAContent = '';
+                $selectBContent = '';
+                $selectCContent = '';
+                $selectDContent = '';
+                $selectEContent = '';
+
+                $questionCount = $value['questionCount'] ; //选项数量
+
+                //获取选项:
+                $questionsOptionsPos = $value['questionsOptionsPos'];
+
+                foreach ($questionsOptionsPos as $key => $item) {
+                    if ($key == 0 && $key <= $questionCount - 1) {
+                        $selectAContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'A';
+                        }
+                    } elseif ($key == 1 && $key <= $questionCount - 1) {
+                        $selectBContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'B';
+                        }
+                    } elseif ($key == 2 && $key <= $questionCount - 1) {
+                        $selectCContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'C';
+                        }
+                    } elseif ($key == 3 && $key <= $questionCount - 1) {
+                        $selectDContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'D';
+                        }
+                    } elseif ($key == 4 && $key <= $questionCount - 1) {
+                        $selectEContent = $item['option'];
+                        if ($item['isTrue'] == true) {
+                            $correctAnswers [] = 'E';
+                        }
+                    }
+                }
+
+                sort($correctAnswers);
+                $correctAnswerStr = implode(',', $correctAnswers); //正确答案
+
+                if ($style == 6) {
+                    $selectAContent = '';
+                    $selectBContent = '';
+                    $selectCContent = '';
+                    $selectDContent = '';
+                    $selectEContent = '';
+                }
+
+                $updateData = [
+                    'style'     => $style,
+                    'select_a'  => $selectAContent,
+                    'select_b'  => $selectBContent,
+                    'select_c'  => $selectCContent,
+                    'select_d'  => $selectDContent,
+                    'select_e'  => $selectEContent,
+                    'question_count'    => $questionCount,
+                    'questions_ex'      => $questions_ex,
+                    'explain'           => $explain,
+                    'correct_answer'    => $correctAnswerStr,
+                    'accuracy'          => $accuracy,
+                    'update_time'       => time()
+                ];
+
+                DB::connection('company')->table($tableNameTopic)->where('id', $topicId)->update($updateData);
+
+            }
+
+            //导出未修改的题目
+            $this->createdFile($needExchangeTopic['directory'], $needExchangeTopic['fileName']);
+            $this->putRow($head);
+
+            $topicInfos = DB::connection('company')->table($tableNameTopic)->where('chapter_id', $chapterId)->get()->toArray();
+
+            foreach ($topicInfos as $topicInfo) {
+
+                if (!in_array($topicInfo['id'], $changeTopicIds)) {
+                    $exportData = [];
+                    $exportData['id'] = $topicInfo['id'];
+                    $exportData['style'] = $this->getStyleName($topicInfo['style']);
+                    $exportData['title'] = $topicInfo['title'];
+                    $exportData['selectA'] = $topicInfo['select_a'];
+                    $exportData['selectB'] = $topicInfo['select_b'];
+                    $exportData['selectC'] = $topicInfo['select_c'];
+                    $exportData['selectD'] = $topicInfo['select_d'];
+                    $exportData['selectE'] = $topicInfo['select_e'];
+                    $exportData['answer'] = $topicInfo['correct_answer'];
+                    $exportData['explain'] = $topicInfo['explain'];
+                    $exportData['accuracy'] = $topicInfo['accuracy'];
+                    $exportData['questions_ex'] = $topicInfo['questions_ex'];
+                    $exportData['chapter_id'] = $chapterName;
+                    $this->putRow($exportData);
+                }
+            }
+
+            $bar->advance();
+        }
+
+        $bar->finish();
+
+        $this->info('');
+        $this->info('导出完成');
+    }
+
+    public function getStyleName($style)
+    {
+        if ($style == 6) {
+            $styleName = '配伍题';
+        } elseif ($style == 2) {
+            $styleName = '单选题';
+        } elseif ($style == 3) {
+            $styleName = '多选题';
+        } else {
+            $styleName = $style;
+        }
+
+        return $styleName;
+    }
+
+
+    public function getFilesRecursively($directory) {
+        // 检查目录是否存在
+        if (!is_dir($directory)) {
+            return "目录不存在";
+        }
+
+        // 获取目录下的所有文件和子目录
+        $files = scandir($directory);
+
+        // 过滤掉 "." 和 ".."(当前目录和上一级目录)
+        $files = array_diff($files, array('.', '..'));
+
+        $result = [];
+        foreach ($files as $file) {
+            // 拼接完整的文件路径
+            $filePath = $directory . DIRECTORY_SEPARATOR . $file;
+
+            // 判断是否为文件
+            if (is_file($filePath)) {
+                // 过滤掉文件名中包含“数据源”的文件
+                if (strpos($file, '数据源') === false) {
+                    // 获取文件名的主名称(去除.txt后缀)
+                    $fileNameWithoutExtension = pathinfo($file, PATHINFO_FILENAME);
+                    // 获取文件所在的目录路径
+                    $fileDir = dirname($filePath);
+
+                    // 去除文件名中的数字(如1, 2, 3...10, 11, 12等)
+                    // 去除文件名中的数字和连字符(-)
+                    $fileNameWithoutNumbers = preg_replace('/\d+/', '', $fileNameWithoutExtension);
+                    // 获取文件的大小
+                    $fileSize = filesize($filePath);
+                    $result[] = [
+                        'fileName' => $fileNameWithoutNumbers, // 文件名(去除.txt后缀)
+                        'data' => $filePath, // 完整路径
+                        'directory' => $fileDir, // 文件所在目录
+                        'size' => $fileSize // 文件大小
+                    ];
+                }
+            } elseif (is_dir($filePath)) {
+                // 如果是目录,则递归调用
+                $result = array_merge($result, $this->getFilesRecursively($filePath));
+            }
+        }
+
+        return $result;
+    }
+
+}

File diff suppressed because it is too large
+ 78 - 0
app/Console/Commands/WashData/WashDataSetQuestionPointStatusCommand.php


+ 55 - 0
app/Console/Kernel.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Console;
+
+use App\Console\Commands\IncrementSyncDrugReportCommand;
+use App\Console\Commands\IncrementSyncDrugReportAssCommand;
+use App\Console\Commands\HistorySyncDrugReportCommand;
+use App\Console\Commands\SignedSyncDrugReportCommand;
+use App\Console\Commands\UpdateDrugReportAssCommand;
+use Illuminate\Console\Scheduling\Schedule;
+use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
+
+class Kernel extends ConsoleKernel
+{
+    /**
+     * The Artisan commands provided by your application.
+     *
+     * @var array
+     */
+    protected $commands = [
+        IncrementSyncDrugReportCommand::class,
+        IncrementSyncDrugReportAssCommand::class,
+        HistorySyncDrugReportCommand::class,
+        SignedSyncDrugReportCommand::class,
+        UpdateDrugReportAssCommand::class,
+    ];
+
+    /**
+     * Define the application's command schedule.
+     *
+     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
+     * @return void
+     */
+    protected function schedule(Schedule $schedule)
+    {
+        $schedule->command('increment-drug-report')->everyThirtyMinutes();
+        $schedule->command('increment-drug-report-ass')->everyThirtyMinutes();
+        $schedule->command('update-drug-report-ass')->everyThreeHours(15);
+        //$schedule->command('drug-report-history')->everyThirtyMinutes();
+        //$schedule->command('drug-report-signed')->everyThirtyMinutes();
+    }
+
+    /**
+     * Register the commands for the application.
+     *
+     * @return void
+     */
+    protected function commands()
+    {
+        // 自动引入任务命令
+        $this->load(__DIR__.'/Commands');
+        // 引入任务路由
+        require base_path('routes/console.php');
+    }
+}

+ 112 - 0
app/Console/开邻数据迁移到Saas.md

@@ -0,0 +1,112 @@
+
+说明:具体的库的名称可以按照实际的进行修改,修改了的话也要同事修改SyncDatabaseCommand文件中的数据库配置
+
+1、创建kailin_migrate_test数据库,把kailin的数据结构和内容传输到kailin_migrate_test
+2、创建company_migrate_test 数据库,把company的数据结构和内容传输到company_migrate_test
+3、创建system_migrate_test数据库,把system的数据结构和内容传输到system_migrate_test
+
+4、在kailin_migrate_test执行sql:
+SET FOREIGN_KEY_CHECKS=0;
+ALTER TABLE `kailin_amount_record` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 COMMENT '公司ID' AFTER `pay_time`;
+ALTER TABLE `kailin_amount_record` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_banner` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `update_time`;
+ALTER TABLE `kailin_banner` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_coupon` ADD COLUMN `company_id` int(10) UNSIGNED NOT NULL DEFAULT 1 COMMENT '公司id' AFTER `admin_uid`;
+ALTER TABLE `kailin_coupon` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_coupon_active` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 COMMENT '公司ID' AFTER `active_rule`;
+ALTER TABLE `kailin_coupon_active` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_coupon_reward_rule` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 COMMENT '商店ID' AFTER `city_ids`;
+ALTER TABLE `kailin_coupon_reward_rule` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 COMMENT '公司ID' AFTER `shop_id`;
+ALTER TABLE `kailin_custom` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 COMMENT '店铺id' AFTER `company_id`;
+ALTER TABLE `kailin_custom_amount` ADD COLUMN `company_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '商户公司ID' AFTER `transfer_amount`;
+ALTER TABLE `kailin_custom_amount` ADD COLUMN `shop_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_custom_clockin_record` COMMENT = '客户打卡';
+ALTER TABLE `kailin_custom_clockin_record` ADD COLUMN `company_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '商户公司ID' AFTER `coupon_id`;
+ALTER TABLE `kailin_custom_clockin_record` ADD COLUMN `shop_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_custom_coupon` ADD COLUMN `company_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '商户公司ID' AFTER `admin_uid`;
+ALTER TABLE `kailin_custom_coupon` ADD COLUMN `shop_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_custom_redpacket` ADD COLUMN `company_id` bigint(20) NOT NULL DEFAULT 1 COMMENT '商户公司ID' AFTER `amount_record_id`;
+ALTER TABLE `kailin_custom_redpacket` ADD COLUMN `shop_id` bigint(20) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_custom_score` ADD COLUMN `company_id` bigint(20) NOT NULL DEFAULT 1 AFTER `use_score`;
+ALTER TABLE `kailin_custom_score` ADD COLUMN `shop_id` bigint(20) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_lottery_order` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `rule`;
+ALTER TABLE `kailin_lottery_order` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_lottery_recruitment` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 0 AFTER `max_reward`;
+ALTER TABLE `kailin_lottery_recruitment` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 0 AFTER `company_id`;
+ALTER TABLE `kailin_lottery_recruitment_record` ADD COLUMN `reward_type` int(255) unsigned NOT NULL DEFAULT '0' COMMENT '奖品类型' AFTER `reward_id`;
+ALTER TABLE `kailin_lottery_riddle` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 COMMENT '公司ID' AFTER `max_reward`;
+ALTER TABLE `kailin_lottery_riddle` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_lottery_score` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `rule`;
+ALTER TABLE `kailin_lottery_score` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_orders` ADD COLUMN `company_id` int(10) UNSIGNED NOT NULL DEFAULT 1 COMMENT '商户ID' AFTER `custom_uid`;
+ALTER TABLE `kailin_orders` ADD COLUMN `shop_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_orders` ADD COLUMN `port_type` tinyint(3) NOT NULL DEFAULT 0 COMMENT '0:小程序;1:H5直播' AFTER `shop_id`;
+ALTER TABLE `kailin_orders` ADD COLUMN `producer_id` int(10) NOT NULL COMMENT '生产厂家ID' AFTER `port_type`;
+
+UPDATE `kailin_orders` SET `shop_id` = `business_id`;
+
+ALTER TABLE `kailin_orders_banner` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `city_ids`;
+ALTER TABLE `kailin_orders_banner` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_orders_product` ADD COLUMN `company_id` int(10) UNSIGNED NOT NULL DEFAULT 1 COMMENT '商户ID' AFTER `custom_uid`;
+ALTER TABLE `kailin_orders_product` ADD COLUMN `shop_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_orders_product` ADD COLUMN `producer_id` int(10) NOT NULL COMMENT '生产工厂ID' AFTER `shop_id`;
+UPDATE `kailin_orders_product` SET `shop_id` = `business_id`;
+
+ALTER TABLE `kailin_orders_receipt` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `admin_uid`;
+ALTER TABLE `kailin_orders_receipt` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_producer` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `status`;
+ALTER TABLE `kailin_producer` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_product` ADD COLUMN `shop_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER `producer_id`;
+ALTER TABLE `kailin_product` ADD COLUMN `exp_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '有效期至' AFTER `shop_id`;
+UPDATE `kailin_product` SET `shop_id` = `business_id`;
+ALTER TABLE `kailin_product_spec` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `input_type`;
+ALTER TABLE `kailin_product_spec` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_product_type` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `status`;
+ALTER TABLE `kailin_product_type` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_promo` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 COMMENT '公司ID' AFTER `admin_uid`;
+ALTER TABLE `kailin_promo` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_recruitment_active` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `active_rule`;
+ALTER TABLE `kailin_recruitment_active` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_redpacket` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `end_time`;
+ALTER TABLE `kailin_redpacket` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_redpacket_active` ADD COLUMN `company_id` int(10) UNSIGNED NOT NULL DEFAULT 1 COMMENT '商户ID' AFTER `is_arrive`;
+ALTER TABLE `kailin_redpacket_active` ADD COLUMN `shop_id` int(10) UNSIGNED NOT NULL DEFAULT 1 COMMENT '店铺ID' AFTER `company_id`;
+ALTER TABLE `kailin_regiment` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `end_time`;
+ALTER TABLE `kailin_regiment` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_regiment_active` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER `company_id`;
+UPDATE `kailin_regiment_active` SET `shop_id` = `business_id`;
+ALTER TABLE `kailin_riddle_active` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `rule`;
+ALTER TABLE `kailin_riddle_active` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_riddle_answer` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `question_id`;
+ALTER TABLE `kailin_riddle_answer` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_riddle_question` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `status`;
+ALTER TABLE `kailin_riddle_question` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_score_clockin_active` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `status`;
+ALTER TABLE `kailin_score_clockin_active` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_score_orders` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 COMMENT '公司ID' AFTER `custom_uid`;
+ALTER TABLE `kailin_score_orders` ADD COLUMN `shop_id` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER `company_id`;
+UPDATE `kailin_score_orders` SET `shop_id` = `business_id`;
+ALTER TABLE `kailin_score_product` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `admin_uid`;
+ALTER TABLE `kailin_score_product` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_score_record` ADD COLUMN `company_id` bigint(20) NOT NULL DEFAULT 1 AFTER `custom_uid`;
+ALTER TABLE `kailin_score_record` ADD COLUMN `shop_id` bigint(20) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_share_message` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `status`;
+ALTER TABLE `kailin_share_message` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_share_message` ADD UNIQUE INDEX `fk_kailin_share_message_pages_UNIQUE`(`pages`, `item_id`, `shop_id`) USING BTREE;
+ALTER TABLE `kailin_shop_cart` ADD COLUMN `company_id` bigint(20) NOT NULL DEFAULT 1 AFTER `checked`;
+ALTER TABLE `kailin_shop_cart` ADD COLUMN `shop_id` bigint(20) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_stock` ADD COLUMN `company_id` int(10) NOT NULL DEFAULT 1 AFTER `status`;
+ALTER TABLE `kailin_stock` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+ALTER TABLE `kailin_stock_out` ADD COLUMN `company_id` int(10) NULL DEFAULT 1 AFTER `admin_uid`;
+ALTER TABLE `kailin_stock_out` ADD COLUMN `shop_id` int(10) NOT NULL DEFAULT 1 AFTER `company_id`;
+SET FOREIGN_KEY_CHECKS=1;
+
+5、在company_migrate_test执行sql:
+ALTER TABLE company_migrate_test.kailin_score_orders ADD COLUMN business_id int(10) NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER shop_id;
+ALTER TABLE company_migrate_test.kailin_score_orders ADD COLUMN remark varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '店铺ID' AFTER track_number;
+ALTER TABLE company_migrate_test.kailin_regiment_active ADD COLUMN business_id int(10) NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER shop_id;
+ALTER TABLE company_migrate_test.kailin_product ADD COLUMN business_id int(10) NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER shop_id;
+ALTER TABLE company_migrate_test.kailin_orders_product ADD COLUMN business_id int(10) NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER shop_id;
+ALTER TABLE company_migrate_test.kailin_orders ADD COLUMN business_id int(10) NOT NULL DEFAULT 0 COMMENT '店铺ID' AFTER custom_uid;
+
+6、执行php artisan sync:database

+ 68 - 0
app/Events/ExternalAndFollowOperateEvent.php

@@ -0,0 +1,68 @@
+<?php
+
+
+namespace App\Events;
+
+//外部人员与跟进员工操作事件新增,删除外部人员,删除跟进员工
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+class ExternalAndFollowOperateEvent
+{
+    use Dispatchable, InteractsWithSockets,SerializesModels;
+
+    /*
+     * 企微ID
+     */
+    public $corpId;
+
+    /*
+    * 跟进成员ID
+    */
+    public $followUserId;
+
+    /*
+    * 外部联系人ID
+    */
+    public $externalUserId;
+
+    /*
+     * 事件类型:1:添加好友、2:删除企业客户(用户)、3:删除跟进人员(内部员工)
+    */
+    public $eventType;
+    /*
+     * 企业自定义的state参数,用于区分不同的添加渠道
+    */
+    public $state = '';
+
+    /**
+     * Create a new event instance.
+     *
+     * @param $corpId
+     * @param $followUserId
+     * @param $externalUserId
+     * @param $eventType
+     * @param $state
+     */
+    public function __construct($corpId, $followUserId , $externalUserId, $eventType, $state='')
+    {
+        $this->corpId = $corpId;
+        $this->followUserId = $followUserId;
+        $this->externalUserId = $externalUserId;
+        $this->eventType = $eventType;
+        $this->state     = $state;
+    }
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('channel-name');
+    }
+
+}

+ 93 - 0
app/Events/ExternalAndGroupOperateEvent.php

@@ -0,0 +1,93 @@
+<?php
+
+
+namespace App\Events;
+
+
+use Illuminate\Broadcasting\Channel;
+use Illuminate\Broadcasting\InteractsWithSockets;
+use Illuminate\Broadcasting\PrivateChannel;
+use Illuminate\Foundation\Events\Dispatchable;
+use Illuminate\Queue\SerializesModels;
+
+//外部人员群操作事件进群,退群
+class ExternalAndGroupOperateEvent
+{
+    use Dispatchable, InteractsWithSockets, SerializesModels;
+
+    /*
+    * 企微ID
+    */
+    public $corpId;
+
+    /*
+    * 群ID
+    */
+    public $chatId;
+
+    /*
+    * 变更的群成员数组
+    */
+    public $memChangeArray;
+
+    /*
+    * 外部联系人ID
+    */
+    public $externalUserId;
+
+    /*
+    * 变更类型,当前默认update
+    */
+    public $changeType;
+
+    /*
+    * 变更详情类型:1:成员入群、2:成员退群
+    */
+    public $updateDetailType;
+
+    /*
+    * 入群时有值,0:成员邀请入群、3:扫描群二维码入群
+    */
+    public $joinScene;
+
+    /*
+    * 退群时有值,0:自己退群、1:群主/群管理员移出
+    */
+    public $quitScene;
+
+
+    /**
+     * Create a new event instance.
+     *
+     * @param $corpId
+     * @param $chatId
+     * @param $memChangeArray
+     * @param $changeType
+     * @param $updateDetailType
+     * @param $joinScene
+     * @param $quitScene
+     */
+    public function __construct($corpId, $chatId, $memChangeArray, $changeType, $updateDetailType, $joinScene, $quitScene)
+    {
+        $this->corpId               = $corpId;
+        $this->chatId               = $chatId;
+        $this->memChangeArray       = $memChangeArray;
+        $this->changeType           = $changeType;
+        $this->updateDetailType     = $updateDetailType;
+        $this->joinScene            = $joinScene;
+        $this->quitScene            = $quitScene;
+    }
+
+    /**
+     * Get the channels the event should broadcast on.
+     *
+     * @return Channel|array
+     */
+    public function broadcastOn()
+    {
+        return new PrivateChannel('channel-name');
+    }
+
+
+
+}

+ 64 - 0
app/Exceptions/Api/ApiException.php

@@ -0,0 +1,64 @@
+<?php namespace App\Exceptions\Api;
+
+use Exception;
+use Throwable;
+/**
+ * 接口异常
+ */
+class ApiException extends Exception
+{
+
+    // 错误返回码
+    protected $errCode   = 'error';
+    // 错误返回数据
+    protected $errData   = '';
+    // 本地化前缀
+    protected $langkey   = 'message';
+    
+    /**
+     * 抛出错误信息
+     * 
+     * @param   String          $message    — [optional] The Exception message to throw.
+     * @param   Mixed           $errData    — 错误返回数据
+     * @param   Int             $code       — [optional] The Exception code.
+     * @param   Throwable       $previous   — [optional] The previous throwable used for the exception chaining.
+     * @return  Mixed
+     * 
+     */
+    public function __construct( $message="",$errData='',$code=0,?Throwable $previous=null )
+    {
+        // 如果本地化前缀存在则本地化处理
+        if( $this->langkey ) {
+            // 消息key
+            $msgKey             = $this->langkey.'.'.$message;
+            // 提示信息本地化
+            $trans              = trans($msgKey);
+            // 如果没有找到对应的提示,使用原提示信息
+            $message            = ($trans == $msgKey) ? $message : $trans;
+        }
+        // 传递数据到父级
+        parent::__construct($message, $code, $previous);
+        // 错误信息说
+        if( $errData )          $this->errData = $errData;
+    }
+
+    /**
+     * 获取错误返回码
+     * 
+     */
+    public function getErrCode()
+    {
+        return $this->errCode;
+    }
+
+    /**
+     * 获取返回数据
+     * 
+     */
+    public function getErrData()
+    {
+        return $this->errData;
+    }
+
+
+}

+ 11 - 0
app/Exceptions/Api/LoginException.php

@@ -0,0 +1,11 @@
+<?php namespace App\Exceptions\Api;
+
+
+class LoginException extends ApiException
+{
+
+    // 错误返回码
+    protected $errCode   = 'no_login';
+    // 本地化前缀
+    protected $langkey   = 'api_login';
+}

+ 15 - 0
app/Exceptions/Api/ValidException.php

@@ -0,0 +1,15 @@
+<?php namespace App\Exceptions\Api;
+
+
+/**
+ * 接口验证签名错误抛出
+ * 
+ */
+class ValidException extends ApiException
+{
+
+    // 错误返回码
+    protected $errCode   = 'error';
+    // 本地化前缀
+    protected $langkey   = '';
+}

+ 15 - 0
app/Exceptions/Api/VerifySignException.php

@@ -0,0 +1,15 @@
+<?php namespace App\Exceptions\Api;
+
+
+/**
+ * 接口验证签名错误抛出
+ * 
+ */
+class VerifySignException extends ApiException
+{
+
+    // 错误返回码
+    protected $errCode   = 'no_access';
+    // 本地化前缀
+    protected $langkey   = 'api_access';
+}

+ 13 - 0
app/Exceptions/Company/ShopException.php

@@ -0,0 +1,13 @@
+<?php namespace App\Exceptions\Company;
+
+
+use App\Exceptions\Api\ApiException;
+
+class ShopException extends ApiException
+{
+
+    // 错误返回码
+    protected $errCode   = 'error';
+    // 本地化前缀
+    protected $langkey   = 'company_shop';
+}

+ 80 - 0
app/Exceptions/Handler.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace App\Exceptions;
+
+use App\Exceptions\Api\ApiException;
+use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use App\Facades\Servers\Logs\Log;
+use Throwable;
+
+class Handler extends ExceptionHandler
+{
+    /**
+     * 不报告的异常类型的列表
+     *
+     * @var array
+     */
+    protected $dontReport = [
+        //
+    ];
+
+    /**
+     * A list of the inputs that are never flashed for validation exceptions.
+     *
+     * @var array
+     */
+    protected $dontFlash = [
+        'current_password',
+        'password',
+        'password_confirmation',
+    ];
+
+    /**
+     * Register the exception handling callbacks for the application.
+     *
+     * @return void
+     */
+    public function register()
+    {
+        $this->reportable(function (Throwable $e) {})->stop();
+    }
+
+
+    /**
+     * 将异常呈现到HTTP响应中
+     * 
+     * 
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function render($request, Throwable $e){
+        // 如果是异常类型,获取异常的消息
+        $msg            = $e->getMessage();
+        // 错误信息
+        $msg            = $msg ? $msg : '404';
+        // 错误数据
+        $data           = '';
+        // 错误码
+        $code           = 'sys_error';
+        // http状态码
+        $status         = 200;
+        // 日志信息
+        $message        = request()->ip().' ' .request()->method().' '. request()->getPathInfo().' '.$msg .' of file '.$e->getFile().' on line '.$e->getLine();
+        // 如果是接口异常
+        if( $e instanceof ApiException ){
+            // 错误码
+            $code       = $e->getErrCode();
+            // 错误数据
+            $data       = $e->getErrData();
+        }else{
+            // 拼接报错文件位置
+            $message    .= ' of file '.$e->getFile().' on line '.$e->getLine();
+        }
+        // 要返回的数据
+        $data           = ['code'=>($code == 'sys_error' ? 'error' : $code),'msg'=>$msg,'data'=>$data];
+        // 记录日志
+        Log::info($code,$message,['param'=>request()->all(),'return'=>$data,'user_agent'=>request()->header('user-agent')],['filename'=>str_ireplace('/','_',trim(request()->getPathInfo(),'/'))]);
+        // 返回结果
+        return          response()->json($data,$status,[],JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
+    }
+
+}

+ 21 - 0
app/Facades/Servers/Aliyun/IpLocation.php

@@ -0,0 +1,21 @@
+<?php namespace App\Facades\Servers\Aliyun;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * @method string|array getCityByIp(string $ip='',string $field='') 获取IP归属地
+ * 
+ * @see \App\Servers\Aliyun\IpLocation
+ */
+class IpLocation extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Aliyun\IpLocation';
+    }
+}

+ 27 - 0
app/Facades/Servers/Aliyun/Sms.php

@@ -0,0 +1,27 @@
+<?php namespace App\Facades\Servers\Aliyun;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 短信发送
+ * 
+ * @method static array sendSms(string $phone,string $signName,string $templateCode,array $param=[]) 发送短信
+ * 
+ * @see \App\Servers\Aliyun\Sms
+ * 
+ * 
+ */
+class Sms extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Aliyun\Sms';
+    }
+}
+
+?>

+ 28 - 0
app/Facades/Servers/Encrypts/AccessToken.php

@@ -0,0 +1,28 @@
+<?php namespace App\Facades\Servers\Encrypts;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 用户权限令牌(JWT)
+ * 
+ * @method static string    encode(array $data)             加密数据
+ * @method static array     decode(string $access_token)    解密数据
+ * 
+ * @author  liuxaingxin
+ * @see \App\Servers\Encrypts\AccessToken
+ * 
+ */
+class AccessToken extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Encrypts\AccessToken';
+    }
+}
+
+?>

+ 26 - 0
app/Facades/Servers/Encrypts/ApiSign.php

@@ -0,0 +1,26 @@
+<?php namespace App\Facades\Servers\Encrypts;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 接口签名
+ * 
+ * @method static array encode( array $contents ,string $appid, string $appkey)  加密
+ * 
+ * @see \App\Servers\Encrypts\ApiSign
+ * 
+ */
+class ApiSign extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Encrypts\ApiSign';
+    }
+}
+
+?>

+ 24 - 0
app/Facades/Servers/Encrypts/Simple.php

@@ -0,0 +1,24 @@
+<?php namespace App\Facades\Servers\Encrypts;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 自定义简单对称加密
+ * 
+ * @see \App\Servers\Encrypts\Simple
+ * 
+ */
+class Simple extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Encrypts\Simple';
+    }
+}
+
+?>

+ 29 - 0
app/Facades/Servers/Logs/Log.php

@@ -0,0 +1,29 @@
+<?php namespace App\Facades\Servers\Logs;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * @method static mixed log(string $name,string $message,array $content=[],int $level=Logger::INFO,array $options=[])
+ * @method static mixed  debug(string $name,string $message,array $content=[],array $options=[])
+ * @method static mixed  info(string $name,string $message,array $content=[],array $options=[])
+ * @method static mixed  notice(string $name,string $message,array $content=[],array $options=[])
+ * @method static mixed  warning(string $name,string $message,array $content=[],array $options=[])
+ * @method static mixed  error(string $name,string $message,array $content=[],array $options=[])
+ * @method static mixed  critical(string $name,string $message,array $content=[],array $options=[])
+ * @method static mixed  alert(string $name,string $message,array $content=[],array $options=[])
+ * @method static mixed  emergency(string $name,string $message,array $content=[],array $options=[])
+ * 
+ * @see \App\Servers\Logs\Log
+ */
+class Log extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return 'App\Servers\Logs\Log';
+    }
+}

+ 33 - 0
app/Facades/Servers/Redis/Redis.php

@@ -0,0 +1,33 @@
+<?php namespace App\Facades\Servers\Redis;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * @method static  \Illuminate\Redis\Connections\Connection connection(string $name = null)
+ * @method static  \Illuminate\Redis\Limiters\ConcurrencyLimiterBuilder funnel(string $name)
+ * @method static  \Illuminate\Redis\Limiters\DurationLimiterBuilder throttle(string $name)
+ * @method static bool     set(string $key,string $value,['EX'|'PX'],[$expire],['NX'|'XX'])   设置指定key的值 
+ * @method static mixed    get(string $key)      获取指定key的值 
+ * @method static bool setex(string $key,int $seconds,string $value) 将值value关联到key,并将key的过期时间设为seconds(以秒为单位)。
+ * @method static bool pErsist(string $key)     移除给定key的过期时间,使得key永不过期
+ * @method static bool expire(string $key,int $expire)     为给定key设置生存时间
+ * @method static bool del(mixed $key,...$otherkey)   删除已存在的键。不存在的 key 会被忽略
+ * @method static int setBit(string $key,int $offset, int$value)   设置或清除指定偏移量上的位(bit)。返回值是:指定偏移量原来储存的位$value
+ * @method static int getBit(string $key,int $offset)          获取指定偏移量上的位(bit)。返回值是:字符串值指定偏移量上的位(bit)。当偏移量 OFFSET 比字符串值的长度大,或者 key 不存在时,返回 0 。
+ * @method static int exists(string $key)         检查给定 key 是否存在,若 key 存在返回 1 ,否则返回 0 。
+ * 
+ * @see \Illuminate\Redis\RedisManager
+ * @see \Illuminate\Contracts\Redis\Factory
+ */
+class Redis extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return 'redis';
+    }
+}

+ 23 - 0
app/Facades/Servers/Redis/RedisLock.php

@@ -0,0 +1,23 @@
+<?php namespace App\Facades\Servers\Redis;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * @method static miexd lock(string $key,string $value, int $ex=20)     上异步锁
+ * @method static miexd unlock($key,$value)          释放锁
+ * 
+ * @see \App\Servers\Redis\RedisLock
+ * 
+ */
+class RedisLock extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return 'App\Servers\Redis\RedisLock';
+    }
+}

+ 27 - 0
app/Facades/Servers/Safe/TextModeration.php

@@ -0,0 +1,27 @@
+<?php namespace App\Facades\Servers\Safe;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 反欺诈
+ * 
+ * @method mixed localCheck($content)           只用本地词库检测
+ * @method mixed textModeration(string $content)检测内容
+ * 
+ * @see \App\Servers\Safe\TextModeration
+ * 
+ */
+class TextModeration extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Safe\TextModeration';
+    }
+}
+
+?>

+ 26 - 0
app/Facades/Servers/ShortUrl/Client.php

@@ -0,0 +1,26 @@
+<?php namespace App\Facades\Servers\ShortUrl;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 短链
+ * 
+ * @method mixed createUrl(string $originUrl) 获取短链
+ * 
+ * @see \App\Servers\ShortUrl\Client
+ * 
+ */
+class Client extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\ShortUrl\Client';
+    }
+}
+
+?>

+ 28 - 0
app/Facades/Servers/Sms/GuoDu.php

@@ -0,0 +1,28 @@
+<?php namespace App\Facades\Servers\Sms;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 短信发送
+ * 
+ * @method static array sendSms($desMobile,$content,$sign) 发送短信
+ * @method static int|array surplus() 短信余额
+ * 
+ * @see \App\Servers\Sms\GuoDu
+ * 
+ * 
+ */
+class GuoDu extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Sms\GuoDu';
+    }
+}
+
+?>

+ 28 - 0
app/Facades/Servers/Sms/VerifyCode.php

@@ -0,0 +1,28 @@
+<?php namespace App\Facades\Servers\Sms;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 短信发送
+ * 
+ * @method static array sendCode($phone,$verifyCode) 发送短信验证码
+ * @method static array sendRemind($phone,$verifyCode) 发送短信
+ *
+ * @see \App\Servers\Sms\VerifyCode
+ * 
+ * 
+ */
+class VerifyCode extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Sms\VerifyCode';
+    }
+}
+
+?>

+ 28 - 0
app/Facades/Servers/Tencent/Cos.php

@@ -0,0 +1,28 @@
+<?php namespace App\Facades\Servers\Tencent;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ * 
+ * @method  array listObjectsAll($prefix='',$filter_name='',$marker='',$maxkeys=16,$delimiter='/',$result=[])   获取指定目录下的所有对象
+ * @method  array|string upload($key,$body,$headers=[])     上传对象
+ * @method  array|bool deleteObject($key,$versionId=null)   删除单个对象
+ * 
+ * @see \App\Servers\Tencent\Cos
+ * 
+ */
+class Cos extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Tencent\Cos';
+    }
+}
+
+?>

+ 26 - 0
app/Facades/Servers/Tencent/Sms.php

@@ -0,0 +1,26 @@
+<?php namespace App\Facades\Servers\Tencent;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 短信发送
+ * 
+ * @method static array sendSms(string $phone,string $signName,string $templateCode,array $param=[]) 发送短信
+ * 
+ * @see \App\Servers\Tencent\Sms
+ * 
+ */
+class Sms extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Tencent\Sms';
+    }
+}
+
+?>

+ 31 - 0
app/Facades/Servers/Wechat/Mini.php

@@ -0,0 +1,31 @@
+<?php namespace App\Facades\Servers\Wechat;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ * 
+ * @method static string getUrlLink($path,$query='')     获取UrlLink
+ * @method static array  getUserPhone($code)        手机号授权
+ * @method static string  getAccessToken()          获取AccessToken
+ * @method static mixed  queryUrlLink($urlLink)     查询加密UrlLink
+ * @method static mixed  getUnlimit(string $scene, array $optional = [])                获取小程序码
+ * 
+ * 
+ * @see \App\Servers\Wechat\Mini
+ * 
+ */
+class Mini extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Wechat\Mini';
+    }
+}
+
+?>

+ 31 - 0
app/Facades/Servers/Wechat/Official.php

@@ -0,0 +1,31 @@
+<?php namespace App\Facades\Servers\Wechat;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ *
+ * @method static \EasyWeChat\OfficialAccount\Application   getApp()                   公众号
+ * @method static array                                     userFromCode($code)        获取用户授权信息
+ * @method static array                                     getJssdkConfig($url='')    获取JSSDK的配置数组
+ * @method static array                                     sendSubscription($params)  发送一次性订阅消息
+ * @method static array                                     send($params)              发送模板消息
+ *
+ * 
+ * @see \App\Servers\Wechat\Official
+ * 
+ */
+class Official extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Wechat\Official';
+    }
+}
+
+?>

+ 50 - 0
app/Facades/Servers/Wechat/OpenPlat.php

@@ -0,0 +1,50 @@
+<?php namespace App\Facades\Servers\Wechat;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ * 
+ * @method static \EasyWeChat\OpenPlatform\Application                                  getApp()                                                    获取应用实例
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\OfficialAccount\Application       getOfficial(string $appId)                                  获取授权公众号实例
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getMini(string $appId)                                      获取授权小程序实例
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getUserPhone(string $appId, string $refreshToken)           手机号授权
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getTrialQRCode(string $appId, string $fileName, string $path)  生产小程序体验版二维码
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           commit(string $appId, int $templateId, string $extJson, string $version, string $description)    提交小程序体验版
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           userFromCode(string $code, string $appId,)    获取user  公众号
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           modify(string $appId,array $params)    配置小程序服务器域名
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getUrlLink(string $appId,string $path,string $query)    获取UrlLink
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getUnlimit(string $appId,string $scene, array $optional = [])         获取小程序码
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           release(string $appId)      发布最后一个审核通过的小程序代码版本
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           submitAudit(string $appId,array $data)      提交代码审核
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getAllCategoryName(string $appId)      获取类目名称信息
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getMaterial(string $appId, int $mediaId)      获取永久素材
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           jscode2session(string $code, string $appId)    登录获取用户openid
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           setPrivacySetting(string $appId, array $data)    登录获取用户openid
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getAccountBasicInfo(string $appId)    获取小程序信息
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getVersionInfo(string $appId)    获取小程序版本信息
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           modifyJumpDomain(string $appId,array $params)   配置小程序业务域名
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getLatestAuditStatus(string $appId)   查询审核结果
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getAccessToken(string $appId)   查询审核结果
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getOfficialAccessToken(string $appId)   查询审核结果
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           getScheme(string $appId,string $path,string $query)    获取小程序 scheme 码
+ *
+ * @method static \EasyWeChat\OpenPlatform\Authorizer\MiniProgram\Application           applyMsgPlugin(string $appId)   申请物流信息
+ *
+ * @see \App\Servers\Wechat\OpenPlat
+ * 
+ */
+class OpenPlat extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Wechat\OpenPlat';
+    }
+}
+
+?>

+ 30 - 0
app/Facades/Servers/Wechat/OpenWork.php

@@ -0,0 +1,30 @@
+<?php namespace App\Facades\Servers\Wechat;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ * 
+ * @method static \EasyWeChat\OpenWork\Application  getApp()                                获取应用实例
+ * @method static \EasyWeChat\Work\Application      getWork(string $authCorpId)             获取授权企企微实例
+ * @method static string                            getPermanentCode(string $authCorpId)    通过授权方企微ID获取永久授权码
+ * @method static array                             getAuthorization($authCorpId)           获取授权企业授权授权信息
+ * @method static string                            getErrmsg($errcode)                     获取错误信息
+ * 
+ * @see \App\Servers\Wechat\OpenWork
+ * 
+ */
+class OpenWork extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Wechat\OpenWork';
+    }
+}
+
+?>

+ 30 - 0
app/Facades/Servers/Wechat/OpenWorkWeb.php

@@ -0,0 +1,30 @@
+<?php namespace App\Facades\Servers\Wechat;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ * 
+ * @method static \EasyWeChat\OpenWork\Application  getApp()                                获取应用实例
+ * @method static \EasyWeChat\Work\Application      getWork(string $authCorpId)             获取授权企企微实例
+ * @method static string                            getPermanentCode(string $authCorpId)    通过授权方企微ID获取永久授权码
+ * @method static array                             getAuthorization($authCorpId)           获取授权企业授权授权信息
+ * @method static string                            getErrmsg($errcode)                     获取错误信息
+ * 
+ * @see \App\Servers\Wechat\OpenWorkWeb
+ * 
+ */
+class OpenWorkWeb extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Wechat\OpenWorkWeb';
+    }
+}
+
+?>

+ 31 - 0
app/Facades/Servers/Wechat/QuestionMini.php

@@ -0,0 +1,31 @@
+<?php namespace App\Facades\Servers\Wechat;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ * 
+ * @method static string getUrlLink($path,$query='')     获取UrlLink
+ * @method static array  getUserPhone($code)        手机号授权
+ * @method static string  getAccessToken()          获取AccessToken
+ * @method static mixed  queryUrlLink($urlLink)     查询加密UrlLink
+ * @method static mixed  getUnlimit(string $scene, array $optional = [])                获取小程序码
+ * 
+ * 
+ * @see \App\Servers\Wechat\QuestionMini
+ * 
+ */
+class QuestionMini extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\Wechat\QuestionMini';
+    }
+}
+
+?>

+ 32 - 0
app/Facades/Servers/WeiBan/OpenApi.php

@@ -0,0 +1,32 @@
+<?php namespace App\Facades\Servers\WeiBan;
+
+use Illuminate\Support\Facades\Facade;
+
+/**
+ * 对象存储
+ * 
+ * @method static string getAccessToken()                                                                   获取凭证
+ * @method static array  getUserList(int $limit,int $offset,int $startTime=0,string $source='remark')       获取用户列表
+ * @method static array  getUserDetail(string $id)                                                          获取用户详情
+ * @method static array  getUserListByPhone($phone)                                                         通过手机获取用户列表
+ * @method static array  getTagList(int $limit=100,int $offset=0)                                           获取标签列表
+ * @method static array  markTags($extUserid,$addTags,$rmTags,$staffId)                                     给客户打标签
+ * @method static array  addTag($staffId,$extUserid,$tagGroup,$tagName)                                     给客户添加企业标签
+ * 
+ * @see \App\Servers\WeiBan\OpenApi
+ * 
+ */
+class OpenApi extends Facade
+{
+    /**
+     * Get the registered name of the component.
+     *
+     * @return string
+     */
+    protected static function getFacadeAccessor()
+    {
+        return '\App\Servers\WeiBan\OpenApi';
+    }
+}
+
+?>

+ 402 - 0
app/Helpers/functions.php

@@ -0,0 +1,402 @@
+<?php
+
+use App\Facades\Servers\Logs\Log;
+use Illuminate\Support\Facades\DB;
+use Intervention\Image\Facades\Image;
+/**
+ * 响应请求
+ * @param	mixed		$data	数据
+ * @param	string		$code	状态码
+ * @param	mixed		$log	是否记录日志
+ * 
+ * */
+function json_send($data,$code=200){
+    // 日志名称
+    $name                   = empty($data['code']) ? 'unknown' : $data['code'];
+    // 获取提示消息
+    $msg                    = empty($data['msg']) ? '' : $data['msg'];
+    // 如果提示数据不是成功或者是local 
+    if( ($name != 'success' && $name != 'retry' ) || config('app.env') == 'local' ) {
+        // 消息key
+        $msgKey             = 'message.'.$msg;
+        // 提示信息本地化
+        $trans              = trans($msgKey);
+        // 如果没有找到对应的提示,使用原提示信息
+        $data['msg']        = ( $trans == $msgKey ) ? $msg : $trans;
+        // 消息
+        $msg                = request()->ip().' ' .request()->method().' '. request()->getPathInfo().' '.$msg;
+        // 记录内容
+        $content            = ['param'=>request()->all(),'return'=>$data,'user_agent'=>request()->header('user-agent')];
+        // 记录日志
+        Log::info($name,$msg,$content,['filename'=>str_ireplace('/','_',trim(request()->getPathInfo(),'/'))]);
+    }
+    // 默认返回数据
+    $data['data']           = isset($data['data']) ? $data['data'] : '';
+	// 返回数据
+	return			        response()->json($data,$code,[],JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
+}
+
+/**
+ * 雪花算法生成分布式ID
+ * 
+ * @param	int		    $dataCenterId   数据中心ID 0-31
+ * @param	int		    $machineId     任务进程ID 0-31
+ * 
+ * @return	string		分布式ID
+ * */
+function make_snow_flake($dataCenterID=0,$machineId=0){
+    // 获取redis配置
+    $redis              = config('database.redis.default');
+    // 主机地址
+    $config['host']     = empty($redis['host'])  ? '127.0.0.1' : $redis['host'];
+    // 端口
+    $config['port']     = empty($redis['port'])  ? 6379 : $redis['port'];
+    // 密码
+    if( !empty($redis['password']) )  $config['auth']       = $redis['password'];
+    // 数据库
+    if( !empty($redis['database']) )  $config['dbIndex']    = $redis['database'];
+    // 实例化
+    $IdWorker           = \wantp\Snowflake\IdWorker::getIns($dataCenterID,$machineId);
+    // 生成
+    return              $IdWorker->setRedisCountServer($config)->id();
+}
+
+
+/**
+ * 终端设备系统
+ * 
+ * @param	string		$agent 		HTTP_USER_AGENT数据
+ * 
+ * */
+function get_agent_system($agent=''){
+    //HTTP_USER_AGENT数据
+    $agent								= $agent ? $agent : request()->header('user-agent');
+    //iOS
+    if( stripos($agent, 'iphone') || stripos($agent, 'ipad') ) return 'iOS';
+    //iOS
+    if( stripos($agent, 'macintosh') )	return 'MacOS';
+    //安卓
+    if( stripos($agent, 'android') ) 	return 'Android';
+    //安卓
+    if( stripos($agent, 'okhttp') ) 	return 'Android';
+    //Windows
+    if( stripos($agent, 'win') ) 		return 'Windows';
+    //Linux
+    if( stripos($agent, 'linux') )		return 'Linux';
+    //Unix
+    if( stripos($agent, 'unix') )		return 'Unix';
+    //Other
+    return 'Other';
+}
+
+/**
+ * 路径兼容输出
+ * 
+ * @param	string	    $url	文件路径
+ * @param	int|null	$width  新图片宽度
+ * @param	int|null	$height 新图片高度(如果没有填写高度,把高度等比例缩小)
+ * @param	string		$type 	文件类型
+ * 
+ * @author	刘相欣
+ * */
+function path_compat( $url,$witdh=null,$higth=null,$type='') {
+    // 没有路径的统一返回原数据
+    if( !$url )             return $type ? $url : request()->root().'/'.ltrim(resize($url,$witdh,$higth),'/');
+    // 如果是带有http的路径,直接返回
+    if( stripos('$'.$url, 'http') ) {
+        // 腾讯云链
+        if( stripos( $url, 'myqcloud.com') ){
+            // 开启全球域名全球加速时, 替换COS全球加速域名
+            if( config('open_accelerate','') )  $url = str_ireplace('.cos.'.config('tencent.cos.region','').'.myqcloud.com','.cos.accelerate.myqcloud.com',$url);
+            // 如果需要开启CDN,局部开启,全局开启
+            if( request()->is('api/*') && (request('open_cdn',0) || config('open_cdn',0) ) && config('open_cdn_domain','') ){
+                // 先把https 换成 http
+                $url = str_ireplace('https','http',$url);
+                // 替换域名
+                $url = str_ireplace(config('tencent.cos.bucket','').'-'.config('tencent.cos.app_id','').'.cos.'.config('tencent.cos.region','').'.myqcloud.com',config('open_cdn_domain',''),$url);
+            }
+        }
+        // 获取扩展信息
+        $type		        = strtolower(pathinfo($url, PATHINFO_EXTENSION));
+        // 如果信息不存在
+        if( !$type )        return $url;
+        // 不是图片直接返回路径
+        if( !in_array($type,['jpg','png','jpeg','gif','bmp']) ) return $url;
+        // 需要后缀的OSS参数
+        $param		        = '';
+        // 阿里云的OSS
+        if( stripos( $url, 'aliyuncs.com') ){
+            // 判断图片的宽高进行缩放处理
+            if( $witdh && $higth ){
+                // 宽高都存在时,固定宽高,将延伸出指定w与h的矩形框外的最小图片进行居中裁剪
+                $param      = '?x-oss-process=image/resize,m_fill,limit_0,w_'.$witdh.',h_'.$higth;
+            }else if($witdh){
+                $param      = '?x-oss-process=image/resize,w_'.$witdh;
+            }else if($higth){
+                $param      = '?x-oss-process=image/resize,h_'.$higth;
+            }
+        }
+        // 腾讯云的COS
+        if( stripos( $url, 'myqcloud.com') ){
+            if( $witdh && $higth ){
+                // 宽高都存在时,固定宽高,将延伸出指定w与h的矩形框外的最小图片进行居中裁剪
+                $param	= '?imageView2/2/w/'.$witdh.'/h/'.$higth;
+            }else if($witdh){
+                $param	= '?imageView2/2/w/'.$witdh;
+            }else if($higth){
+                $param	= '?imageView2/2/h/'.$higth;
+            }
+        }
+        // 路径
+        return $param ? $url.$param : $url;
+    }
+    // 类型
+    $type		        = strtolower(pathinfo($url, PATHINFO_EXTENSION));
+    // 是图片则返回缩略图
+    if( in_array($type,['jpg','png','jpeg','gif','bmp']) )  return rtrim((config('img_cdn_domain')?config('img_cdn_domain'):request()->root()),'/').'/'.ltrim(resize($url,$witdh,$higth),'/');
+    // 判断是否存在文件
+    if( file_exists('uploads/'.$url) )                      return request()->root().'/uploads/'.ltrim($url,'/');
+    // 其他类型文件,统一返回
+    return              request()->root().'/'.ltrim($url,'/');
+}
+
+/**
+ * 自动生成新尺寸 的图片
+ * @param string $filename 文件名
+ * @param int $width 新图片宽度
+ * @param int $height 新图片高度(如果没有填写高度,把高度等比例缩小)
+ */
+function resize( $filename, $width=null, $height=null) {
+    // 如果不存在图片路径
+    if( !is_file(DIR_IMAGE.$filename)||empty($filename) )  $filename = config('app.no_pic');
+    // 如果没有宽高,给原图
+    if( !$width && !$height ) return 'uploads/'.$filename;
+    // 调整当前图像的边界到给定的宽和高
+    $img        = Image::make(DIR_IMAGE.$filename)->resizeCanvas($width, $height);
+    // 获取文件名称
+    $name       = pathinfo($filename,PATHINFO_FILENAME);
+    // 缓存名
+    $cacheName  = $width ? $name.'_w' .$width : $name;
+    $cacheName  = $height ? $cacheName.'_h' .$height : $cacheName;
+    // 名称拼接宽高
+    $filename   = str_replace($name,$cacheName,$filename);
+    // 缓存文件路径
+    $newFile    = 'cache/'.$filename;
+    // 如果目录不存在,创建目录
+    if( !is_dir(dirname(DIR_IMAGE.$newFile)) ) mkdir(dirname(DIR_IMAGE.$newFile),0755,true);
+    // 保存新图
+    $img->save(DIR_IMAGE.$newFile);
+    // 返回结果
+    return      'uploads/'.$newFile;
+}
+
+
+/**
+ * 新检查用户操作权限
+ *
+ * @param int        $uid  账号ID
+ * @param string     $module  模块
+ * 
+ */
+function is_super($uid,$module='manager'){
+    switch ($module){
+        case 'manager':
+            $AdminUserModel      = new App\Models\Manager\AdminUser();
+            break;
+        case 'company':
+            $AdminUserModel      = new App\Models\Manager\AdminUserCompany();
+            break;
+        case 'shop':
+            $AdminUserModel      = new App\Models\Manager\AdminUserShop();
+            break;
+    }
+    // 返回结果
+    $isSuper                    = $AdminUserModel->query()->where([['uid','=',$uid]])->value('is_super');
+    // 默认无权限
+    return                      $isSuper ? true : false;
+}
+
+/**
+ * 新检查用户操作权限
+ *
+ * @param int        $uid  账号ID
+ * @param string     $uid  模块
+ */
+function allow_show_phone($uid,$module='manager'){
+    switch ($module){
+        case 'manager':
+            $AuthGroupAccess      =   new App\Models\Manager\AuthGroupAccess();
+            break;
+        case 'company':
+            $AuthGroupAccess      =   new App\Models\Manager\AuthGroupAccessCompany();
+            break;
+        case 'shop':
+            $AuthGroupAccess      =   new App\Models\Manager\AuthGroupAccessShop();
+            break;
+    }
+    // 返回结果
+    return                        $AuthGroupAccess->showPhoneByUid($uid);
+}
+
+
+/**
+ * 把返回的数据集转换成Tree
+ * @param	array		$list	要转换的数据集
+ * @param	string	    $pid	parent标记字段
+ * @param	string	    $level	level标记字段
+ * @return	array
+ * 
+ * @author	麦当苗儿 <zuojiazi@vip.qq.com>
+ * 
+ */
+function list_to_tree($list, $pk='id', $pid = 'pid', $child = 'children', $root = 0) {
+    // 创建Tree
+    $tree = array();
+    if(is_array($list)) {
+        // 创建基于主键的数组引用
+        $refer = array();
+        foreach ($list as $key => $data) {
+            $refer[$data[$pk]] =& $list[$key];
+        }
+        foreach ($list as $key => $data) {
+            // 判断是否存在parent
+            $parentId =  $data[$pid];
+            if ($root == $parentId) {
+                $tree[] =& $list[$key];
+            }else{
+                if (isset($refer[$parentId])) {
+                    $parent =& $refer[$parentId];
+                    $parent[$child][] =& $list[$key];
+                }
+            }
+        }
+    }
+    return $tree;
+}
+
+/**
+ * 格式化字节
+ * @param	int  $byte 		字节数值
+ * 
+ * */
+function byte_format($byte){
+    // 如果是GB
+    if( $byte >= (1024 * 1024 * 1024) )		return round( $byte / (1024 * 1024 * 1024),2 ).' GB';
+    // 如果是MB
+    if( $byte >= (1024 * 1024) )			return round( $byte / (1024 * 1024),2 ).' MB';
+    // 如果是KB
+    if( $byte >= 1024 )					    return round( $byte / (1024),2 ).' KB';
+    // 如果是字节
+    return $byte.' B';
+}
+
+/**
+ * 二维数组排序,根据某个字段
+ * @param   array       $array      要排序的数组
+ * @param   string      $key        要排序的键字段
+ * @param   string      $sort       排序类型 SORT_ASC SORT_DESC 
+ * @return  array       排序后的数组
+ */
+function array_sort($array, $key, $sort = SORT_DESC) {
+    // 获取字段数据
+    $keys       = array_column($array,$key);
+    // 进行排序
+    array_multisort($keys, $sort, $array);
+    // 返回结果
+    return      $array;
+}
+
+/**
+ * 二维数组排序,根据某个字段
+ * @param   string      $phoneNumber        手机号
+ */
+function hide_phone($phoneNumber) {
+    return substr_replace($phoneNumber, '****', 3, 4);
+}
+
+
+/**
+ * 省份去除自治区等后缀
+ * 
+ * @param   string      $province       省份
+ * 
+ */
+function province_rtrim($province) {
+    // 长度2个字的不处理
+    if( mb_strlen($province) <= 2 ) return $province;
+    // 省份信息
+    $province   = str_ireplace(['省','回族','维吾尔','壮族','自治区','特别行政区'],'',$province);
+    // 返回结果
+    return      $province;
+}
+
+/**
+ * 城市去除自治州等后缀
+ * 
+ * 
+ * @param   string      $city       地级市单位
+ * 
+ */
+function city_rtrim($city) {
+    // 长度2个字的不处理
+    if( mb_strlen($city) <= 2 ) return $city;
+    // 名称
+    $result             = mb_strrchr($city,'市',true);
+    // 返回结果
+    if( $result )       return $result;
+    // 名称
+    $result             = mb_strrchr($city,'自治州',true);
+    // 返回结果
+    if( $result )       return $result;
+    // 名称
+    $result             = mb_strrchr($city,'盟',true);
+    // 返回结果
+    if( $result )       return $result;
+    // 名称
+    $result             = mb_strrchr($city,'地区‌',true);
+    // 返回结果
+    if( $result )       return $result;
+    // 返回结果
+    return              $city;
+}
+
+/**
+ * 将字符串追加到某个需要切割成数组的字符串数组
+ * 
+ * 
+ * @param   string      $dest    目标字符串
+ * @param   string      $source  需要追加的字符串
+ * @param   string      $sep     目标字符串切割方式
+ * 
+ */
+function push_str_arr($dest,$source,$sep=',') {
+    // 转数组
+    $result			= $dest ? explode($sep,$dest) : [];
+    $result[]		= $source;
+    $result			= array_values(array_unique($result));
+    // 返回结果
+    return          $result;
+}
+
+
+/**
+ * 多行多条数据范围转数组
+ * 
+ * @param   string      $scope      范围字符串
+ * 
+ * @return  array       范围数组
+ */
+function scope_to_array($scope){
+    // 兼容逗号问题
+    $scope				= str_ireplace(',',',',$scope);
+    $scope				= str_ireplace("\r\n",',',$scope);
+    $scope				= str_ireplace("\n",',',$scope);
+    // 转数组处理
+    $scope				= explode(',',$scope);
+    // 去重
+    $scope				= array_unique($scope);
+    // 返回结果
+    return              array_values($scope);
+}
+
+?>

+ 201 - 0
app/Http/Controllers/Api/Api.php

@@ -0,0 +1,201 @@
+<?php namespace App\Http\Controllers\Api;
+
+use App\Exceptions\Api\VerifySignException;
+use App\Exceptions\Api\LoginException;
+use App\Exceptions\Api\ApiException;
+use App\Facades\Servers\Encrypts\AccessToken;
+use App\Facades\Servers\Encrypts\ApiSign;
+use App\Http\Controllers\Controller;
+
+/**
+ * 接口控制器
+ * 
+ * @author 刘相欣
+ * 
+ * */
+class Api extends Controller{
+
+	/**
+	 * 登录验证
+	 * 
+	 * @return	Int			$authcode		登录信息
+	 * 
+	 */
+	public function checkLogin($authcode=''){
+		// 默认接参
+		$authcode											= $authcode ? $authcode : request('authcode','');
+        $port                                               = request('port','');
+		// 如果没有有登录信息
+		if( !$authcode )									throw new LoginException("please_login");
+		// 解码
+		$decode												= AccessToken::decode($authcode);
+		// 错误返回
+		if( isset($decode['error']) )						throw new LoginException("login_error");
+		// 如果没有过期时间,过期处理
+		if( empty($decode['exp']) )							throw new LoginException("login_exprie");
+		// 如果过期,过期处理
+		if( $decode['exp'] <= time() )						throw new LoginException("login_exprie");
+		// 登录的商户ID
+		$loginCompanyId										= empty($decode['company_id']) ? 0 : $decode['company_id'];
+		// 如果登录的商户不一致,提醒重新登录
+		if( $loginCompanyId != request('company_id',0) )	throw new LoginException("please_login");
+		// 从缓存获取登录数据
+		$memAuthcode 										= (new Custom())->getLoginAuthCodeByUid($decode['sub'],$port);
+		// 不存在登录信息,提示失败
+		if( !$memAuthcode )									throw new LoginException("login_miss");
+		// 如果是32位的话
+		if( strlen($memAuthcode) == 32 )					$authcode = md5($authcode);
+		// 如果存在登录数据,并且不是当前用户的缓存登录,提示失败
+		if( $authcode != $memAuthcode )						throw new LoginException("login_miss");
+		// 返回用户ID
+		return												$decode['sub'];
+	}
+
+	/**
+	 * 登录验证
+	 * 
+	 * @return	Int		用户ID
+	 * 
+	 */
+	public function getUid($authcode=''){
+		// 默认接参
+		$authcode											= $authcode ? $authcode : request('authcode','');
+		// 如果没有有登录信息
+		if( !$authcode )									return 0;
+		// 尝试执行
+		try {
+			// 解码
+			$decode											= AccessToken::decode($authcode);
+			// 错误返回
+			if( isset($decode['error']) )					return 0;
+			// 如果没有过期时间,过期处理
+			if( empty($decode['exp']) )						return 0;
+			// 如果过期,过期处理
+			if( $decode['exp'] <= time() )					return 0;
+			// 登录的商户ID
+			$loginCompanyId									= empty($decode['company_id']) ? 0 : $decode['company_id'];
+			// 如果登录的商户不一致,提醒重新登录
+			if( $loginCompanyId != request('company_id',0)) return 0;
+			// 返回用户ID
+			return											$decode['sub'];
+		} catch (\Throwable $th) {
+			// 返回0
+			return											0;
+		}
+	}
+
+	/**
+	 * 接口验签
+	 * 
+	 * @param		[Array]		$data		参与验证的参数
+	 * 
+	 * 
+	 * */
+	public function verify_sign($data=[]){
+		// 获取参数
+		$data									= $data ? $data : request()->all();
+		// 没有签名
+		if( empty($data['sign']) )				throw new VerifySignException('sign_miss');
+		// 没有签名
+		if( empty($data['appid']) )				throw new VerifySignException('appid_miss');
+		// 传了key
+		if( !empty($data['key']) )				throw new VerifySignException('appkey_no_allow');
+		// 没有请求时间
+		if( empty($data['timestamp'])  )        throw new VerifySignException('sign_timeout');
+        // 请求过期
+		if( abs(time() -$data['timestamp'] ) > 86400 ) throw new VerifySignException('sign_timeout');
+        // 已使用过的签名不允许二次使用
+		$this->checkSignUsed($data['sign']);
+		// 从配置读取接口验签应用数据
+		$verify 								= ['2DEA6A19BDCA383B'=>'FD6F87E89EABAF1063A849D971B68618'];
+		// 是否有对应的appid
+		if( !isset($verify[$data['appid']]) )	throw new VerifySignException('appid_nofund');
+		// appkey
+		$appkey									= $verify[$data['appid']];
+		// 获得签名参数数据
+		$sign									= $data['sign'];
+		// 删除传入签名参数
+		unset($data['sign']);
+		// 删除上传文件的字段
+		if( isset($data['file']))				unset($data['file']);
+		// 键名字典排序
+		ksort($data);
+		// 空字符串
+		$param									= '';
+		// 循环参数对象,拼接url字符串
+		foreach ( $data as $key => $value ) {
+			// 如果键值为空(空字符串 空数组 空集合 空对象 字符串0 数字0 NULL FALSE),此参数省略;另 '000' 这类0组成的字符串 不为空
+			if( strtolower($value)  == 'false' || strtolower($value)  == 'null' || empty($value) || ( is_numeric($value) && $value == 0 && substr_count($value,'.') == 1 ) ) continue;
+			// 判断键值是否是数组,对象 ,如果是的话,键值转json字符串
+			if( is_array( $value ) || is_object($value) ) $value = json_encode($value,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
+			// 如果键值为真则 键值转md5加密(此处小写) 为假则直接拼接,0特殊处理
+			$param  .= '&'.$key.'='.$value;
+		}
+		// 转码
+		$param									= ltrim($param,'&');
+		// 解码
+		$param									= rawurldecode($param);
+		// 拼接私钥数据
+		$param									.= '&key='.$appkey;
+		// 比对签名,数据加密(MD5后转大写),$param.'=>'.strtoupper(md5($param)).'=>'.$sign
+		if( strtoupper(md5($param)) != $sign)	throw new VerifySignException('sign_error');
+		// 验签通过
+		return									['success'=>'验签成功'];
+	}
+
+	/**
+	 * 判断签名是否已用
+	 * 
+	 * @param		[Array]		$data		参与验证的参数
+	 * 
+	 * 
+	 * */
+	public  function checkSignUsed($sign='')	{
+		// 获取签名
+		$is_used								= cache('ApiSign:'.$sign);
+		// 如果签名已用
+		if( $is_used )							throw new VerifySignException('sign_used');
+		// 存储已经验证过的签名
+		cache(['ApiSign:'.$sign=>$sign],60);
+		// 成功返回
+		return									['success'=>'签名可用'];
+	}
+
+	/**
+	 * 接口验签
+	 * 
+	 * @param	Array		$data		参与验证的参数
+	 * 
+	 * 
+	 * */
+	public function sign($data)					{
+		// 返回加密结果
+		return									ApiSign::encode($data);
+	}
+
+    /**
+     * 店铺验证
+     *
+     */
+    public function checkShop($uid='', $shopId=''){
+		// 商户ID
+		$companyId										    = request('company_id',0);
+        // 默认接参
+        $shopId											    = $shopId ? $shopId : request('shop_id',0);
+		// 访问记录
+		$CustomShopRecord									= (new CustomShopRecord());
+		// 如果没有店铺,获取用户记录最后访问的店铺
+        if (!$shopId)  										$shopId = $uid ? $CustomShopRecord->query()->where(['company_id'=>$companyId,'custom_uid'=>$uid])->orderByDesc('visit_time')->value('shop_id') : $shopId;
+		// 如果还是没有店铺,获取商户下的第一个店铺
+		if (!$shopId)                                       $shopId = (new Shop())->query()->where(['company_id'=>$companyId])->orderBy('id')->value('id');
+		// 如果有shopId
+        if ( $shopId && $uid )                     			{
+            // 判断是不是有对应的店铺数据
+            $recordId                       				= $CustomShopRecord->where(['custom_uid'=>$uid,'company_id'=>$companyId,'shop_id'=>$shopId])->value('id');
+            // 更新还是新增
+            $recordId                       				? $CustomShopRecord->edit($recordId,['visit_time'=>time()]) : $CustomShopRecord->add(['custom_uid'=>$uid,'shop_id'=>$shopId,'company_id'=>$companyId,'visit_time'=>time()]); 
+        }
+        // 返回
+        return												['shop_id'=>$shopId,'company_id'=>$companyId];
+    }
+}

+ 236 - 0
app/Http/Controllers/Api/DrugReportInfoController.php

@@ -0,0 +1,236 @@
+<?php
+
+namespace App\Http\Controllers\Api;
+
+use App\Servers\DrugReportInfoService;
+use App\Models\DrugReportAss;
+use App\Servers\Aliyun\Oss;
+use Carbon\Carbon;
+use ZipArchive;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\DB;
+
+class DrugReportInfoController extends Api
+{
+    /**
+     * 查询医药报告选项
+     */
+    public function get_list(DrugReportAss $DrugReportAss)
+    {
+        $start_time                         = request('start_time', 0);
+        $end_time                           = request('end_time', 0);
+        $produce_ent_id                     = request('produce_ent_id', 0);
+        $from_ref_ent_id                    = request('from_ref_ent_id', 0);
+        $bill_code                          = request('bill_code', 0);
+        $physic_name                        = request('physic_name', 0);
+        $batch_no                           = request('batch_no', 0);
+        $drug_report_sign_status            = request('drug_report_sign_status');
+        $seal_status                        = request('seal_status');
+        $limit						        = request('limit',10);
+        $map                                = [];
+        if ($start_time){
+            $start_time                     = strtotime($start_time);
+            $map[]                          = ['drug_report_ass.bill_time','>=', $start_time];
+        }
+        if ($end_time){
+            $end_time                       = Carbon::parse($end_time)->endOfDay()->timestamp;
+            $map[]                          = ['drug_report_ass.bill_time','<=', $end_time];
+        }
+        if ($produce_ent_id)        $map[] = ['drug_report_ass.produce_ent_id','=', $produce_ent_id];
+        if ($from_ref_ent_id)       $map[] = ['drug_report_ass.from_ref_ent_id','=', $from_ref_ent_id];
+        if ($bill_code)             $map[] = ['drug_report_ass.bill_code','=', $bill_code];
+        if ($physic_name)           $map[] = ['drug_report_ass.physic_name','=', $physic_name];
+        if ($batch_no)              $map[] = ['drug_report_ass.batch_no','=', $batch_no];
+        if (!is_null($seal_status))              $map[] = ['drug_report_ass.seal_status','=', $seal_status];
+        $countMap                   = $map;
+        if (!is_null($drug_report_sign_status))              $map[] = ['drug_report_ass.drug_report_sign_status','=', $drug_report_sign_status];
+        $Paginator                  =   $DrugReportAss->query()
+            /*->leftJoin('drug_report_info', function ($join) {
+                $join->on('drug_report_ass.batch_no', '=', 'drug_report_info.batch_no')
+                    ->on('drug_report_ass.drug_id', '=', 'drug_report_info.drug_id');
+            })*/
+            ->where($map)
+            ->select( ['drug_report_ass.id','drug_report_ass.bill_code','drug_report_ass.batch_no','drug_report_ass.drug_id','drug_report_ass.crt_date',
+                'drug_report_ass.produce_ent_id','drug_report_ass.produce_ent_name','drug_report_ass.from_ref_ent_id',
+                'drug_report_ass.from_ent_name','drug_report_ass.drug_report_sign_status','drug_report_ass.seal_status',
+                'drug_report_ass.seal_report_url','drug_report_ass.report_name','drug_report_ass.physic_name','drug_report_ass.pkg_spec','drug_report_ass.prepn_spec','drug_report_ass.prepn_type_desc',
+                DB::raw('(
+                    SELECT drug_report_info.seal_report_url
+                    FROM drug_report_info
+                    WHERE drug_report_info.batch_no = drug_report_ass.batch_no
+                    AND drug_report_info.drug_id = drug_report_ass.drug_id
+                    ORDER BY drug_report_info.id DESC LIMIT 1
+                ) as info_seal_report_url'),
+                DB::raw('(
+                    SELECT drug_report_info.is_seal
+                    FROM drug_report_info
+                    WHERE drug_report_info.batch_no = drug_report_ass.batch_no
+                    AND drug_report_info.drug_id = drug_report_ass.drug_id
+                    ORDER BY drug_report_info.id DESC LIMIT 1
+                ) as is_seal')
+            ])
+            ->orderBy('id', 'desc')
+            ->paginate($limit);
+        foreach ($Paginator->items() as &$item){
+            if (in_array($item['seal_status'],[5,6])){
+                if ($item['info_seal_report_url']){
+                    $item['seal_report_url'] = $item['info_seal_report_url'];
+                }
+            }
+        }
+        $countData                  = $DrugReportAss->query()
+            ->where($countMap)
+            ->select(['drug_report_sign_status',DB::raw('count(*) as count')])
+            ->groupBy('drug_report_sign_status')
+            ->get();
+        $count                  = $DrugReportAss->query()
+            ->where($countMap)
+            ->count();
+        $data['count']                   = $count;
+        $data['pending_send_count']      = 0;
+        $data['pending_signed_count']    = 0;
+        $data['signed_count']            = 0;
+        $data['refuse_count']            = 0;
+        foreach ($countData as $value){
+            switch ($value['drug_report_sign_status']){
+                case 0:
+                    $data['pending_send_count'] = $value['count'];
+                    break;
+                case 2:
+                    $data['pending_signed_count'] = $value['count'];
+                    break;
+                case 3:
+                    $data['signed_count'] = $value['count'];
+                    break;
+                case 4:
+                    $data['refuse_count'] = $value['count'];
+                    break;
+            }
+
+        }
+        // 获取数据
+        $data['total']				= $Paginator->total();
+        $data['current_page']		= $Paginator->currentPage();
+        $data['per_page']			= $Paginator->perPage();
+        $data['last_page']			= $Paginator->lastPage();
+        $data['data']				= $Paginator->items();
+        return						json_send(['code'=>'success','msg'=>'获取成功','data'=>$data]);
+
+    }
+    /**
+     * 下载包
+     *
+     * */
+    public function down_excel(DrugReportAss $DrugReportAss)
+    {
+        $ids						        = request('ids','');
+        $list                       =   $DrugReportAss->query();
+        if($ids){
+            $ids        = explode(',',$ids);
+            $list       =   $list->whereIn('drug_report_ass.id',$ids);
+        }
+        $list           =   $list->orderBy('drug_report_ass.id', 'desc')
+            ->select( ['drug_report_ass.seal_status','drug_report_ass.bill_code','drug_report_ass.batch_no','drug_report_ass.drug_id','drug_report_ass.physic_name','drug_report_ass.crt_date','drug_report_ass.seal_report_url','drug_report_ass.report_name',
+                DB::raw('(
+                    SELECT drug_report_info.seal_report_url
+                    FROM drug_report_info
+                    WHERE drug_report_info.batch_no = drug_report_ass.batch_no
+                    AND drug_report_info.drug_id = drug_report_ass.drug_id
+                    ORDER BY drug_report_info.id DESC LIMIT 1
+                ) as info_seal_report_url')])
+            ->get()
+            ->toArray();
+        if (!$list)     return json_send(['code'=>'error','msg'=>'没有数据']);
+        $localUrl    = $this->down($list);
+        $fileName = basename($localUrl);
+        //上传oss
+        $oss = new Oss();
+        $url    = $oss->uploadFile($fileName,$localUrl,'','zip');
+        unlink($localUrl);
+        return json_send(['code'=>'success','msg'=>'下载成功','data'=>['url'=>$url]]);
+    }
+    /**
+     * 下载文件到本地
+     *
+     * */
+    public function down($list){
+        // 创建临时目录
+        $tempDir = public_path('/uploads/' . uniqid('drug_'));
+        if (!file_exists($tempDir)) {
+            mkdir($tempDir, 0755, true);
+        }
+        // 下载图片到临时目录
+        foreach ($list as $item) {
+            if (in_array($item['seal_status'],[5,6]) && $item['info_seal_report_url']){
+                $item['seal_report_url']    = $item['info_seal_report_url'];
+            }
+            if (!$item['seal_report_url']) continue;
+            try {
+                $content               = file_get_contents($item['seal_report_url']);
+                $localPath = $tempDir . '/' . $item['report_name'];
+                file_put_contents($localPath, $content);
+            } catch (\Exception $e) {
+                return json_send(['code'=>'error','msg'=>'下载失败','data'=>$e->getMessage()]);
+            }
+        }
+        // 创建 ZIP 文件
+        $zipPath = public_path('/uploads/zip/' . uniqid('drug_')).'.zip';
+        $zip = new ZipArchive();
+        if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
+            $files = scandir($tempDir);
+            foreach ($files as $file) {
+                if ($file != "." && $file != "..") {
+                    $filePath = $tempDir . '/' . $file;
+                    if (file_exists($filePath)) {
+                        $zip->addFile($filePath, $file);
+                    }
+                }
+            }
+            $zip->close();
+        }
+
+        // 清理临时目录
+        $this->deleteDir($tempDir);
+
+        return $zipPath;
+    }
+    /**
+     * 删除目录及其内容
+     */
+    private function deleteDir(string $dirPath): void
+    {
+        if (!is_dir($dirPath)) {
+            return;
+        }
+        $files = scandir($dirPath);
+        foreach ($files as $file) {
+            if ($file != "." && $file != "..") {
+                $filePath = $dirPath . '/' . $file;
+                if (is_dir($filePath)) {
+                    $this->deleteDir($filePath);
+                } else {
+                    unlink($filePath);
+                }
+            }
+        }
+        rmdir($dirPath);
+    }
+
+    /**
+     * 查询医药报告选项
+     */
+    public function option( DrugReportInfoService $service)
+    {
+        try {
+
+            $data = [
+                'produce_ent' => $service->getProduceEntIdOption(),
+                'from_ref_ent' => $service->getFromRefEntIdOption(),
+            ];
+            return						json_send(['code'=>'success','msg'=>'获取成功','data'=>$data]);
+        } catch (\Exception $e) {
+            return json_send(['code'=>'error','msg'=>'获取失败','data'=>$e->getMessage()]);
+        }
+    }
+}

+ 47 - 0
app/Http/Controllers/Api/Login.php

@@ -0,0 +1,47 @@
+<?php namespace App\Http\Controllers\Api;
+
+use App\Http\Controllers\Api\Api;
+use App\Models\Admin;
+
+/**
+ * 管理后台登录控制器
+ *
+ * @author 刘相欣
+ *
+ * */
+class Login extends Api {
+
+	/**
+	 * 登录方法				/api/login/index
+	 * 
+	 * @param string 	username	登录账号
+	 * @param string 	password	登录密码
+	 * 
+	 * */
+    public function index(Admin $AdminUser){
+		// 接收数据
+		$username							= request('username','');
+		// 接收数据
+		$password							= request('password','');
+		// 查询用户
+		$admin								= $AdminUser->orWhere('username',$username)->orWhere('phone',$username)->first(['uid','username','phone','status','password','insert_time','update_time']);
+        // 用户不存在
+		if( !$admin )	return json_send(['code'=>'error','msg'=>'密码错误或账号不存在']);
+		// 用户不存在
+		if( $admin['status'] ) 				return json_send(['code'=>'error','msg'=>'该账号已停用']);
+		// 转数组
+		$admin								= $admin->toArray();
+		// 比对密码
+		if( md5($password) != $admin['password']) return json_send(['code'=>'error','msg'=>'密码错误或账号不存在']);
+
+		// 登录
+		$accessToken 						= $AdminUser->Login($admin['uid'],'admin');
+		// 比对密码
+		if( isset($accessToken['error']) ) 	return json_send(['code'=>'error','msg'=>'登录失败','data'=>$accessToken['data']]);
+		// 登录名称
+		$accessToken['username']			= $admin['username'];
+		// 表单令牌
+		return								json_send(['code'=>'success','msg'=>'登录成功','data'=>$accessToken]);
+    }
+
+}

+ 181 - 0
app/Http/Controllers/Api/QueryDrugReportController.php

@@ -0,0 +1,181 @@
+<?php
+
+namespace App\Http\Controllers\Api;
+
+use App\Servers\QueryDrugReportService;
+use Carbon\Carbon;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use App\Servers\SyncDrugReportService;
+
+class QueryDrugReportController extends Api
+{
+    /**
+     * 查询上游企业的待签收药检报告信息
+     * (?begin_time=2025-11-01 00:00:00&end_time=2025-11-31 23:59:59&page=1&page_size=20)
+     * @param Request $request
+     * @param QueryDrugReportService $service
+     * @return JsonResponse
+     * @throws \Exception
+     */
+    public function queryDrugReport(Request $request, QueryDrugReportService $service)
+    {
+        $beginTime = $request->get("begin_time") ?? Carbon::now()->startOfMonth()->toDateTimeString();
+        $endTime = $request->get("end_time") ?? Carbon::now()->endOfMonth()->toDateTimeString();
+        $page = $request->get("page") ?? 1;
+        $pageSize = $request->get("page_size") ?? 20;
+        $data = $service->queryDrugReport($beginTime, $endTime, $page, $pageSize);
+        dd($data);
+//        return response()->json($data);
+    }
+
+    /**
+     * 查询上传报告信息接口
+     * (?begin_time=2025-03-01 00:00:00&end_time=2025-03-31 23:59:59&page=1&page_size=20&is_seal=0)
+     * @param Request $request
+     * @param QueryDrugReportService $service
+     * @return JsonResponse
+     * @throws \Exception
+     */
+    public function querySealDrugReport(Request $request, QueryDrugReportService $service)
+    {
+        $beginTime = $request->get("start_time") ?? Carbon::now()->startOfMonth()->toDateTimeString();
+        $endTime = $request->get("end_time") ?? Carbon::now()->endOfMonth()->toDateTimeString();
+        $page = $request->get("page") ?? 1;
+        $pageSize = $request->get("page_size") ?? 20;
+        $isSeal = $request->get("is_seal") ?? 0;
+        $data = $service->querySealDrugReport($beginTime, $endTime, $page, $pageSize, $isSeal);
+        dd($data);
+//        return response()->json($data);
+    }
+
+    /**
+     * 药检报告操作日志
+     * (?begin_date=2025-11-01&end_date=2025-11-31&page=1&page_size=20)
+     * @param Request $request
+     * @param QueryDrugReportService $service
+     * @return JsonResponse
+     * @throws \Exception
+     */
+    public function drugReportOptHistory(Request $request, QueryDrugReportService $service)
+    {
+        $beginDate = $request->get("start_time") ?? Carbon::now()->startOfMonth()->toDateString();
+        $endDate = $request->get("end_time") ?? Carbon::now()->endOfMonth()->toDateString();
+        $page = $request->get("page") ?? 1;
+        $pageSize = $request->get("page_size") ?? 20;
+        $data = $service->drugReportOptHistory($beginDate, $endDate, $page, $pageSize);
+        dd($data);
+//        return response()->json($data);
+    }
+    /**
+     * 查询上游出库单,支持以自己身份查询或者货主身份查询
+     * (?begin_time=2025-03-01 00:00:00&end_time=2025-03-31 23:59:59&page=1&page_size=20&is_seal=0)
+     * @param Request $request
+     * @param QueryDrugReportService $service
+     * @return JsonResponse
+     * @throws \Exception
+     */
+    public function queryDrugReportass(Request $request, QueryDrugReportService $service)
+    {
+        $beginTime = $request->get("start_time") ?? Carbon::now()->startOfMonth()->toDateTimeString();
+        $endTime = $request->get("end_time") ?? Carbon::now()->endOfMonth()->toDateTimeString();
+        $page = $request->get("page") ?? 1;
+        $pageSize = $request->get("page_size") ?? 20;
+        $isSeal = $request->get("is_seal") ?? 0;
+        $batchNo = $request->get("batch_no") ?? 0;
+        $drugId = $request->get("drug_id") ?? 0;
+        $time_type = $request->get("time_type") ?? "0";
+        $batch_no = $request->get("batch_no") ?? '';
+        $drug_id = $request->get("drug_id") ?? '';
+        $bill_code = $request->get("bill_code") ?? '';
+        $data = $service->querydrugreportass($beginTime, $endTime, $page, $pageSize, $isSeal,$time_type,$bill_code,$batch_no,$drug_id);
+        dd($data);
+//        return response()->json($data);
+    }
+    /**
+     * 同步上游出库单数据,支持以自己身份查询或者货主身份查询
+     * (?begin_time=2025-03-01 00:00:00&end_time=2025-03-31 23:59:59&page=1&page_size=20&is_seal=0)
+     * @param Request $request
+     * @param QueryDrugReportService $service
+     * @return JsonResponse
+     * @throws \Exception
+     */
+    public function syncDrugReportass(Request $request,SyncDrugReportService $SyncDrugReportService)
+    {
+        $source = $request->get('source', 'querydrugreportass');
+        $isFull = (bool)$request->get('full', true);
+        $startTime = $request->get('start_time') ?? Carbon::today()->startOfDay();
+        $endTime = $request->get('end_time') ?? ''; // Carbon::now()
+        $page = $request->get('page') ?? 1;
+        $pageSize = $request->get('page_size') ?? 20;
+
+        if (empty($source)) {
+            echo "参数错误,未指定来源[source]", PHP_EOL;
+            return Command::FAILURE;
+        }
+
+        echo $isFull ? "全量同步开始" : "增量同步开始", sprintf('[%d, %s, %s, %d, %d]……', $isFull, $startTime, $endTime, $page, $pageSize), PHP_EOL;
+
+        $count = $SyncDrugReportService->syncDrugReport($source, $isFull, $startTime, $endTime, $page, $pageSize);
+
+        echo $isFull ? "全量同步结束" : "增量同步结束", ",共处理 {$count} 条记录!", PHP_EOL;
+//        return response()->json($data);
+    }
+    /**
+     * 查询一张网报告操作日志
+     * (?begin_time=2025-03-01 00:00:00&end_time=2025-03-31 23:59:59&page=1&page_size=20&is_seal=0)
+     * @param Request $request
+     * @param QueryDrugReportService $service
+     * @return JsonResponse
+     * @throws \Exception
+     */
+    public function historyAll(Request $request, QueryDrugReportService $service)
+    {
+        $beginTime = $request->get("begin_time") ?? Carbon::now()->startOfMonth()->toDateTimeString();
+        $endTime = $request->get("end_time") ?? Carbon::now()->endOfMonth()->toDateTimeString();
+        $page = $request->get("page") ?? 1;
+        $pageSize = $request->get("page_size") ?? 20;
+        $isSeal = $request->get("is_seal") ?? 0;
+        $batchNo = $request->get("batch_no") ?? 0;
+        $drugId = $request->get("drug_id") ?? 0;
+        $data = $service->querydrugreportass($beginTime, $endTime, $page, $pageSize, $isSeal);
+        dd($data);
+//        return response()->json($data);
+    }
+    /**
+     * 查询一段时间内已经签收的单据
+     */
+    public function getSignedAss(Request $request, QueryDrugReportService $service)
+    {
+        $beginTime = $request->get("start_time") ?? Carbon::now()->startOfMonth()->toDateTimeString();
+        $endTime = $request->get("end_time") ?? Carbon::now()->endOfMonth()->toDateTimeString();
+        $page = $request->get("page") ?? 1;
+        $pageSize = $request->get("page_size") ?? 20;
+        $data = $service->queryDrugReportSignedAss($beginTime, $endTime, $page, $pageSize);
+        dd($data);
+//        return response()->json($data);
+    }
+    /**
+     * 查询药检报告签章信息
+     */
+    public function synergyDrugReport(Request $request, QueryDrugReportService $service)
+    {
+        $reportId = $request->get("report_id") ?? 0;
+        $data = $service->synergyDrugReport($reportId);
+        dd($data);
+//        return response()->json($data);
+    }
+    /**
+     * 查询已经签收但是还未盖章的单据
+     */
+    public function signednotstampedbill(Request $request, QueryDrugReportService $service)
+    {
+        $beginTime = $request->get("start_time") ?? Carbon::now()->startOfMonth()->toDateTimeString();
+        $endTime = $request->get("end_time") ?? Carbon::now()->endOfMonth()->toDateTimeString();
+        $page = $request->get("page") ?? 1;
+        $pageSize = $request->get("page_size") ?? 20;
+        $data = $service->signednotstampedbill($beginTime, $endTime, $page, $pageSize);
+        dd($data);
+//        return response()->json($data);
+    }
+}

+ 57 - 0
app/Http/Controllers/Controller.php

@@ -0,0 +1,57 @@
+<?php namespace App\Http\Controllers;
+
+// 基础控制器
+use Illuminate\Routing\Controller as BaseController;
+/**
+ * 控制器总控
+ * 
+ */
+class Controller extends BaseController
+{
+    /**
+	 * 构造方法,初始化
+	 * 
+	 * */
+	public function __construct(){
+		// 初始化
+		if( method_exists($this, '_initialize') ) $this->_initialize();
+		// 初始化
+		if( method_exists($this, '__init') ) $this->__init();
+	}
+
+
+	/**
+     * 操作错误跳转的快捷方法
+     * @param  Mixed     $msg 提示信息
+     * @param  String    $url 跳转的URL地址
+     * @param  Int   $wait 跳转等待时间
+	 * 
+     * @return Void
+     */
+    protected function error($msg = '', $url = null, $wait = 3)
+	{
+		if ( is_null($url) ) {
+            $url = request()->ajax() ? '' : 'javascript:history.back(-1);';
+        } elseif ('' !== $url) {
+            $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : url($url);
+        }
+		// 返回结果
+		return		view('error',['msg'=>$msg,'url'=>$url,'wait'=>$wait]);
+	}
+    /**
+     * 保存用户操作历史
+     *
+     * @param   Int         $uid        用户ID
+     * @param   String      $table      操作表
+     * @param   Int         $type       操作类型
+     * @param   Array       $oldData    旧数据
+     * @param   Array       $oldData    新数据
+     *
+     */
+    protected function addAdminHistory($uid,$table,$id,$type,$oldData,$newData) {
+        //记录详细操作历史
+        $AdminHistory			= (new AdminHistory());
+        // 记录数据
+        $AdminHistory->addAll($uid,$table,$id,$type,$oldData,$newData);
+    }
+}

+ 64 - 0
app/Http/Kernel.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace App\Http;
+
+use Illuminate\Foundation\Http\Kernel as HttpKernel;
+/**
+ * Http请求核心
+ * @property  array   $middleware           全局中间件
+ * @property  array   $middlewareGroups     中间分组
+ * @property  array   $routeMiddleware      路由中间件
+ * 
+ */
+class Kernel extends HttpKernel
+{
+    /**
+     * 全局HTTP中间件堆栈
+     *
+     * 这些中间件在应用程序的每个请求期间都会运行.
+     *
+     * @var array
+     */
+    protected $middleware = [
+        // 获取应该信任的主机
+       // \App\Http\Middleware\TrustHosts::class,
+        // 关于设置信任代理有关的中间件
+        //\App\Http\Middleware\TrustProxies::class,
+        // 设置跨域请求  config/cors.php 可配置
+        \Fruitcake\Cors\HandleCors::class,
+        // post数据大小
+        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
+        // 修剪字符串
+        \App\Http\Middleware\TrimStrings::class,
+        // 将空字符串字段转换为 null
+        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
+        // 读取语言环境
+        \App\Http\Middleware\LoadLocale::class,
+    ];
+
+    /**
+     * 应用程序的路由中间件组
+     *
+     * 这些中间件在应用程序的每个路由分组请求期间都会运行.
+     * @var array
+     */
+    protected $middlewareGroups = [
+        'api' => [
+            // 动态路由绑定
+            \Illuminate\Routing\Middleware\SubstituteBindings::class,
+            // 先验证登录
+            \App\Http\Middleware\Company\Login::class,
+        ],
+    ];
+
+    /**
+     * 应用程序的路由中间件
+     *
+     * 这些中间件可以分配给组或单独使用
+     *
+     * @var array
+     */
+    protected $routeMiddleware = [
+        
+    ];
+}

+ 80 - 0
app/Http/Middleware/Api/SwitchCompanyDb.php

@@ -0,0 +1,80 @@
+<?php
+
+
+namespace App\Http\Middleware\Api;
+
+
+use Illuminate\Http\Request;
+
+use Closure;
+
+class SwitchCompanyDb
+{
+    // 无需验证的路径
+    protected   $except     = [
+        'api/wechat_pay/notify',    // 微信支付回调无需通过验证
+        'api/wechat_transfer/notify',    // 微信支付回调无需通过验证
+        'api/question_bank/question_reception/wechat_transfer/notify',    // 题库微信支付回调无需通过验证
+        'api/login/wxop_login_uri',      // 微信公众号登录
+        'api/login/wxop_auth',      // 微信公众号登录
+        'api/question_bank/question_reception/orders/jsapi_notify',  //题库小程序微信支付回调
+        'api/live_push_notify/notify',        //直播推流回调
+        'api/short_link/get_origin_url',        //解析短链
+        'api/short_link/create'        //创建短链
+    ];
+
+    public function handle(Request $request, Closure $next)
+    {
+        // 当前路径
+        $path                                                           = ltrim($request->getPathInfo(), '/');
+        // 判断是否需要切换数据库
+        if ( in_array($path, $this->except) )                           return $next($request);
+        // 提现通知需要单独判断
+        if( stripos('&'.$path,'api/wechat_transfer/notify') == 1 )      return $next($request);
+        // 题库提现通知需要单独判断
+        if( stripos('&'.$path,'api/question_bank/question_reception/wechat_transfer/notify') == 1 )      return $next($request);
+        // 退款回调需要单独判断
+        if( stripos('&'.$path,'api/wechat_pay/refund_notify') == 1 )    return $next($request);
+        // 退款回调需要单独判断
+        if( stripos('&'.$path,'api/login/wxop_auth') == 1 )             return $next($request);
+        // 判断有没有companyid
+        $companyId                                                      = $request->input('company_id', 0);
+        // 没有获取appid对应的
+        if( !$companyId )                                               {
+            //从请求中获取appid
+            $appId                                                      = $request->input('app_id', '');
+            //通过corpId获取company_id
+            $companyId                                                  = $appId ? (new Component)->getOne($appId,'company_id') :  $companyId;
+            // 没有获取appid对应的
+            if( !$companyId )                                           {
+                // 店铺ID
+                $shopId                                                 = $request->input('shop_id', 0);
+                // 店铺查询商户ID
+                $companyId                                              = $shopId ? (new Shop)->getOne($appId,'company_id') : $companyId;
+                // 如果还是没有company_id
+                if( !$companyId )                                       {
+                    // 获取请求的域名来源
+                    $domain                                             = $request->server('HTTP_REFERER');
+                    // 如果有来源
+                    if( $domain )                                       {
+                        // 获取数据
+                        $domain                                         = parse_url($domain, PHP_URL_HOST);
+                        // 从域名中获取company_id
+                        $companyId                                      = $domain ? (new CompanyDomainConfig)->getOne($domain,'company_id') : $companyId;
+                    }
+                }
+            }
+        }
+        // 测试服默认访问商户1  大方无隅测试,正式服默认访问商户3 大方无隅测试
+        $companyId                              = $companyId ? (int) $companyId : (config('app.env','') == 'local' ? 1 : 3);
+        // 切换数据库
+        $result                                 = (new CompanyDbConfig)->reconnectDb($companyId); 
+        // 如果有错误
+        if ( isset($result['error']) )          return json_send(['code'=>'no_company','msg'=>$result['error']]);
+        // 商户ID
+        $request['company_id']                  = $companyId;
+        // 下一个中间件
+        return                                  $next($request);
+    }
+
+}

+ 111 - 0
app/Http/Middleware/Company/AccessAuth.php

@@ -0,0 +1,111 @@
+<?php
+
+namespace App\Http\Middleware\Company;
+
+use Closure;
+use Illuminate\Http\Request;
+use App\Models\Manager\AuthRuleCompany;
+
+// 访问权限验证
+class AccessAuth
+{
+    // 无需验证的路径
+    protected   $except     = [
+                                'company/login/index',
+                                'company/upload/sign_url',
+                                'company/shop/list',
+                                'company/auth_rule/list',
+                                'company/city/list',
+                                'company/product/list_except',
+                                'company/custom/index_except',
+                                'company/coupon/index_except',
+                                'company/product_class/get_list',
+                                'company/open_plat/install/wxwork_auth',
+                                'company/product/get_spec_html',
+                                'company/charging_record/index',
+                                'company/charging_record/get_data',
+                                'company/charging_record/create',
+                                'company/charging_record/get_paycode_url',
+                                'company/charging_record/get_detail',
+                                'company/charging_record/get_use_record',
+                                'company/orders/express_list',
+                                'company/tag/tags/get_all',
+                                'company/short_link/create',
+                                'company/company_limit/get_remind',
+                            ];
+    //默认配置
+    protected   $_config    = [
+                                'auth_on'           => true, // 认证开关
+                                'auth_type'         => 1, // 认证方式,1为实时认证;2为登录认证。
+                                'auth_group'        => 'auth_group', // 用户组数据表名
+                                'auth_group_access' => 'auth_group_access', // 用户-用户组关系表
+                                'auth_rule'         => 'auth_rule' // 权限规则表
+                            ];
+    /**
+	 * $prefix表前缀
+	 */
+    public function __construct()
+    {
+        // 判断配置
+        if ( config('AUTH_CONFIG') )        {
+            //可设置配置项 AUTH_CONFIG, 此配置项为数组。
+            $this->_config                  = array_merge($this->_config, config('AUTH_CONFIG'));
+        }
+    }
+
+
+
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle(Request $request, Closure $next)
+    {
+        // 当前路径
+        $path  = ltrim($request->getPathInfo(), '/');
+        // 小程序/公众号的授权需要单独判断
+        if( stripos('&'.$path,'company/open_plat/install/wxwork_auth') == 1 ) return $next($request);
+        // 判断是否需要验证登录
+        if ( !in_array($path, $this->except) ) {
+            // 用户ID
+            $uid                                                    = request('access_token.uid',0);
+            // 如果未登录
+            if( !$uid )                                             return json_send(['code'=>'no_login','msg'=>'请登录账号','data'=>['请您登录']]);
+            // 如果不是超级管理员
+            if ( !request('access_token.is_super',0) )              {
+                // 进行验证
+                if ( !$this->check($path, $uid ) )                  return json_send(['code'=>'error','msg'=>'您没有操作权限','data'=>['没有操作权限']]);
+            }
+        }
+        // 返回下一个闭包
+        return                              $next($request);
+    }
+
+    /**
+     * 检查权限
+     * @param name string|array  需要验证的规则列表
+     * @param uid  int           认证用户的id
+     * @return boolean           通过验证返回true;失败返回false
+     */
+    private function check($path, $uid)
+    {	
+        // 未开启验证,直接通过
+        if ( !$this->_config['auth_on'] )   return true;
+        // 获取用户需要验证的所有有效规则列表
+        $authList                           = (new AuthRuleCompany())->getAuthList($uid,'company');
+        // 切割path
+        $path                               = explode('/', $path);
+        // 没有控制器
+        if( count($path) < 2 )              $path[] = 'index';
+        // 没有方法
+        if( count($path) < 3 )              $path[] = 'index';
+        // 切割path
+        $path                               = implode('/', $path);
+        // 判断是否通过验证
+        return                              in_array($path, $authList);
+    }
+
+}

+ 69 - 0
app/Http/Middleware/Company/Login.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Http\Middleware\Company;
+
+use App\Facades\Servers\Encrypts\AccessToken;
+use Closure;
+use Illuminate\Http\Request;
+use App\Models\Admin;
+
+class Login
+{
+    // 无需验证的路径
+    protected   $except     = [
+                                'api/login/index',
+                            ];
+    //默认配置
+    protected   $_config    = [
+                                'auth_on'           => true, // 认证开关
+                                'auth_type'         => 1, // 认证方式,1为实时认证;2为登录认证。
+                                'auth_group'        => 'auth_group', // 用户组数据表名
+                                'auth_group_access' => 'auth_group_access', // 用户-用户组关系表
+                                'auth_rule'         => 'auth_rule' // 权限规则表     
+                            ];
+    /**
+	 * $prefix表前缀
+	 */
+    public function __construct()
+    {
+        // 判断配置
+        if ( config('AUTH_CONFIG') )        {
+            //可设置配置项 AUTH_CONFIG, 此配置项为数组。
+            $this->_config                  = array_merge($this->_config, config('AUTH_CONFIG'));
+        }
+    }
+
+
+
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * 
+     * @return mixed
+     */
+    public function handle(Request $request, Closure $next)
+    {
+        // 当前路径
+        $path  = ltrim($request->getPathInfo(), '/');
+        // 判断是否需要验证登录
+        if ( !in_array($path, $this->except) ) {
+            // 获取登录结果
+            $token                                  = (string) $request->input('access_token_admin','');
+            // 解码
+            $userInfo                               = AccessToken::decode($token);
+            // 判断登录时效
+            if( isset($userInfo['error']) )         return json_send(['code'=>'no_login','msg'=>'请您登录','data'=>$userInfo['error']]);
+            if( $userInfo['type'] != 'admin' )    return json_send(['code'=>'no_login','msg'=>'请您登录','data'=>'登录失效']);
+            if( $userInfo['expire'] < time() )      return json_send(['code'=>'no_login','msg'=>'登录失效,请您重新登录','data'=>'登录失效']);
+            // 是否是超管
+            //$userInfo['is_super']                   = is_super($userInfo['uid'],'admin') ? 1 : 0;
+            // 追加入
+            $request['access_token']                = $userInfo;
+        }
+        // 返回下一个闭包
+        return                                      $next($request);
+    }
+
+}

+ 22 - 0
app/Http/Middleware/EncryptCookies.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
+
+
+/**
+ * Cookies加密
+ */
+class EncryptCookies extends Middleware
+{
+
+    /**
+     * 不应加密的Cookie的名称
+     *
+     * @var array
+     */
+    protected $except = [
+        //
+    ];
+}

+ 69 - 0
app/Http/Middleware/LoadLocale.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\App;
+
+/**
+ * 读取语言环境
+ * 
+ */
+class LoadLocale
+{
+    /**
+     * 处理传入请求
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @return mixed
+     */
+    public function handle(Request $request, Closure $next)
+    {
+        // 设置语言
+        $request    = $this->setLocale($request);
+        // 返回结果
+        return      $next($request);
+    }
+    
+    /**
+     * 自动侦测当前语言
+     * @param  \Illuminate\Http\Request  $request
+     */
+    private function setLocale(Request $request){
+        // 默认语言
+        $langSet = config('lang.default_lang','en');
+        // 多语言自动侦测变量名
+        if ($request->get(config('lang.detect_var','lang'))) {
+            // url中设置了语言变量
+            $langSet = strtolower($request->get(config('lang.detect_var','lang')));
+        } elseif ($request->header(config('lang.header_var','lang'))) {
+            // Header中设置了语言变量
+            $langSet = strtolower($request->header(config('lang.header_var','lang')));
+            
+        } elseif ($request->cookie(config('lang.cookie_var','lang'))) {
+            // Cookie中设置了语言变量
+            $langSet = strtolower($request->cookie(config('lang.cookie_var','lang')));
+        } elseif ($request->server('HTTP_ACCEPT_LANGUAGE')) {
+            $matches = [];
+            // 自动侦测浏览器语言
+            $match = preg_match('/^([a-z\d\-]+)/i', $request->server('HTTP_ACCEPT_LANGUAGE'), $matches);
+            // 如果有匹配语言,获取第一条语言
+            if ($match)  $langSet = strtolower($matches[1]);
+        }
+        // 统一分割符号
+        $langSet         = str_ireplace('_','-',$langSet);
+        // 允许语言列表
+        $allow_lang_list = config('lang.allow_lang_list');
+        // 如果不在允许语言内,使用默认语言
+        if( !empty($allow_lang_list) && !in_array($langSet,$allow_lang_list) ) $langSet = config('lang.default_lang','en');
+        // 请求语言设置为最终语言
+        $request->lang   = $langSet;
+        // 设置语言
+        App::setLocale($langSet);
+        // 返回结果
+        return $request;
+    }
+
+
+}

+ 17 - 0
app/Http/Middleware/PreventRequestsDuringMaintenance.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
+
+class PreventRequestsDuringMaintenance extends Middleware
+{
+    /**
+     * 启用维护模式时应可访问的URI。
+     *
+     * @var array
+     */
+    protected $except = [
+        //
+    ];
+}

+ 32 - 0
app/Http/Middleware/RedirectIfAuthenticated.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use App\Providers\RouteServiceProvider;
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+class RedirectIfAuthenticated
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \Closure  $next
+     * @param  string|null  ...$guards
+     * @return mixed
+     */
+    public function handle(Request $request, Closure $next, ...$guards)
+    {
+        $guards = empty($guards) ? [null] : $guards;
+
+        foreach ($guards as $guard) {
+            if (Auth::guard($guard)->check()) {
+                return redirect(RouteServiceProvider::HOME);
+            }
+        }
+
+        return $next($request);
+    }
+}

+ 23 - 0
app/Http/Middleware/TrimStrings.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
+
+// 修剪字符串
+class TrimStrings extends Middleware
+{
+    /**
+     * 不应修剪的属性的名称。
+     *
+     * @var array
+     */
+    protected $except = [
+        // 密码空格不做修剪
+        'current_password',
+        'password',
+        'password_confirmation',
+        // 兼容App端 创建订单时候,终端名称误带后缀空格倒是签名失败
+        'client',
+    ];
+}

+ 20 - 0
app/Http/Middleware/TrustHosts.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Http\Middleware\TrustHosts as Middleware;
+
+class TrustHosts extends Middleware
+{
+    /**
+     * 获取应该信任的主机模式
+     *
+     * @return array
+     */
+    public function hosts()
+    {
+        return [
+            $this->allSubdomainsOfApplicationUrl(),
+        ];
+    }
+}

+ 24 - 0
app/Http/Middleware/TrustProxies.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Fideloper\Proxy\TrustProxies as Middleware;
+use Illuminate\Http\Request;
+
+// 关于设置信任代理有关的中间件
+class TrustProxies extends Middleware
+{
+    /**
+     * 此应用程序的受信任代理。
+     *
+     * @var array|string|null
+     */
+    protected $proxies;
+
+    /**
+     * 应用于检测代理的标头(headers)
+     *
+     * @var int
+     */
+    protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
+}

+ 52 - 0
app/Http/Requests/Api/Coupon.php

@@ -0,0 +1,52 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+/**
+ * 优惠券验证器
+ * 
+ */
+class Coupon extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 'name'       => 'string|max:20',
+            'coupon_id'     => 'required|integer|gt:0',
+            'page'          => 'integer|gte:0',
+            'limit'         => 'integer|gte:0',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_product'         => ['coupon_id','page','limit'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'coupon_id.required'   		=> '请选择优惠券',
+            'coupon_id.integer'   		=> '优惠券ID格式错误',
+            'coupon_id.gt'   		    => '优惠券ID格式错误',
+            'page.integer'   		    => '页码格式错误',
+            'page.gte'   		        => '页码格式错误',
+            'limit.integer'   		    => '取数格式错误',
+            'limit.gte'   		        => '取数格式错误',
+        ];
+    }
+
+}

+ 45 - 0
app/Http/Requests/Api/Coupon/Active.php

@@ -0,0 +1,45 @@
+<?php namespace App\Http\Requests\Api\Coupon;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 活动
+ * 
+ */
+class Active extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            'id'                => 'required|integer|gt:0',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_detail'            => ['id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'id.required'   	        => '请选择参与活动',
+            'id.integer'   	            => '活动ID有误',
+            'id.gt'   	                => '活动ID有误',
+        ];
+    }
+
+}

+ 45 - 0
app/Http/Requests/Api/Coupon/Custom.php

@@ -0,0 +1,45 @@
+<?php namespace App\Http\Requests\Api\Coupon;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 客户优惠券
+ * 
+ */
+class Custom extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            'coupon_id'         => 'required|integer|gt:0',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_coupon'            => ['coupon_id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'coupon_id.required'   	        => '请选择要领取的优惠券',
+            'coupon_id.integer'   	        => '优惠券ID有误',
+            'coupon_id.gt'   	            => '优惠券ID有误',
+        ];
+    }
+
+}

+ 45 - 0
app/Http/Requests/Api/Custom.php

@@ -0,0 +1,45 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+/**
+ * 客户验证器
+ * 
+ */
+class Custom extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 地址
+            'city'                  => 'required|max:15',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes             = [
+        'set_city'                  => ['city'],
+        'get_city'                  => [],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'city.required'   	    => '请选择城市',
+        ];
+    }
+
+}

+ 70 - 0
app/Http/Requests/Api/CustomAddr.php

@@ -0,0 +1,70 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+/**
+ * 地址验证器
+ * 
+ */
+class CustomAddr extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 地址
+            'id'                    => 'required|integer|gt:0',
+            'contact_province'      => 'required|max:15',
+            'contact_city'          => 'required|max:15',
+            'contact_area'          => 'required|max:15',
+            'contact_addr'          => 'required|max:64',
+            // 备注
+            'contact_name'          => 'required|max:20',
+            'contact_shop'          => 'max:20',
+            'contact_phone'         => 'required|phone',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'add'                   => ['contact_province','contact_city','contact_area','contact_name','contact_phone','contact_shop'],
+        'edit'                  => ['id','contact_province','contact_city','contact_area','contact_name','contact_phone','contact_shop'],
+        'del'                   => ['id'],
+        'set_default'           => ['id'],
+        'get_guess_addr'        => ['contact_shop'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'contact_province.required' => '请选择地区',
+            'contact_city.required'   	=> '请选择地区',
+            'contact_area.required'   	=> '请选择地区',
+            'contact_addr.required'   	=> '请填写地址',
+            'contact_province.max'   	=> '省份超长啦',
+            'contact_city.max'   	    => '城市超长啦',
+            'contact_area.max'   	    => '区县超长啦',
+            'contact_addr.max'   	    => '地址超长啦',
+            'contact_name.required'   	=> '请填写收件人',
+            'contact_shop.max'   	    => '店铺最多20字',
+            'contact_phone.required'    => '请填写收件人手机号',
+            'contact_phone.phone'       => '请填写正确的手机号',
+            'id.required'               => '请选择地址',
+            'id.integer'                => '地址格式有误',
+            'id.gt'                     => '地址格式有误',
+        ];
+    }
+
+}

+ 64 - 0
app/Http/Requests/Api/CustomCompany.php

@@ -0,0 +1,64 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+/**
+ * 资质验证器
+ * 
+ */
+class CustomCompany extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 地址
+            'province'      => 'required|max:10',
+            'city'          => 'required|max:10',
+            'area'          => 'required|max:10',
+            'license_addr'  => 'required|max:64',
+            // 备注
+            'remark'        => 'required|max:20',
+            'remark_company'=> 'required|max:20',
+            // 资质
+            'license_name'  => 'required|max:20',
+            'license_code'  => 'required|size:18',
+            'legal_name'    => 'required|max:20',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'repre'               => ['remark','province','city'],
+        'custom'              => ['remark','remark_company','province','city','area','license_name','license_code','license_addr','legal_name'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'city.required'   		    => '请选择城市',
+            'province.required'   	    => '请选择省份',
+            'area.required'   		    => '请选择区县',
+            'remark.required'   	    => '请填写姓名',
+            'remark_company.required'   => '请填写终端名称',
+            'license_name.required'     => '请填写营业执照名称',
+            'license_code.required'     => '请填写营业执照编号',
+            'license_addr.required'   	=> '请填写地址',
+            'legal_name.required'       => '请填写法人代表姓名',
+            // 'legal_idcard.required'     => '请填写法人代表身份证号',
+        ];
+    }
+
+}

+ 71 - 0
app/Http/Requests/Api/CustomShopResource.php

@@ -0,0 +1,71 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+/**
+ * 地址验证器
+ * 
+ */
+class CustomShopResource extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 地址
+            'id'                    => 'required|integer|gt:0',
+            'contact_province'      => 'required|max:15',
+            'contact_city'          => 'required|max:15',
+            'contact_area'          => 'required|max:15',
+            'contact_addr'          => 'required|max:64',
+            // 备注
+            'contact_name'          => 'required|max:20',
+            'contact_shop'          => 'max:20',
+            'contact_phone'         => 'required|phone',
+            'business_license_image'=> 'required',
+            'drug_business_license_image'=> 'required',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'add'                   => ['contact_province','contact_city','contact_area','contact_name','contact_phone','contact_shop','business_license_image','drug_business_license_image'],
+        'edit'                  => ['id','contact_province','contact_city','contact_area','contact_name','contact_phone','contact_shop','business_license_image','drug_business_license_image'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'contact_province.required' => '请选择地区',
+            'contact_city.required'   	=> '请选择地区',
+            'contact_area.required'   	=> '请选择地区',
+            'contact_addr.required'   	=> '请填写地址',
+            'contact_province.max'   	=> '省份超长啦',
+            'contact_city.max'   	    => '城市超长啦',
+            'contact_area.max'   	    => '区县超长啦',
+            'contact_addr.max'   	    => '地址超长啦',
+            'contact_name.required'   	=> '请填写收件人',
+            'contact_shop.max'   	    => '店铺最多20字',
+            'contact_phone.required'    => '请填写收件人手机号',
+            'contact_phone.phone'       => '请填写正确的手机号',
+            'id.required'               => '请选择地址',
+            'id.integer'                => '地址格式有误',
+            'id.gt'                     => '地址格式有误',
+            'business_license_image.required'   => '请上传营业执照图片',
+            'drug_business_license_image.required'   => '请上传药品经营许可证图片',
+        ];
+    }
+
+}

+ 69 - 0
app/Http/Requests/Api/CustomerForm/Company/CustomerForm.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Http\Requests\Api\CustomerForm\Company;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * SAAS自定义表单接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-10-29
+ * 
+ */
+class CustomerForm extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules =      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'status'            => 'required|integer|in:0,1',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+        ];
+
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'detail'             => ['id'],
+        'list'               => ['page', 'limit'],
+        'add'                      => [],
+        'edit'                  => ['id', 'status'],
+        'set_status'              => ['id', 'status'],
+        'delete'                  => ['id'],
+    ];
+
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+        ];
+    }
+}

+ 176 - 0
app/Http/Requests/Api/DynamicQrcode/Templates.php

@@ -0,0 +1,176 @@
+<?php
+
+namespace App\Http\Requests\Api\DynamicQrcode;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * 活码工具接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-08-22
+ * 
+ */
+class Templates extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules=      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'status'            => 'required|integer|in:0,1',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'sort'              => 'required|integer|min:0',
+            'dynamic_qrcode_image_url' => 'required',
+            'link_url'           => 'required',
+            'switching_method'   => 'required|integer|in:1,2,3,4',
+            'restriction_type'   => 'required|integer|in:1,2',
+            'open_repeat_addition' => 'required|integer|in:0,1',
+            'domain_address'     => 'required',
+            'open_landing_page'   => 'required|integer|in:0,1',
+            'open_advanced_config'   => 'required|integer|in:0,1',
+            // 落地页条件规则
+            'open_safety_certification' => 'required_if:open_landing_page,1',
+            'open_guidance_prompt' => 'required_if:open_landing_page,1',
+            'activity_title' => 'required_if:open_landing_page,1',
+            'event_description' => 'required_if:open_landing_page,1',
+            'contact_event_organizer' => 'required_if:open_landing_page,1',
+            // 高级配置条件规则
+            'open_backup_content_display' => 'required_if:open_advanced_config,1',
+            'open_wechat_press_frequently_qrcode' => 'required_if:open_advanced_config,1',
+        ];
+        // 添加条件验证规则 - 使用Rule::requiredIf
+        $rules['customer_service_qrcode'] = [
+            Rule::requiredIf(function () {
+                return $this->input('open_landing_page') == 1 && 
+                       $this->input('contact_event_organizer') == 1;
+            })
+        ];
+        $rules['contact_phone'] = [
+            Rule::requiredIf(function () {
+                return $this->input('open_landing_page') == 1 && 
+                       $this->input('contact_event_organizer') == 2;
+            })
+        ];
+        $rules['backup_content'] = [
+            Rule::requiredIf(function () {
+                return $this->input('open_advanced_config') == 1 && 
+                       $this->input('open_backup_content_display') == 1;
+            })
+        ];
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'detail'             => ['id'],
+        'list'               => ['page', 'limit'],
+        'add'                      => [
+            'name',
+            'switching_method',
+            'restriction_type',
+            'open_repeat_addition',
+            'domain_address',
+            'open_landing_page',
+            'open_safety_certification',
+            'open_guidance_prompt',
+            'activity_title',
+            'event_description',
+            'contact_event_organizer',
+            'customer_service_qrcode',
+            'contact_phone',
+            'open_advanced_config',
+            'backup_content',
+            'open_wechat_press_frequently_qrcode',
+            'open_backup_content_display',
+            'status'
+        ],
+        'edit'                  => [
+            'id',
+            'name',
+            'switching_method',
+            'restriction_type',
+            'open_repeat_addition',
+            'domain_address',
+            'open_landing_page',
+            'open_safety_certification',
+            'open_guidance_prompt',
+            'activity_title',
+            'event_description',
+            'contact_event_organizer',
+            'customer_service_qrcode',
+            'contact_phone',
+            'open_advanced_config',
+            'backup_content',
+            'open_wechat_press_frequently_qrcode',
+            'open_backup_content_display',
+            'status'
+        ],
+        'set_status'              => ['id', 'status'],
+        'delete'                  => ['id'],
+    ];
+
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+            'sort.required'         => '排序未知',
+            'sort.integer'          => '排序格式错误',
+            'sort.min'              => '排序格式错误',
+            'dynamic_qrcode_image_url.required' => '活码二维码未知',
+            'link_url.required' => '活码链接地址未知',
+            'switching_method.required' => '切换方式未知',
+            'switching_method.integer' => '切换方式格式错误',
+            'switching_method.in' => '切换方式格式错误',
+            'restriction_type.required' => '上限类型未知',
+            'restriction_type.integer' => '上限类型格式错误',
+            'restriction_type.in' => '上限类型格式错误',
+            'open_repeat_addition.required' => '重复进群/加人未知',
+            'open_repeat_addition.integer' => '重复进群/加人格式错误',
+            'open_repeat_addition.in' => '重复进群/加人格式错误',
+            'domain_address.required' => '域名地址未知',
+            'open_landing_page.required' => '落地页开关未知',
+            'open_landing_page.integer' => '落地页开关格式错误',
+            'open_landing_page.in' => '落地页开关格式错误',
+            'open_advanced_config.required' => '高级开关未知',
+            'open_advanced_config.integer' => '高级开关格式错误',
+            'open_advanced_config.in' => '高级开关格式错误',
+            // 其他消息...
+            'open_safety_certification.required_if' => '当开启落地页时,安全认证标识必填',
+            'open_guidance_prompt.required_if' => '当开启落地页时,常按引导提示必填',
+            'activity_title.required_if' => '当开启落地页时,活动标题必填',
+            'event_description.required_if' => '当开启落地页时,活动描述必填',
+            'contact_event_organizer.required_if' => '当开启落地页时,联系活动方必填',
+            'customer_service_qrcode.required' => '当开启落地页并且联系活动方为客服时,客服二维码必填',
+            'contact_phone.required' => '当开启落地页时,联系活动方式为电话时,联系电话必填',
+            'open_backup_content_display.required_if' => '当开启高级配置时,备用内容显示必填',
+            'backup_content.required' => '当开启高级配置且备用内容显示开启时,备用内容必填',
+            'open_wechat_press_frequently_qrcode.required_if' => '当开启高级配置时,微信常按识别二维码必填',
+        ];
+    }
+}

+ 52 - 0
app/Http/Requests/Api/LiveReward/Order.php

@@ -0,0 +1,52 @@
+<?php namespace App\Http\Requests\Api\LiveReward;
+
+use App\Http\Requests\BaseRequest;
+use App\Rules\JsonArray;
+/**
+ * 订单验证器
+ * 
+ */
+class Order extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 'name'       => 'string|max:20',
+            'id'                => 'required|integer|gt:0',
+            'live_id'           => 'required|integer|gt:0',
+            'gift_id'           => 'required|integer|gt:0',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'create'                => ['live_id','gift_id'],
+        'get_detail'            => ['id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'id.required'   	        => '请选择订单',
+            'id.integer'   	            => '请选择订单',
+            'id.gt'   	                => '请选择订单',
+            'live_id.required'   	    => '请选择直播间',
+            'gift_id.required'   	    => '请选择打赏礼物',
+        ];
+    }
+
+}

+ 46 - 0
app/Http/Requests/Api/Login.php

@@ -0,0 +1,46 @@
+<?php  namespace  App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 管理后台登录验证
+ * @author  liuxaingxin
+ *
+ * */
+class Login extends BaseRequest{
+
+    /**
+     *
+     * @return  array
+     */
+    public  function  rules(){
+        return [
+            'phone'     =>  'required|phone',
+            'code'      =>  'required',
+            'sms_code'  =>  'required|check_code',
+            'email_code'  =>  'required|check_code',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+		'login'  		        => ['phone','code'],
+        'phone'  		        => ['phone','sms_code'],
+        'email'  		        => ['email','email_code'],
+	];
+
+    /**
+     *
+     * @return  array
+     */
+    public  function  messages(){
+        return [
+            'phone.required'  =>  '请输入登录账号',
+            'email.required'  =>  '请输入登录邮箱账号',
+            'code.required'   =>  '请输入登录验证码',
+            'sms_code.required'   =>  '请输入登录验证码',
+            'email_code.required'   =>  '请输入登录验证码',
+        ];
+    }
+
+}

+ 39 - 0
app/Http/Requests/Api/Login/Phone.php

@@ -0,0 +1,39 @@
+<?php  namespace  App\Http\Requests\Api\Login;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 管理后台登录验证
+ * @author  liuxaingxin
+ *
+ * */
+class Phone extends BaseRequest{
+
+    /**
+     *
+     * @return  array
+     */
+    public  function  rules(){
+        return [
+            'phone'     =>  'required',
+            'code'      =>  'required|check_code',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+		'login'  		        => ['phone','code'],
+	];
+
+    /**
+     *
+     * @return  array
+     */
+    public  function  messages(){
+        return [
+            'phone.required'  =>  '请输入登录账号',
+            'code.required'   =>  '请输入登录验证码',
+        ];
+    }
+
+}

+ 67 - 0
app/Http/Requests/Api/Lottery/LiveRecord.php

@@ -0,0 +1,67 @@
+<?php namespace App\Http\Requests\Api\Lottery;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 积分记录
+ * 
+ */
+class LiveRecord extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 'name'       => 'string|max:20',
+            'id'                => 'required|integer|gt:0',
+            'lottery_id'        => 'required|integer|gt:0',
+            'addr_id'           => 'required|integer|gt:0',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'live_id'           => 'required|integer|gt:0',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_list'              => ['lottery_id'],
+        'set_addr'              => ['addr_id','id'],
+        'get_user_list'       => ['page','limit'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'lottery_id.required'   	=> '请选择活动ID',
+            'lottery_id.integer'   	    => '活动ID有误',
+            'lottery_id.gt'   	        => '活动ID有误',
+            'id.required'   	        => '请选择奖品记录',
+            'id.integer'   	            => '奖品记录ID有误',
+            'id.gt'   	                => '奖品记录ID有误',
+            'addr_id.required'   	    => '请选择地址',
+            'addr_id.integer'   	    => '地址ID有误',
+            'addr_id.gt'   	            => '地址ID有误',
+            'page.integer'   	        => '页码有误',
+            'page.min'   	            => '页码有误',
+            'limit.integer'   	        => '每页数量有误',
+            'limit.min'   	            => '每页数量有误',
+            'live_id.required'   	    => '请选择直播间',
+            'live_id.integer'   	    => '直播间ID有误',
+            'live_id.gt'   	            => '直播间ID有误',
+        ];
+    }
+
+}

+ 56 - 0
app/Http/Requests/Api/Lottery/OrderRecord.php

@@ -0,0 +1,56 @@
+<?php namespace App\Http\Requests\Api\Lottery;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 积分记录
+ * 
+ */
+class OrderRecord extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 'name'       => 'string|max:20',
+            'id'                => 'required|integer|gt:0',
+            'lottery_id'        => 'required|integer|gt:0',
+            'addr_id'           => 'required|integer|gt:0'
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_list'              => ['lottery_id'],
+        'set_addr'              => ['addr_id','id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'lottery_id.required'   	=> '请选择活动ID',
+            'lottery_id.integer'   	    => '活动ID有误',
+            'lottery_id.gt'   	        => '活动ID有误',
+            'id.required'   	        => '请选择奖品记录',
+            'id.integer'   	            => '奖品记录ID有误',
+            'id.gt'   	                => '奖品记录ID有误',
+            'addr_id.required'   	    => '请选择地址',
+            'addr_id.integer'   	    => '地址ID有误',
+            'addr_id.gt'   	            => '地址ID有误',
+        ];
+    }
+
+}

+ 56 - 0
app/Http/Requests/Api/Lottery/ScoreRecord.php

@@ -0,0 +1,56 @@
+<?php namespace App\Http\Requests\Api\Lottery;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 积分记录
+ * 
+ */
+class ScoreRecord extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 'name'       => 'string|max:20',
+            'id'                => 'required|integer|gt:0',
+            'lottery_id'        => 'required|integer|gt:0',
+            'addr_id'           => 'required|integer|gt:0'
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_list'              => ['lottery_id'],
+        'set_addr'              => ['addr_id','id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'lottery_id.required'   	=> '请选择活动ID',
+            'lottery_id.integer'   	    => '活动ID有误',
+            'lottery_id.gt'   	        => '活动ID有误',
+            'id.required'   	        => '请选择奖品记录',
+            'id.integer'   	            => '奖品记录ID有误',
+            'id.gt'   	                => '奖品记录ID有误',
+            'addr_id.required'   	    => '请选择地址',
+            'addr_id.integer'   	    => '地址ID有误',
+            'addr_id.gt'   	            => '地址ID有误',
+        ];
+    }
+
+}

+ 58 - 0
app/Http/Requests/Api/Orders.php

@@ -0,0 +1,58 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+use App\Rules\JsonArray;
+/**
+ * 订单验证器
+ * 
+ */
+class Orders extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 'name'       => 'string|max:20',
+            'id'                => 'required|integer|gt:0',
+            // 产品信息
+            'product_list' 	    => ['required','json',new JsonArray(['product_id'=>'required|numeric|gt:0','product_skuid'=>'numeric|gte:0','buy_num'=>'required|integer|gte:0'])],
+            // 下单地址
+            'addr_id' 	        => 'required|integer|gt:0',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_list'              => [],
+        'create'                => ['product_list','addr_id'],
+        'cancel'                => ['id'],
+        'get_item'              => ['id'],
+        'get_detail'            => ['id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'id.required'   	        => '请选择订单',
+            'id.integer'   	            => '请选择订单',
+            'id.gt'   	                => '请选择订单',
+            'product_list.required'   	=> '请选择需下单产品',
+            'product_list.json'   		=> '下单产品数据格式有误',
+            'addr_id.required'   	    => '请选择收货地址',
+        ];
+    }
+
+}

+ 50 - 0
app/Http/Requests/Api/Orders/Receipt.php

@@ -0,0 +1,50 @@
+<?php namespace App\Http\Requests\Api\Orders;
+
+use App\Http\Requests\BaseRequest;
+
+/**
+ * 活动
+ * 
+ */
+class Receipt extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            'order_id'          => 'required|integer|gt:0',
+            'file'              => 'required|file|image|max:5120',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'apply'            => ['order_id'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'order_id.required'   	=> '请选择订单',
+            'order_id.integer'   	=> '订单ID有误',
+            'order_id.gt'   	    => '订单ID有误',
+            'file.required'   	    => '请上传图片',
+            'file.file'   	        => '请上传图片',
+            'file.image'   	        => '请上传图片',
+            'file.max'   	        => '图片请勿超过5MB',
+        ];
+    }
+
+}

+ 62 - 0
app/Http/Requests/Api/Product.php

@@ -0,0 +1,62 @@
+<?php namespace App\Http\Requests\Api;
+
+use App\Http\Requests\BaseRequest;
+/**
+ * 产品验证器
+ * 
+ */
+class Product extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 编辑时排除ID
+        // 返回结果
+        return      [
+            // 有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 bail 添加到规则中:
+            // 验证字段,验证规则,提示信息
+            // 'name'          => 'string|max:20',
+            'id'            => 'required|integer|gt:0',
+            'page'          => 'integer|gte:0',
+            'limit'         => 'integer|gte:0',
+            'class_id'      => 'required|integer|gt:0',
+            'code'          => 'required',
+            'shop_name'     => 'required',
+        ];
+    }
+
+    // 场景列表
+    protected   $scenes         = [
+        'get_list'                      => ['page','limit'],
+        'get_detail'                    => ['id'],
+        'get_sku'                       => ['id'],
+        'get_product'                   => ['class_id'],
+        'get_product_by_code'           => ['code', 'shop_name'],
+	];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'id.required'   		    => '请选择产品',
+            'id.integer'   		        => '产品ID格式错误',
+            'id.gt'   		            => '产品ID格式错误',
+            'page.integer'   		    => '页码格式错误',
+            'page.gte'   		        => '页码格式错误',
+            'limit.integer'   		    => '取数格式错误',
+            'limit.gte'   		        => '取数格式错误',
+            'class_id.required'         => '请选择产品分类',
+            'class_id.integer'          => '产品分类ID格式错误',
+            'class_id.gt'               => '产品分类ID格式错误',
+        ];
+    }
+
+}

+ 71 - 0
app/Http/Requests/Api/Promoter/ApplicationForm.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace App\Http\Requests\Api\Promoter;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * 推广员申请接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-09-04
+ * 
+ */
+class ApplicationForm extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules =      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'status'            => 'required|integer|in:0,1',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'sort'              => 'required|integer|min:0',
+            'sms_code'  =>  'required',
+        ];
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'detail'             => [],
+        'list'               => ['page', 'limit'],
+        'add'                      => ['sms_code'],
+        'edit'                  => [],
+        'set_status'              => ['id', 'status'],
+        'delete'                  => ['id'],
+    ];
+
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+            'sms_code.required'   =>  '请输入验证码',
+        ];
+    }
+}

+ 69 - 0
app/Http/Requests/Api/Promoter/CommonProduct.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Http\Requests\Api\Promoter;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * 推广员公共商品接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-10-09
+ * 
+ */
+class CommonProduct extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules =      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'status'            => 'required|integer|in:0,1',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'sort'              => 'required|integer|min:0',
+        ];
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'detail'             => [],
+        'list'               => ['page', 'limit'],
+        'add'                      => [],
+        'edit'                  => [],
+        'set_status'              => ['id', 'status'],
+        'delete'                  => ['id'],
+    ];
+
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+        ];
+    }
+}

+ 74 - 0
app/Http/Requests/Api/Promoter/Configuration.php

@@ -0,0 +1,74 @@
+<?php
+
+namespace App\Http\Requests\Api\Promoter;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * 推广员配置接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-09-18
+ * 
+ */
+class Configuration extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules =      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'status'            => 'required|integer|in:0,1',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'sort'              => 'required|integer|min:0',
+            'code_name'         => 'required|string',
+        ];
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'detail'             => [],
+        'list'               => ['page', 'limit'],
+        'add'                      => [],
+        'edit'                  => [],
+        'set_status'              => ['id', 'status'],
+        'delete'                  => ['id'],
+        'detail'     => ['code_name'],
+        'progress'   => [],
+    ];
+
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+            'code_name.required'    => '配置编码code_name未知',
+            'code_name.string'    => '配置编码ode_name格式错误',
+        ];
+    }
+}

+ 69 - 0
app/Http/Requests/Api/Promoter/Customers.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Http\Requests\Api\Promoter;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * 推广员客户接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-09-09
+ * 
+ */
+class Customers extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules =      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'status'            => 'required|integer|in:0,1',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'sort'              => 'required|integer|min:0',
+        ];
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'detail'             => [],
+        'list'               => ['page', 'limit'],
+        'add'                      => [],
+        'edit'                  => [],
+        'set_status'              => ['id', 'status'],
+        'delete'                  => ['id'],
+    ];
+
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+        ];
+    }
+}

+ 70 - 0
app/Http/Requests/Api/Promoter/EarningsRecord.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Http\Requests\Api\Promoter;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * 推广员推广收益记录接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-09-15
+ * 
+ */
+class EarningsRecord extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules =      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'status '          => 'integer|in:0,1',
+            'promoter_userid'   => 'required|integer|gt:0',
+        ];
+
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'add'             => [],
+        'list'             => ['page', 'limit'],
+        'detail'          => ['id'],
+        'set_status'         => ['id', 'status'],
+    ];
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+            'promoter_userid.required'   => '推广员ID未知',
+            'promoter_userid.integer'    => '推广员ID格式错误',
+            'promoter_userid.gt'         => '推广员ID格式错误',
+        ];
+    }
+}

+ 69 - 0
app/Http/Requests/Api/Promoter/Product.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace App\Http\Requests\Api\Promoter;
+
+use App\Http\Requests\BaseRequest;
+use Illuminate\Validation\Rule;
+
+/**
+ * 推广员商品接口验证
+ * @author 唐远望
+ * @version 1.0
+ * @date 2025-09-09
+ * 
+ */
+class Product extends BaseRequest
+{
+    /**
+     * 获取应用于请求的规则
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        // 返回结果
+        $rules =      [
+            'name'                 => 'required',
+            'id'                => 'required|integer|gt:0',
+            'status'            => 'required|integer|in:0,1',
+            'page'              => 'integer|min:1',
+            'limit'             => 'integer|min:1',
+            'sort'              => 'required|integer|min:0',
+        ];
+        return $rules;
+    }
+
+
+    // 场景列表
+    protected   $scenes         = [
+        'detail'             => [],
+        'list'               => ['page', 'limit'],
+        'add'                      => [],
+        'edit'                  => [],
+        'set_status'              => ['id', 'status'],
+        'delete'                  => ['id'],
+    ];
+
+
+    /**
+     * 获取已定义验证规则的错误消息
+     *
+     * @return array
+     */
+    public function messages()
+    {
+        return [
+            'name.required'     => '名称必填',
+            'id.required'       => 'ID未知',
+            'id.integer'        => 'ID格式错误',
+            'id.gt'               => 'ID格式错误',
+            'status.required'   => '状态未知',
+            'status.integer'    => '状态格式错误',
+            'status.in'         => '状态格式错误',
+            'page.integer'      => '页码格式错误',
+            'page.min'          => '页码格式错误',
+            'limit.integer'     => '每页数量格式错误',
+            'limit.min'         => '每页数量格式错误',
+        ];
+    }
+}

Some files were not shown because too many files changed in this diff