Browse Source

first commit

yinbin 5 months ago
commit
66fbd274f7
100 changed files with 5913 additions and 0 deletions
  1. 5 0
      .env
  2. 0 0
      .env.development
  3. 7 0
      .env.production
  4. 25 0
      .gitignore
  5. 1 0
      .htaccess
  6. 66 0
      README.md
  7. 29 0
      app/Casts/Htmlspecialchar.php
  8. 29 0
      app/Casts/Json.php
  9. 404 0
      app/Common/UploadCommon.php
  10. 34 0
      app/Common/common.php
  11. 27 0
      app/Console/Kernel.php
  12. 17 0
      app/Exceptions/ApiException.php
  13. 34 0
      app/Exceptions/Code.php
  14. 127 0
      app/Exceptions/Handler.php
  15. 46 0
      app/Exceptions/Message.php
  16. 73 0
      app/Exceptions/Status.php
  17. 13 0
      app/Facades/WechatServiceFacades.php
  18. 31 0
      app/Http/Controllers/Admin/AdminController.php
  19. 40 0
      app/Http/Controllers/Admin/AuthController.php
  20. 74 0
      app/Http/Controllers/Admin/Channel/DistributorController.php
  21. 30 0
      app/Http/Controllers/Admin/Finance/StatisticController.php
  22. 34 0
      app/Http/Controllers/Admin/FinanceController.php
  23. 25 0
      app/Http/Controllers/Admin/IndexController.php
  24. 31 0
      app/Http/Controllers/Admin/Order/AlarmController.php
  25. 72 0
      app/Http/Controllers/Admin/Order/IndexController.php
  26. 51 0
      app/Http/Controllers/Admin/OrderController.php
  27. 77 0
      app/Http/Controllers/Admin/Project/CategoryController.php
  28. 82 0
      app/Http/Controllers/Admin/Project/IndexController.php
  29. 76 0
      app/Http/Controllers/Admin/System/MenusController.php
  30. 48 0
      app/Http/Controllers/Admin/System/RegionController.php
  31. 30 0
      app/Http/Controllers/Admin/System/RoleAuthController.php
  32. 53 0
      app/Http/Controllers/Admin/System/RoleController.php
  33. 22 0
      app/Http/Controllers/Admin/System/UploadController.php
  34. 31 0
      app/Http/Controllers/Admin/SystemController.php
  35. 25 0
      app/Http/Controllers/Admin/Task/AgentStatisticController.php
  36. 83 0
      app/Http/Controllers/Admin/User/ArtificerController.php
  37. 77 0
      app/Http/Controllers/Admin/User/MemberController.php
  38. 127 0
      app/Http/Controllers/Admin/Wechat/OfficialAccountController.php
  39. 29 0
      app/Http/Controllers/Admin/Wechat/WechatController.php
  40. 38 0
      app/Http/Controllers/Auth/AuthenticatedSessionController.php
  41. 26 0
      app/Http/Controllers/Auth/EmailVerificationNotificationController.php
  42. 53 0
      app/Http/Controllers/Auth/NewPasswordController.php
  43. 39 0
      app/Http/Controllers/Auth/PasswordResetLinkController.php
  44. 36 0
      app/Http/Controllers/Auth/RegisteredUserController.php
  45. 32 0
      app/Http/Controllers/Auth/VerifyEmailController.php
  46. 12 0
      app/Http/Controllers/Controller.php
  47. 36 0
      app/Http/Controllers/Frontend/Account/UserController.php
  48. 24 0
      app/Http/Controllers/Frontend/Wechat/OfficialAccountController.php
  49. 25 0
      app/Http/Controllers/Frontend/Wechat/WechatController.php
  50. 71 0
      app/Http/Kernel.php
  51. 83 0
      app/Http/Middleware/Admin/Authenticate.php
  52. 19 0
      app/Http/Middleware/Authenticate.php
  53. 17 0
      app/Http/Middleware/EncryptCookies.php
  54. 27 0
      app/Http/Middleware/EnsureEmailIsVerified.php
  55. 17 0
      app/Http/Middleware/PreventRequestsDuringMaintenance.php
  56. 30 0
      app/Http/Middleware/RedirectIfAuthenticated.php
  57. 19 0
      app/Http/Middleware/TrimStrings.php
  58. 20 0
      app/Http/Middleware/TrustHosts.php
  59. 28 0
      app/Http/Middleware/TrustProxies.php
  60. 22 0
      app/Http/Middleware/ValidateSignature.php
  61. 17 0
      app/Http/Middleware/VerifyCsrfToken.php
  62. 36 0
      app/Http/Requests/Admin/CommonIdRequest.php
  63. 42 0
      app/Http/Requests/Admin/LoginRequest.php
  64. 38 0
      app/Http/Requests/Admin/PageRequest.php
  65. 43 0
      app/Http/Requests/Admin/RegisterRequest.php
  66. 85 0
      app/Http/Requests/Auth/LoginRequest.php
  67. 23 0
      app/Http/Services/Admin/AdminService.php
  68. 55 0
      app/Http/Services/Admin/AuthService.php
  69. 58 0
      app/Http/Services/Admin/Channel/DistributorService.php
  70. 95 0
      app/Http/Services/Admin/Finance/StatisticService.php
  71. 286 0
      app/Http/Services/Admin/FinanceService.php
  72. 62 0
      app/Http/Services/Admin/Order/AlarmService.php
  73. 270 0
      app/Http/Services/Admin/Order/IndexService.php
  74. 198 0
      app/Http/Services/Admin/OrderService.php
  75. 66 0
      app/Http/Services/Admin/Project/CategoryService.php
  76. 192 0
      app/Http/Services/Admin/Project/IndexService.php
  77. 59 0
      app/Http/Services/Admin/System/MenusService.php
  78. 50 0
      app/Http/Services/Admin/System/RegionService.php
  79. 32 0
      app/Http/Services/Admin/System/RoleAuthService.php
  80. 47 0
      app/Http/Services/Admin/System/RoleService.php
  81. 32 0
      app/Http/Services/Admin/System/UploadService.php
  82. 280 0
      app/Http/Services/Admin/SystemService.php
  83. 61 0
      app/Http/Services/Admin/Task/AgentStatisticService.php
  84. 86 0
      app/Http/Services/Admin/TokenService.php
  85. 103 0
      app/Http/Services/Admin/User/ArtificerService.php
  86. 80 0
      app/Http/Services/Admin/User/MemberService.php
  87. 77 0
      app/Http/Services/Admin/Wechat/OfficialAccountService.php
  88. 149 0
      app/Http/Services/BaseService.php
  89. 39 0
      app/Http/Services/Frontend/Account/UserService.php
  90. 37 0
      app/Http/Services/Frontend/Wechat/OfficialAccountService.php
  91. 99 0
      app/Models/Admin.php
  92. 60 0
      app/Models/AdminRole.php
  93. 47 0
      app/Models/Alarm.php
  94. 48 0
      app/Models/Artificer.php
  95. 30 0
      app/Models/ArtificerSite.php
  96. 26 0
      app/Models/ArtificerTime.php
  97. 27 0
      app/Models/Asset.php
  98. 66 0
      app/Models/Category.php
  99. 22 0
      app/Models/Distributor.php
  100. 31 0
      app/Models/Menu.php

+ 5 - 0
.env

@@ -0,0 +1,5 @@
+
+# 网站标题
+VITE_GLOB_APP_TITLE = "嘀咚点到"
+# 是否生成打包报告
+VITE_BUILD_REPORT = "true"

+ 0 - 0
.env.development


+ 7 - 0
.env.production

@@ -0,0 +1,7 @@
+
+# 网站标题
+VITE_GLOB_APP_TITLE = "嘀咚点到"
+# 是否生成打包报告
+VITE_BUILD_REPORT = "true"
+
+VITE_API_BASE_URL = "https://api.niusenyun.com"

+ 25 - 0
.gitignore

@@ -0,0 +1,25 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+vendor/

+ 1 - 0
.htaccess

@@ -0,0 +1 @@
+ 

+ 66 - 0
README.md

