mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
feat(database): 添加用户角色权限系统及相关监控功能
- 创建用户表(users)包含基本信息和认证字段 - 创建角色表(roles)用于权限控制 - 创建权限表(permissions)定义系统权限 - 创建用户角色关联表(user_roles)建立用户与角色关系 - 创建角色权限关联表(role_permissions)建立角色与权限关系 - 创建迁移记录表(migrations)追踪数据库变更 - 添加AdminController提供管理员面板功能 - 实现系统监控、配置管理、缓存清理等功能 - 添加AOP切面编程支持的各种通知类型 - 实现告警管理AlertManager支持多渠道告警 - 添加文档注解接口规范
This commit is contained in:
552
fendx-framework/fendx-cli/src/Generator/ModelGenerator.php
Normal file
552
fendx-framework/fendx-cli/src/Generator/ModelGenerator.php
Normal file
@@ -0,0 +1,552 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Fendx\CLI\Generator;
|
||||
|
||||
class ModelGenerator extends CodeGenerator
|
||||
{
|
||||
protected function loadTemplates(): void
|
||||
{
|
||||
$this->templates = [
|
||||
'model' => $this->getModelTemplate(),
|
||||
'property' => $this->getPropertyTemplate(),
|
||||
'method' => $this->getMethodTemplate(),
|
||||
'migration' => $this->getMigrationTemplate(),
|
||||
'factory' => $this->getFactoryTemplate(),
|
||||
'seeder' => $this->getSeederTemplate()
|
||||
];
|
||||
}
|
||||
|
||||
public function generate(string $name, array $options = []): bool
|
||||
{
|
||||
if (!$this->validateName($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$className = $this->getClassName($name);
|
||||
$tableName = $options['table'] ?? $this->getTableName($name);
|
||||
$fields = $options['fields'] ?? [];
|
||||
$timestamps = $options['timestamps'] ?? true;
|
||||
$softDeletes = $options['soft_deletes'] ?? false;
|
||||
$relationships = $options['relationships'] ?? [];
|
||||
$generateMigration = $options['migration'] ?? true;
|
||||
$generateFactory = $options['factory'] ?? false;
|
||||
$generateSeeder = $options['seeder'] ?? false;
|
||||
|
||||
if (!empty($fields)) {
|
||||
if (is_string($fields)) {
|
||||
$fields = $this->parseFields($fields);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成模型
|
||||
$modelContent = $this->generateModel($className, $tableName, $fields, $timestamps, $softDeletes, $relationships);
|
||||
$modelPath = $this->getNamespacePath('Model');
|
||||
$modelFilePath = $modelPath . '/' . $className . '.php';
|
||||
|
||||
if (!$this->createFile($modelFilePath, $modelContent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成迁移文件
|
||||
if ($generateMigration) {
|
||||
$this->generateMigration($className, $tableName, $fields, $timestamps, $softDeletes);
|
||||
}
|
||||
|
||||
// 生成工厂文件
|
||||
if ($generateFactory) {
|
||||
$this->generateFactory($className, $fields);
|
||||
}
|
||||
|
||||
// 生成种子文件
|
||||
if ($generateSeeder) {
|
||||
$this->generateSeeder($className);
|
||||
}
|
||||
|
||||
$this->showMessage('success', "Model '{$className}' generated successfully!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function generateModel(string $className, string $tableName, array $fields, bool $timestamps, bool $softDeletes, array $relationships): string
|
||||
{
|
||||
$useStatements = [];
|
||||
$properties = '';
|
||||
$methods = '';
|
||||
|
||||
// 基础use语句
|
||||
$useStatements[] = 'use Fendx\\ORM\\Model;';
|
||||
|
||||
if ($softDeletes) {
|
||||
$useStatements[] = 'use Fendx\\ORM\\Traits\\SoftDeletes;';
|
||||
}
|
||||
|
||||
// 生成属性
|
||||
foreach ($fields as $field) {
|
||||
$properties .= $this->generateProperty($field);
|
||||
}
|
||||
|
||||
// 生成关系方法
|
||||
foreach ($relationships as $relationship) {
|
||||
$methods .= $this->generateRelationshipMethod($relationship);
|
||||
}
|
||||
|
||||
// 生成访问器和修改器
|
||||
foreach ($fields as $field) {
|
||||
$methods .= $this->generateAccessorMutator($field);
|
||||
}
|
||||
|
||||
$useBlock = implode("\n", array_unique($useStatements)) . "\n\n";
|
||||
|
||||
return $this->renderTemplate('model', [
|
||||
'namespace' => $this->getFullNamespace('Model'),
|
||||
'use_block' => $useBlock,
|
||||
'class_name' => $className,
|
||||
'table_name' => $tableName,
|
||||
'timestamps' => $timestamps ? 'true' : 'false',
|
||||
'soft_deletes' => $softDeletes ? 'use SoftDeletes;' : '',
|
||||
'properties' => $properties,
|
||||
'methods' => $methods
|
||||
]);
|
||||
}
|
||||
|
||||
private function generateProperty(array $field): string
|
||||
{
|
||||
$name = $field['name'];
|
||||
$type = $this->getPhpType($field['type']);
|
||||
$nullable = isset($field['options']['nullable']);
|
||||
|
||||
$docBlock = $this->generatePropertyDocBlock($field);
|
||||
|
||||
return $this->renderTemplate('property', [
|
||||
'doc_block' => $docBlock,
|
||||
'property_name' => $name,
|
||||
'property_type' => $type,
|
||||
'nullable' => $nullable ? '?' : ''
|
||||
]);
|
||||
}
|
||||
|
||||
private function generatePropertyDocBlock(array $field): string
|
||||
{
|
||||
$type = $this->getPhpType($field['type']);
|
||||
$comment = '';
|
||||
|
||||
if (isset($field['options']['unique'])) {
|
||||
$comment .= ' (unique)';
|
||||
}
|
||||
if (isset($field['options']['default'])) {
|
||||
$comment .= ' (default: ' . $field['options']['default'] . ')';
|
||||
}
|
||||
if (isset($field['options']['nullable'])) {
|
||||
$comment .= ' (nullable)';
|
||||
}
|
||||
|
||||
return "/**\n * @var {$type}{$comment}\n */";
|
||||
}
|
||||
|
||||
private function generateRelationshipMethod(array $relationship): string
|
||||
{
|
||||
$type = $relationship['type'];
|
||||
$relatedModel = $relationship['model'];
|
||||
$foreignKey = $relationship['foreign_key'] ?? null;
|
||||
$localKey = $relationship['local_key'] ?? null;
|
||||
|
||||
switch ($type) {
|
||||
case 'hasOne':
|
||||
return $this->generateHasOneMethod($relatedModel, $foreignKey, $localKey);
|
||||
case 'hasMany':
|
||||
return $this->generateHasManyMethod($relatedModel, $foreignKey, $localKey);
|
||||
case 'belongsTo':
|
||||
return $this->generateBelongsToMethod($relatedModel, $foreignKey, $localKey);
|
||||
case 'belongsToMany':
|
||||
return $this->generateBelongsToManyMethod($relatedModel, $foreignKey, $localKey);
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
private function generateHasOneMethod(string $relatedModel, ?string $foreignKey, ?string $localKey): string
|
||||
{
|
||||
$methodName = $this->getVariableName($relatedModel);
|
||||
$params = '';
|
||||
|
||||
if ($foreignKey) {
|
||||
$params .= ", '{$foreignKey}'";
|
||||
}
|
||||
if ($localKey) {
|
||||
$params .= ", '{$localKey}'";
|
||||
}
|
||||
|
||||
return <<<PHP
|
||||
/**
|
||||
* Get the associated {$relatedModel}.
|
||||
*/
|
||||
public function {$methodName}(): HasOne
|
||||
{
|
||||
return \$this->hasOne({$relatedModel}::class{$params});
|
||||
}
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function generateHasManyMethod(string $relatedModel, ?string $foreignKey, ?string $localKey): string
|
||||
{
|
||||
$methodName = $this->getPluralName($this->getVariableName($relatedModel));
|
||||
$params = '';
|
||||
|
||||
if ($foreignKey) {
|
||||
$params .= ", '{$foreignKey}'";
|
||||
}
|
||||
if ($localKey) {
|
||||
$params .= ", '{$localKey}'";
|
||||
}
|
||||
|
||||
return <<<PHP
|
||||
/**
|
||||
* Get the associated {$relatedModel} records.
|
||||
*/
|
||||
public function {$methodName}(): HasMany
|
||||
{
|
||||
return \$this->hasMany({$relatedModel}::class{$params});
|
||||
}
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function generateBelongsToMethod(string $relatedModel, ?string $foreignKey, ?string $localKey): string
|
||||
{
|
||||
$methodName = $this->getVariableName($relatedModel);
|
||||
$params = '';
|
||||
|
||||
if ($foreignKey) {
|
||||
$params .= ", '{$foreignKey}'";
|
||||
}
|
||||
if ($localKey) {
|
||||
$params .= ", '{$localKey}'";
|
||||
}
|
||||
|
||||
return <<<PHP
|
||||
/**
|
||||
* Get the associated {$relatedModel}.
|
||||
*/
|
||||
public function {$methodName}(): BelongsTo
|
||||
{
|
||||
return \$this->belongsTo({$relatedModel}::class{$params});
|
||||
}
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function generateBelongsToManyMethod(string $relatedModel, ?string $foreignKey, ?string $localKey): string
|
||||
{
|
||||
$methodName = $this->getPluralName($this->getVariableName($relatedModel));
|
||||
$params = '';
|
||||
|
||||
if ($foreignKey) {
|
||||
$params .= ", '{$foreignKey}'";
|
||||
}
|
||||
if ($localKey) {
|
||||
$params .= ", '{$localKey}'";
|
||||
}
|
||||
|
||||
return <<<PHP
|
||||
/**
|
||||
* Get the associated {$relatedModel} records.
|
||||
*/
|
||||
public function {$methodName}(): BelongsToMany
|
||||
{
|
||||
return \$this->belongsToMany({$relatedModel}::class{$params});
|
||||
}
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function generateAccessorMutator(array $field): string
|
||||
{
|
||||
$name = $field['name'];
|
||||
$type = $this->getPhpType($field['type']);
|
||||
$methods = '';
|
||||
|
||||
// 生成访问器
|
||||
$accessorName = 'get' . $this->getClassName($name) . 'Attribute';
|
||||
$methods .= <<<PHP
|
||||
/**
|
||||
* Get the {$name} attribute.
|
||||
*/
|
||||
public function {$accessorName}(): {$type}
|
||||
{
|
||||
return \$this->attributes['{$name}'];
|
||||
}
|
||||
|
||||
PHP;
|
||||
|
||||
// 生成修改器
|
||||
$mutatorName = 'set' . $this->getClassName($name) . 'Attribute';
|
||||
$methods .= <<<PHP
|
||||
/**
|
||||
* Set the {$name} attribute.
|
||||
*/
|
||||
public function {$mutatorName}({$type} \$value): void
|
||||
{
|
||||
\$this->attributes['{$name}'] = \$value;
|
||||
}
|
||||
|
||||
PHP;
|
||||
|
||||
return $methods;
|
||||
}
|
||||
|
||||
private function generateMigration(string $className, string $tableName, array $fields, bool $timestamps, bool $softDeletes): void
|
||||
{
|
||||
$migrationName = 'create_' . $tableName . '_table';
|
||||
$migrationClassName = $this->getClassName($migrationName);
|
||||
|
||||
$content = $this->generateMigrationContent($migrationClassName, $tableName, $fields, $timestamps, $softDeletes);
|
||||
|
||||
$migrationPath = $this->getNamespacePath('../database/migrations');
|
||||
$timestamp = date('Y_m_d_His');
|
||||
$filename = $timestamp . '_' . $migrationName . '.php';
|
||||
$filePath = $migrationPath . '/' . $filename;
|
||||
|
||||
$this->createFile($filePath, $content);
|
||||
}
|
||||
|
||||
private function generateMigrationContent(string $className, string $tableName, array $fields, bool $timestamps, bool $softDeletes): string
|
||||
{
|
||||
$upMethods = '';
|
||||
$downMethods = '';
|
||||
|
||||
// 生成字段定义
|
||||
foreach ($fields as $field) {
|
||||
$upMethods .= $this->generateMigrationField($field);
|
||||
}
|
||||
|
||||
// 生成时间戳
|
||||
if ($timestamps) {
|
||||
$upMethods .= " \$table->timestamps();\n";
|
||||
$downMethods .= " \$table->dropTimestamps();\n";
|
||||
}
|
||||
|
||||
// 生成软删除
|
||||
if ($softDeletes) {
|
||||
$upMethods .= " \$table->softDeletes();\n";
|
||||
$downMethods .= " \$table->dropSoftDeletes();\n";
|
||||
}
|
||||
|
||||
return $this->renderTemplate('migration', [
|
||||
'class_name' => $className,
|
||||
'table_name' => $tableName,
|
||||
'up_methods' => $upMethods,
|
||||
'down_methods' => $downMethods
|
||||
]);
|
||||
}
|
||||
|
||||
private function generateMigrationField(array $field): string
|
||||
{
|
||||
$name = $field['name'];
|
||||
$type = $field['type'];
|
||||
$options = $field['options'] ?? [];
|
||||
|
||||
$method = "\$table->{$type}('{$name}')";
|
||||
|
||||
if (isset($options['nullable'])) {
|
||||
$method .= '->nullable()';
|
||||
}
|
||||
|
||||
if (isset($options['default'])) {
|
||||
$default = $options['default'];
|
||||
if (is_string($default)) {
|
||||
$default = "'{$default}'";
|
||||
}
|
||||
$method .= "->default({$default})";
|
||||
}
|
||||
|
||||
if (isset($options['unique'])) {
|
||||
$method .= '->unique()';
|
||||
}
|
||||
|
||||
return " {$method};\n";
|
||||
}
|
||||
|
||||
private function generateFactory(string $className, array $fields): void
|
||||
{
|
||||
$factoryClassName = $className . 'Factory';
|
||||
$definition = $this->generateFactoryDefinition($className, $fields);
|
||||
|
||||
$content = $this->renderTemplate('factory', [
|
||||
'namespace' => $this->getFullNamespace('Database\\Factories'),
|
||||
'class_name' => $factoryClassName,
|
||||
'model_class' => $className,
|
||||
'definition' => $definition
|
||||
]);
|
||||
|
||||
$factoryPath = $this->getNamespacePath('../database/factories');
|
||||
$filePath = $factoryPath . '/' . $factoryClassName . '.php';
|
||||
|
||||
$this->createFile($filePath, $content);
|
||||
}
|
||||
|
||||
private function generateFactoryDefinition(string $className, array $fields): string
|
||||
{
|
||||
$definition = '';
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$name = $field['name'];
|
||||
$type = $field['type'];
|
||||
$faker = $this->getFakerMethod($type, $field['options'] ?? []);
|
||||
|
||||
$definition .= " '{$name}' => {$faker},\n";
|
||||
}
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
||||
private function getFakerMethod(string $type, array $options): string
|
||||
{
|
||||
$fakerMap = [
|
||||
'string' => '$this->faker->sentence()',
|
||||
'varchar' => '$this->faker->word()',
|
||||
'text' => '$this->faker->paragraph()',
|
||||
'int' => '$this->faker->numberBetween(1, 1000)',
|
||||
'integer' => '$this->faker->numberBetween(1, 1000)',
|
||||
'bigint' => '$this->faker->numberBetween(1, 1000000)',
|
||||
'float' => '$this->faker->randomFloat(2, 0, 1000)',
|
||||
'double' => '$this->faker->randomFloat(2, 0, 1000)',
|
||||
'bool' => '$this->faker->boolean()',
|
||||
'boolean' => '$this->faker->boolean()',
|
||||
'date' => '$this->faker->date()',
|
||||
'datetime' => '$this->faker->datetime()',
|
||||
'timestamp' => '$this->faker->datetime()',
|
||||
'email' => '$this->faker->email()',
|
||||
'url' => '$this->faker->url()'
|
||||
];
|
||||
|
||||
return $fakerMap[$type] ?? '$this->faker->word()';
|
||||
}
|
||||
|
||||
private function generateSeeder(string $className): void
|
||||
{
|
||||
$seederClassName = $className . 'Seeder';
|
||||
$modelVariable = $this->getVariableName($className);
|
||||
|
||||
$content = $this->renderTemplate('seeder', [
|
||||
'namespace' => $this->getFullNamespace('Database\\Seeders'),
|
||||
'class_name' => $seederClassName,
|
||||
'model_class' => $className,
|
||||
'model_variable' => $modelVariable,
|
||||
'model_namespace' => $this->getFullNamespace('Model')
|
||||
]);
|
||||
|
||||
$seederPath = $this->getNamespacePath('../database/seeders');
|
||||
$filePath = $seederPath . '/' . $seederClassName . '.php';
|
||||
|
||||
$this->createFile($filePath, $content);
|
||||
}
|
||||
|
||||
private function getModelTemplate(): string
|
||||
{
|
||||
return <<<PHP
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{namespace}};
|
||||
|
||||
{{use_block}}class {{class_name}} extends Model
|
||||
{
|
||||
protected string \$table = '{{table_name}}';
|
||||
protected bool \$timestamps = {{timestamps}};
|
||||
{{soft_deletes}}
|
||||
|
||||
{{properties}}
|
||||
{{methods}}
|
||||
}
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function getPropertyTemplate(): string
|
||||
{
|
||||
return <<<PHP
|
||||
{{doc_block}}
|
||||
private {{nullable}}{{property_type}} \${{property_name}};
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function getMethodTemplate(): string
|
||||
{
|
||||
return <<<PHP
|
||||
{{method_content}}
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function getMigrationTemplate(): string
|
||||
{
|
||||
return <<<PHP
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Fendx\Database\Migration;
|
||||
use Fendx\Database\Schema\Blueprint;
|
||||
|
||||
class {{class_name}} extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
\$this->schema->create('{{table_name}}', function (Blueprint \$table) {
|
||||
{{up_methods}});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
\$this->schema->dropIfExists('{{table_name}}');
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function getFactoryTemplate(): string
|
||||
{
|
||||
return <<<PHP
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{namespace}};
|
||||
|
||||
use Fendx\Database\Factories\Factory;
|
||||
use {{model_namespace}}\{{model_class}};
|
||||
|
||||
class {{class_name}} extends Factory
|
||||
{
|
||||
protected string \$model = {{model_class}}::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
{{definition}};
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
}
|
||||
|
||||
private function getSeederTemplate(): string
|
||||
{
|
||||
return <<<PHP
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace {{namespace}};
|
||||
|
||||
use Fendx\Database\Seeder;
|
||||
use {{model_namespace}}\{{model_class}};
|
||||
|
||||
class {{class_name}} extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
{{model_class}}::factory()->count(10)->create();
|
||||
}
|
||||
}
|
||||
PHP;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user