Files
nl-admin-api/app/Service/core/CodeGenerationService.php
2025-05-12 13:12:57 +08:00

458 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Service\core;
use App\BaseApp\BaseNotAuthService;
use App\Models\AdminModel;
use App\Service\common\JWTService;
use App\Service\common\UtilsService;
use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use ZipArchive;
class CodeGenerationService
{
const TEXT_TYPE = [
'LONGTEXT',
'TEXT'
];
const INT_TYPE = [
'INT',
'TINYINT',
'BIGINT',
'SMALLINT',
'FLOAT',
];
private static mixed $_instance;
private ?UtilsService $utils;
// 大驼峰
private string $upperCamelCase = '';
// 小驼峰
private string $lowerCamelCase = '';
// 下划线
private string $underLineCase = '';
private string $classComment = '';
// - 连接符
private string $dashCase = '';
/**
* @var string 建表sql语句
*/
private string $sql = '';
private array $param = [];
private string $selectField = '"id"';
private string $insertField = '';
private string $updateField = '';
private string $viewForm = '';
private string $viewSearch = '';
private string $searchField = '';
private string $viewData = '';
private array $tmpFile = [];
public function __construct()
{
$this->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());
}
}
}