@@ -0,0 +1,66 @@
+<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
+
+<p align="center">
+<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
+<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
+<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
+<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
+</p>
+
+## About Laravel
+
+Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
+
+- [Simple, fast routing engine](https://laravel.com/docs/routing).
+- [Powerful dependency injection container](https://laravel.com/docs/container).
+- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
+- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
+- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
+- [Robust background job processing](https://laravel.com/docs/queues).
+- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
+
+Laravel is accessible, powerful, and provides tools required for large, robust applications.
+
+## Learning Laravel
+
+Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
+
+You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
+
+If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 2000 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
+
+## Laravel Sponsors
+
+We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com).
+
+### Premium Partners
+
+- **[Vehikl](https://vehikl.com/)**
+- **[Tighten Co.](https://tighten.co)**
+- **[WebReinvent](https://webreinvent.com/)**
+- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
+- **[64 Robots](https://64robots.com)**
+- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
+- **[Cyber-Duck](https://cyber-duck.co.uk)**
+- **[DevSquad](https://devsquad.com/hire-laravel-developers)**
+- **[Jump24](https://jump24.co.uk)**
+- **[Redberry](https://redberry.international/laravel/)**
+- **[Active Logic](https://activelogic.com)**
+- **[byte5](https://byte5.de)**
+- **[OP.GG](https://op.gg)**
+
+## Contributing
+
+Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
+
+## Code of Conduct
+
+In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
+
+## Security Vulnerabilities
+
+If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
+
+## License
+
+The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

+ 29 - 0
app/Casts/Htmlspecialchar.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Casts;
+
+use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
+use Illuminate\Database\Eloquent\Model;
+
+class Htmlspecialchar implements CastsAttributes
+{
+    /**
+     * Cast the given value.
+     *
+     * @param  array<string, mixed>  $attributes
+     */
+    public function get(Model $model, string $key, mixed $value, array $attributes): mixed
+    {
+        return htmlspecialchars_decode($value);
+    }
+
+    /**
+     * Prepare the given value for storage.
+     *
+     * @param  array<string, mixed>  $attributes
+     */
+    public function set(Model $model, string $key, mixed $value, array $attributes): mixed
+    {
+        return htmlspecialchars($value);
+    }
+}

+ 29 - 0
app/Casts/Json.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Casts;
+
+use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
+use Illuminate\Database\Eloquent\Model;
+
+class Json implements CastsAttributes
+{
+    /**
+     * Cast the given value.
+     *
+     * @param  array<string, mixed>  $attributes
+     */
+    public function get(Model $model, string $key, mixed $value, array $attributes): mixed
+    {
+        return json_decode($value, true);
+    }
+
+    /**
+     * Prepare the given value for storage.
+     *
+     * @param  array<string, mixed>  $attributes
+     */
+    public function set(Model $model, string $key, mixed $value, array $attributes): mixed
+    {
+        return json_encode($value);
+    }
+}

+ 404 - 0
app/Common/UploadCommon.php

@@ -0,0 +1,404 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/12/27 19:10
+ */
+
+namespace App\Common;
+
+
+use Illuminate\Http\Request;
+
+class UploadCommon
+{
+    private $error = false;
+    private string $formName = 'file';
+
+
+//app.post('/upload', (req, res) => {
+//    const rangeHeader = req . headers['content-range'];
+//    const fileSize = parseInt(rangeHeader . split('/')[1]);
+//    const [start, end] = rangeHeader.replace(/bytes=([0-9]+)-([0-9]+)/, '$1-$2').split('-');  // 在此处处理文件上传逻辑,将接收到的文件片段追加到最终文件中  // 例如,可以使用fs模块将片段写入磁盘上的文件
+//    res.sendStatus(200);});
+
+    public function upload(Request $request)
+    {
+//        $arrFileTypes = [
+//            'image' => ['title' => 'Image files', 'extensions' => $uploadSetting['file_types']['image']['extensions']],
+//            'video' => ['title' => 'Video files', 'extensions' => $uploadSetting['file_types']['video']['extensions']],
+//            'audio' => ['title' => 'Audio files', 'extensions' => $uploadSetting['file_types']['audio']['extensions']],
+//            'file'  => ['title' => 'Custom files', 'extensions' => $uploadSetting['file_types']['file']['extensions']]
+//        ];
+
+        // 第一步 检测文件
+        $fileImage = $request->file($this->formName);
+        if (!$fileImage->isValid()) {
+            $this->error = "非法文件!";
+            return false;
+        }
+
+        // 第二步 检测类型
+//        if (empty($arrData["filetype"])) {
+//            $arrData["filetype"] = "image";
+//        }
+//        $fileType = $arrData["filetype"];
+
+        $originalName = $request->input('name');
+        $fileExtension = strtolower(get_file_extension($originalName));
+        $allowedExtensions = explode(',', 'jpg,jpeg,png,bmp,webp');
+        if (!in_array($fileExtension, $allowedExtensions) || $fileExtension == 'php') {
+            $this->error = "非法文件类型!";
+            return false;
+        }
+
+        // 第三步 保存文件
+        $uploadDirName = 'upload'; // 上传根目录配置
+        $uploadSubDir = $request->input('dir', 'default');   // 上传目录类型
+        if (empty($uploadSubDir) || !file_exists(public_path($uploadDirName . DIRECTORY_SEPARATOR . $uploadSubDir))) {
+            $uploadSubDir = 'default';
+        }
+
+        $chunk = $request->input('chunk', 0);
+        $chunks = $request->input('chunks', 1);
+        $chunkNext = $chunk + 1;
+        $continuation = !(!$chunk && $chunkNext === $chunks);
+        if ($continuation) { // 续传
+            $fileName = md5($originalName) . '_' . $chunk . '.part';
+            $uploadPath = storage_path($uploadDirName);  // 上传目录绝对路径
+            $filePath = $uploadSubDir;
+        } else {    // 秒传
+            $fileName = md5(uniqid()) . '.' . $fileExtension;
+            $uploadPath = public_path($uploadDirName);  // 上传目录绝对路径
+            $filePath = $uploadSubDir . DIRECTORY_SEPARATOR . date('Ymd');
+        }
+
+        $savePath = $uploadPath . DIRECTORY_SEPARATOR . $filePath;
+        $fileImage->move($savePath, $fileName);
+
+        // 合并图片
+        if ($continuation && $chunkNext === intval($chunks)) {
+            $fileName = md5(uniqid()) . '.' . $fileExtension;
+            $filePath = $uploadSubDir . DIRECTORY_SEPARATOR . date('Ymd');
+            $continuationDir = public_path($uploadDirName) . DIRECTORY_SEPARATOR . $filePath;
+            $continuationPath = $continuationDir . DIRECTORY_SEPARATOR . $fileName;
+            if (!file_exists($continuationDir)) {
+                mkdir($continuationDir, 0777, true);
+            }
+            // 合并临时文件
+            if (!$out = @fopen($continuationPath, "wb")) {
+                $this->error = "上传目录不可写";
+                return false;
+            }
+            if (flock($out, LOCK_EX)) {
+                for ($index = 0; $index < $chunks; $index++) {
+                    $inPath = $savePath . DIRECTORY_SEPARATOR . md5($originalName) . '_' . $index . '.part';
+                    if (!$in = @fopen($inPath, "rb")) {
+                        break;
+                    }
+                    while ($buff = fread($in, 4096)) {
+                        fwrite($out, $buff);
+                    }
+                    fclose($in);
+                    unlink($inPath);
+                }
+                flock($out, LOCK_UN);
+            }
+            @fclose($out);
+            @rmdir($savePath);
+            $continuation = false;  // 停止续传
+            $savePath = $continuationDir;
+        }
+
+        $responseData = [
+            "status" => $continuation ? 'uploading' : 'done',
+            "percent" => number_format($chunkNext / $chunks, 2),
+            "chunk" => $chunkNext
+        ];
+        // 完成续传
+        if (!$continuation) {
+            // 第四步 检测大小
+
+            // 第五步 检测重复图片
+            $fileMd5 = md5_file($savePath . DIRECTORY_SEPARATOR . $fileName);
+
+            // 第六步 保存数据
+
+
+            // 第七步 返回数据
+            $host = $request->getSchemeAndHttpHost();
+            $url = DIRECTORY_SEPARATOR . $uploadDirName . DIRECTORY_SEPARATOR . $filePath . DIRECTORY_SEPARATOR . $fileName;
+            // composer require intervention/image
+            // use Intervention\Image\Facades\Image;
+            // $imageHeight = Image::make($path)->height();
+            $responseData["response"] = [
+                'filepath' => $filePath,
+                "name" => $fileName,
+                'preview_url' => $host . $url,
+                'url' => $url,
+                "file_md5" => $fileMd5
+            ];
+        }
+        return $responseData;
+
+
+//        if (array_key_exists($arrData["filetype"], $arrFileTypes)) {
+//        $extensions = $uploadSetting['file_types'][$arrData["filetype"]]['extensions'];
+        $fileTypeUploadMaxFileSize = 10240;
+//        } else {
+//            $this->error = '上传文件类型配置错误!';
+//            return false;
+//        }
+//        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { // other CORS headers if any...
+//            exit; // finish preflight CORS requests here
+//        }
+
+        $cleanupTargetDir = false; // Remove old files
+        $maxFileAge = 5 * 3600; // Temp file age in seconds
+
+        $fileUploadMaxFileSize = 10485760;  //  10M
+//        $fileUploadMaxFileSize = empty($fileUploadMaxFileSize) ? 2097152 : $fileUploadMaxFileSize;//默认2M
+
+        $strWebPath = "";//"upload" . DS;
+        $strId = $request->all("id");
+        $strDate = date('Ymd');
+
+//        $adminId = cmf_get_current_admin_id();
+//        $userId  = cmf_get_current_user_id();
+//        $userId  = empty($adminId) ? $userId : $adminId;
+
+        $targetDir = $_ENV['UPLOAD_TMP_PATH'] . DIRECTORY_SEPARATOR . 'upload' . DIRECTORY_SEPARATOR; // 断点续传 need
+//        if (!file_exists($targetDir)) {
+//            mkdir($targetDir, 0777, true);
+//        }
+
+        /**
+         * 断点续传 need
+         */
+        $strFilePath = md5($originalName);
+        $chunk = $request->input("chunk", 0);// isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
+        $chunks = $request->input("chunks", 1);//isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;
+
+        // Open temp file
+        if (!$out = @fopen($targetDir . "{$strFilePath}_{$chunk}.parttmp", "wb")) {
+            $this->error = "上传文件临时目录不可写" . $targetDir;
+            return false;
+        }
+        // Read binary input stream and append it to temp file
+        if (!$in = @fopen($fileImage->getRealPath(), "rb")) {
+            $this->error = "Failed to open input stream!";
+            return false;
+        }
+
+        while ($buff = fread($in, 4096)) {
+            fwrite($out, $buff);
+        }
+
+        @fclose($out);
+        @fclose($in);
+
+        rename($targetDir . "{$strFilePath}_{$chunk}.parttmp", $targetDir . "{$strFilePath}_{$chunk}.part");
+
+        $done = true;
+        for ($index = 0; $index < $chunks; $index++) {
+            if (!file_exists($targetDir . "{$strFilePath}_{$index}.part")) {
+                $done = false;
+                break;
+            }
+        }
+
+        if (!$done) {
+            /**
+             * 断点续传 need
+             */
+//            header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+//            header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
+//            header("Cache-Control: no-store, no-cache, must-revalidate");
+//            header("Cache-Control: post-check=0, pre-check=0", false);
+//            header("Pragma: no-cache");
+//            header("Access-Control-Allow-Origin: *"); // Support CORS
+//            die('');//分片没上传完
+//            $response = Response::create();
+//            throw new HttpResponseException($response);
+        }
+
+        $uploadPath = base_path() . 'upload/';
+
+        $fileSaveName = (empty($app) ? '' : $app . '/') . $strDate . '/' . md5(uniqid()) . "." . $strFileExtension;
+        $strSaveFilePath = $uploadPath . $fileSaveName; //TODO 测试 windows 下
+        $strSaveFileDir = dirname($strSaveFilePath);
+        if (!file_exists($strSaveFileDir)) {
+            mkdir($strSaveFileDir, 0777, true);
+        }
+
+        // 合并临时文件
+        if (!$out = @fopen($strSaveFilePath, "wb")) {
+            $this->error = "上传目录不可写";
+            return false;
+        }
+
+        if (flock($out, LOCK_EX)) {
+            for ($index = 0; $index < $chunks; $index++) {
+                if (!$in = @fopen($targetDir . "{$strFilePath}_{$index}.part", "rb")) {
+                    break;
+                }
+
+                while ($buff = fread($in, 4096)) {
+                    fwrite($out, $buff);
+                }
+
+                fclose($in);
+                unlink("{$targetDir}{$strFilePath}_{$index}.part");
+            }
+            flock($out, LOCK_UN);
+        }
+        @fclose($out);
+
+        $fileImage = new File($strSaveFilePath, 'r');
+        $arrInfo = [
+            "name" => $originalName,
+            "type" => $fileImage->getMime(),
+            "tmp_name" => $strSaveFilePath,
+            "error" => 0,
+            "size" => $fileImage->getSize(),
+        ];
+
+        $fileImage->setSaveName($fileSaveName);
+        $fileImage->setUploadInfo($arrInfo);
+
+        /**
+         * 断点续传 end
+         */
+
+        if (!$fileImage->validate(['size' => $fileUploadMaxFileSize])->check()) {
+            $error = $fileImage->getError();
+            unset($fileImage);
+            unlink($strSaveFilePath);
+            $this->error = $error;
+            return false;
+        }
+
+        //  $url=$first['url'];
+        $storageSetting = cmf_get_cmf_settings('storage');
+        $qiniuSetting = $storageSetting['Qiniu']['setting'];
+        //$url=preg_replace('/^https/', $qiniu_setting['protocol'], $url);
+        //$url=preg_replace('/^http/', $qiniu_setting['protocol'], $url);
+
+        $arrInfo = [];
+        if (config('FILE_UPLOAD_TYPE') == 'Qiniu' && $qiniuSetting['enable_picture_protect']) {
+            //todo  qiniu code ...
+            // $previewUrl = $url.$qiniuSetting['style_separator'].$qiniuSetting['styles']['thumbnail300x300'];
+            // $url= $url.$qiniuSetting['style_separator'].$qiniuSetting['styles']['watermark'];
+        } else {
+
+            if (empty($fileImage)) {
+                $this->error = $fileImage->getError();
+                return false;
+            } else {
+                $arrInfo["user_id"] = $userId;
+                $arrInfo["file_size"] = $fileImage->getSize();
+                $arrInfo["create_time"] = time();
+                $arrInfo["file_md5"] = md5_file($strSaveFilePath);
+                $arrInfo["file_sha1"] = sha1_file($strSaveFilePath);
+                $arrInfo["file_key"] = $arrInfo["file_md5"] . md5($arrInfo["file_sha1"]);
+                $arrInfo["filename"] = $fileImage->getInfo("name");
+                $arrInfo["file_path"] = $strWebPath . $fileSaveName;
+                $arrInfo["suffix"] = $fileImage->getExtension();
+            }
+
+        }
+
+        //关闭文件对象
+        $fileImage = null;
+        //检查文件是否已经存在
+        $assetModel = new AssetModel();
+        $objAsset = $assetModel->where(["user_id" => $userId, "file_key" => $arrInfo["file_key"]])->find();
+
+        $storage = cmf_get_option('storage');
+
+        if (empty($storage['type'])) {
+            $storage['type'] = 'Local';
+        }
+
+        $needUploadToRemoteStorage = false;//是否要上传到云存储
+        if ($objAsset && $storage['type'] == 'Local') {
+            $arrAsset = $objAsset->toArray();
+            //$arrInfo["url"] = $this->request->domain() . $arrAsset["file_path"];
+            $arrInfo["file_path"] = $arrAsset["file_path"];
+            if (file_exists($uploadPath . $arrInfo["file_path"])) {
+                @unlink($strSaveFilePath); // 删除已经上传的文件
+            } else {
+                $oldFileDir = dirname($uploadPath . $arrInfo["file_path"]);
+
+                if (!file_exists($oldFileDir)) {
+                    mkdir($oldFileDir, 0777, true);
+                }
+
+                @rename($strSaveFilePath, $uploadPath . $arrInfo["file_path"]);
+            }
+
+        } else {
+            $needUploadToRemoteStorage = true;
+        }
+
+        if ($objAsset) {
+            $assetModel->where('id', $objAsset['id'])->update(['filename' => $arrInfo["filename"]]);
+        } else {
+            $assetModel->allowField(true)->save($arrInfo);
+        }
+
+        //删除临时文件
+//        for ($index = 0; $index < $chunks; $index++) {
+//            // echo $targetDir . "{$strFilePath}_{$index}.part";
+//            @unlink($targetDir . "{$strFilePath}_{$index}.part");
+//        }
+        @rmdir($targetDir);
+
+        if ($storage['type'] != 'Local') { //  增加存储驱动
+            $watermark = cmf_get_plugin_config($storage['type']);
+            $storage = new Storage($storage['type'], $storage['storages'][$storage['type']]);
+
+            if ($needUploadToRemoteStorage) {
+                session_write_close();
+                $result = $storage->upload($arrInfo["file_path"], $uploadPath . $arrInfo["file_path"], $fileType);
+                if (!empty($result)) {
+                    return array_merge([
+                        'filepath' => $arrInfo["file_path"],
+                        "name" => $arrInfo["filename"],
+                        'id' => $strId,
+                        'preview_url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
+                        'url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
+                    ], $result);
+                }
+            } else {
+                $previewUrl = $fileType == 'image' ? $storage->getPreviewUrl($arrInfo["file_path"]) : $storage->getFileDownloadUrl($arrInfo["file_path"]);
+                $url = $fileType == 'image' ? $storage->getImageUrl($arrInfo["file_path"], $watermark['styles_watermark']) : $storage->getFileDownloadUrl($arrInfo["file_path"]);
+                //测试ing
+                return [
+                    'filepath' => $arrInfo["file_path"],
+                    "name" => $arrInfo["filename"],
+                    'id' => $strId,
+                    'preview_url' => $previewUrl,
+                    'url' => $url,
+                ];
+            }
+
+
+        }
+
+        return [
+            'filepath' => $arrInfo["file_path"],
+            "name" => $arrInfo["filename"],
+            'id' => $strId,
+            'preview_url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
+            'url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
+        ];
+
+    }
+
+    public function getError()
+    {
+        return $this->error;
+    }
+}

+ 34 - 0
app/Common/common.php

@@ -0,0 +1,34 @@
+<?php
+/**
+ * 自定义公共函数
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/30 13:50
+ */
+
+/**
+ * 根据起点坐标和终点坐标测距离
+ * @param array $from [起点坐标(经纬度),例如:array(118.012951,36.810024)]
+ * @param array $to [终点坐标(经纬度)]
+ * @param bool $km 是否以公里为单位 false:米 true:公里(千米)
+ * @param int $decimal 精度 保留小数位数
+ * @return float 距离数值
+ */
+function get_distance(array $from, array $to, bool $km = true, int $decimal = 2): float
+{
+    sort($from);
+    sort($to);
+    $EARTH_RADIUS = 6370.996; // 地球半径系数
+
+    $distance = $EARTH_RADIUS * 2 * asin(sqrt(pow(sin(($from[0] * pi() / 180 - $to[0] * pi() / 180) / 2), 2) + cos($from[0] * pi() / 180) * cos($to[0] * pi() / 180) * pow(sin(($from[1] * pi() / 180 - $to[1] * pi() / 180) / 2), 2))) * 1000;
+
+    if ($km) {
+        $distance = $distance / 1000;
+    }
+
+    return round($distance, $decimal);
+}
+
+function get_file_extension($filename){
+    return substr($filename, strrpos($filename, '.') + 1);
+}

+ 27 - 0
app/Console/Kernel.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Console;
+
+use Illuminate\Console\Scheduling\Schedule;
+use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
+
+class Kernel extends ConsoleKernel
+{
+    /**
+     * Define the application's command schedule.
+     */
+    protected function schedule(Schedule $schedule): void
+    {
+        // $schedule->command('inspire')->hourly();
+    }
+
+    /**
+     * Register the commands for the application.
+     */
+    protected function commands(): void
+    {
+        $this->load(__DIR__.'/Commands');
+
+        require base_path('routes/console.php');
+    }
+}

+ 17 - 0
app/Exceptions/ApiException.php

@@ -0,0 +1,17 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 17:32
+ */
+
+namespace App\Exceptions;
+
+class ApiException extends \Exception
+{
+    public function __construct(array $apiErrorConst, \Throwable $previous = null)
+    {
+        parent::__construct($apiErrorConst['message'], $apiErrorConst['code'], $previous);
+    }
+}

+ 34 - 0
app/Exceptions/Code.php

@@ -0,0 +1,34 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/24 10:41
+ */
+
+namespace App\Exceptions;
+
+class Code
+{
+    const BAD_REQUEST = 40000;
+    const INTERNAL_SERVER_ERROR = 50000;
+    const Ok = 20000;
+
+    const PARES_ERROR = 50001;
+    const REFLECTION_EXCEPTION = 50002;
+    const RUNTIME_EXCEPTION = 50003;
+    const ERROR_EXCEPTION = 50004;
+    const Error = 50005;
+    const BAD_METHOD_CALL_EXCEPTION = 50006;
+
+    const INVALID_ARGUMENT_EXCEPTION = 60000;
+    const MODEL_NOT_FOUND_EXCEPTION = 60001;
+    const QUERY_EXCEPTION = 60002;
+
+    const TOKEN_ERROR_KEY = 70001;
+    const TOKEN_ERROR_SET = 50012;
+    const TOKEN_ERROR_BLACK = 50008;
+    const TOKEN_ERROR_EXPIRED = 50014;
+    const TOKEN_ERROR_JWT = 50012;
+    const TOKEN_ERROR_JTB = 50012;
+}

+ 127 - 0
app/Exceptions/Handler.php

@@ -0,0 +1,127 @@
+<?php
+
+namespace App\Exceptions;
+
+use BadMethodCallException;
+use Error;
+use Illuminate\Database\Eloquent\ModelNotFoundException;
+use Illuminate\Database\QueryException;
+use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
+use Illuminate\Validation\ValidationException;
+use InvalidArgumentException;
+use ParseError;
+use Throwable;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
+use Tymon\JWTAuth\Exceptions\TokenExpiredException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+
+class Handler extends ExceptionHandler
+{
+    private int $code = 0;
+    private string $message = Message::INTERNAL_SERVER_ERROR;
+    private array $data = [];
+    private int $status = Status::INTERNAL_SERVER_ERROR;
+
+    /**
+     * The list of the inputs that are never flashed to the session on validation exceptions.
+     *
+     * @var array<int, string>
+     */
+    protected $dontFlash = [
+        'current_password',
+        'password',
+        'password_confirmation',
+    ];
+
+    /**
+     * Register the exception handling callbacks for the application.
+     */
+    public function register(): void
+    {
+        $this->reportable(function (Throwable $e) {
+            //
+        });
+    }
+
+    private function setException(Throwable $e)
+    {
+        if ($e instanceof ParseError) {  // 语法错误
+            $this->code = Code::PARES_ERROR;
+            $this->message = Message::PARES_ERROR;
+        } elseif ($e instanceof BadMethodCallException) {   // 语法错误
+            $this->code = Code::BAD_METHOD_CALL_EXCEPTION;
+            $this->message = Message::Error;
+        } else if ($e instanceof ApiException) {    // 接口错误
+            $this->code = $e->getCode();
+            $this->message = $e->getMessage();
+        }
+        elseif ($e instanceof QueryException) {  // 数据库查询错误
+            $this->code = Code::QUERY_EXCEPTION;
+            $this->message = Message::QUERY_EXCEPTION; // '数据库查询错误';
+        }
+        /*else if ($e instanceof Error) {
+            $this->code = Code::Error;
+            $this->message = Message::Error;
+        }
+        // 数据库错误
+        // 数据库链接错误
+        elseif ($e instanceof InvalidArgumentException) {
+            $this->code = Code::INVALID_ARGUMENT_EXCEPTION;
+            $this->message = Message::INVALID_ARGUMENT_EXCEPTION; // '数据库链接错误';
+        } // 数据模型错误
+        elseif ($e instanceof ModelNotFoundException) {
+            $this->code = Code::MODEL_NOT_FOUND_EXCEPTION;
+            $this->message = Message::MODEL_NOT_FOUND_EXCEPTION; // '数据库模型错误';
+        }
+        // 服务器错误
+        // 映射错误
+        elseif ($e instanceof \ReflectionException) {
+            $this->code = Code::REFLECTION_EXCEPTION;
+            $this->message = Message::REFLECTION_EXCEPTION; // '服务器映射错误';
+        } // 运行时错误
+        elseif ($e instanceof \RuntimeException) {
+            $this->code = Code::RUNTIME_EXCEPTION;
+            $this->message = Message::RUNTIME_EXCEPTION; // '服务器运行时错误';
+        } // 框架运行错误
+        elseif ($e instanceof \ErrorException) {
+            $this->code = Code::ERROR_EXCEPTION;
+            $this->message = Message::ERROR_EXCEPTION; // '服务器框架运行错误';
+        }*/
+
+        // 验证错误
+        else if ($e instanceof ValidationException) {
+            $this->code = Code::BAD_REQUEST;
+            $this->message = array_values($e->errors())[0][0];
+        }
+    }
+
+    public function render($request, Throwable $e)
+    {
+        if ($request->is('api/admin/*')) {
+            $this->setException($e);
+            if ($this->code) {
+                if ($e instanceof ValidationException) {
+                    $field = array_keys($e->errors())[0];
+                    $this->data = compact('field');
+                    $this->status = Status::BAD_REQUEST;
+                    $this->message = env("APP_DEBUG") ? $this->message : Message::COMMON_EXCEPTION;
+                } else if ($e instanceof ApiException) {
+                    $this->status = Status::OK;
+                } else {
+                    $this->data = [
+                        'file' => $e->getFile(),
+                        'line' => $e->getLine()
+                    ];
+                    if (env("APP_DEBUG")) {
+                        $this->data['message'] = (($e instanceof ModelNotFoundException) ? $e->getModel() : $e->getMessage());
+                        $this->data['trace'] = $e->getTrace();
+                    } else $this->data['message'] = Message::COMMON_EXCEPTION;
+                }
+            }
+            return response(['code' => $this->code, 'message' => $this->message, 'data' => $this->data], $this->status);
+        }
+        return parent::render($request, $e);
+    }
+
+}

+ 46 - 0
app/Exceptions/Message.php

@@ -0,0 +1,46 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/24 10:42
+ */
+
+namespace App\Exceptions;
+
+class Message
+{
+    const BAD_REQUEST = '服务端异常!';
+    const INTERNAL_SERVER_ERROR = '服务器错误!';
+    const Ok = '操作成功!';
+    const PARES_ERROR = '服务器语法错误!';
+    const Error = '服务器语法错误,请注意查看信息!';
+    const REFLECTION_EXCEPTION = '服务器异常映射!';
+    const RUNTIME_EXCEPTION = '服务器运行时错误!';
+    const ERROR_EXCEPTION = '服务器框架运行出错!';
+    const INVALID_ARGUMENT_EXCEPTION = '数据库链接问题!';
+    const MODEL_NOT_FOUND_EXCEPTION = '数据模型错误!';
+    const QUERY_EXCEPTION = '数据库DB错误!';
+
+    const COMMON_EXCEPTION = '网络错误!';
+    const API_ERROR_EXCEPTION = '操作失败!';
+    const ADD_API_ERROR = '添加失败!';
+    const ADD_API_SUCCESS = '添加成功!';
+    const UPDATE_API_ERROR = '修改失败!';
+    const UPDATE_API_SUCCESS = '修改成功!';
+    const STATUS_API_ERROR = '调整失败!';
+    const STATUS_API_SUCCESS = '调整成功!';
+
+    const DELETE_API_ERROR = '删除失败!';
+    const DELETE_API_SUCCESS = '删除成功!';
+
+    const DELETE_RECYCLE_API_ERROR = '恢复失败!';
+    const DELETE_RECYCLE_API_SUCCESS = '恢复成功!';
+
+    const TOKEN_ERROR_KEY = 'apikey错误!';     // 70001
+    const TOKEN_ERROR_SET = '请先登录!';        // 50012
+    const TOKEN_ERROR_BLACK = 'token 非法!';  // 50008
+    const TOKEN_ERROR_EXPIRED = 'token 过期!';  // 50014
+    const TOKEN_ERROR_JWT = '请先登录!';         //  50012
+    const TOKEN_ERROR_JTB = '请先登录!';          // 50012
+}

+ 73 - 0
app/Exceptions/Status.php

@@ -0,0 +1,73 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/24 10:41
+ */
+
+namespace App\Exceptions;
+
+class Status
+{
+//1**:请求收到,继续处理
+    //2**:操作成功收到,分析、接受
+    //3**:完成此请求必须进一步处理
+    //4**:请求包含一个错误语法或不能完成
+    //5**:服务器执行一个完全有效请求失败
+    const CONTINUE = 100;    //客户必须继续发出请求
+    const SWITCHING_PROTOCOLS = 101; //客户要求服务器根据请求转换HTTP协议版本
+    const PROCESSING = 102;
+
+
+    const OK = 200;   //交易成功
+    const CREATED = 201;    // 提示知道新文件的URL
+    const ACCEPTED = 202;   //接受和处理、但处理未完成
+    const NON_AUTHORIATIVE_INFORMATION = 203;  //返回信息不确定或不完整
+    const NO_CONTENT = 204;   //请求收到,但返回信息为空
+    const RESET_CONTENT = 205;     //服务器完成了请求,用户代理必须复位当前已经浏览过的文件
+    const PARTIAL_CONTENT = 206;  //服务器已经完成了部分用户的GET请求
+    const MULTI_STATUS = 207;
+
+
+    const MULTIPLE_CHOICES = 300;  //请求的资源可在多处得到
+    const MOVED_PERMANENTLY = 301; //删除请求数据
+    const FOUND = 302;   //在其他地址发现了请求数据
+    const SEE_OTHER = 303;//建议客户访问其他URL或访问方式
+    const NOT_MODIFIED = 304;//客户端已经执行了GET,但文件未变化
+    const USER_PROXY = 305;//请求的资源必须从服务器指定的地址得到
+    const UNUSED = 306;//前一版本HTTP中使用的代码,现行版本中不再使用
+    const TEMPORARY_REDIRECT = 307;//申明请求的资源临时性删除
+
+
+    const BAD_REQUEST = 400; //错误请求,如语法错误
+    const UNAUTHORIZED = 401;//请求授权失败
+    const PAYMENT_GRANTED = 402;//保留有效ChargeTo头响应
+    const FORBIDDEN = 403;//请求不允许
+    const FILE_NOT_FOUND = 404;//没有发现文件、查询或URl
+    const METHOD_NOT_ALLOWED = 405;//用户在Request-Line字段定义的方法不允许
+    const NOT_ACCEPTABLE = 406;//根据用户发送的Accept拖,请求资源不可访问
+    const PROXY_AUTHENTICATION_REQUIRED = 407;//类似401,用户必须首先在代理服务器上得到授权
+    const REQUEST_TIME_OUT = 408;//客户端没有在用户指定的饿时间内完成请求
+    const CONFLICT = 409;//对当前资源状态,请求不能完成
+    const GONE = 410;//服务器上不再有此资源且无进一步的参考地址
+    const LENGTH_REQUIRED = 411;//服务器拒绝用户定义的Content-Length属性请求
+    const PRECONDITION_FAILED = 412;//一个或多个请求头字段在当前请求中错误
+    const REQUEST_ENTITY_TOO_LARGE = 413;//请求的资源大于服务器允许的大小
+    const REQUEST_URL_TOO_LARGE = 414;//请求的资源URL长于服务器允许的长度
+    const UNSUPPORTED_MEDIA_TYPE = 415;//请求资源不支持请求项目格式
+    const REQUESTED_RANGE_NOT_SATISFIABLE = 416;//请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求也不包含If-Range请求头字段
+    const EXPECTATION_FAILED = 417;//服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下一级服务器不能满足请求
+    const UNPROCESSABLE_ENTITY = 422;
+    const LOCKED = 423;
+    const FAILED_DEPENDENCY = 424;
+
+
+    const INTERNAL_SERVER_ERROR = 500;//服务器产生内部错误
+    const NOT_IMPLEMENTED = 501;//服务器不支持请求的函数
+    const BAD_GATEWAY = 502;//服务器暂时不可用,有时是为了防止发生系统过载
+    const SERVICE_UNAVAILABLE = 503;//服务器过载或暂停维修
+    const GATEWAY_TIMEOUT = 504;//关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长
+    const HTTP_VERSION_ONT_SUPPORTED = 505;//服务器不支持或拒绝支请求头中指定的HTTP版本
+    const INSUFFICIENT_STORAGE = 507;
+}

+ 13 - 0
app/Facades/WechatServiceFacades.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Facades;
+
+use Illuminate\Support\Facades\Facade;
+
+class WechatServiceFacades extends Facade
+{
+    protected static  function wechatServiceFacades(): string
+    {
+        return 'wechatService';
+    }
+}

+ 31 - 0
app/Http/Controllers/Admin/AdminController.php

@@ -0,0 +1,31 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:53
+ */
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\RegisterRequest;
+use App\Http\Services\Admin\AdminService;
+
+class AdminController extends Controller
+{
+    public function store(RegisterRequest $request)
+    {
+        return (new AdminService())->store($request->only([
+            'name',
+            'email',
+            'password'
+        ]));
+    }
+
+    public function user()
+    {
+        $admin = new AdminService();
+        $admin->user();
+    }
+}

+ 40 - 0
app/Http/Controllers/Admin/AuthController.php

@@ -0,0 +1,40 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/24 11:49
+ */
+
+namespace App\Http\Controllers\Admin;
+
+use App\Exceptions\ApiException;
+use App\Exceptions\Code;
+use App\Exceptions\Message;
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\LoginRequest;
+use App\Http\Services\Admin\AuthService;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
+use Tymon\JWTAuth\Exceptions\TokenExpiredException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+
+class AuthController extends Controller
+{
+    public function store(LoginRequest $request)
+    {
+        $data = $request->only(['username', 'password']);
+        return (new AuthService())->login(['name' => $data['username'], 'password' => $data['password']]);
+    }
+
+    public function menus()
+    {
+        return (new AuthService())->menus();
+    }
+
+    public function destroy()
+    {
+        return (new AuthService())->logout();
+    }
+
+}

+ 74 - 0
app/Http/Controllers/Admin/Channel/DistributorController.php

@@ -0,0 +1,74 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Channel;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Services\Admin\Channel\DistributorService;
+use App\Http\Services\Admin\System\MenusService;
+use Illuminate\Http\Request;
+
+class DistributorController extends Controller
+{
+
+    public function index(PageRequest $request)
+    {
+        return (new DistributorService())->index($request->only(['page', 'pageSize','name','status']));
+    }
+
+    public function all()
+    {
+        return (new DistributorService())->all();
+    }
+
+    /**
+     * 创建菜单
+     * Method : Interface store
+     * @param Request $request
+     * @return mixed
+     */
+    public function store(Request $request)
+    {
+        return (new MenusService())->store($request->only([
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function detail(Request $request)
+    {
+
+        $id = $request->route('id');
+        return (new MenusService())->detail($id);
+    }
+
+    public function update(Request $request)
+    {
+        return (new MenusService())->update($request->only([
+            'id',
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+}

+ 30 - 0
app/Http/Controllers/Admin/Finance/StatisticController.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Finance;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Services\Admin\Finance\StatisticService;
+use App\Http\Services\Admin\Order\IndexService;
+use App\Http\Services\Admin\OrderService;
+use Illuminate\Http\Request;
+
+class StatisticController extends Controller
+{
+    //
+    public function income(PageRequest $request)
+    {
+        return (new StatisticService())->income($request->only([
+            'page',
+            'pageSize',
+            'type',
+            'province',
+            'city',
+            'date',
+            'month',
+            'year'
+        ]));
+    }
+
+}

+ 34 - 0
app/Http/Controllers/Admin/FinanceController.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use Illuminate\Http\Request;
+
+class FinanceController extends Controller
+{
+    //
+    public function index(PageRequest $request)
+    {
+
+    }
+
+    public function withdraw(PageRequest $request)
+    {
+        return (new FinanceService())->withdraw($request->only(['page','pageSize','member','payee','status']));
+    }
+
+    public function audit(CommonIdRequest $request)
+    {
+        return (new FinanceService())->audit($request->only(['id','status']));
+    }
+    public function withdrawLog(PageRequest $request)
+    {
+        return (new FinanceService())->withdrawLog($request->only(['page','pageSize','user_id']));
+
+    }
+}

+ 25 - 0
app/Http/Controllers/Admin/IndexController.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/24 18:03
+ */
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Http\Services\Admin\AuthService;
+use App\Http\Services\Admin\TokenService;
+
+class IndexController extends Controller
+{
+    public function info(){
+        return (new TokenService())->info();
+    }
+
+    public function refreshToken()
+    {
+        return (new TokenService())->refreshToken();
+    }
+}

+ 31 - 0
app/Http/Controllers/Admin/Order/AlarmController.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Order;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Services\Admin\Order\AlarmService;
+use App\Http\Services\Admin\Order\IndexService;
+use App\Http\Services\Admin\OrderService;
+use Illuminate\Http\Request;
+
+class AlarmController extends Controller
+{
+    //
+    public function index(PageRequest $request)
+    {
+        return (new AlarmService())->index($request->only(['page','pageSize']));
+    }
+
+    public function realtime(Request $request)
+    {
+        return (new AlarmService())->realtime($request->only(['time']));
+    }
+
+    public function destroy(CommonIdRequest $request)
+    {
+
+    }
+
+}

+ 72 - 0
app/Http/Controllers/Admin/Order/IndexController.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Order;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Services\Admin\Order\IndexService;
+use App\Http\Services\Admin\OrderService;
+use Illuminate\Http\Request;
+
+class IndexController extends Controller
+{
+    //
+    public function index(PageRequest $request)
+    {
+        return (new IndexService())->index($request->only([
+            'page',
+            'pageSize',
+            'order_sn',
+            'key',
+            'status',
+            'created_at',
+            'updated_at'
+        ]));
+    }
+    
+    public function start(CommonIdRequest $request)
+    {
+        return (new IndexService())->start($request->only(['id']));
+    }
+
+    public function receive(CommonIdRequest $request)
+    {
+        return (new IndexService())->receive($request->only(['id']));
+    }
+
+    public function reset(CommonIdRequest $request)
+    {
+        return (new IndexService())->reset($request->only(['id']));
+    }
+
+    public function destroy(CommonIdRequest $request)
+    {
+        return (new IndexService())->delete($request->only(['id']));
+    }
+
+    public function grab(CommonIdRequest $request)
+    {
+        return (new IndexService())->grab($request->only(['id']));
+    }
+
+    public function over(CommonIdRequest $request)
+    {
+        return (new IndexService())->over($request->only(['id']));
+    }
+
+    public function vicinity(Request $request)
+    {
+        return (new IndexService())->vicinity($request->only(['id','page','pageSize']));
+    }
+
+    public function new(Request $request)
+    {
+        return (new IndexService())->new($request->only(['time']));
+    }
+
+    public function alarm(Request $request)
+    {
+        return (new IndexService())->alarm($request->only(['page','pageSize']));
+    }
+}

+ 51 - 0
app/Http/Controllers/Admin/OrderController.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\OrderService;
+use Illuminate\Http\Request;
+
+class OrderController extends Controller
+{
+    //
+    public function index(PageRequest $request)
+    {
+        return (new OrderService())->index($request->only([
+            'page',
+            'pageSize',
+            'order_sn',
+            'key',
+            'status',
+            'created_at',
+            'updated_at'
+        ]));
+    }
+
+    public function reset(CommonIdRequest $request)
+    {
+        return (new OrderService())->reset($request->only(['id']));
+    }
+
+    public function destroy(CommonIdRequest $request)
+    {
+        return (new OrderService())->delete($request->only(['id']));
+    }
+
+    public function grab(CommonIdRequest $request)
+    {
+        return (new OrderService())->grab($request->only(['id']));
+    }
+
+    public function over(CommonIdRequest $request)
+    {
+        return (new OrderService())->over($request->only(['id']));
+    }
+
+    public function vicinity(Request $request)
+    {
+        return (new OrderService())->vicinity($request->only(['id','page','pageSize']));
+    }
+}

+ 77 - 0
app/Http/Controllers/Admin/Project/CategoryController.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Project;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\Project\CategoryService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\SystemService;
+use Illuminate\Http\Request;
+
+class CategoryController extends Controller
+{
+
+    public function index(PageRequest $request)
+    {
+        return (new CategoryService())->index($request->only(['page', 'pageSize', 'name', 'status']));
+    }
+
+    /**
+     * 创建菜单
+     * Method : Interface store
+     * @param Request $request
+     * @return mixed
+     */
+    public function store(Request $request)
+    {
+        return (new MenusService())->store($request->only([
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function detail(Request $request)
+    {
+
+        $id = $request->route('id');
+        return (new MenusService())->detail($id);
+    }
+
+    public function update(Request $request)
+    {
+        return (new MenusService())->update($request->only([
+            'id',
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function all()
+    {
+        return (new CategoryService())->all();
+    }
+}

+ 82 - 0
app/Http/Controllers/Admin/Project/IndexController.php

@@ -0,0 +1,82 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Project;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\Project\CategoryService;
+use App\Http\Services\Admin\Project\IndexService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\SystemService;
+use App\Models\Project;
+use Illuminate\Http\Request;
+
+class IndexController extends Controller
+{
+
+    public function index(PageRequest $request)
+    {
+        return (new IndexService())->index($request->only(['page', 'pageSize', 'name', 'category','status']));
+    }
+
+    public function regionTree(Request $request)
+    {
+        $id = $request->route('id');
+        return (new IndexService())->regionTree($id);
+    }
+
+    public function region(Request $request)
+    {
+        $id = $request->route('id');
+        return (new IndexService())->region($id);
+    }
+
+    public function regionStore(Request $request)
+    {
+        return (new IndexService())->regionStore($request->only(['id', 'region_ids']));
+    }
+
+    public function regionDestroy(Request $request)
+    {
+        return (new IndexService())->regionDestroy($request->only(['id', 'project_id']));
+    }
+
+    public function regionUpdate(Request $request)
+    {
+        return (new IndexService())->regionUpdate($request->only(['id', 'minute', 'price', 'order_price']));
+    }
+
+    public function store(Request $request)
+    {
+        $category = $request->input('category');
+        $data = $request->only(['banner', 'brief', 'content', 'icon', 'minute', 'name', 'order_price', 'price', 'is_show']);
+        return (new IndexService())->store($data, $category);
+    }
+
+    public function detail(Request $request)
+    {
+        $id = $request->route('id');
+        return (new IndexService())->detail($id);
+    }
+
+    public function update(Request $request)
+    {
+        $category = $request->input('category');
+        $data = $request->only(['id', 'banner', 'brief', 'content', 'icon', 'minute', 'name', 'order_price', 'price', 'is_show']);
+        return (new IndexService())->update($data, $category);
+    }
+
+    public function status(Request $request){
+        $data = $request->only(['id','is_show']);
+        return (new IndexService())->status($data);
+    }
+
+    public function destroy(Request $request)
+    {
+        $id = $request->route('id');
+        return (new IndexService())->destroy(is_array($id) ?: [$id]);
+    }
+}

+ 76 - 0
app/Http/Controllers/Admin/System/MenusController.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Http\Controllers\Admin\System;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\SystemService;
+use Illuminate\Http\Request;
+
+class MenusController extends Controller
+{
+
+    public function index(PageRequest $request)
+    {
+        return (new MenusService())->index($request->only(['page', 'pageSize']));
+    }
+
+    /**
+     * 创建菜单
+     * Method : Interface store
+     * @param Request $request
+     * @return mixed
+     */
+    public function store(Request $request)
+    {
+        return (new MenusService())->store($request->only([
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function detail(Request $request)
+    {
+
+        $id = $request->route('id');
+        return (new MenusService())->detail($id);
+    }
+
+    public function update(Request $request)
+    {
+        return (new MenusService())->update($request->only([
+            'id',
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function all()
+    {
+        return (new MenusService())->all();
+    }
+}

+ 48 - 0
app/Http/Controllers/Admin/System/RegionController.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace App\Http\Controllers\Admin\System;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\System\RegionService;
+use App\Http\Services\Admin\SystemService;
+use Illuminate\Http\Request;
+
+class RegionController extends Controller
+{
+
+    public function index(PageRequest $request)
+    {
+
+    }
+
+    /**
+     * 创建菜单
+     * Method : Interface store
+     * @param Request $request
+     * @return mixed
+     */
+    public function store(Request $request)
+    {
+
+    }
+
+    public function detail(Request $request)
+    {
+
+    }
+
+    public function update(Request $request)
+    {
+
+    }
+
+    public function all()
+    {
+        return (new RegionService())->all();
+    }
+}

+ 30 - 0
app/Http/Controllers/Admin/System/RoleAuthController.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Controllers\Admin\System;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\System\RoleAuthService;
+use App\Http\Services\Admin\System\RoleService;
+use App\Http\Services\Admin\SystemService;
+use Illuminate\Http\Request;
+
+class RoleAuthController extends Controller
+{
+
+    public function store(Request $request)
+    {
+        return (new RoleAuthService())->store($request->only(['role_id', 'auth_ids']));
+    }
+
+    public function detail(Request $request)
+    {
+        $id = $request->route('id');
+        return (new RoleAuthService())->detail($id);
+    }
+
+}

+ 53 - 0
app/Http/Controllers/Admin/System/RoleController.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Http\Controllers\Admin\System;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\System\RoleService;
+use App\Http\Services\Admin\SystemService;
+use Illuminate\Http\Request;
+
+class RoleController extends Controller
+{
+    public function index(PageRequest $request)
+    {
+        return (new MenusService())->index($request->only(['page', 'pageSize']));
+    }
+
+    public function store(Request $request)
+    {
+        return (new RoleService())->store($request->only(['id', 'ids']));
+    }
+
+    public function detail(Request $request)
+    {
+
+        $id = $request->route('id');
+        return (new MenusService())->detail($id);
+    }
+
+    public function update(Request $request)
+    {
+        return (new MenusService())->update($request->only([
+            'id',
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+}

+ 22 - 0
app/Http/Controllers/Admin/System/UploadController.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Controllers\Admin\System;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\System\UploadService;
+use App\Http\Services\Admin\SystemService;
+use Illuminate\Http\Request;
+
+class UploadController extends Controller
+{
+
+    public function store(Request $request)
+    {
+        return (new UploadService())->store($request);
+    }
+}

+ 31 - 0
app/Http/Controllers/Admin/SystemController.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\SystemService;
+use Illuminate\Http\Request;
+
+class SystemController extends Controller
+{
+    //
+    public function index(PageRequest $request)
+    {
+
+    }
+
+    public function users(PageRequest $request)
+    {
+        return (new SystemService())->users($request->only(['page','pageSize','status']));
+    }
+
+    public function roles(PageRequest $request)
+    {
+        return (new SystemService())->roles($request->only(['page','pageSize','status']));
+    }
+
+}

+ 25 - 0
app/Http/Controllers/Admin/Task/AgentStatisticController.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Task;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\Project\CategoryService;
+use App\Http\Services\Admin\Project\IndexService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\SystemService;
+use App\Http\Services\Admin\Task\AgentStatisticService;
+use App\Models\Project;
+use Illuminate\Http\Request;
+
+class AgentStatisticController extends Controller
+{
+
+    public function daily(Request $request)
+    {
+        return (new AgentStatisticService())->daily($request->only(['date']));
+    }
+}

+ 83 - 0
app/Http/Controllers/Admin/User/ArtificerController.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Http\Controllers\Admin\User;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\SystemService;
+use App\Http\Services\Admin\User\ArtificerService;
+use App\Http\Services\Admin\User\MemberService;
+use Illuminate\Http\Request;
+
+class ArtificerController extends Controller
+{
+
+    public function index(PageRequest $request)
+    {
+        return (new ArtificerService())->index($request->only(['page', 'pageSize', 'key', 'phone', 'status']));
+    }
+
+    /**
+     * 创建菜单
+     * Method : Interface store
+     * @param Request $request
+     * @return mixed
+     */
+    public function store(Request $request)
+    {
+        return (new MenusService())->store($request->only([
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function detail(Request $request)
+    {
+
+        $id = $request->route('id');
+        return (new MenusService())->detail($id);
+    }
+
+    public function update(Request $request)
+    {
+        return (new MenusService())->update($request->only([
+            'id',
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function all()
+    {
+        return (new MenusService())->all();
+    }
+
+    public function audit(Request $request)
+    {
+        return (new ArtificerService())->audit($request->only(['id', 'status']));
+    }
+}

+ 77 - 0
app/Http/Controllers/Admin/User/MemberController.php

@@ -0,0 +1,77 @@
+<?php
+
+namespace App\Http\Controllers\Admin\User;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Services\Admin\FinanceService;
+use App\Http\Services\Admin\OrderService;
+use App\Http\Services\Admin\System\MenusService;
+use App\Http\Services\Admin\SystemService;
+use App\Http\Services\Admin\User\MemberService;
+use Illuminate\Http\Request;
+
+class MemberController extends Controller
+{
+
+    public function index(PageRequest $request)
+    {
+        return (new MemberService())->index($request->only(['page', 'pageSize','key','user_status','distributor_id','create_time']));
+    }
+
+    /**
+     * 创建菜单
+     * Method : Interface store
+     * @param Request $request
+     * @return mixed
+     */
+    public function store(Request $request)
+    {
+        return (new MenusService())->store($request->only([
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function detail(Request $request)
+    {
+
+        $id = $request->route('id');
+        return (new MenusService())->detail($id);
+    }
+
+    public function update(Request $request)
+    {
+        return (new MenusService())->update($request->only([
+            'id',
+            'title',
+            'name',
+            'type',
+            'icon',
+            'pid',
+            'path',
+            'component',
+            'redirect',
+            'sort',
+            'status',
+            'hide',
+            'keep_alive'
+        ]));
+    }
+
+    public function all()
+    {
+        return (new MenusService())->all();
+    }
+}

+ 127 - 0
app/Http/Controllers/Admin/Wechat/OfficialAccountController.php

@@ -0,0 +1,127 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2024/1/23 15:55
+ */
+
+namespace App\Http\Controllers\Admin\Wechat;
+
+use App\Http\Services\Admin\Wechat\OfficialAccountService;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Log;
+use Overtrue\LaravelWeChat\EasyWeChat;
+
+class OfficialAccountController
+{
+    public function serve(Request $request)
+    {
+        Log::info('request arrived.'); # 注意:Log 为 Laravel 组件,所以它记的日志去 Laravel 日志看,而不是 EasyWeChat 日志
+
+        $wechat = EasyWeChat::officialAccount();
+
+        $server = $wechat->getServer();
+
+        try {
+            $server->addEventListener('subscribe', function ($message, \Closure $next) {
+
+                Log::info('subscribe.' . $message['ToUserName']);
+//                return [
+//                    'MsgType' => 'text',
+//                    'ToUserName' => $message['FromUserName'],
+//                    'FromUserName' => $message['ToUserName'],
+//                    'Content' => 'nihao',
+//                    'CreateTime' => time(),
+//                ];
+                return '感谢您关注 EasyWeChat!';
+            });
+        } catch (\Throwable $e) {
+            Log::info('subscribe error.');
+
+        }
+
+        $server->addEventListener('unsubscribe', function ($message, \Closure $next) {
+            Log::info('unsubscribe.' . $message);
+            $next($message);
+        });
+
+        try {
+            $server->addMessageListener('text', function ($message) {
+
+                Log::info('text.' . $message);
+//                return 'text!';
+                return [
+                    'touser' => $message['FromUserName'],
+                    'template_id' => 'PBaedliMneE_syJZDD1bhpEKlsKwk0WlUqSHZ8uG1c8',
+                    'url' => 'https://easywechat.com',
+                    'data' => [
+                        'key1' => 'VALUE',
+                        'key2' => 'VALUE2',
+                        'key3' => 'VALUE3',
+                        'key4' => 'VALUE4',
+                        'key5' => 'VALUE5',
+                    ],
+                ];
+            });
+        } catch (\Throwable $e) {
+            Log::info('error.');
+        }
+
+
+//
+//        $server->setMessageHandler(function($message) use ($app){
+//            if ($message->MsgType=='event') {
+//                $user_openid = $message->FromUserName;
+//                if ($message->Event=='subscribe') {
+//                            //下面是你点击关注时,进行的操作
+//                $user_info['unionid'] = $message->ToUserName;
+//                $user_info['openid'] = $user_openid;
+//                $userService = $app->user;
+//                $user = $userService->get($user_info['openid']);
+//                $user_info['subscribe_time'] = $user['subscribe_time'];
+//                $user_info['nickname'] = $user['nickname'];
+//                $user_info['avatar'] = $user['headimgurl'];
+//                $user_info['sex'] = $user['sex'];
+//                $user_info['province'] = $user['province'];
+//                $user_info['city'] = $user['city'];
+//                $user_info['country'] = $user['country'];
+//                $user_info['is_subscribe'] = 1;
+//          //下面有些是WxStudent相关的方法,就是一些数据库的操作,由于数据库不同,要执行的操作也不一样,所以就只写了一个方法名
+//                if (WxStudent::weixin_attention($user_info)) {
+//                    return '欢迎关注';
+//                }else{
+//                    return '您的信息由于某种原因没有保存,请重新关注';
+//                }
+//            }else if ($message->Event=='unsubscribe') {
+//                            //取消关注时执行的操作,(注意下面返回的信息用户不会收到,因为你已经取消关注,但别的操作还是会执行的<如:取消关注的时候,要把记录该用户从记录微信用户信息的表中删掉>)
+//                if (WxStudent::weixin_cancel_attention($user_openid)) {
+//                    return '已取消关注';
+//                }
+//            }
+//            }
+//
+//        });
+
+
+        $response = $server->serve();
+        Log::info('request arrived.');
+        return response($response->getBody());
+    }
+
+    public function oauth(Request $request)
+    {
+        return (new OfficialAccountService())->oauth($request->only(['redirect_url','scopes']));
+    }
+
+    public function oauth_callback(Request $request)
+    {
+        return (new OfficialAccountService())->oauth_callback($request->only(['code']));
+    }
+    
+    public function signature()
+    {
+        return (new OfficialAccountService())->signature();
+    }
+}
+

+ 29 - 0
app/Http/Controllers/Admin/Wechat/WechatController.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Http\Controllers\Admin\Wechat;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Log;
+use Overtrue\LaravelWeChat\EasyWeChat;
+
+class WechatController extends Controller
+{
+
+    public function serve(Request $request)
+    {
+        Log::info('request arrived.'); # 注意:Log 为 Laravel 组件,所以它记的日志去 Laravel 日志看,而不是 EasyWeChat 日志
+
+//        $wechat = EasyWeChat::officialAccount();
+
+        $wechat = app('easywechat.official_account.default');
+        $wechat->server->setMessageHandler(function($message){
+            return "欢迎关注 overtrue!";
+        });
+
+        Log::info('return response.');
+
+        return $wechat->server->serve();
+    }
+}

+ 38 - 0
app/Http/Controllers/Auth/AuthenticatedSessionController.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Auth\LoginRequest;
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use Illuminate\Support\Facades\Auth;
+
+class AuthenticatedSessionController extends Controller
+{
+    /**
+     * Handle an incoming authentication request.
+     */
+    public function store(LoginRequest $request): Response
+    {
+        $request->authenticate();
+
+        $request->session()->regenerate();
+
+        return response()->noContent();
+    }
+
+    /**
+     * Destroy an authenticated session.
+     */
+    public function destroy(Request $request): Response
+    {
+        Auth::guard('web')->logout();
+
+        $request->session()->invalidate();
+
+        $request->session()->regenerateToken();
+
+        return response()->noContent();
+    }
+}

+ 26 - 0
app/Http/Controllers/Auth/EmailVerificationNotificationController.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use App\Http\Controllers\Controller;
+use App\Providers\RouteServiceProvider;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+
+class EmailVerificationNotificationController extends Controller
+{
+    /**
+     * Send a new email verification notification.
+     */
+    public function store(Request $request): JsonResponse|RedirectResponse
+    {
+        if ($request->user()->hasVerifiedEmail()) {
+            return redirect()->intended(RouteServiceProvider::HOME);
+        }
+
+        $request->user()->sendEmailVerificationNotification();
+
+        return response()->json(['status' => 'verification-link-sent']);
+    }
+}

+ 53 - 0
app/Http/Controllers/Auth/NewPasswordController.php

@@ -0,0 +1,53 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use App\Http\Controllers\Controller;
+use Illuminate\Auth\Events\PasswordReset;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Hash;
+use Illuminate\Support\Facades\Password;
+use Illuminate\Support\Str;
+use Illuminate\Validation\Rules;
+use Illuminate\Validation\ValidationException;
+
+class NewPasswordController extends Controller
+{
+    /**
+     * Handle an incoming new password request.
+     *
+     * @throws \Illuminate\Validation\ValidationException
+     */
+    public function store(Request $request): JsonResponse
+    {
+        $request->validate([
+            'token' => ['required'],
+            'email' => ['required', 'email'],
+            'password' => ['required', 'confirmed', Rules\Password::defaults()],
+        ]);
+
+        // Here we will attempt to reset the user's password. If it is successful we
+        // will update the password on an actual user model and persist it to the
+        // database. Otherwise we will parse the error and return the response.
+        $status = Password::reset(
+            $request->only('email', 'password', 'password_confirmation', 'token'),
+            function ($user) use ($request) {
+                $user->forceFill([
+                    'password' => Hash::make($request->password),
+                    'remember_token' => Str::random(60),
+                ])->save();
+
+                event(new PasswordReset($user));
+            }
+        );
+
+        if ($status != Password::PASSWORD_RESET) {
+            throw ValidationException::withMessages([
+                'email' => [__($status)],
+            ]);
+        }
+
+        return response()->json(['status' => __($status)]);
+    }
+}

+ 39 - 0
app/Http/Controllers/Auth/PasswordResetLinkController.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use App\Http\Controllers\Controller;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Password;
+use Illuminate\Validation\ValidationException;
+
+class PasswordResetLinkController extends Controller
+{
+    /**
+     * Handle an incoming password reset link request.
+     *
+     * @throws \Illuminate\Validation\ValidationException
+     */
+    public function store(Request $request): JsonResponse
+    {
+        $request->validate([
+            'email' => ['required', 'email'],
+        ]);
+
+        // We will send the password reset link to this user. Once we have attempted
+        // to send the link, we will examine the response then see the message we
+        // need to show to the user. Finally, we'll send out a proper response.
+        $status = Password::sendResetLink(
+            $request->only('email')
+        );
+
+        if ($status != Password::RESET_LINK_SENT) {
+            throw ValidationException::withMessages([
+                'email' => [__($status)],
+            ]);
+        }
+
+        return response()->json(['status' => __($status)]);
+    }
+}

+ 36 - 0
app/Http/Controllers/Auth/RegisteredUserController.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use App\Exceptions\ApiException;
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\RegisterRequest;
+use App\Models\User;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Hash;
+
+class RegisteredUserController extends Controller
+{
+    /**
+     * Handle an incoming registration request.
+     *
+     * @throws \Illuminate\Validation\ValidationException
+     * @throws ApiException
+     */
+    public function store(RegisterRequest $request): ApiException
+    {
+//        throw new ApiException(['code'=>888,'message'=>'hahaha']);
+        $user = User::create([
+            'name' => $request->name,
+            'email' => $request->email,
+            'password' => Hash::make($request->password),
+        ]);
+
+
+//        event(new Registered($user));
+
+        $return = Auth::guard('api')->attempt($user);
+dd($return);
+        return response()->noContent();
+    }
+}

+ 32 - 0
app/Http/Controllers/Auth/VerifyEmailController.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Controllers\Auth;
+
+use App\Http\Controllers\Controller;
+use App\Providers\RouteServiceProvider;
+use Illuminate\Auth\Events\Verified;
+use Illuminate\Foundation\Auth\EmailVerificationRequest;
+use Illuminate\Http\RedirectResponse;
+
+class VerifyEmailController extends Controller
+{
+    /**
+     * Mark the authenticated user's email address as verified.
+     */
+    public function __invoke(EmailVerificationRequest $request): RedirectResponse
+    {
+        if ($request->user()->hasVerifiedEmail()) {
+            return redirect()->intended(
+                config('app.frontend_url').RouteServiceProvider::HOME.'?verified=1'
+            );
+        }
+
+        if ($request->user()->markEmailAsVerified()) {
+            event(new Verified($request->user()));
+        }
+
+        return redirect()->intended(
+            config('app.frontend_url').RouteServiceProvider::HOME.'?verified=1'
+        );
+    }
+}

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

@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
+use Illuminate\Foundation\Validation\ValidatesRequests;
+use Illuminate\Routing\Controller as BaseController;
+
+class Controller extends BaseController
+{
+    use AuthorizesRequests, ValidatesRequests;
+}

+ 36 - 0
app/Http/Controllers/Frontend/Account/UserController.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Http\Controllers\Frontend\Account;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\CommonIdRequest;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Services\Admin\Order\AlarmService;
+use App\Http\Services\Admin\Order\IndexService;
+use App\Http\Services\Admin\OrderService;
+use Illuminate\Http\Request;
+
+class UserController extends Controller
+{
+    //
+    public function index(PageRequest $request)
+    {
+//        return (new AlarmService())->index($request->only(['page','pageSize']));
+    }
+
+    public function getUserByOpenID(Request $request)
+    {
+        return (new UserService())->getUserByOpenID($request->only(['open_id']));
+    }
+
+    public function realtime(Request $request)
+    {
+
+    }
+
+    public function destroy(CommonIdRequest $request)
+    {
+
+    }
+
+}

+ 24 - 0
app/Http/Controllers/Frontend/Wechat/OfficialAccountController.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Controllers\Frontend\Wechat;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use App\Http\Services\Frontend\Wechat\OfficialAccountService;
+use Illuminate\Http\Request;
+
+class OfficialAccountController extends Controller
+{
+    //photos.index
+    //photos.create
+    //photos.store
+    //photos.show
+    //photos.edit
+    //photos.update
+    //photos.destroy
+    public function login(Request $request)
+    {
+        return (new OfficialAccountService())->login($request->only(['open_id']));
+    }
+
+}

+ 25 - 0
app/Http/Controllers/Frontend/Wechat/WechatController.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Http\Controllers\Frontend\Wechat;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Admin\PageRequest;
+use Illuminate\Support\Facades\Log;
+
+class WechatController extends Controller
+{
+
+    public function serve(PageRequest $request)
+    {
+        Log::info('request arrived.'); # 注意:Log 为 Laravel 组件,所以它记的日志去 Laravel 日志看,而不是 EasyWeChat 日志
+
+        $wechat = app('wechat');
+        $wechat->server->setMessageHandler(function($message){
+            return "欢迎关注 overtrue!";
+        });
+
+        Log::info('return response.');
+
+        return $wechat->server->serve();
+    }
+}

+ 71 - 0
app/Http/Kernel.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace App\Http;
+
+use Illuminate\Foundation\Http\Kernel as HttpKernel;
+
+class Kernel extends HttpKernel
+{
+    /**
+     * The application's global HTTP middleware stack.
+     *
+     * These middleware are run during every request to your application.
+     *
+     * @var array<int, class-string|string>
+     */
+    protected $middleware = [
+        // \App\Http\Middleware\TrustHosts::class,
+        \App\Http\Middleware\TrustProxies::class,
+        \Illuminate\Http\Middleware\HandleCors::class,
+        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
+        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
+        \App\Http\Middleware\TrimStrings::class,
+        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
+    ];
+
+    /**
+     * The application's route middleware groups.
+     *
+     * @var array<string, array<int, class-string|string>>
+     */
+    protected $middlewareGroups = [
+        'web' => [
+            \App\Http\Middleware\EncryptCookies::class,
+            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+            \Illuminate\Session\Middleware\StartSession::class,
+            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+            \App\Http\Middleware\VerifyCsrfToken::class,
+            \Illuminate\Routing\Middleware\SubstituteBindings::class,
+        ],
+
+        'api' => [
+//            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
+            \Illuminate\Session\Middleware\StartSession::class,
+            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
+            \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
+            \Illuminate\Routing\Middleware\SubstituteBindings::class,
+        ],
+    ];
+
+    /**
+     * The application's middleware aliases.
+     *
+     * Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
+     *
+     * @var array<string, class-string|string>
+     */
+    protected $middlewareAliases = [
+        'auth' => \App\Http\Middleware\Authenticate::class,
+        'auth.admin' => \App\Http\Middleware\Admin\Authenticate::class,
+        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
+        'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
+        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
+        'can' => \Illuminate\Auth\Middleware\Authorize::class,
+        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
+        'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
+        'signed' => \App\Http\Middleware\ValidateSignature::class,
+        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+//        'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
+    ];
+}

+ 83 - 0
app/Http/Middleware/Admin/Authenticate.php

@@ -0,0 +1,83 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/24 16:25
+ */
+
+namespace App\Http\Middleware\Admin;
+
+use App\Exceptions\ApiException;
+use App\Exceptions\Code;
+use App\Exceptions\Message;
+use Closure;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Config;
+use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
+use Tymon\JWTAuth\Exceptions\JWTException;
+use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
+use Tymon\JWTAuth\Exceptions\TokenExpiredException;
+use Tymon\JWTAuth\Exceptions\TokenInvalidException;
+use Tymon\JWTAuth\Facades\JWTAuth;
+
+class Authenticate
+{
+    public function handle(Request $request, Closure $next)
+    {
+        $route_data = $request->route();
+        $url = str_replace($route_data->getAction()['prefix'] . '/', "", $route_data->uri);
+        $url_arr = [
+            'login',
+            'logout'
+        ];
+        $api_key = $request->header('apikey');
+        if ($api_key != config('admin.api_key')) {
+            throw new ApiException(['code' => Code::TOKEN_ERROR_KEY, 'message' => Message::TOKEN_ERROR_KEY]);
+        }
+        if (in_array($url, $url_arr)) {
+            return $next($request);
+        }
+
+        try {
+            Config::set('auth.defaults.guard','admin');
+            if (!JWTAuth::parseToken()->authenticate()) {  //获取到用户数据,并赋值给$user   'msg' => '用户不存在'
+                throw new ApiException(['code' => Code::TOKEN_ERROR_SET, 'message' => Message::TOKEN_ERROR_SET]);
+            }
+        } catch (TokenBlacklistedException $e) {
+            // 这个时候是老的token被拉到黑名单了
+            throw new ApiException(['code' => Code::TOKEN_ERROR_BLACK, 'message' => Message::TOKEN_ERROR_BLACK]);
+        } catch (TokenExpiredException $e) {
+            //token已过期
+            try {
+                // 刷新用户的 token
+                $token = JWTAuth::parseToken()->refresh();
+                $access_token = 'Bearer '.$token;
+                $request->headers->set('Authorization',$access_token);
+
+                // 使用一次性登录以保证此次请求的成功
+                Auth::guard('admin')->onceUsingId(JWTAuth::manager()->getPayloadFactory()->buildClaimsCollection()->toPlainArray()['sub']);
+
+                $response = $next($request);
+                $response->headers->set('Access-Control-Expose-Headers',"Authorization");
+                $response->headers->set('Authorization', $access_token);
+                return $response;
+            } catch (JWTException $exception) {
+                // 如果捕获到此异常,即代表 refresh 也过期了,用户无法刷新令牌,需要重新登录。
+                throw new ApiException(['code' => Code::TOKEN_ERROR_EXPIRED, 'message' => Message::TOKEN_ERROR_EXPIRED]);
+            }
+
+//            throw new ApiException(['code' => Code::TOKEN_ERROR_EXPIRED, 'message' => Message::TOKEN_ERROR_EXPIRED]);
+        } catch (TokenInvalidException $e) {
+            //token无效
+            throw new ApiException(['code' => Code::TOKEN_ERROR_JWT, 'message' => Message::TOKEN_ERROR_JWT]);
+        } catch (JWTException $e) {
+            //'缺少token'
+            throw new ApiException(['code' => Code::TOKEN_ERROR_JTB, 'message' => Message::TOKEN_ERROR_JTB]);
+        }
+        // 写入日志
+//        (new OperationLogService())->store($user['id']);
+        return $next($request);
+    }
+}

+ 19 - 0
app/Http/Middleware/Authenticate.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Auth\Middleware\Authenticate as Middleware;
+use Illuminate\Http\Exceptions\HttpResponseException;
+use Illuminate\Http\Request;
+
+class Authenticate extends Middleware
+{
+    /**
+     * Get the path the user should be redirected to when they are not authenticated.
+     */
+    protected function redirectTo(Request $request): ?string
+    {
+//        throw new HttpResponseException(response()->json(['code' => 401]));
+        return $request->expectsJson() ? null : route('login');
+    }
+}

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

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
+
+class EncryptCookies extends Middleware
+{
+    /**
+     * The names of the cookies that should not be encrypted.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        //
+    ];
+}

+ 27 - 0
app/Http/Middleware/EnsureEmailIsVerified.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Contracts\Auth\MustVerifyEmail;
+use Illuminate\Http\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+class EnsureEmailIsVerified
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
+     */
+    public function handle(Request $request, Closure $next): Response
+    {
+        if (! $request->user() ||
+            ($request->user() instanceof MustVerifyEmail &&
+            ! $request->user()->hasVerifiedEmail())) {
+            return response()->json(['message' => 'Your email address is not verified.'], 409);
+        }
+
+        return $next($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
+{
+    /**
+     * The URIs that should be reachable while maintenance mode is enabled.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        //
+    ];
+}

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

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

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

@@ -0,0 +1,19 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
+
+class TrimStrings extends Middleware
+{
+    /**
+     * The names of the attributes that should not be trimmed.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        'current_password',
+        'password',
+        'password_confirmation',
+    ];
+}

+ 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
+{
+    /**
+     * Get the host patterns that should be trusted.
+     *
+     * @return array<int, string|null>
+     */
+    public function hosts(): array
+    {
+        return [
+            $this->allSubdomainsOfApplicationUrl(),
+        ];
+    }
+}

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

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Http\Middleware\TrustProxies as Middleware;
+use Illuminate\Http\Request;
+
+class TrustProxies extends Middleware
+{
+    /**
+     * The trusted proxies for this application.
+     *
+     * @var array<int, string>|string|null
+     */
+    protected $proxies;
+
+    /**
+     * The headers that should be used to detect proxies.
+     *
+     * @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;
+}

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

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
+
+class ValidateSignature extends Middleware
+{
+    /**
+     * The names of the query string parameters that should be ignored.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        // 'fbclid',
+        // 'utm_campaign',
+        // 'utm_content',
+        // 'utm_medium',
+        // 'utm_source',
+        // 'utm_term',
+    ];
+}

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

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
+
+class VerifyCsrfToken extends Middleware
+{
+    /**
+     * The URIs that should be excluded from CSRF verification.
+     *
+     * @var array<int, string>
+     */
+    protected $except = [
+        //
+    ];
+}

+ 36 - 0
app/Http/Requests/Admin/CommonIdRequest.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Http\Requests\Admin;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class CommonIdRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'id' => 'required|integer'
+        ];
+    }
+
+    public function messages()
+    {
+        return [
+            'id.required' => '缺少参数!',
+            'id.integer' => '参数错误!',
+        ];
+    }
+}

+ 42 - 0
app/Http/Requests/Admin/LoginRequest.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Http\Requests\Admin;
+
+use App\Models\User;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Validation\Rules;
+
+class LoginRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'username' => ['required', 'string', 'lowercase', 'max:255'],
+            'password' => ['required']
+//            'password' => ['required', 'regex:/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!$%@#£€*?&]{6,}$/']
+//            'password' => ['required', Rules\Password::defaults()]
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'username.required' => '请输入用户名',
+            'password.required' => '请输入密码',
+        ];
+    }
+
+}

+ 38 - 0
app/Http/Requests/Admin/PageRequest.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace App\Http\Requests\Admin;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class PageRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'page' => 'required|integer|min:1',
+            'pageSize' => 'required|integer|min:1',
+        ];
+    }
+
+    public function messages(){
+        return [
+            'page.required' 				=> '缺少参数(page)!',
+            'page.integer' 	=> '(page)参数错误!',
+            'pageSize.required' 				=> '缺少参数(pageSize)!',
+            'pageSize.integer' 	=> '(pageSize)参数错误!',
+        ];
+    }
+}

+ 43 - 0
app/Http/Requests/Admin/RegisterRequest.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Http\Requests\Admin;
+
+use App\Models\User;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Validation\Rules;
+
+class RegisterRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'name' => ['required', 'string', 'max:255'],
+            'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
+            'password' => ['required', 'confirmed', Rules\Password::defaults()]
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'name.required' => '请输入账号',
+            'email.required' => '请输入邮箱',
+            'password.required' => '请输入密码',
+            'password_confirmation.required' => '两次输入密码不一致',
+        ];
+    }
+
+}

+ 85 - 0
app/Http/Requests/Auth/LoginRequest.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Http\Requests\Auth;
+
+use Illuminate\Auth\Events\Lockout;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\RateLimiter;
+use Illuminate\Support\Str;
+use Illuminate\Validation\ValidationException;
+
+class LoginRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'email' => ['required', 'string', 'email'],
+            'password' => ['required', 'string'],
+        ];
+    }
+
+    /**
+     * Attempt to authenticate the request's credentials.
+     *
+     * @throws \Illuminate\Validation\ValidationException
+     */
+    public function authenticate(): void
+    {
+        $this->ensureIsNotRateLimited();
+
+        if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
+            RateLimiter::hit($this->throttleKey());
+
+            throw ValidationException::withMessages([
+                'email' => __('auth.failed'),
+            ]);
+        }
+
+        RateLimiter::clear($this->throttleKey());
+    }
+
+    /**
+     * Ensure the login request is not rate limited.
+     *
+     * @throws \Illuminate\Validation\ValidationException
+     */
+    public function ensureIsNotRateLimited(): void
+    {
+        if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
+            return;
+        }
+
+        event(new Lockout($this));
+
+        $seconds = RateLimiter::availableIn($this->throttleKey());
+
+        throw ValidationException::withMessages([
+            'email' => trans('auth.throttle', [
+                'seconds' => $seconds,
+                'minutes' => ceil($seconds / 60),
+            ]),
+        ]);
+    }
+
+    /**
+     * Get the rate limiting throttle key for the request.
+     */
+    public function throttleKey(): string
+    {
+        return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip());
+    }
+}

+ 23 - 0
app/Http/Services/Admin/AdminService.php

@@ -0,0 +1,23 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\User;
+
+class AdminService extends BaseService
+{
+    public function store(array $data)
+    {
+        $data['password'] = bcrypt($data['password']);
+        return $this->commonCreate(User::query(),$data);
+    }
+
+}

+ 55 - 0
app/Http/Services/Admin/AuthService.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Menu;
+use App\Models\Role;
+use App\Models\RoleAuth;
+use App\Models\User;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+
+class AuthService extends BaseService
+{
+    public function login(array $data)
+    {
+        try {
+            $token = (new TokenService())->setToken($data);
+        } catch (\Exception $e) {
+            $this->apiError('用户名或密码错误');
+        }
+        return $this->apiSuccess('登录成功!', $token);
+    }
+
+    public function logout()
+    {
+        (new TokenService())->logout();
+        return $this->apiSuccess('退出成功!');
+    }
+
+    public function menus()
+    {
+        $user = (new TokenService())->user();
+        $auth_ids = RoleAuth::whereIn('role_id', $user->roles)->pluck('auth_ids')->toArray();
+        $menu_ids = explode(',',join(',', $auth_ids));
+//        DB::enableQueryLog();   // 启用查询日志
+        $data = Menu::query()->where('status', 1)
+            ->whereIn('id', $menu_ids)
+            ->oldest('sort')
+            ->get()
+            ->toArray();
+//        $query = DB::getQueryLog();  // 获取查询日志
+        $menu = $this->buildMenu($data);
+        return $this->apiSuccess('', $menu);
+    }
+
+
+}

+ 58 - 0
app/Http/Services/Admin/Channel/DistributorService.php

@@ -0,0 +1,58 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Channel;
+
+use App\Http\Services\BaseService;
+use App\Models\Distributor;
+use App\Models\Menu;
+
+class DistributorService extends BaseService
+{
+    public function index($data)
+    {
+        $model = $this->queryCondition(Distributor::query(), $data);
+
+        $list = $model->latest('id')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function all()
+    {
+        $model = $this->queryCondition(Distributor::query(), []);
+
+        $list = $model->latest('id')->select('id', 'name')->get()
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list,
+        ]);
+    }
+
+    public function store(array $data)
+    {
+        return $this->commonCreate(Menu::query(), $data);
+    }
+
+    public function detail($id)
+    {
+
+        $data = Menu::query()->find($id)->toArray();
+        return $this->apiSuccess('', $data);
+    }
+
+    public function update(array $data)
+    {
+        return $this->commonUpdate(Menu::query(), $data['id'], $data);
+    }
+
+}

+ 95 - 0
app/Http/Services/Admin/Finance/StatisticService.php

@@ -0,0 +1,95 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Finance;
+
+use App\Http\Services\BaseService;
+use App\Models\Distributor;
+use App\Models\Menu;
+use App\Models\StatisticIncome;
+use Illuminate\Support\Facades\DB;
+
+class StatisticService extends BaseService
+{
+    public function income($data)
+    {
+        $model = $this->queryCondition(StatisticIncome::query(), $data);
+
+        if (!empty($data['province'])) {
+            $model->where('province', $data['province']);
+        }
+
+        if (!empty($data['city'])) {
+            $model->where('city', $data['city']);
+        }
+
+        $amountModel = clone $model;
+        $model->select(['province', 'city', 'district']);
+        $selectColumn = "SUM(order_count) as order_count,SUM(pay_price) as pay_price,SUM(artificer_income) as artificer_income,SUM(promotion_income) as promotion_income,SUM(total_income) as total_income,SUM(platform_income) as platform_income,SUM(agent_income) as agent_income,SUM(market_income) as market_income";
+        $amountModel->selectRaw($selectColumn);
+        if (!empty($data['type']) && $data['type'] !== '0') {
+//            $model->selectRaw("SUM(order_count) as order_count,SUM(pay_price) as pay_price,SUM(artificer_income) as artificer_income,SUM(promotion_income) as promotion_income,SUM(total_income) as total_income,SUM(platform_income) as platform_income,SUM(agent_income) as agent_income,SUM(market_income) as market_income");
+            $model->selectRaw($selectColumn);
+
+            // 按月查询
+            if ($data['type'] === '1') {
+                $model->selectRaw("DATE_FORMAT(`time`, '%Y-%m') as month")
+                    ->groupBy(['province', 'city', 'district', 'month'])
+//                    ->latest('month');
+                    ->orderByDesc('month');
+
+                if (!empty($data['month'])) {
+                    $where = [
+                        ['time', 'like', "%${data['month']}%"]
+                    ];
+//                    $model->where('time', 'like', "%${data['month']}%");
+                    $model->where($where);
+                    $amountModel->where($where);
+                }
+            }
+            // 按年查询
+            if ($data['type'] === '2') {
+                $model->selectRaw("DATE_FORMAT(`time`, '%Y') as year")
+                    ->groupBy(['province', 'city', 'district', 'year'])
+//                    ->latest('year');
+                    ->orderByDesc('year');
+                if (!empty($data['year'])) {
+                    $where = [
+                        ['time', 'like', "%${data['year']}%"]
+                    ];
+                    $model->where($where);
+                    $amountModel->where($where);
+//                    $model->where('time', 'like', "%${data['year']}%");
+                }
+            }
+        } else {
+            $model->selectRaw('time as date,order_count,pay_price,artificer_income,promotion_income,total_income,platform_income,agent_income,market_income')
+//                ->latest('id');
+                ->orderByDesc('date');
+            if (!empty($data['date'])) {
+                $where = [
+                    ['time', 'like', "%${data['date']}%"]
+                ];
+                $model->where($where);
+                $amountModel->where($where);
+//                $model->where('time', 'like', "%${data['date']}%");
+            }
+        }
+        $amountData = $amountModel->first()->toArray();
+        $list = $model->orderBy('province')
+            ->orderBy('city')
+            ->orderBy('district')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total'],
+            'amountData' => $amountData
+        ]);
+    }
+}

+ 286 - 0
app/Http/Services/Admin/FinanceService.php

@@ -0,0 +1,286 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin;
+
+use App\Exceptions\ApiException;
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Artificer;
+use App\Models\ArtificerSite;
+use App\Models\Order;
+use App\Models\OrderGrab;
+use App\Models\ProjectRelevancy;
+use App\Models\User;
+use App\Models\UserWithdraw;
+use App\Models\UserWithdrawLog;
+use Exception;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+
+class FinanceService extends BaseService
+{
+    private $message = [
+        'no_data' => '当前订单不存在!',
+        'delete_success' => '删除成功',
+        'delete_fail' => '删除失败!',
+        'reset_success' => '重置成功',
+        'reset_fail' => '重置失败!',
+        'over_success' => '结束成功',
+        'over_fail' => '结束失败!',
+        'audit_success' => '审核操作成功',
+        'audit_fail' => '审核操作失败!'
+    ];
+
+    /**
+     * 提现管理
+     * Method : Interface withdraw
+     * @param array $data
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Foundation\Application|\Illuminate\Http\Response
+     */
+    public function withdraw(array $data)
+    {
+        $model = $this->queryCondition(UserWithdraw::query(), $data, 'order_sn');
+        if (!empty($data['member'])) {
+            $where = [
+                ['user_nickname', 'like', '%' . $data['member'] . '%', 'or'],
+                ['mobile', 'like', '%' . $data['member'] . '%', 'or']
+            ];
+            $ids = User::where($where)->pluck('id');
+            $model->whereIn('user_id', $ids);
+        }
+        if (!empty($data['payee'])) {
+            $where = [
+                ['zfb_name', 'like', '%' . $data['payee'] . '%', 'or'],
+                ['zfb_action', 'like', '%' . $data['payee'] . '%', 'or']
+            ];
+            $model->where($where);
+        }
+
+
+//        $model->with(['project', 'user', 'artificer']);
+        $list = $model
+//            ->select('id', 'order_sn', 'price', 'project_id', 'type', 'user_id', 'create_time', 'pay_time', 'return_time', 'appoint_time', 'voucher_price', 'balance_price', 'pay_price', 'pay_type', 'tax_price', 'status', 'jiedan_js_id', 'jiedan_time', 'start_time', 'finish_time', 'province', 'city', 'district', 'address', 'lng', 'lat')
+//            ->orderBy('id', 'desc')
+            ->with(['user', 'user.artificer', 'user.distributor', 'admin'])
+            ->latest('create_time')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function audit(array $data)
+    {
+        $status = $data['status'];
+        $id = $data['id'];
+        $admin = Auth::user();
+
+        DB::beginTransaction();
+        try {
+            if ($status == 2) {
+                $withdraw = UserWithdraw::where('id', $id)->lockForUpdate()->first();
+                !$withdraw && $this->apiError($this->message['audit_fail']);
+                $withdraw->status && $this->apiError($this->message['audit_fail']);
+                $user = User::where('id', $withdraw->user_id)->lockForUpdate()->first();
+                !$user && $this->apiError($this->message['audit_fail']);
+                $user->where('id', $withdraw->user_id)->increment('balance', $withdraw->money);
+
+                $withdraw_data['status'] = $status;
+                $withdraw_data['chuli_time'] = time();
+                $withdraw_data['admin_id'] = $admin['id'];
+                $this->commonUpdate(UserWithdraw::query(), $withdraw->id, $withdraw_data, '', $this->message['audit_fail'], false);
+
+                $withdraw_log_data['user_id'] = $withdraw->user_id;
+                $withdraw_log_data['money'] = $withdraw->money;
+                $withdraw_log_data['type'] = 100;
+                $withdraw_log_data['remark'] = '提现拒绝,余额返还';
+                $withdraw_log_data['add_time'] = time();
+                $withdraw_log_data['obj_id'] = intval($id);
+                $withdraw_log_data['yue'] = sprintf("%01.2f",floatval($user->balance) + floatval($withdraw->money));
+                $response = $this->commonCreate(UserWithdrawLog::query(), $withdraw_log_data, $this->message['audit_success'], $this->message['audit_fail'], false);
+            }
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function withdrawLog(array $data)
+    {
+
+        $model = $this->queryCondition(UserWithdrawLog::query(), $data,);
+//        if (!empty($data['key'])) {
+//            $where = [
+//                ['id', 'like', '%' . $data['key'] . '%'],
+//                ['user_nickname', 'like', '%' . $data['key'] . '%', 'or'],
+//                ['mobile', 'like', '%' . $data['key'] . '%', 'or']
+//            ];
+//            $ids = User::where($where)->pluck('id');
+//            $model->whereIn('user_id', $ids);
+//        }
+
+        $list = $model
+//            ->with(['user','user.artificer','user.distributor','admin'])
+            ->with(['user.artificer', 'withdraw.admin', 'order', 'order.project', 'order.user'])
+            ->where('user_id', $data['user_id'])
+            ->latest('add_time')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    /**
+     * 重置接单技师
+     * Method : Interface reset
+     * @param array $data
+     * @throws \App\Exceptions\ApiException
+     */
+    public function reset(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !$order && $this->apiError($this->message['no_data']);
+            !in_array($order->status, [1, 2]) && $this->apiError($this->message['reset_fail']);
+            $artificer_id = $order->jiedan_js_id;
+            // 存在指派技师
+            if ($artificer_id) {
+                $grab = OrderGrab::where(['order_id' => $order_id, 'js_id' => $artificer_id])->lockForUpdate()->first();
+                if ($grab) {
+                    // 重置抢单表状态
+                    $grab_data['status'] = 0;
+                    $this->commonUpdate(OrderGrab::query(), $grab->id, $grab_data, '', $this->message['reset_fail'], false);
+                }
+            }
+            // 重置接单技师
+            $order_data['jiedan_js_id'] = 0;
+            $response = $this->commonUpdate(Order::query(), $order_id, $order_data, $this->message['reset_success'], $this->message['reset_fail'], false);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function delete(array $data)
+    {
+        $delete_id = $data['id'];
+        $order = Order::find($delete_id);
+        !$order && $this->apiError($this->message['no_data']);
+        $order->status && $this->apiError($this->message['delete_fail']);
+        return $this->commonIsDelete(Order::query(), [$delete_id], $this->message['delete_success'], $this->message['delete_fail']);
+    }
+
+    public function over(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !in_array($order->status, [2, 6]) && $this->apiError($this->message['over_fail']);
+            $data = [
+                'status' => 3,
+                'finish_time' => time()
+            ];
+            $response = $this->commonUpdate(Order::query(), $order_id, $data, $this->message['over_success'], $this->message['over_fail']);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function grab(array $data)
+    {
+        $order_id = $data['id'];
+        $order = Order::find($order_id)->toArray();
+        $list = OrderGrab::with('artificer.site')->where('order_id', $order_id)->oldest('create_time')->get()->toArray();
+
+        foreach ($list as &$item) {
+            if ($item['artificer']['site'])
+                $item['distance'] = get_distance([$order['lng'], $order['lat']], [$item['artificer']['site']['lng'], $item['artificer']['site']['lat']]);
+        }
+
+        return $this->apiSuccess('', ['list' => $list]);
+    }
+
+    public function vicinity(array $data)
+    {
+        $code = 156370600;
+        $order_id = $data['id'];
+        $page = $data['page'];
+        $pageSize = $data['pageSize'];
+        $range_artificer_ids = [];
+
+        // 第一步 按照城市编码获取技师定位数据
+        $order = Order::find($order_id);
+        $site = ArtificerSite::with('time')->where('city_code', $code)->select('js_id', 'lng', 'lat')->get()->toArray();
+        $week = date('w', time());
+        $date = date('H:i');
+        foreach ($site as $item) {
+            $distance = get_distance([$order->lng, $order->lat], [$item['lng'], $item['lat']]);
+
+            $status = 0;
+            if ($distance <= 60 && !in_array($item['js_id'], $range_artificer_ids)) {
+
+                if ($item['time'] && $item['time']['times'] && in_array($week, json_decode($item['time']['weeks']))) {
+                    // 开关类型
+                    $status = 1;
+                    // 时间段类型
+//                    foreach ($item['time']['times'] as $vo){
+//                      if(strtotime($date) >= strtotime($vo['js_start_time']) && strtotime($date) <= strtotime($vo['js_end_time'])){
+//                          $status = 1;
+//                          break;
+//                      }
+//                    }
+
+                }
+                $status && array_push($range_artificer_ids, $item['js_id']);
+            }
+        }
+
+//        $artificer_ids = array_column($site,'js_id');
+        // 第二步 筛选开通项目技师
+        $auth_artificer_ids = ProjectRelevancy::whereIn('js_id', $range_artificer_ids)->where('project_id', $order->project_id)->where('status', '1')->pluck('js_id')->toArray();
+        $list = Artificer::with('site')->whereIn('id', $auth_artificer_ids)->where('js_status', '1')->get()->toArray();
+
+        // 第三步 循环排序距离
+        foreach ($list as &$item) {
+            $item['distance'] = get_distance([$order->lng, $order->lat], [$item['site']['lng'], $item['site']['lat']]);
+        }
+
+        // 根据列进行排序
+        $distances = array_column($list, 'distance');
+        array_multisort($distances, SORT_ASC, $list);
+        // 第四步 分页
+        $offset = ($page - 1) * $pageSize;  // 开始截取的索引
+        $result = array_slice($list, $offset, $pageSize);
+
+        return $this->apiSuccess('', ['list' => $list, 'total' => count($list)]);
+    }
+}

+ 62 - 0
app/Http/Services/Admin/Order/AlarmService.php

@@ -0,0 +1,62 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Order;
+
+use App\Exceptions\ApiException;
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Alarm;
+use App\Models\Artificer;
+use App\Models\ArtificerSite;
+use App\Models\Order;
+use App\Models\OrderGrab;
+use App\Models\ProjectRelevancy;
+use App\Models\User;
+use Exception;
+use Illuminate\Support\Facades\DB;
+
+class AlarmService extends BaseService
+{
+    public function index(array $data)
+    {
+        $model = Alarm::query();
+        $model->with(['order.project', 'order.user'=>function($query){
+            $query->withTrashed();
+        }, 'order.artificer']);
+        $model->whereHas('order.project');
+        $model->whereHas('order.artificer');
+        $list = $model
+            ->orderBy('id', 'desc')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function realtime(array $data)
+    {
+        $where['status'] = 0;
+        $currentTime = time();
+        $orderNumber = 0;
+        if (!empty($data['time'])) {
+            $time = [
+                $data['time'] + 1, $currentTime
+            ];
+            $orderNumber = Alarm::query()->where($where)
+                ->whereBetween('create_time', $time)
+                ->count('id');
+        }
+        return $this->apiSuccess('', [
+            'number' => $orderNumber,
+            'time' => $currentTime
+        ]);
+    }
+}

+ 270 - 0
app/Http/Services/Admin/Order/IndexService.php

@@ -0,0 +1,270 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Order;
+
+use App\Exceptions\ApiException;
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Alarm;
+use App\Models\Artificer;
+use App\Models\ArtificerSite;
+use App\Models\Order;
+use App\Models\OrderGrab;
+use App\Models\ProjectRelevancy;
+use App\Models\User;
+use Exception;
+use Illuminate\Support\Facades\DB;
+
+class IndexService extends BaseService
+{
+    private $message = [
+        'no_data' => '当前订单不存在!',
+        'err_data' => '数据错误!',
+        'delete_success' => '删除成功',
+        'delete_fail' => '删除失败!',
+        'reset_success' => '重置成功',
+        'reset_fail' => '重置失败!',
+        'over_success' => '结束成功',
+        'over_fail' => '结束失败!',
+        'receive_success' => '接单成功',
+        'receive_fail' => '接单失败!',
+        'start_success' => '开始服务成功',
+        'start_fail' => '开始服务失败!',
+    ];
+
+    public function index(array $data)
+    {
+        $status = null;
+        if (!empty($data['status']) && $data['status'] === '9') {
+            $status = $data['status'];
+            unset($data['status']);
+        }
+        $model = $this->queryCondition(Order::query(), $data, 'order_sn');
+        if (!empty($data['key'])) {
+            $where = [
+                ['id', 'like', '%' . $data['key'] . '%'],
+                ['user_nickname', 'like', '%' . $data['key'] . '%', 'or'],
+                ['mobile', 'like', '%' . $data['key'] . '%', 'or']
+            ];
+            $ids = User::where($where)->pluck('id');
+            $model->whereIn('user_id', $ids);
+        }
+
+        if (!empty($status)) {
+            $model->whereIn('status', ['1', '2', '3', '4', '6']);
+        }
+
+        $model->with(['project', 'user', 'artificer']);
+        $list = $model->select('id', 'order_sn', 'price', 'project_id', 'type', 'user_id', 'create_time', 'pay_time', 'return_time', 'appoint_time', 'voucher_price', 'balance_price', 'pay_price', 'pay_type', 'tax_price', 'status', 'jiedan_js_id', 'jiedan_time', 'start_time', 'finish_time', 'province', 'city', 'district', 'address', 'lng', 'lat')
+            ->orderBy('id', 'desc')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    /**
+     * 临时接单
+     * Notes :
+     * Method : Interface receive
+     * @param array $data
+     */
+    public function receive(array $data)
+    {
+        $order_id = $data['id'];
+        $order = Order::where('id', $order_id)->where('status','1')->first();
+        if($order){
+            $params = ['status' => '2','jiedan_time' => time(),'js_delete' => 2];
+            return $this->commonUpdate(Order::query(), $order_id, $params, $this->message['receive_success'], $this->message['receive_fail'], false);
+        }
+        $this->apiError($this->message['err_data'], 400);
+    }
+
+    /**
+     * 开始服务
+     * Notes :
+     * Method : Interface receive
+     * @param array $data
+     */
+    public function start(array $data)
+    {
+        $order_id = $data['id'];
+        $order = Order::where('id', $order_id)->where('status','2')->first();
+        if($order){
+            $params = ['status' => '6','start_time' => time()];
+            return $this->commonUpdate(Order::query(), $order_id, $params, $this->message['start_success'], $this->message['start_fail'], false);
+        }
+        $this->apiError($this->message['err_data'], 400);
+    }
+    
+    /**
+     * 重置接单技师
+     * Method : Interface reset
+     * @param array $data
+     * @throws \App\Exceptions\ApiException
+     */
+    public function reset(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !$order && $this->apiError($this->message['no_data']);
+            !in_array($order->status, [1, 2]) && $this->apiError($this->message['reset_fail']);
+            $artificer_id = $order->jiedan_js_id;
+            // 存在指派技师
+            if ($artificer_id) {
+                $grab = OrderGrab::where(['order_id' => $order_id, 'js_id' => $artificer_id])->lockForUpdate()->first();
+                if ($grab) {
+                    // 重置抢单表状态
+                    $grab_data['status'] = 0;
+                    $this->commonUpdate(OrderGrab::query(), $grab->id, $grab_data, '', $this->message['reset_fail'], false);
+                }
+            }
+            // 重置接单技师
+            $order_data['jiedan_js_id'] = 0;
+            $order_data['status'] = '1';
+            $response = $this->commonUpdate(Order::query(), $order_id, $order_data, $this->message['reset_success'], $this->message['reset_fail'], false);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function delete(array $data)
+    {
+        $delete_id = $data['id'];
+        $order = Order::find($delete_id);
+        !$order && $this->apiError($this->message['no_data']);
+        $order->status && $this->apiError($this->message['delete_fail']);
+        return $this->commonIsDelete(Order::query(), [$delete_id], $this->message['delete_success'], $this->message['delete_fail']);
+    }
+
+    public function over(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !in_array($order->status, [2, 6]) && $this->apiError($this->message['over_fail']);
+            $data = [
+                'status' => 3,
+                'finish_time' => time()
+            ];
+            $response = $this->commonUpdate(Order::query(), $order_id, $data, $this->message['over_success'], $this->message['over_fail']);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function grab(array $data)
+    {
+        $order_id = $data['id'];
+        $order = Order::find($order_id)->toArray();
+        $list = OrderGrab::with('artificer.site')->where('order_id', $order_id)->oldest('create_time')->get()->toArray();
+
+        foreach ($list as &$item) {
+            if ($item['artificer']['site'])
+                $item['distance'] = get_distance([$order['lng'], $order['lat']], [$item['artificer']['site']['lng'], $item['artificer']['site']['lat']]);
+        }
+
+        return $this->apiSuccess('', ['list' => $list]);
+    }
+
+    public function vicinity(array $data)
+    {
+//        $code = 156370600;
+        $order_id = $data['id'];
+        $page = $data['page'];
+        $pageSize = $data['pageSize'];
+        $range_artificer_ids = [];
+
+        // 第一步 按照城市编码获取技师定位数据
+        $order = Order::find($order_id);
+        $site = ArtificerSite::with('time')->select('js_id', 'lng', 'lat')->get()->toArray();
+//        $site = ArtificerSite::with('time')->where('city_code', $code)->select('js_id', 'lng', 'lat')->get()->toArray();
+        $week = date('w', time());
+        $date = date('H:i');
+        foreach ($site as $item) {
+            $distance = get_distance([$order->lng, $order->lat], [$item['lng'], $item['lat']]);
+            $status = 0;
+            if ($distance <= 60 && !in_array($item['js_id'], $range_artificer_ids)) {
+                $times = $item && $item['time'] ? $item['time']['times'] : null;
+                if (is_string($times))
+                    $items = json_decode($times, true);
+                if ($item['time'] && !empty($items) && is_array($items) && in_array($week, json_decode($item['time']['weeks']))) {
+                    // 开关类型
+//                    $status = 1;
+                    // 时间段类型
+                    foreach ($items as $vo) {
+                        if (strtotime($date) >= strtotime($vo['js_start_time']) && strtotime($date) <= strtotime($vo['js_end_time'])) {
+                            $status = 1;
+                            break;
+                        }
+                    }
+                }
+                $status && array_push($range_artificer_ids, $item['js_id']);
+            }
+        }
+
+//        $artificer_ids = array_column($site,'js_id');
+        // 第二步 筛选开通项目技师
+        $auth_artificer_ids = ProjectRelevancy::whereIn('js_id', $range_artificer_ids)->where('project_id', $order->project_id)->where('status', '1')->pluck('js_id')->toArray();
+        $list = Artificer::with('site')->whereIn('id', $auth_artificer_ids)->where('js_status', '1')->get()->toArray();
+
+        // 第三步 循环排序距离
+        foreach ($list as &$item) {
+            $item['distance'] = get_distance([$order->lng, $order->lat], [$item['site']['lng'], $item['site']['lat']]);
+        }
+
+        // 根据列进行排序
+        $distances = array_column($list, 'distance');
+        array_multisort($distances, SORT_ASC, $list);
+        // 第四步 分页
+        $offset = ($page - 1) * $pageSize;  // 开始截取的索引
+        $result = array_slice($list, $offset, $pageSize);
+
+        return $this->apiSuccess('', ['list' => $result, 'total' => count($list)]);
+    }
+
+    public function new($data)
+    {
+        $where['status'] = '0';
+        $where['user_delete'] = 1;
+        $where['js_delete'] = 1;
+        $currentTime = time();
+        $orderNumber = 0;
+        if (!empty($data['time'])) {
+
+            $time = [
+                $data['time'] + 1, $currentTime
+            ];
+            $orderNumber = Order::query()->where($where)
+                ->whereBetween('create_time', $time)
+                ->count('id');
+        }
+        return $this->apiSuccess('', [
+            'number' => $orderNumber,
+            'time' => $currentTime
+        ]);
+    }
+}

+ 198 - 0
app/Http/Services/Admin/OrderService.php

@@ -0,0 +1,198 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin;
+
+use App\Exceptions\ApiException;
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Artificer;
+use App\Models\ArtificerSite;
+use App\Models\Order;
+use App\Models\OrderGrab;
+use App\Models\ProjectRelevancy;
+use App\Models\User;
+use Exception;
+use Illuminate\Support\Facades\DB;
+
+class OrderService extends BaseService
+{
+    private $message = [
+        'no_data' => '当前订单不存在!',
+        'delete_success' => '删除成功',
+        'delete_fail' => '删除失败!',
+        'reset_success' => '重置成功',
+        'reset_fail' => '重置失败!',
+        'over_success' => '结束成功',
+        'over_fail' => '结束失败!',
+    ];
+
+    public function index(array $data)
+    {
+        $model = $this->queryCondition(Order::query(), $data, 'order_sn');
+        if (!empty($data['key'])) {
+            $where = [
+                ['id', 'like', '%' . $data['key'] . '%'],
+                ['user_nickname', 'like', '%' . $data['key'] . '%', 'or'],
+                ['mobile', 'like', '%' . $data['key'] . '%', 'or']
+            ];
+            $ids = User::where($where)->pluck('id');
+            $model->whereIn('user_id', $ids);
+        }
+        $model->with(['project', 'user', 'artificer']);
+        $list = $model->select('id', 'order_sn', 'price', 'project_id', 'type', 'user_id', 'create_time', 'pay_time', 'return_time', 'appoint_time', 'voucher_price', 'balance_price', 'pay_price', 'pay_type', 'tax_price', 'status', 'jiedan_js_id', 'jiedan_time', 'start_time', 'finish_time', 'province', 'city', 'district', 'address', 'lng', 'lat')
+            ->orderBy('id', 'desc')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    /**
+     * 重置接单技师
+     * Method : Interface reset
+     * @param array $data
+     * @throws \App\Exceptions\ApiException
+     */
+    public function reset(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !$order && $this->apiError($this->message['no_data']);
+            !in_array($order->status, [1, 2]) && $this->apiError($this->message['reset_fail']);
+            $artificer_id = $order->jiedan_js_id;
+            // 存在指派技师
+            if ($artificer_id) {
+                $grab = OrderGrab::where(['order_id' => $order_id, 'js_id' => $artificer_id])->lockForUpdate()->first();
+                if ($grab) {
+                    // 重置抢单表状态
+                    $grab_data['status'] = 0;
+                    $this->commonUpdate(OrderGrab::query(), $grab->id, $grab_data, '', $this->message['reset_fail'], false);
+                }
+            }
+            // 重置接单技师
+            $order_data['jiedan_js_id'] = 0;
+            $order_data['status'] = '1';
+            $response = $this->commonUpdate(Order::query(), $order_id, $order_data, $this->message['reset_success'], $this->message['reset_fail'], false);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function delete(array $data)
+    {
+        $delete_id = $data['id'];
+        $order = Order::find($delete_id);
+        !$order && $this->apiError($this->message['no_data']);
+        $order->status && $this->apiError($this->message['delete_fail']);
+        return $this->commonIsDelete(Order::query(), [$delete_id], $this->message['delete_success'], $this->message['delete_fail']);
+    }
+
+    public function over(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !in_array($order->status, [2, 6]) && $this->apiError($this->message['over_fail']);
+            $data = [
+                'status' => 3,
+                'finish_time' => time()
+            ];
+            $response = $this->commonUpdate(Order::query(), $order_id, $data, $this->message['over_success'], $this->message['over_fail']);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function grab(array $data)
+    {
+        $order_id = $data['id'];
+        $order = Order::find($order_id)->toArray();
+        $list = OrderGrab::with('artificer.site')->where('order_id', $order_id)->oldest('create_time')->get()->toArray();
+
+        foreach ($list as &$item) {
+            if ($item['artificer']['site'])
+                $item['distance'] = get_distance([$order['lng'], $order['lat']], [$item['artificer']['site']['lng'], $item['artificer']['site']['lat']]);
+        }
+
+        return $this->apiSuccess('', ['list' => $list]);
+    }
+
+    public function vicinity(array $data)
+    {
+        $code = 156370600;
+        $order_id = $data['id'];
+        $page = $data['page'];
+        $pageSize = $data['pageSize'];
+        $range_artificer_ids = [];
+
+        // 第一步 按照城市编码获取技师定位数据
+        $order = Order::find($order_id);
+        //$site = ArtificerSite::with('time')->where('city_code', $code)->select('js_id', 'lng', 'lat')->get()->toArray();
+        $site = ArtificerSite::with('time')->select('js_id', 'lng', 'lat')->get()->toArray();
+        $week = date('w', time());
+        $date = date('H:i');
+        foreach ($site as $item) {
+            $distance = get_distance([$order->lng, $order->lat], [$item['lng'], $item['lat']]);
+
+            $status = 0;
+            if ($distance <= 60 && !in_array($item['js_id'], $range_artificer_ids)) {
+
+                if ($item['time'] && $item['time']['times'] && in_array($week, json_decode($item['time']['weeks']))) {
+                    // 开关类型
+                    $status = 1;
+                    // 时间段类型
+                    foreach ($item['time']['times'] as $vo){
+                      if(strtotime($date) >= strtotime($vo['js_start_time']) && strtotime($date) <= strtotime($vo['js_end_time'])){
+                          $status = 1;
+                          break;
+                      }
+                    }
+
+                }
+                $status && array_push($range_artificer_ids, $item['js_id']);
+            }
+        }
+
+//        $artificer_ids = array_column($site,'js_id');
+        // 第二步 筛选开通项目技师
+        $auth_artificer_ids = ProjectRelevancy::whereIn('js_id', $range_artificer_ids)->where('project_id', $order->project_id)->where('status', '1')->pluck('js_id')->toArray();
+        $list = Artificer::with('site')->whereIn('id', $auth_artificer_ids)->where('js_status', '1')->get()->toArray();
+
+        // 第三步 循环排序距离
+        foreach ($list as &$item) {
+            $item['distance'] = get_distance([$order->lng, $order->lat], [$item['site']['lng'], $item['site']['lat']]);
+        }
+
+        // 根据列进行排序
+        $distances = array_column($list, 'distance');
+        array_multisort($distances, SORT_ASC, $list);
+        // 第四步 分页
+        $offset = ($page - 1) * $pageSize;  // 开始截取的索引
+        $result = array_slice($list, $offset, $pageSize);
+
+        return $this->apiSuccess('', ['list' => $result, 'total' => count($list)]);
+    }
+}

+ 66 - 0
app/Http/Services/Admin/Project/CategoryService.php

@@ -0,0 +1,66 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Project;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Category;
+use App\Models\Menu;
+use App\Models\ProjectCategory;
+use App\Models\User;
+
+class CategoryService extends BaseService
+{
+    public function index($data)
+    {
+        $model = Category::query();
+
+        if (!empty($data['name'])) {
+            $model = $model->where('category_name', 'like', '%' . $data['name'] . '%');
+        }
+        if (isset($data['status']) && $data['status'] != '') {
+            $model = $model->where('is_show', $data['status']);
+        }
+
+        $list = $model->oldest('list_order')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function store(array $data)
+    {
+
+    }
+
+    public function detail($id)
+    {
+
+    }
+
+    public function update(array $data)
+    {
+
+    }
+
+    public function all()
+    {
+        $list = Category::query()
+            ->select(['category_name','id'])
+            ->get()
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list
+        ]);
+    }
+
+}

+ 192 - 0
app/Http/Services/Admin/Project/IndexService.php

@@ -0,0 +1,192 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Project;
+
+use App\Common\Upload;
+use App\Exceptions\ApiException;
+use App\Exceptions\Message;
+use App\Http\Services\BaseService;
+use App\Models\Menu;
+use App\Models\Project;
+use App\Models\ProjectCategory;
+use App\Models\ProjectRelevancy;
+use App\Models\ProjectServiceRelevancy;
+use App\Models\Region;
+use Illuminate\Support\Facades\DB;
+
+class IndexService extends BaseService
+{
+    public function index($data)
+    {
+        $model = Project::query();
+
+        if (!empty($data['name'])) {
+            $model = $model->where('name', 'like', '%' . $data['name'] . '%');
+        }
+        if (isset($data['status']) && $data['status'] != '') {
+            $model = $model->where('is_show', $data['status']);
+        }
+        if(!empty($data['category'])){
+            $category = $data['category'];
+            unset($data['category']);
+            $project_ids = ProjectCategory::query()->where('category_id',$category)->pluck('project_id')->toArray();
+            $model->whereIn('id', $project_ids);
+        }
+        $list = $model->with('category')
+            ->oldest('list_order')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        $previewUrl = 'https://dd.niusenyun.com' . DIRECTORY_SEPARATOR . 'upload';
+        foreach ($list['data'] as $key => $item) {
+            if (!str_starts_with($item['icon'], "http") && !str_starts_with($item['icon'], "/")) {
+                $list['data'][$key]['icon'] = $previewUrl . DIRECTORY_SEPARATOR . $item['icon'];
+            }
+        }
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function regionTree($id)
+    {
+        $service_ids = ProjectServiceRelevancy::query()->where('project_id', $id)->pluck('service_id')->toArray();
+        $city_ids = Region::query()->where('depth', 3)->whereIn('id', $service_ids)->pluck('parentId')->toArray();
+        $model = Region::query();
+        $model->where('depth', 1);
+        $model->with([
+            'children' => function ($query) use ($city_ids) {
+                $query->whereIn('id', $city_ids);
+            },
+            'children.children' => function ($query) use ($service_ids) {
+                $query->whereIn('id', $service_ids);
+            }, 'children.children.service' => function ($query) use ($id) {
+                $query->where('project_id', $id);
+            }]);
+        $model->whereHas('children.children', function ($query) use ($service_ids) {
+            $query->whereIn('id', $service_ids);
+        });
+        $data = $model->get()->toArray();
+        return $this->apiSuccess('', $data);
+    }
+
+    public function region($id)
+    {
+        $data = ProjectServiceRelevancy::query()->where('project_id', $id)->pluck('service_id')->toArray();
+        return $this->apiSuccess('', $data);
+    }
+
+    public function regionStore(array $data)
+    {
+        $project = Project::find($data['id']);
+        $relevancyData = [];
+        foreach ($data['region_ids'] as $region_id) {
+            $relevancyData[] = [
+                'project_id' => $data['id'],
+                'service_id' => $region_id,
+                'minute' => $project['minute'],
+                'price' => $project['price'],
+                'order_price' => $project['order_price']
+            ];
+        }
+        return $this->commonCreate(ProjectServiceRelevancy::query(), $relevancyData, '', '', false);
+    }
+
+    public function regionDestroy($data)
+    {
+        try {
+            $model = ProjectServiceRelevancy::query();
+            $model->where($data);
+            return $this->commonDestroy($model, [$data['id']]);
+        } catch (ApiException $e) {
+            return $e;
+        }
+    }
+
+    public function regionUpdate($data)
+    {
+        return $this->commonUpdate(ProjectServiceRelevancy::query(), $data['id'], $data);
+    }
+
+    public function store($data, $category)
+    {
+        DB::beginTransaction();
+        try {
+            $res = Project::query()->create($data)->toArray();
+            $categoryRelevancyData = [];
+            foreach ($category as $item) {
+                $categoryRelevancyData[] = [
+                    'project_id' => $res['id'],
+                    'category_id' => $item
+                ];
+            }
+            $this->commonCreate(ProjectCategory::query(), $categoryRelevancyData, '', '', false);
+            DB::commit();
+        } catch (\Exception $e) {
+            DB::rollback();
+            $this->apiError();
+        }
+        return $this->apiSuccess();
+    }
+
+    public function detail($id)
+    {
+        $data = Project::query()->with('category')->find($id)->toArray();
+        $previewUrl = 'https://dd.niusenyun.com' . DIRECTORY_SEPARATOR . 'upload';
+        if (!str_starts_with($data['icon'], "http") && !str_starts_with($data['icon'], "/")) {
+            $data['icon'] = $previewUrl . DIRECTORY_SEPARATOR . $data['icon'];
+        }
+        foreach ($data['banner'] as $key => $item) {
+            if (!str_starts_with($item['url'], "http") && !str_starts_with($item['url'], "/")) {
+                $data['banner'][$key]['url'] = $previewUrl . DIRECTORY_SEPARATOR . $item['url'];
+            }
+        }
+        return $this->apiSuccess('', $data);
+    }
+
+    public function update(array $data, $category)
+    {
+        try {
+            DB::beginTransaction();
+            $this->commonUpdate(Project::query(), $data['id'], $data);
+            $categoryRelevancyData = [];
+            foreach ($category as $item) {
+                $categoryRelevancyData[] = [
+                    'project_id' => $data['id'],
+                    'category_id' => $item
+                ];
+            }
+            ProjectCategory::query()->where('project_id', $data['id'])->forceDelete();
+            $this->commonCreate(ProjectCategory::query(), $categoryRelevancyData, '', '', false);
+            DB::commit();
+        } catch (\Exception $e) {
+            DB::rollback();
+            $this->apiError();
+        }
+        return $this->apiSuccess();
+    }
+
+    public function status($data)
+    {
+        try {
+            if (Project::query()->where('id', $data['id'])->update($data)) {
+                return $this->apiSuccess(Message::UPDATE_API_SUCCESS);
+            }
+            $this->apiError(Message::UPDATE_API_ERROR);
+        } catch (ApiException $e) {
+            return $e;
+        }
+    }
+
+    public function destroy(array $ids)
+    {
+        return $this->commonIsDelete(Project::query(), $ids);
+    }
+
+}

+ 59 - 0
app/Http/Services/Admin/System/MenusService.php

@@ -0,0 +1,59 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\System;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Menu;
+use App\Models\User;
+
+class MenusService extends BaseService
+{
+    public function index($data)
+    {
+        $model = $this->queryCondition(Menu::query(), $data);
+
+        $list = $model->oldest('sort')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function store(array $data)
+    {
+        return $this->commonCreate(Menu::query(), $data);
+    }
+
+    public function detail($id)
+    {
+
+        $data = Menu::query()->find($id)->toArray();
+        return $this->apiSuccess('', $data);
+    }
+
+    public function update(array $data)
+    {
+        return $this->commonUpdate(Menu::query(), $data['id'], $data);
+    }
+
+    public function all()
+    {
+        $list = Menu::query()->oldest('sort')
+            ->select(['title','pid','id'])
+            ->get()
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list
+        ]);
+    }
+
+}

+ 50 - 0
app/Http/Services/Admin/System/RegionService.php

@@ -0,0 +1,50 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\System;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Menu;
+use App\Models\Region;
+use App\Models\User;
+
+class RegionService extends BaseService
+{
+    public function index($data)
+    {
+
+    }
+
+    public function store(array $data)
+    {
+
+    }
+
+    public function detail($id)
+    {
+
+    }
+
+    public function update(array $data)
+    {
+
+    }
+
+    public function all()
+    {
+        $list = Region::query()->where('depth', 1)->where('isUse', 1)
+            ->with(['children.children'])
+            ->get()
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list
+        ]);
+    }
+
+}

+ 32 - 0
app/Http/Services/Admin/System/RoleAuthService.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\System;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Menu;
+use App\Models\RoleAuth;
+use App\Models\User;
+
+class RoleAuthService extends BaseService
+{
+
+    public function store(array $data)
+    {
+        $roleAuthData = RoleAuth::where('role_id', $data['role_id'])->first();
+        $roleAuthData && $roleAuthData->delete();
+        return $this->commonCreate(RoleAuth::query(), $data);
+    }
+
+    public function detail($id)
+    {
+        $data = RoleAuth::where('role_id', $id)->first()?->toArray() ?? [];
+        return $this->apiSuccess('',$data);
+    }
+}

+ 47 - 0
app/Http/Services/Admin/System/RoleService.php

@@ -0,0 +1,47 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\System;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Menu;
+use App\Models\User;
+
+class RoleService extends BaseService
+{
+    public function index($data)
+    {
+        $model = $this->queryCondition(Menu::query(), $data);
+
+        $list = $model->oldest('sort')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function store(array $data)
+    {
+        return $this->commonCreate(Menu::query(), $data);
+    }
+
+    public function detail($id)
+    {
+
+        $data = Menu::query()->find($id)->toArray();
+        return $this->apiSuccess('', $data);
+    }
+
+    public function update(array $data)
+    {
+        return $this->commonUpdate(Menu::query(), $data['id'], $data);
+    }
+}

+ 32 - 0
app/Http/Services/Admin/System/UploadService.php

@@ -0,0 +1,32 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\System;
+
+use App\Common\UploadCommon;
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Menu;
+use App\Models\User;
+
+class UploadService extends BaseService
+{
+    public function store($request)
+    {
+        $uploadCommon = new UploadCommon();
+
+        $data = $uploadCommon->upload($request);
+        if ($data) {
+            return $this->apiSuccess('', $data);
+        } else {
+            $this->apiError($uploadCommon->getError());
+        }
+
+    }
+
+}

+ 280 - 0
app/Http/Services/Admin/SystemService.php

@@ -0,0 +1,280 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin;
+
+use App\Exceptions\ApiException;
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Artificer;
+use App\Models\ArtificerSite;
+use App\Models\Order;
+use App\Models\OrderGrab;
+use App\Models\ProjectRelevancy;
+use App\Models\Role;
+use App\Models\User;
+use App\Models\UserWithdraw;
+use App\Models\UserWithdrawLog;
+use Exception;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+
+class SystemService extends BaseService
+{
+    private $message = [
+        'no_data' => '当前订单不存在!',
+        'delete_success' => '删除成功',
+        'delete_fail' => '删除失败!',
+        'reset_success' => '重置成功',
+        'reset_fail' => '重置失败!',
+        'over_success' => '结束成功',
+        'over_fail' => '结束失败!',
+        'audit_success' => '审核操作成功',
+        'audit_fail' => '审核操作失败!'
+    ];
+
+    /**
+     * 提现管理
+     * Method : Interface withdraw
+     * @param array $data
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Foundation\Application|\Illuminate\Http\Response
+     */
+    public function users(array $data)
+    {
+        $model = $this->queryCondition(Admin::query(), $data);
+        $list = $model
+            ->latest()
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function roles(array $data)
+    {
+        $model = $this->queryCondition(Role::query(), $data);
+        $list = $model
+            ->latest('create_time')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+
+    public function audit(array $data)
+    {
+        $status = $data['status'];
+        $id = $data['id'];
+        $admin = Auth::user();
+
+        DB::beginTransaction();
+        try {
+            if ($status == 2) {
+                $withdraw = UserWithdraw::where('id', $id)->lockForUpdate()->first();
+                !$withdraw && $this->apiError($this->message['audit_fail']);
+                $withdraw->status && $this->apiError($this->message['audit_fail']);
+                $user = User::where('id', $withdraw->user_id)->lockForUpdate()->first();
+                !$user && $this->apiError($this->message['audit_fail']);
+                $user->where('id', $withdraw->user_id)->increment('balance', $withdraw->money);
+
+                $withdraw_data['status'] = $status;
+                $withdraw_data['chuli_time'] = time();
+                $withdraw_data['admin_id'] = $admin['id'];
+                $this->commonUpdate(UserWithdraw::query(), $withdraw->id, $withdraw_data, '', $this->message['audit_fail'], false);
+
+                $withdraw_log_data['user_id'] = $withdraw->user_id;
+                $withdraw_log_data['money'] = $withdraw->money;
+                $withdraw_log_data['type'] = 100;
+                $withdraw_log_data['remark'] = '提现拒绝,余额返还';
+                $withdraw_log_data['add_time'] = time();
+                $withdraw_log_data['obj_id'] = intval($id);
+                $withdraw_log_data['yue'] = sprintf("%01.2f",floatval($user->balance) + floatval($withdraw->money));
+                $response = $this->commonCreate(UserWithdrawLog::query(), $withdraw_log_data, $this->message['audit_success'], $this->message['audit_fail'], false);
+            }
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function withdrawLog(array $data)
+    {
+
+        $model = $this->queryCondition(UserWithdrawLog::query(), $data,);
+//        if (!empty($data['key'])) {
+//            $where = [
+//                ['id', 'like', '%' . $data['key'] . '%'],
+//                ['user_nickname', 'like', '%' . $data['key'] . '%', 'or'],
+//                ['mobile', 'like', '%' . $data['key'] . '%', 'or']
+//            ];
+//            $ids = User::where($where)->pluck('id');
+//            $model->whereIn('user_id', $ids);
+//        }
+
+        $list = $model
+//            ->with(['user','user.artificer','user.distributor','admin'])
+            ->with(['user.artificer', 'withdraw.admin', 'order', 'order.project', 'order.user'])
+            ->where('user_id', $data['user_id'])
+            ->latest('add_time')
+            ->paginate($data['pageSize'])
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    /**
+     * 重置接单技师
+     * Method : Interface reset
+     * @param array $data
+     * @throws \App\Exceptions\ApiException
+     */
+    public function reset(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !$order && $this->apiError($this->message['no_data']);
+            !in_array($order->status, [1, 2]) && $this->apiError($this->message['reset_fail']);
+            $artificer_id = $order->jiedan_js_id;
+            // 存在指派技师
+            if ($artificer_id) {
+                $grab = OrderGrab::where(['order_id' => $order_id, 'js_id' => $artificer_id])->lockForUpdate()->first();
+                if ($grab) {
+                    // 重置抢单表状态
+                    $grab_data['status'] = 0;
+                    $this->commonUpdate(OrderGrab::query(), $grab->id, $grab_data, '', $this->message['reset_fail'], false);
+                }
+            }
+            // 重置接单技师
+            $order_data['jiedan_js_id'] = 0;
+            $response = $this->commonUpdate(Order::query(), $order_id, $order_data, $this->message['reset_success'], $this->message['reset_fail'], false);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function delete(array $data)
+    {
+        $delete_id = $data['id'];
+        $order = Order::find($delete_id);
+        !$order && $this->apiError($this->message['no_data']);
+        $order->status && $this->apiError($this->message['delete_fail']);
+        return $this->commonIsDelete(Order::query(), [$delete_id], $this->message['delete_success'], $this->message['delete_fail']);
+    }
+
+    public function over(array $data)
+    {
+        DB::beginTransaction();
+        try {
+            $order_id = $data['id'];
+            $order = Order::where('id', $order_id)->lockForUpdate()->first();
+            !in_array($order->status, [2, 6]) && $this->apiError($this->message['over_fail']);
+            $data = [
+                'status' => 3,
+                'finish_time' => time()
+            ];
+            $response = $this->commonUpdate(Order::query(), $order_id, $data, $this->message['over_success'], $this->message['over_fail']);
+            DB::commit();
+        } catch (ApiException $e) {
+            DB::rollback();
+            $this->apiError($e->getMessage(), $e->getCode());
+        } catch (Exception $e) {
+            DB::rollback();
+            throw $e;
+        }
+        return $response;
+    }
+
+    public function grab(array $data)
+    {
+        $order_id = $data['id'];
+        $order = Order::find($order_id)->toArray();
+        $list = OrderGrab::with('artificer.site')->where('order_id', $order_id)->oldest('create_time')->get()->toArray();
+
+        foreach ($list as &$item) {
+            if ($item['artificer']['site'])
+                $item['distance'] = get_distance([$order['lng'], $order['lat']], [$item['artificer']['site']['lng'], $item['artificer']['site']['lat']]);
+        }
+
+        return $this->apiSuccess('', ['list' => $list]);
+    }
+
+    public function vicinity(array $data)
+    {
+        $code = 156370600;
+        $order_id = $data['id'];
+        $page = $data['page'];
+        $pageSize = $data['pageSize'];
+        $range_artificer_ids = [];
+
+        // 第一步 按照城市编码获取技师定位数据
+        $order = Order::find($order_id);
+        $site = ArtificerSite::with('time')->where('city_code', $code)->select('js_id', 'lng', 'lat')->get()->toArray();
+        $week = date('w', time());
+        $date = date('H:i');
+        foreach ($site as $item) {
+            $distance = get_distance([$order->lng, $order->lat], [$item['lng'], $item['lat']]);
+
+            $status = 0;
+            if ($distance <= 60 && !in_array($item['js_id'], $range_artificer_ids)) {
+
+                if ($item['time'] && $item['time']['times'] && in_array($week, json_decode($item['time']['weeks']))) {
+                    // 开关类型
+                    $status = 1;
+                    // 时间段类型
+//                    foreach ($item['time']['times'] as $vo){
+//                      if(strtotime($date) >= strtotime($vo['js_start_time']) && strtotime($date) <= strtotime($vo['js_end_time'])){
+//                          $status = 1;
+//                          break;
+//                      }
+//                    }
+
+                }
+                $status && array_push($range_artificer_ids, $item['js_id']);
+            }
+        }
+
+//        $artificer_ids = array_column($site,'js_id');
+        // 第二步 筛选开通项目技师
+        $auth_artificer_ids = ProjectRelevancy::whereIn('js_id', $range_artificer_ids)->where('project_id', $order->project_id)->where('status', '1')->pluck('js_id')->toArray();
+        $list = Artificer::with('site')->whereIn('id', $auth_artificer_ids)->where('js_status', '1')->get()->toArray();
+
+        // 第三步 循环排序距离
+        foreach ($list as &$item) {
+            $item['distance'] = get_distance([$order->lng, $order->lat], [$item['site']['lng'], $item['site']['lat']]);
+        }
+
+        // 根据列进行排序
+        $distances = array_column($list, 'distance');
+        array_multisort($distances, SORT_ASC, $list);
+        // 第四步 分页
+        $offset = ($page - 1) * $pageSize;  // 开始截取的索引
+        $result = array_slice($list, $offset, $pageSize);
+
+        return $this->apiSuccess('', ['list' => $list, 'total' => count($list)]);
+    }
+}

+ 61 - 0
app/Http/Services/Admin/Task/AgentStatisticService.php

@@ -0,0 +1,61 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Task;
+
+use App\Http\Services\BaseService;
+use App\Models\Menu;
+use App\Models\Order;
+use App\Models\StatisticIncome;
+use Illuminate\Support\Facades\DB;
+
+class AgentStatisticService extends BaseService
+{
+    public function daily($data)
+    {
+        try {
+            if (empty($data['date']))
+                $yesterday = date('Y-m-d', strtotime("-1 day")); // 获取昨天日期
+            else
+                $yesterday = $data['date'];
+            $startTime = strtotime($yesterday . ' 00:00:00'); // 设置开始时间为昨天0点
+            $endTime = strtotime($yesterday . ' 23:59:59'); // 设置结束时间为昨天24点
+            $order = DB::table('js_order as order')
+                ->whereBetween('create_time', [$startTime, $endTime])
+                ->whereIn('order.status', ['3', '4'])
+                ->select(['province', 'city', 'district'])
+//                ->selectRaw("DATE_FORMAT(MAX(create_time), '%Y-%m-%d') as time")
+                ->selectRaw("DATE_FORMAT(FROM_UNIXTIME(MAX(create_time)), '%Y-%m-%d') as time")
+                ->selectRaw("COUNT(*) as order_count, SUM(pay_price) as pay_price")
+                ->selectRaw("SUM(xh_user_money_log.money) as artificer_income")
+                ->selectRaw("SUM(pay_price - IFNULL(xh_user_money_log.money, 0) - IFNULL(xh_promotion_log.money, 0)) as total_income")
+                ->selectRaw("SUM(pay_price - IFNULL(xh_user_money_log.money, 0) - IFNULL(xh_promotion_log.money, 0)) * 0.4 as platform_income")
+                ->selectRaw("SUM(pay_price - IFNULL(xh_user_money_log.money, 0) - IFNULL(xh_promotion_log.money, 0)) * 0.4 as agent_income")
+                ->selectRaw("SUM(pay_price - IFNULL(xh_user_money_log.money, 0) - IFNULL(xh_promotion_log.money, 0)) * 0.2 as market_income")
+                ->selectRaw("SUM(IFNULL(xh_promotion_log.money, 0)) as promotion_income")
+                ->leftJoin('user_money_log as user_money_log', function ($join) {
+//                    $join->on('user_money_log.order_sn', '=', 'order.order_sn')->where('user_money_log.type', 8);
+                    $join->on('user_money_log.obj_id', '=', 'order.id')->where('user_money_log.type', 8);
+                })
+                ->leftJoin('user_money_log as promotion_log', function ($join) {
+                    $join->on('promotion_log.obj_id', '=', 'order.id')
+                        ->where('promotion_log.is_type', 1)
+                        ->whereIn('promotion_log.type', [3, 4]);
+                })
+                ->groupBy(['province', 'city', 'district'])
+                ->get()->map(function ($value) {
+                    return (array)$value;
+                })->toArray();
+            StatisticIncome::query()->insert($order);
+            return $yesterday . '-统计完成';
+        } catch (\Exception $e) {
+            return $yesterday . '-统计失败';
+        }
+    }
+
+}

+ 86 - 0
app/Http/Services/Admin/TokenService.php

@@ -0,0 +1,86 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/24 12:25
+ */
+
+namespace App\Http\Services\Admin;
+
+use App\Exceptions\ApiException;
+use App\Exceptions\Code;
+use App\Exceptions\Message;
+use App\Http\Services\BaseService;
+use Exception;
+use Illuminate\Contracts\Routing\ResponseFactory;
+use Illuminate\Foundation\Application;
+use Illuminate\Http\Response;
+use Illuminate\Support\Facades\Config;
+use Tymon\JWTAuth\Exceptions\TokenBlacklistedException;
+use Tymon\JWTAuth\Facades\JWTAuth;
+
+class TokenService extends BaseService
+{
+    public function __construct()
+    {
+        Config::set('auth.defaults.guard', 'admin');
+//        Config::set('jwt.ttl',60);
+    }
+
+    public function setToken($data)
+    {
+        if (!$token = JWTAuth::attempt($data)) {
+            $this->apiError('token生成失败');
+        }
+        return $this->respondWithToken($token);
+    }
+
+    /**
+     * Notes : 刷新Token
+     * Method : Interface refreshToken
+     * @return \Illuminate\Contracts\Foundation\Application|ResponseFactory|Application|Response
+     * @throws ApiException
+     */
+    public function refreshToken(): Application|Response|\Illuminate\Contracts\Foundation\Application|ResponseFactory
+    {
+        try {
+            $oldToken = JWTAuth::getToken();
+            $token = JWTAuth::refresh($oldToken);
+        } catch (TokenBlacklistedException $e) {
+            // token加入黑名单
+            throw new ApiException(['code' => Code::TOKEN_ERROR_BLACK, 'message' => Message::TOKEN_ERROR_BLACK]);
+        }
+        return $this->apiSuccess('', $this->respondWithToken($token));
+    }
+
+    public function user(): object
+    {
+        return JWTAuth::parseToken()->touser();
+    }
+
+    public function info()
+    {
+        $data = $this->user();
+        return $this->apiSuccess('', ['name' => $data['name']]);
+    }
+
+    public function logout()
+    {
+        try {
+            JWTAuth::parseToken()->invalidate();
+        } catch (Exception $e) {
+        }
+
+    }
+
+    protected function respondWithToken($token): array
+    {
+        return [
+            'token' => $token,
+            'token_type' => 'Bearer',
+            'expires_in' => JWTAuth::factory()->getTTL() * 60
+        ];
+    }
+
+}

+ 103 - 0
app/Http/Services/Admin/User/ArtificerService.php

@@ -0,0 +1,103 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\User;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Artificer;
+use App\Models\Menu;
+use App\Models\User;
+use Illuminate\Support\Facades\DB;
+
+class ArtificerService extends BaseService
+{
+    public function index($data)
+    {
+        $model = User::query()->where('user_type', 3);
+        if (!empty($data['phone'])) {
+            $keyWhere = [
+//                ['id', '=', $data['key'], 'or'],
+                ['user_login', 'like', '%' . $data['phone'] . '%', 'or'],
+//                ['user_nickname', 'like', '%' . $data['key'] . '%', 'or'],
+                ['mobile', 'like', '%' . $data['phone'] . '%', 'or'],
+            ];
+            $model->where($keyWhere);
+        }
+        $model->with('artificer');
+        if (isset($data['status']) && $data['status'] !== '' || !empty($data['key'])) {
+            $model->whereHas('artificer', function ($query) use ($data) {
+                if (isset($data['status']) && $data['status'] !== '') {
+                    $statusWhere = [];
+                    if ($data['status'] === '-1') {
+                        $statusWhere['apply_num'] = 0;
+                        $statusWhere['js_status'] = '0';
+                    } else {
+                        $statusWhere[] = ['apply_num', '>', 0];
+                        $statusWhere['js_status'] = $data['status'];
+                    }
+                    $query->where($statusWhere);
+                }
+                if (!empty($data['key'])) {
+                    $keyWhere = [
+                        ['id', '=', $data['key'], 'or'],
+                        ['name', 'like', '%' . $data['key'] . '%', 'or'],
+                        ['phone', 'like', '%' . $data['key'] . '%', 'or']
+                    ];
+                    $query->where($keyWhere);
+                }
+            });
+        }
+
+        $list = $model
+            ->latest('id')
+            ->paginate($data['pageSize'])
+            ->toArray();
+
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function store(array $data)
+    {
+        return $this->commonCreate(Menu::query(), $data);
+    }
+
+    public function detail($id)
+    {
+
+        $data = Menu::query()->find($id)->toArray();
+        return $this->apiSuccess('', $data);
+    }
+
+    public function update(array $data)
+    {
+        return $this->commonUpdate(Menu::query(), $data['id'], $data);
+    }
+
+    public function all()
+    {
+        $list = Menu::query()->oldest('sort')
+            ->select(['title', 'pid', 'id'])
+            ->get()
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list
+        ]);
+    }
+
+    public function audit(array $data)
+    {
+        $update_data = [
+            'js_status' => $data['status']
+        ];
+        return $this->commonUpdate(Artificer::query(), $data['id'], $update_data);
+    }
+}

+ 80 - 0
app/Http/Services/Admin/User/MemberService.php

@@ -0,0 +1,80 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\User;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Menu;
+use App\Models\User;
+use Illuminate\Support\Facades\DB;
+
+class MemberService extends BaseService
+{
+    public function index($data)
+    {
+        $model = $this->queryCondition(User::query()->where('user_type', 2), $data);
+        if (!empty($data['key'])) {
+            $keyWhere = [
+                ['id', '=', $data['key'], 'or'],
+                ['user_login', 'like', '%' . $data['key'] . '%', 'or'],
+                ['user_nickname', 'like', '%' . $data['key'] . '%', 'or'],
+                ['mobile', 'like', '%' . $data['key'] . '%', 'or'],
+            ];
+            $model->where($keyWhere);
+        }
+        if (!empty($data['distributor_id'])) {
+            $model->where('distributor_id', $data['distributor_id']);
+        }
+
+        if (isset($data['user_status']) && $data['user_status'] !== '') {
+            $model->where('user_status', $data['user_status']);
+        }
+        if (!empty($data['create_time']) && count($data['create_time']) === 2) {
+           $model->whereBetween('create_time',[strtotime($data['create_time'][0]),strtotime($data['create_time'][1])]);
+        }
+
+        $list = $model->with(['distributor'])
+            ->latest('id')
+            ->paginate($data['pageSize'])
+            ->toArray();
+
+        return $this->apiSuccess('', [
+            'list' => $list['data'],
+            'total' => $list['total']
+        ]);
+    }
+
+    public function store(array $data)
+    {
+        return $this->commonCreate(Menu::query(), $data);
+    }
+
+    public function detail($id)
+    {
+
+        $data = Menu::query()->find($id)->toArray();
+        return $this->apiSuccess('', $data);
+    }
+
+    public function update(array $data)
+    {
+        return $this->commonUpdate(Menu::query(), $data['id'], $data);
+    }
+
+    public function all()
+    {
+        $list = Menu::query()->oldest('sort')
+            ->select(['title', 'pid', 'id'])
+            ->get()
+            ->toArray();
+        return $this->apiSuccess('', [
+            'list' => $list
+        ]);
+    }
+}

+ 77 - 0
app/Http/Services/Admin/Wechat/OfficialAccountService.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Admin\Wechat;
+
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\User;
+
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Session;
+use Overtrue\LaravelWeChat\EasyWeChat;
+
+class OfficialAccountService extends BaseService
+{
+    public function oauth(array $data)
+    {
+        $wechat = EasyWeChat::officialAccount();
+        $oauth = $wechat->getOAuth();
+        $scopes = $data['scopes'] ?: 'snsapi_base';
+        // 未登录
+        //callback_url 是授权回调的URL
+        //生成完整的授权URL
+        $redirectUrl = $oauth->scopes([$scopes])->redirect($data['redirect_url']);
+        return $this->apiSuccess('', ['redirectUrl' => $redirectUrl]);
+
+    }
+
+    public function oauth_callback(array $data)
+    {
+        $wechat = EasyWeChat::officialAccount();
+        $oauth = $wechat->getOAuth();
+        // 获取 OAuth 授权用户信息
+        $user = $oauth->userFromCode($data['code']);
+        $wechat_user = $user;
+        return $this->apiSuccess('', ['wechatUser' => $wechat_user]);
+    }
+    
+    public function signature()
+    {
+        $wechat = EasyWeChat::officialAccount();
+
+        $ticket = $wechat->getTicket();
+        $jsapi_ticket = $ticket->getTicket();
+
+        // 当前URL
+        $url = request('url',request()->header('referer'));
+
+        // 生成签名
+        $nonceStr = 'didongdiandao'; // 请用随机算法生成
+        $timestamp = time(); // 当前时间戳
+
+        $string1 = "jsapi_ticket={$jsapi_ticket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";
+        $signature = sha1($string1);
+
+        $wechatConfig = $wechat->getConfig();
+
+
+        // 数据用于前端显示
+        $signPackage = [
+            "appId" => $wechatConfig['app_id'],
+            "nonceStr" => $nonceStr,
+            "timestamp" => $timestamp,
+            "url" => $url,
+            "signature" => $signature,
+            "rawString" => $string1
+        ];
+
+        return $this->apiSuccess('', ['signPackage' => $signPackage]);
+    }
+
+}

+ 149 - 0
app/Http/Services/BaseService.php

@@ -0,0 +1,149 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:59
+ */
+
+namespace App\Http\Services;
+
+use App\Exceptions\ApiException;
+use App\Exceptions\Code;
+use App\Exceptions\Message;
+use function PHPUnit\Framework\stringStartsWith;
+
+class BaseService
+{
+    function queryCondition(object $model, array $params, string $key = 'name'): object
+    {
+        if (!empty($params['created_at'])) {
+            $model = $model->whereBetween('created_at', $params['created_at']);
+        }
+        if (!empty($params['updated_at'])) {
+            $model = $model->whereBetween('updated_at', $params['updated_at']);
+        }
+        if (!empty($params[$key])) {
+            $model = $model->where($key, 'like', '%' . $params[$key] . '%');
+        }
+        if (isset($params['status']) && $params['status'] != '') {
+            $model = $model->where('status', $params['status']);
+        }
+
+        return $model;
+    }
+
+    public function commonCreate($model, array $data = [], string $successMessage = Message::ADD_API_SUCCESS, string $errorMessage = Message::ADD_API_ERROR, $timestamps = true)
+    {
+        if ($timestamps)
+            $data['created_at'] = date('Y-m-d H:i:s');
+//        $model->save();
+//        $token = Auth::fromUser($user);
+
+        if ($model->insert($data)) {
+            return $this->apiSuccess($successMessage);
+        }
+
+        $this->apiError($errorMessage);
+    }
+
+    public function commonUpdate($model, $id, array $data = [], string $successMessage = Message::UPDATE_API_SUCCESS, string $errorMessage = Message::UPDATE_API_ERROR, $timestamps = true)
+    {
+//        if ($timestamps) $data['updated_at'] = date('Y-m-d H:i:s');
+        if ($model->where('id', $id)->update($data)) {
+            return $this->apiSuccess($successMessage);
+        }
+        $this->apiError($errorMessage);
+    }
+
+    public function apiSuccess(string $message = '', array $data = array(), int $code = Code::Ok,)
+    {
+        if (!$message) {
+            $message = Message::Ok;
+        }
+        return response([
+            'code' => $code,
+            'message' => $message,
+            'data' => $data
+        ]);
+    }
+
+    /**
+     * @throws ApiException
+     */
+    public function apiError(string $message = Message::API_ERROR_EXCEPTION, int $code = Code::BAD_REQUEST)
+    {
+        throw new ApiException([
+            'code' => $code,
+            'message' => $message
+        ]);
+    }
+
+    /**
+     * 物理删除
+     * Method : Interface commonDestroy
+     * @param $model
+     * @param array $ArrId
+     * @param string $successMessage
+     * @param string $errorMessage
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Foundation\Application|\Illuminate\Http\Response|void
+     * @throws ApiException
+     */
+    public function commonDestroy($model, array $ArrId, string $successMessage = Message::DELETE_API_SUCCESS, string $errorMessage = Message::DELETE_API_ERROR)
+    {
+        if ($model->whereIn('id', $ArrId)->forceDelete()) {
+            return $this->apiSuccess($successMessage);
+        }
+        $this->apiError($errorMessage);
+    }
+
+    /**
+     * 软删除
+     * @description
+     **/
+    public function commonIsDelete($model, array $ids, string $successMessage = Message::DELETE_API_SUCCESS, string $errorMessage = Message::DELETE_API_ERROR)
+    {
+        if ($model->whereIn('id', $ids)->delete()) {
+            return $this->apiSuccess($successMessage);
+        }
+        $this->apiError($errorMessage);
+    }
+
+    public function buildMenu(array $array, int $pid = 0): array
+    {
+        $tree = array();
+        foreach ($array as $key => $value) {
+            if ($value['pid'] == $pid) {
+                $path = trim($value['path'], '/');
+                $name = ucfirst($value['name'] ?: $path);
+                $value['children'] = $this->buildMenu($array, $value['id']);
+                $menu = [
+                    'name' => $name,
+                    'path' => '/' . $path,
+                    "meta" => [
+                        "locale" => $value['title'],
+                        "icon" => $value['icon'],
+                        "order" => $value['sort'],
+
+//                    "activeMenu" => true,
+//                    "hideChildrenInMenu" => true,
+                    ]
+                ];
+                if ($value['component'])
+                    $menu['component'] = $value['component'];
+                if ($value['redirect'])
+                    $menu['redirect'] = $value['redirect'];
+                if ($value['children'])
+                    $menu['children'] = $value['children'];
+                if ($value['requires_auth'])
+                    $menu['meta']['requiresAuth'] = true;
+                if ($value['hide'])
+                    $menu['meta']['hideInMenu'] = true;
+                if ($value['keep_alive'])
+                    $menu['meta']['keepAlive'] = true;
+                $tree[] = $menu;
+            }
+        }
+        return $tree;
+    }
+}

+ 39 - 0
app/Http/Services/Frontend/Account/UserService.php

@@ -0,0 +1,39 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Frontend\Account;
+
+use App\Exceptions\ApiException;
+use App\Http\Services\BaseService;
+use App\Models\Admin;
+use App\Models\Artificer;
+use App\Models\ArtificerSite;
+use App\Models\Order;
+use App\Models\OrderGrab;
+use App\Models\ProjectRelevancy;
+use App\Models\User;
+use App\Models\UserWithdraw;
+use App\Models\UserWithdrawLog;
+use Exception;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
+
+class UserService extends BaseService
+{
+    public function getUserByOpenID(array $data)
+    {
+        $open_id = $data['open_id'];
+
+
+        $delete_id = $data['id'];
+        $order = Order::find($delete_id);
+        !$order && $this->apiError($this->message['no_data']);
+        $order->status && $this->apiError($this->message['delete_fail']);
+        return $this->commonIsDelete(Order::query(), [$delete_id], $this->message['delete_success'], $this->message['delete_fail']);
+    }
+}

+ 37 - 0
app/Http/Services/Frontend/Wechat/OfficialAccountService.php

@@ -0,0 +1,37 @@
+<?php
+/**
+ * @Name
+ * @Description
+ * @Author 刘学玺
+ * @Date 2023/11/23 20:57
+ */
+
+namespace App\Http\Services\Frontend\Wechat;
+
+use App\Http\Services\BaseService;
+use App\Models\Order;
+use App\Models\User;
+
+class OfficialAccountService extends BaseService
+{
+    public function login(array $data)
+    {
+
+        $open_id = $data['open_id'];
+
+        $exists = User::where('open_id',$open_id)->exists();
+        if($exists){
+            // token
+            return $this->apiSuccess('',['exists' => $exists]);
+        }
+        else{
+            return $this->apiSuccess('',['exists' => $exists]);
+        }
+
+        $delete_id = $data['id'];
+        $order = Order::find($delete_id);
+        !$order && $this->apiError($this->message['no_data']);
+        $order->status && $this->apiError($this->message['delete_fail']);
+        return $this->commonIsDelete(Order::query(), [$delete_id], $this->message['delete_success'], $this->message['delete_fail']);
+    }
+}

+ 99 - 0
app/Models/Admin.php

@@ -0,0 +1,99 @@
+<?php
+
+namespace App\Models;
+
+// use Illuminate\Contracts\Authenticate\MustVerifyEmail;
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Foundation\Auth\User as Authenticatable;
+use Illuminate\Notifications\Notifiable;
+use Laravel\Sanctum\HasApiTokens;
+use Tymon\JWTAuth\Contracts\JWTSubject;
+
+class Admin extends Authenticatable implements JWTSubject
+{
+    use HasApiTokens, HasFactory, Notifiable;
+
+//    use SoftDeletes;
+
+    // 设置表名
+    protected $table = 'admin';
+
+    // 设置主键
+//    public $primaryKey = 'id';
+
+    /**
+     * 自动生成时间戳
+     */
+//    public $timestamps = FALSE;
+
+//    const CREATED_AT = 'create_time';
+//    const UPDATED_AT = 'update_time';
+    // 自定义软删除字段 默认 deleted_at
+//    const DELETED_AT = 'delete_at';
+
+    /**
+     * 日期时间的存储格式
+     *
+     * @var string
+     */
+//    protected $dateFormat = 'Y-m-d H:i:s';
+
+
+    /**
+     * The attributes that are mass assignable.
+     *
+     * @var array<int, string>
+     */
+    protected $fillable = [];
+
+
+    /**
+     * The attributes that should be hidden for serialization.
+     *
+     * @var array<int, string>
+     */
+    protected $hidden = [
+        'password',
+        'remember_token',
+    ];
+
+    /**
+     * The attributes that should be cast.
+     *
+     * @var array<string, string>
+     */
+    protected $casts = [
+        'create_at' => 'datetime', //'datetime:Y-m-d H:i:s'
+        'password' => 'hashed',
+    ];
+
+    protected $appends = ['roles'];
+
+    public function getRolesAttribute()
+    {
+        return AdminRole::where(['admin_id' => $this->id])->pluck('id');
+    }
+
+    protected function serializeDate(DateTimeInterface $date): string
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getJWTIdentifier()
+    {
+        return $this->getKey();
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function getJWTCustomClaims(): array
+    {
+        return [];
+    }
+
+}

+ 60 - 0
app/Models/AdminRole.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace App\Models;
+
+// use Illuminate\Contracts\Authenticate\MustVerifyEmail;
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Foundation\Auth\User as Authenticatable;
+use Illuminate\Notifications\Notifiable;
+use Laravel\Sanctum\HasApiTokens;
+use Tymon\JWTAuth\Contracts\JWTSubject;
+
+class AdminRole extends Model
+{
+    use HasFactory, SoftDeletes;
+
+    // 设置表名
+    protected $table = 'admin_role';
+
+    /**
+     * 日期时间的存储格式
+     *
+     * @var string
+     */
+//    protected $dateFormat = 'Y-m-d H:i:s';
+
+
+    /**
+     * The attributes that are mass assignable.
+     *
+     * @var array<int, string>
+     */
+    protected $fillable = [];
+
+
+    /**
+     * The attributes that should be hidden for serialization.
+     *
+     * @var array<int, string>
+     */
+    protected $hidden = [];
+
+    /**
+     * The attributes that should be cast.
+     *
+     * @var array<string, string>
+     */
+    protected $casts = [
+//        'create_time' => 'datetime', //'datetime:Y-m-d H:i:s'
+//        'password' => 'hashed',
+    ];
+
+//    protected function serializeDate(DateTimeInterface $date): string
+//    {
+//        return $date->format('Y-m-d H:i:s');
+//    }
+
+}

+ 47 - 0
app/Models/Alarm.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace App\Models;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class Alarm extends Model
+{
+    use HasFactory,SoftDeletes;
+
+    protected $table = 'js_call';
+
+    protected $hidden = [
+        //
+    ];
+
+    public $timestamps = FALSE;
+
+    const DELETED_AT = 'delete_time';
+
+    protected $dateFormat = 'U';
+
+    protected $appends = [];
+
+    protected function serializeDate(DateTimeInterface $date): string
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+    public function getIsBusyAttribute()
+    {
+        return Order::where(['jiedan_js_id' => $this->id, 'status' => 6])->count();
+    }
+
+    public function getSiteAttribute()
+    {
+        return ArtificerSite::where(['js_id' => $this->id])->first();
+    }
+
+    public function order()
+    {
+        return $this->hasOne(Order::class, 'id', 'order_id');
+    }
+}

+ 48 - 0
app/Models/Artificer.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace App\Models;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class Artificer extends Model
+{
+    use HasFactory,SoftDeletes;
+
+    protected $table = 'js';
+
+    protected $hidden = [
+        //
+    ];
+
+    const CREATED_AT = 'create_time';
+    const UPDATED_AT = 'update_time';
+    // 自定义软删除字段 默认 deleted_at
+    const DELETED_AT = 'delete_time';
+
+    protected $dateFormat = 'U';
+
+    protected $appends = ['is_busy'];
+
+    protected function serializeDate(DateTimeInterface $date): string
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+    public function getIsBusyAttribute()
+    {
+        return Order::where(['jiedan_js_id' => $this->id, 'status' => 6])->count();
+    }
+
+    public function getSiteAttribute()
+    {
+        return ArtificerSite::where(['js_id' => $this->id])->first();
+    }
+
+    public function site()
+    {
+        return $this->hasOne(ArtificerSite::class, 'js_id', 'id');
+    }
+}

+ 30 - 0
app/Models/ArtificerSite.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Models;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class ArtificerSite extends Model
+{
+    use HasFactory;
+
+    protected $table = 'js_site';
+
+    protected $hidden = [
+        //
+    ];
+
+    public $timestamps = false;
+
+    protected $casts = [];
+
+    protected $appends = [];
+
+    public function time(): \Illuminate\Database\Eloquent\Relations\HasOne
+    {
+        return $this->hasOne(ArtificerTime::class, 'js_id', 'js_id');
+    }
+}

+ 26 - 0
app/Models/ArtificerTime.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App\Models;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class ArtificerTime extends Model
+{
+    use HasFactory;
+
+    protected $table = 'js_time';
+
+    protected $hidden = [
+        //
+    ];
+
+    public $timestamps = false;
+
+    protected $casts = [];
+
+    protected $appends = [];
+
+}

+ 27 - 0
app/Models/Asset.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace App\Models;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Asset extends Model
+{
+    use HasFactory;
+
+//    protected $table = 'asset';
+
+    protected $hidden = [
+        //
+    ];
+
+    public $timestamps = FALSE;
+
+    protected $appends = [];
+
+    protected function serializeDate(DateTimeInterface $date): string
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+}

+ 66 - 0
app/Models/Category.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Models;
+
+// use Illuminate\Contracts\Authenticate\MustVerifyEmail;
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Foundation\Auth\User as Authenticatable;
+use Illuminate\Notifications\Notifiable;
+use Laravel\Sanctum\HasApiTokens;
+use Tymon\JWTAuth\Contracts\JWTSubject;
+
+class Category extends Model
+{
+    use HasFactory, SoftDeletes;
+
+    // 设置表名
+    protected $table = 'js_project_category';
+
+    /**
+     * 日期时间的存储格式
+     *
+     * @var string
+     */
+//    protected $dateFormat = 'Y-m-d H:i:s';
+
+    const CREATED_AT = 'create_time';
+    const UPDATED_AT = 'update_time';
+    // 自定义软删除字段 默认 deleted_at
+    const DELETED_AT = 'delete_time';
+
+    protected $dateFormat = 'U';
+
+    /**
+     * The attributes that are mass assignable.
+     *
+     * @var array<int, string>
+     */
+    protected $fillable = [];
+
+
+    /**
+     * The attributes that should be hidden for serialization.
+     *
+     * @var array<int, string>
+     */
+    protected $hidden = [];
+
+    /**
+     * The attributes that should be cast.
+     *
+     * @var array<string, string>
+     */
+    protected $casts = [
+//        'create_time' => 'datetime', //'datetime:Y-m-d H:i:s'
+//        'password' => 'hashed',
+    ];
+
+    protected function serializeDate(DateTimeInterface $date): string
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+}

+ 22 - 0
app/Models/Distributor.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Distributor extends Model
+{
+    use HasFactory;
+
+    protected $table = 'distributor';
+
+    protected $hidden = [
+        //
+    ];
+
+    public $timestamps = false;
+
+    protected $appends = [];
+
+}

+ 31 - 0
app/Models/Menu.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Models;
+
+use DateTimeInterface;
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class Menu extends Model
+{
+    use HasFactory;
+
+    protected $table = 'menu';
+
+    protected $hidden = [
+        //
+    ];
+
+    protected $casts = [
+        'create_time' => 'datetime',
+    ];
+
+    protected $appends = [];
+
+    protected function serializeDate(DateTimeInterface $date): string
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+
+}

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