From 8e302095407e44c7e505de7c5a4edf978589297c Mon Sep 17 00:00:00 2001 From: lq Date: Mon, 12 May 2025 13:12:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=BD=95=E3=80=81=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/codeGenerationController.php | 38 ++ app/Models/AdminModel.php | 6 + app/Service/AdminService.php | 4 +- app/Service/LoginService.php | 38 +- app/Service/common/UtilsService.php | 57 ++- app/Service/core/CodeGenerationService.php | 457 ++++++++++++++++++ app/Service/core/ZipService.php | 158 ++++++ app/template/api/TemplateController.php | 18 + app/template/api/TemplateModel.php | 17 + app/template/api/TemplateService.php | 87 ++++ composer.json | 2 + composer.lock | 68 ++- config/filesystems.php | 2 +- public/nl_admin.sql | 193 ++++++++ routes/api.php | 8 + 15 files changed, 1114 insertions(+), 39 deletions(-) create mode 100644 app/Http/Controllers/core/codeGenerationController.php create mode 100644 app/Service/core/CodeGenerationService.php create mode 100644 app/Service/core/ZipService.php create mode 100755 app/template/api/TemplateController.php create mode 100644 app/template/api/TemplateModel.php create mode 100644 app/template/api/TemplateService.php create mode 100644 public/nl_admin.sql diff --git a/app/Http/Controllers/core/codeGenerationController.php b/app/Http/Controllers/core/codeGenerationController.php new file mode 100644 index 00000000..1f0ea6f6 --- /dev/null +++ b/app/Http/Controllers/core/codeGenerationController.php @@ -0,0 +1,38 @@ +service = CodeGenerationService::getInstance(); + } + + public function generation() + { + $this->insertField = [ + 'class_name', + 'class_comment', + 'sort', + 'icon', + 'pid', + 'field', + ]; + + $params = $this->checkRequiredFields(request()->post()); + return $this->service->generate($params); +// return jok( +// $this->service->generate([]) +// ); + } + +} diff --git a/app/Models/AdminModel.php b/app/Models/AdminModel.php index ac9d698a..8ffcfe57 100644 --- a/app/Models/AdminModel.php +++ b/app/Models/AdminModel.php @@ -3,6 +3,7 @@ namespace App\Models; use App\BaseApp\BaseModel; +use Illuminate\Database\Eloquent\Relations\HasOne; class AdminModel extends BaseModel { @@ -14,4 +15,9 @@ class AdminModel extends BaseModel * @var list */ protected $guarded = []; + + public function role(): HasOne + { + return $this->hasOne(RoleModel::class, 'id', 'role_id'); + } } diff --git a/app/Service/AdminService.php b/app/Service/AdminService.php index b95db4a2..1fa67448 100644 --- a/app/Service/AdminService.php +++ b/app/Service/AdminService.php @@ -38,7 +38,7 @@ class AdminService extends BaseService public function list(): array { $this->with = [ - 'roles' + 'role' ]; $result = $this->getPageList(); @@ -173,7 +173,7 @@ class AdminService extends BaseService public function myInfo(): mixed { $this->with = [ - 'roles' + 'role' ]; $result = $this->getDetail($this->userId); $result['created_day_at'] = format_time(strtotime($result['created_at']), 'Y-m-d'); diff --git a/app/Service/LoginService.php b/app/Service/LoginService.php index dd2664fb..fa2047b0 100644 --- a/app/Service/LoginService.php +++ b/app/Service/LoginService.php @@ -2,34 +2,16 @@ namespace App\Service; +use App\BaseApp\BaseNotAuthService; use App\Models\AdminModel; use App\Service\common\JWTService; use App\Service\common\UtilsService; use Exception; use Illuminate\Support\Str; -class LoginService +class LoginService extends BaseNotAuthService { - /** - * @var mixed 单例实例,确保该类只有一个全局实例 - */ - private static mixed $_instance; - - /** - * 获取实例 - * @return null|static - */ - public static function getInstance(): null|static - { - $name = get_called_class(); - if (!isset(self::$_instance[$name])) { - self::$_instance[$name] = new static(); - } - - return self::$_instance[$name]; - } - /** * 登录 * @param $phone @@ -39,19 +21,19 @@ class LoginService */ public function login($phone, $password): array { - $userModel = AdminModel::with(['roles:id,name,value'])->where('phone', $phone)->first(); + $userModel = AdminModel::with(['role:id,name,value'])->where('phone', $phone)->first(); if (empty($userModel)) { UtilsService::getInstance()->errorThrow('用户或密码错误!'); // return $this->register($phone, $password); } if (!password_verify($password, $userModel->password)) { - UtilsService::getInstance()->errorThrow('用户或密码错误!'); + UtilsService::getInstance()->errorThrow('用户或密码错误2!'); } if ($userModel->ip !== get_ip()) { $ipTable = json_decode($userModel->ip_table, true); - if (!in_array(get_ip(), $ipTable)) { + if (!in_array(get_ip(), $ipTable?? [])) { $ipTable[] = get_ip(); } $update = AdminModel::where('id', $userModel->id)->update([ @@ -72,8 +54,8 @@ class LoginService 'nick_name' => $userModel->nick_name, 'avatar' => $userModel->avatar, 'email' => $userModel->email, - 'role_name' => $userModel->roles->name, - 'role_value' => $userModel->roles->value, + 'role_name' => $userModel->role->name, + 'role_value' => $userModel->role->value, 'ip' => $userModel->ip, 'ip_table' => $userModel->ip_table, ]; @@ -114,7 +96,7 @@ class LoginService UtilsService::getInstance()->errorThrow('注册失败!'); } - $userInfo = AdminModel::with(['roles:id,name,value'])->where('id', $createModel->id)->first(); + $userInfo = AdminModel::with(['role:id,name,value'])->where('id', $createModel->id)->first(); $result = [ 'id' => $userInfo->id, @@ -122,8 +104,8 @@ class LoginService 'nick_name' => $userInfo->nick_name, 'avatar' => $userInfo->avatar, 'email' => $userInfo->email?? '', - 'role_name' => $userInfo->roles->name, - 'role_value' => $userInfo->roles->value, + 'role_name' => $userInfo->role->name, + 'role_value' => $userInfo->role->value, 'ip' => $userInfo->ip, 'ip_table' => $userInfo->ip_table, ]; diff --git a/app/Service/common/UtilsService.php b/app/Service/common/UtilsService.php index 4bb2f686..7e9402ad 100755 --- a/app/Service/common/UtilsService.php +++ b/app/Service/common/UtilsService.php @@ -94,14 +94,33 @@ class UtilsService */ public function genOpenId(): string { - // 获取当前时间戳(精确到秒) - $timestamp = dechex(time()); + // 获取微秒级时间戳(提高时间精度) + $microtime = microtime(true); + $timestamp = dechex(floor($microtime)); // 秒部分 + $microsec = dechex(($microtime - floor($microtime)) * 1000000); // 微秒部分 - // 生成16字节的随机数据,并转换为32个十六进制字符组成的字符串 - $randomString = bin2hex(random_bytes(16)); + // 生成更多随机字节(20字节=160位熵) + $randomBytes = random_bytes(20); + $randomHex = bin2hex($randomBytes); - // 结合时间和随机字符串,确保总长度为28个字符 - return substr($timestamp . $randomString, 0, 28); + // 获取进程ID和内存ID作为额外熵源 + $pid = dechex(getmypid() % 65536); // 进程ID + $memoryId = dechex(crc32(memory_get_usage(true))); // 内存使用哈希 + + // 打乱组合顺序并截取28个字符 + $components = [ + $timestamp, + $microsec, + substr($randomHex, 0, 16), + substr($randomHex, 16, 8), + $pid, + $memoryId + ]; + shuffle($components); // 打乱顺序 + $combined = implode('', $components); + + // 最终截取(确保固定长度) + return 'nl_'. substr($combined, 0, 28); } /** @@ -244,4 +263,30 @@ class UtilsService } } } + + // 获取项目根目录 + public static function getRootPath(): string + { + return dirname(__DIR__, 3); + } + + // 若文件不存在则创建文件 + public static function createFileHolder($filePath): bool + { + if (!file_exists($filePath)) { + return mkdir($filePath, 777, true); + } + return true; + } + + + // 若文件不存在则创建文件 + public static function createFile($filePath): bool + { + if (!file_exists($filePath)) { + $file = fopen($filePath, 'w'); + fclose($file); + } + return true; + } } diff --git a/app/Service/core/CodeGenerationService.php b/app/Service/core/CodeGenerationService.php new file mode 100644 index 00000000..3086c609 --- /dev/null +++ b/app/Service/core/CodeGenerationService.php @@ -0,0 +1,457 @@ +utils = UtilsService::getInstance(); + } + + /** + * 获取实例 + * @return null|static + */ + public static function getInstance(): null|static + { + $name = get_called_class(); + if (!isset(self::$_instance[$name])) { + self::$_instance[$name] = new static(); + } + + return self::$_instance[$name]; + } + + /** + * 创建临时文件夹,复制模板文件,压缩并返回文件流 + */ + public function download(): BinaryFileResponse|JsonResponse + { + try { + // 模块名称 + $moduleName = '会员模块'; + // 检查/app/public/zip/code-generation/ + if (!Storage::exists('zip/code-generation')) { + Storage::makeDirectory('zip/code-generation'); + } + // 生成唯一的临时目录名 + $tempDir = 'temp/templates/'. $moduleName. '-' . uniqid(); + Storage::makeDirectory($tempDir); + + // 获取模板文件内容 + $templatePath = app_path('template/api/TemplateController.php'); + if (!File::exists($templatePath)) { + return response()->json(['error' => '模板文件不存在'], 404); + } + + $templateContent = File::get($templatePath); + $tempFilePath = "{$tempDir}/TemplateController.php"; + Storage::put($tempFilePath, $templateContent); + + $zip = new ZipService(); + $zipFile = storage_path("/app/public/zip/code-generation/{$moduleName}.zip");//生成压缩文件的路径 + $path = app_path('template');//被压缩文件夹的路径 + $zip->zip($zipFile ,$path ); + + // 返回下载 $path 文件流 + return response()->download($zipFile, "{$moduleName}.zip", ['Content-Type' => 'application/zip']); + + } catch (\Exception $e) { + return response()->json([ + 'error' => '生成模板文件失败', + 'message' => $e->getMessage() + ], 500); + } + } + + private function init() + { + // 处理类名、表名 + $this->upperCamelCase = ucfirst($this->param['class_name']); + $this->lowerCamelCase = lcfirst($this->param['class_name']); + $this->underLineCase = strtolower(preg_replace('/([A-Z])/', '_$1', $this->param['class_name'])); + $this->dashCase = str_replace('_', '-', $this->underLineCase); + $this->classComment = $this->param['class_comment']; + } + + /** + * 代码生成主逻辑 + * + * @param $params + * @throws Exception + */ + public function generate($params) + { + $this->param = $params; + DB::beginTransaction(); + try { + // 初始化信息 + $this->init(); + // 生成数据库 + $this->genDatabase(); + // 生成控制器 + $this->genController(); + // 生成服务层 + $this->genService(); + // 生成模型层 + $this->genModel(); + // 生成路由 + $this->genRoute(); + // 执行sql + $this->querySql(); + ds([ + 'data' => [ + 'sql' => $this->sql, + 'upperCamelCase' => $this->upperCamelCase, + 'lowerCamelCase' => $this->lowerCamelCase, + 'underLineCase' => $this->underLineCase, + 'dashCase' => $this->dashCase, + 'classComment' => $this->classComment, + ], + 'tmp' => $this->tmpFile, + 'params' => $this->param, + ]); +// // 生成导出层 +// $this->genExport(); +// // 生成导入层 +// $this->genImport(); +// // 生成视图 +// $this->genViews(); +// // 生成菜单 +// $this->genMenu(); + } catch (Exception $e) { + DB::rollBack(); + ds([ + 'error' => '生成代码失败', + 'message' => $e->getMessage(), + 'line' => $e->getLine(), + ]); + $this->utils->errorThrow($e->getMessage()); + } + return $this->download(); + } + + + /** + * 生成 数据库表 + * + * @return true + * @throws Exception + */ + public function genDatabase(): bool + { + $tableName = config('database.prefix', 'nl_'). $this->underLineCase; + // 默认sql语句 + $sql = "CREATE TABLE `{$tableName}` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', "; + + $sqlTime = + " `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态 0:正常 1:禁用'," + . " `created_at` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间'," + . "`updated_at` int(11) NOT NULL DEFAULT '0' COMMENT '修改时间'," + . "`deleted_at` int(11) NOT NULL DEFAULT '0' COMMENT '删除时间',"; + + $sqlEnd = "PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{$this->classComment}表';"; + + foreach ($this->param['field'] as $v) { + // 处理所需的字段 + $this->checkSelectField($v); + + // 处理备注 + if (empty($v['comment'])) $v['comment'] = "''"; + + // 处理是否为空 + $v['null'] = !$v['null'] ? '' : 'NOT'; + + // 处理默认值 + if (array_key_exists('default', $v) && isset($v['default'])) { + $v['default'] = 'DEFAULT ' . "'{$v['default']}'"; + } else { + $v['default'] = (strpos($v['type'], 'INT') || in_array($v['type'], self::INT_TYPE)) ? 'DEFAULT ' . 0 : 'DEFAULT ' . '\'\''; + } + + if (in_array($v['type'], self::TEXT_TYPE)) { + // 编写sql语句 + $sql .= " {$v['name']} {$v['type']} {$v['null']} NULL COMMENT '{$v['comment']}', "; + } else { + // 编写sql语句 + $sql .= " {$v['name']} {$v['type']}({$v['type_number']}) {$v['default']} {$v['null']} NULL COMMENT '{$v['comment']}', "; + } + } + + $this->sql = $sql . $sqlTime . $sqlEnd; + + return true; + } + + /** + * 修改api配置文件 + * + * @return void + * @throws Exception + */ + private function genRoute(): void + { + $apiPath = app_path('../routes/api.php'); + $apiFile = file_get_contents($apiPath, 'y'); + $newApi = + "'{$this->dashCase}' => \App\Http\Controllers\Api\CodeGeneration\\{$this->upperCamelCase}Controller::class, // {$this->classComment}} \r\n // 需要登录的路由生成地址"; + + $updateNewFile = str_replace('// 需要登录的路由生成地址', $newApi, $apiFile); + + $updateFile = file_put_contents($apiPath, $updateNewFile); + if (!$updateFile) $this->utils->errorThrow('写入api文件出现了错误'); + } + + /** + * 生成控制器文件 + * @return void + */ + private function genController(): void + { + $this->strReplaceTmp( + app_path('template/api/TemplateController.php'), + app_path("Http/Controllers/Api/CodeGeneration/{$this->upperCamelCase}Controller.php"), + 'server', + 'controller' + ); + } + + /** + * 生成服务层文件 + * @return void + */ + private function genService(): void + { + $this->strReplaceTmp( + app_path('template/api/TemplateService.php'), + app_path("Service/CodeGeneration/{$this->upperCamelCase}Service.php"), + 'server', + 'service' + ); + } + + /** + * 生成模型层文件 + * @return void + */ + private function genModel(): void + { + $this->strReplaceTmp( + app_path('template/api/TemplateModel.php'), + app_path("Models/{$this->upperCamelCase}Model.php"), + 'server', + 'model' + ); + } +// +// /** +// * 生成导出层文件 +// * @return void +// */ +// private function genExport(): void +// { +// $this->strReplaceTmp( +// $this->basePath . $this->tmpPath['server']['export'], +// $this->basePath . $this->newPath['server']['export'], +// 'server', +// 'export' +// ); +// } +// +// /** +// * 生成导入层文件 +// * @return void +// */ +// private function genImport(): void +// { +// $this->strReplaceTmp( +// $this->basePath . $this->tmpPath['server']['import'], +// $this->basePath . $this->newPath['server']['import'], +// 'server', +// 'import' +// ); +// } +// +// /** +// * 生成前端部分文件 +// * @return void +// */ +// private function genViews(): void +// { +// foreach ($this->tmpPath['view'] as $k => $v) { +// $this->utils->createFileHolder($this->baseViewPath . $this->newPath['view_folder'][$k]); +// $this->strReplaceTmp( +// $this->basePath . $v, +// $this->baseViewPath . $this->newPath['view'][$k], +// 'view', +// $k +// ); +// } +// } +// +// /** +// * 生成菜单/授权 +// * +// * @return void +// * @throws Exception +// */ +// private function genMenu(): void +// { +// ds(123); +// RoleService::getInstance()->createRoleMenuRelation(UsersEnum::ADMIN_ID, MenuService::getInstance()->save($this->menu)); +// } + + /** + * 模板内容替换 + * + * @param $template + * @param $path + * @param $type + * @param $fileKey + * @return void + */ + private function strReplaceTmp($template, $path, $type, $fileKey): void + { + $tmpFile = file_get_contents($template); + + $newFile = str_replace('upperCamelCase', $this->upperCamelCase, $tmpFile); + $newFile = str_replace('模板名称', $this->classComment, $newFile); + switch ($fileKey) { + case 'service': + $newFile = str_replace("'tmpSelectField'", $this->selectField . ', "status", "created_at", "updated_at", "deleted_at"', $newFile); + $newFile = str_replace("tmpSearchField", $this->searchField . ',status@=', $newFile); + break; + case 'controller': + $newFile = str_replace("'temInsertField'", '"' . $this->insertField . '"', $newFile); + $newFile = str_replace("'temUpdateField'", '"' . $this->updateField . '"', $newFile); + break; +// case 'export': +// $newFile = str_replace("'tmpExportField'", $this->exportField, $newFile); +// $newFile = str_replace('$row->tmpMapField,', $this->exportFieldRow, $newFile); +// break; +// case 'import': +// $newFile = str_replace("'tmpField' => \$row['tmpField'],", $this->importField, $newFile); +// break; + case 'model': + $newFile = str_replace('underLineCase', cc_camel_case_to_underscore($this->underLineCase), $newFile); + break; + case 'data': + $newFile = str_replace("// 测试列表字段", $this->viewData, $newFile); + $newFile = str_replace("// 测试搜索字段", $this->viewSearch, $newFile); + $newFile = str_replace("// 测试表单字段", $this->viewForm, $newFile); + break; + } + + $this->tmpFile[$type][$fileKey] = $newFile; + if (!file_exists($path)) { + $this->utils->createFile($path); + } + file_put_contents($path, $newFile); + } + + /** + * 设置默认字段 + * + * @param $item + * @return void + */ + private function checkSelectField($item): void + { + if (empty($item)) return; + // 设置列表字段 + if ($item['tableShow']) { + $this->selectField .= ', "' . $item['name'] . '"'; + } + // 设置表单字段 + if ($item['formShow']) { + $isNull = $item['null'] == 1 ? 'true' : 'false'; + $this->insertField .= !empty($this->insertField) ? '","' . $item['name'] : $item['name']; + $this->updateField .= !empty($this->updateField) ? '","' . $item['name'] : 'id","' . $item['name']; + $this->viewForm .= "{\n field: '" . $item['name'] . "',\n label: '" . $item['comment'] . "',\n component: '" . $item['formType'] . "',\n required: " . $isNull . ",\n },\n "; + } + // 设置搜索字段 + if ($item['search']) { + $this->viewSearch .= "{\n field: '" . $item['name'] . "',\n label: '" . $item['comment'] . "',\n component: '" . $item['formType'] . "',\n colProps: { span: 8 },\n },\n "; + $this->searchField .= !empty($this->searchField) ? ',' . $item['name'] . '@' . $item['searchValue'] : $item['name'] . '@' . $item['searchValue']; + } +// // 设置导出字段 +// $this->exportField .= !empty($this->exportField) ? ',"' . $item['comment'] . '"' : '"' . $item['comment'] . '"'; +// $this->exportFieldRow .= '$row->' . $item['name'] . ",\n "; +// // 设置导入字段 +// $this->importField .= "'" . $item['name'] . "' => \$row['" . $item['comment'] . "'],\n "; + // 设置视图数据字段 + $this->viewData .= "{\n title: '" . $item['comment'] . "',\n dataIndex: '" . $item['name'] . "',\n },\n "; + } + + /** + * 执行sql + * + * @return void + * @throws Exception + */ + private function querySql(): void + { + try { + DB::statement($this->sql); + } catch (Exception $e) { + $this->utils->errorThrow($e->getMessage()); + } + } +} diff --git a/app/Service/core/ZipService.php b/app/Service/core/ZipService.php new file mode 100644 index 00000000..4efaa77f --- /dev/null +++ b/app/Service/core/ZipService.php @@ -0,0 +1,158 @@ +zip = new \ZipArchive; + } + + /** + * 创建压缩文件 + * Created by PhpStorm. + * User: EricPan + * Date: 2020/4/22 + * Time: 10:34 + * @param $zipfile + */ + private function createZipFile($zipfile) + { + if (!is_file($zipfile)) { + file_put_contents($zipfile,''); + } + } + /** + * 解压zip文件到指定文件夹 + * + * @access public + * @param string $zipfile 压缩文件路径 + * @param string $path 压缩包解压到的目标路径 + * @return booleam 解压成功返回 true 否则返回 false + */ + public function unzip ($zipfile, $path) { + + if ($this->zip->open($zipfile) === true) { + $file_tmp = @fopen($zipfile, "rb"); + $bin = fread($file_tmp, 15); //只读15字节 各个不同文件类型,头信息不一样。 + fclose($file_tmp); + /* 只针对zip的压缩包进行处理 */ + if (true === $this->getTypeList($bin)) + { + $result = $this->zip->extractTo($path); + $this->zip->close(); + return $result; + } + else + { + return false; + } + } + return false; + } + + /** + * 创建压缩文件 + * @access public + * @param string $zipfile 将要生成的压缩文件路径 + * @param string $folder 将要被压缩的文件夹路径 + * @param null $ignored 要忽略的文件列表 + * @return booleam|bool + * @throws \Exception + */ + public function zip ($zipfile, $folder, $ignored = null) { + + $this->createZipFile($zipfile); + + $this->ignored_names = (is_array($ignored) ? $ignored : $ignored) ? array($ignored) : array(); + if ($this->zip->open($zipfile) !== true) { + throw new \Exception("cannot open <$zipfile>\n"); + } + $folder = substr($folder, -1) == '/' ? substr($folder, 0, strlen($folder)-1) : $folder; + if(strstr($folder, '/')) { + $this->root = substr($folder, 0, strrpos($folder, '/')+1); + $folder = substr($folder, strrpos($folder, '/')+1); + } + $this->createZip($folder); + return $this->zip->close(); + } + /** + * 递归添加文件到压缩包 + * + * @access private + * @param string $folder 添加到压缩包的文件夹路径 + * @param string $parent 添加到压缩包的文件夹上级路径 + * @return void + */ + private function createZip ($folder, $parent=null) { + $full_path = $this->root . $parent . $folder; + $zip_path = $parent . $folder; + $this->zip->addEmptyDir($zip_path); + $dir = new \DirectoryIterator($full_path); + foreach($dir as $file) { + if(!$file->isDot()) { + $filename = $file->getFilename(); + if(!in_array($filename, $this->ignored_names)) { + if($file->isDir()) { + $this->createZip($filename, $zip_path.'/'); + }else { + //第二个参数是重命名文件名,带上路径就可以改变当前文件在压缩包里面的路径. + $this->zip->addFile($full_path.'/'.$filename, $zip_path.'/'.$filename); + } + } + } + } + } + /** + * 读取压缩包文件与目录列表 + * + * @access public + * @param string $zipfile 压缩包文件 + * @return array 文件与目录列表 + */ + public function fileList($zipfile) { + $file_dir_list = array(); + $file_list = array(); + if ($this->zip->open($zipfile) == true) { + for ($i = 0; $i < $this->zip->numFiles; $i++) { + $numfiles = $this->zip->getNameIndex($i); + if (preg_match('/\/$/i', $numfiles)) + { + $file_dir_list[] = $numfiles; + } + else + { + $file_list[] = $numfiles; + } + } + } + return array('files'=>$file_list, 'dirs'=>$file_dir_list); + } + /** + * 得到文件头与文件类型映射表 + * + * @author wengxianhu + * @date 2013-08-10 + * @param $bin string 文件的二进制前一段字符 + * @return boolean + */ + private function getTypeList ($bin) + { + $array = array( + array("504B0304", "zip") + ); + foreach ($array as $v) + { + $blen = strlen(pack("H*", $v[0])); //得到文件头标记字节数 + $tbin = substr($bin, 0, intval($blen)); ///需要比较文件头长度 + if(strtolower($v[0]) == strtolower(array_shift(unpack("H*", $tbin)))) + { + return true; + } + } + return false; + } +} diff --git a/app/template/api/TemplateController.php b/app/template/api/TemplateController.php new file mode 100755 index 00000000..0ca9260d --- /dev/null +++ b/app/template/api/TemplateController.php @@ -0,0 +1,18 @@ +insertField = ['temInsertField']; + $this->updateField = ['temUpdateField']; + $this->service = upperCamelCaseService::getInstance(); + } +} diff --git a/app/template/api/TemplateModel.php b/app/template/api/TemplateModel.php new file mode 100644 index 00000000..987bb1d1 --- /dev/null +++ b/app/template/api/TemplateModel.php @@ -0,0 +1,17 @@ + + */ + protected $guarded = []; +} diff --git a/app/template/api/TemplateService.php b/app/template/api/TemplateService.php new file mode 100644 index 00000000..9ea80d2d --- /dev/null +++ b/app/template/api/TemplateService.php @@ -0,0 +1,87 @@ +selectField = ['tmpSelectField']; + $this->queryField = ['tmpSearchField']; + $this->model = upperCamelCaseModel::class; + } + + /** + * 获取模板名称列表 + * @return array + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + */ + public function list(): array + { + return $this->getPageList(); + } + + /** + * 获取模板名称详情 + * @param $id + * @return mixed + * @throws Exception + */ + public function detail($id) + { + return $this->getDetail($id); + } + + /** + * 模板名称下拉列表 + * @return mixed + */ + public function option() + { + $this->optionField = ['id', 'name']; + return $this->getOption(); + } + + /** + * 创建模板名称 + * @param $params + * @return mixed + * @throws Exception + */ + public function create($params): mixed + { + return $this->insert($params); + } + + /** + * 编辑模板名称 + * @param $id + * @param $params + * @return mixed + * @throws Exception + */ + public function update($id, $params): mixed + { + return $this->save($id, $params); + } + + /** + * 删除模板名称 + * @param $ids + * @return mixed|true + * @throws Exception + */ + public function delete($ids): mixed + { + return $this->del($ids); + } + +} diff --git a/composer.json b/composer.json index dfe9c82c..b9d06efa 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,8 @@ "license": "MIT", "require": { "php": "^8.2", + "ext-zip": "*", + "firebase/php-jwt": "^6.11", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1" }, diff --git a/composer.lock b/composer.lock index 000540cb..59f78622 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "88970a0117c062eed55fa8728fc43833", + "content-hash": "dfbe887acc1dab0ee6b689872e5ab522", "packages": [ { "name": "brick/math", @@ -510,6 +510,69 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" + }, { "name": "fruitcake/php-cors", "version": "v1.3.0", @@ -8079,7 +8142,8 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.2" + "php": "^8.2", + "ext-zip": "*" }, "platform-dev": {}, "plugin-api-version": "2.6.0" diff --git a/config/filesystems.php b/config/filesystems.php index 3d671bd9..52ad8144 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -32,7 +32,7 @@ return [ 'local' => [ 'driver' => 'local', - 'root' => storage_path('app/private'), + 'root' => storage_path('app/public'), 'serve' => true, 'throw' => false, 'report' => false, diff --git a/public/nl_admin.sql b/public/nl_admin.sql new file mode 100644 index 00000000..e5bf0b4a --- /dev/null +++ b/public/nl_admin.sql @@ -0,0 +1,193 @@ +/* + Navicat Premium Dump SQL + + Source Server : 开发库(本地) + Source Server Type : MySQL + Source Server Version : 80404 (8.4.4) + Source Host : localhost:3310 + Source Schema : nl_admin + + Target Server Type : MySQL + Target Server Version : 80404 (8.4.4) + File Encoding : 65001 + + Date: 12/05/2025 13:12:32 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for nl_admin +-- ---------------------------- +DROP TABLE IF EXISTS `nl_admin`; +CREATE TABLE `nl_admin` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `open_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'OpenID,后期整合萧康服务中心会用到', + `avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '头像', + `nick_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '昵称', + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码', + `phone` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户手机', + `email` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户邮箱', + `code` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '业务员推广码', + `role_id` int NOT NULL DEFAULT 0 COMMENT '角色', + `province_id` int NOT NULL DEFAULT 0 COMMENT '省', + `city_id` int NOT NULL DEFAULT 0 COMMENT '市', + `reg_ip` bigint NOT NULL DEFAULT 0 COMMENT '注册IP', + `last_login_time` int NOT NULL DEFAULT 0 COMMENT '最后登录时间', + `ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '最后登录IP', + `ip_table` json NOT NULL COMMENT '常用登录IP地址列表', + `operation_password` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '操作密码', + `desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '备注', + `status` tinyint NOT NULL DEFAULT 1 COMMENT '用户状态 0正常 1禁用', + `created_at` int NOT NULL DEFAULT 0 COMMENT '创建时间', + `updated_at` int NOT NULL DEFAULT 0 COMMENT '更新时间', + `deleted_at` int NOT NULL DEFAULT 0 COMMENT '删除时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '管理员表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of nl_admin +-- ---------------------------- +INSERT INTO `nl_admin` VALUES (1, 'nl_28686ad68682180e26836c721a52', '', '超管', '$2y$12$sMgspfujo/5qnMEFvXH2d.0hYq/7PXhkJREYHbc..4WKt5LH7M8ui', '15100000000', 'workerqi@163.com', '517117', 1, 0, 0, 0, 0, '127.0.0.1', '[\"127.0.0.1\"]', '0', '', 1, 0, 1747026536, 0); + +-- ---------------------------- +-- Table structure for nl_admin_notice +-- ---------------------------- +DROP TABLE IF EXISTS `nl_admin_notice`; +CREATE TABLE `nl_admin_notice` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '消息标题', + `detail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '简介', + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '消息内容', + `type` int NOT NULL DEFAULT 0 COMMENT '消息类型', + `user_id` int NOT NULL DEFAULT 0 COMMENT '关联用户ID', + `status` int NOT NULL DEFAULT 0 COMMENT '状态 0:未读 1:已读', + `created_at` int NOT NULL DEFAULT 0 COMMENT '创建时间', + `updated_at` int NOT NULL DEFAULT 0 COMMENT '更新时间', + `deleted_at` int NOT NULL DEFAULT 0 COMMENT '删除时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 84 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '管理员消息列表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of nl_admin_notice +-- ---------------------------- + +-- ---------------------------- +-- Table structure for nl_api_op_log +-- ---------------------------- +DROP TABLE IF EXISTS `nl_api_op_log`; +CREATE TABLE `nl_api_op_log` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `user_id` int NOT NULL COMMENT '操作用户ID', + `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '操作路由', + `method` tinyint(1) NOT NULL DEFAULT 0 COMMENT '请求方式 0:未知 1:GET 2:POST', + `controller` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '操作的控制器', + `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '操作者IP地址', + `param` json NOT NULL COMMENT '请求携带参数', + `result` json NOT NULL COMMENT '返回json', + `type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '操作状态 0:成功 1:失败', + `result_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '返回状态码', + `platform_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '平台类型 0:总后台 1:门店后台 2:小程序', + `belong_id` int NOT NULL DEFAULT 0 COMMENT '所属平台ID', + `user_type` int NOT NULL DEFAULT 0 COMMENT '用户类型', + `equipment` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '操作系统', + `browser` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '浏览器', + `created_at` int NOT NULL DEFAULT 0 COMMENT '操作时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 16860 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '新的后台API访问日志记录' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of nl_api_op_log +-- ---------------------------- + +-- ---------------------------- +-- Table structure for nl_file +-- ---------------------------- +DROP TABLE IF EXISTS `nl_file`; +CREATE TABLE `nl_file` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `user_id` int NOT NULL DEFAULT 0 COMMENT '用户ID', + `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '文件地址', + `type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '文件类型 0:图片 1:视频 2:音频 3:excel 4:压缩包 ', + `source` tinyint(1) NOT NULL COMMENT '来源 0:后台 1:用户端 2:医生端小程序 3:医生PC端 4:旧的后台', + `created_at` int NOT NULL COMMENT '上传时间', + `updated_at` int NOT NULL DEFAULT 0 COMMENT '更新时间', + `deleted_at` int NOT NULL DEFAULT 0 COMMENT '删除时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 84 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '文件表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of nl_file +-- ---------------------------- + +-- ---------------------------- +-- Table structure for nl_menu +-- ---------------------------- +DROP TABLE IF EXISTS `nl_menu`; +CREATE TABLE `nl_menu` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `title` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '菜单标题', + `icon` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '菜单图标', + `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '页面Name,全局唯一', + `path` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '访问路由', + `component` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '组件路径需要去掉 views/ 和 .vue', + `redirect` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '父级菜单重定向的子集菜单', + `keep_alive` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否开启页面缓存 0:开启 1:关闭', + `hide_in_menu` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否将页面展示在菜单栏 0:展示 1:隐藏', + `affix_tab` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否为置顶页 0:是 1:否', + `badge` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '菜单的徽标', + `badge_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '用于配置页面的徽标类型,0:dot 小红点 1:normal 文本', + `badge_variants` tinyint(1) NOT NULL DEFAULT 0 COMMENT '用于配置页面的徽标颜色 \r\n0:default 1:destructive 2:primary 3:success 4: warning', + `iframe_src` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '内嵌的页面路径', + `pid` int NOT NULL DEFAULT 0 COMMENT '父级菜单id', + `sort` int NOT NULL DEFAULT 0 COMMENT '排序', + `query` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '默认查询参数', + `created_at` int NOT NULL DEFAULT 0 COMMENT '创建时间', + `updated_at` int NOT NULL DEFAULT 0 COMMENT '更新时间', + `deleted_at` int NOT NULL DEFAULT 0 COMMENT '删除时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 37 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '菜单表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of nl_menu +-- ---------------------------- + +-- ---------------------------- +-- Table structure for nl_role +-- ---------------------------- +DROP TABLE IF EXISTS `nl_role`; +CREATE TABLE `nl_role` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色名称', + `value` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色值', + `pid` int NOT NULL DEFAULT 0 COMMENT '上级角色', + `desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '角色说明', + `created_at` int NOT NULL DEFAULT 0 COMMENT '创建时间', + `updated_at` int NOT NULL DEFAULT 0 COMMENT '更新时间', + `deleted_at` int NOT NULL DEFAULT 0 COMMENT '删除时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of nl_role +-- ---------------------------- +INSERT INTO `nl_role` VALUES (1, '超级管理员', 'admin', 0, '', 0, 0, 0); + +-- ---------------------------- +-- Table structure for nl_role_menu_relations +-- ---------------------------- +DROP TABLE IF EXISTS `nl_role_menu_relations`; +CREATE TABLE `nl_role_menu_relations` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键', + `role_id` int NOT NULL DEFAULT 0 COMMENT '角色ID', + `menu_id` int NOT NULL DEFAULT 0 COMMENT '菜单ID', + `created_at` int NOT NULL DEFAULT 0 COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 65 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '角色权限绑定表' ROW_FORMAT = DYNAMIC; + +-- ---------------------------- +-- Records of nl_role_menu_relations +-- ---------------------------- + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/routes/api.php b/routes/api.php index f7d13ff5..30c262d0 100644 --- a/routes/api.php +++ b/routes/api.php @@ -6,16 +6,24 @@ use Illuminate\Support\Facades\Route; Route::get('/', function () { return view('welcome'); }); +/* + * -----------------------------------以下注释不要删除-------------------------------- + * 不需要登录的路由生成地址 + * 需要登录的路由生成地址 + */ /** * 自动注册路由 */ UtilsService::class::getInstance()->autoRouteRegister([ '' => \App\Http\Controllers\Api\LoginController::class, // 登录控制器 + 'code' => \App\Http\Controllers\core\codeGenerationController::class, // 代码生成控制器 + // 不需要登录的路由生成地址 ]); Route::group([], function () { UtilsService::class::getInstance()->autoRouteRegister([ 'user' => \App\Http\Controllers\Api\AdminController::class, // 用户控制器 + // 需要登录的路由生成地址 ]); });