Files
MrleeAdmin/backend/app/service/core/enum/EnumService.php
T

724 lines
20 KiB
PHP
Raw Normal View History

2026-08-31 11:33:59 +08:00
<?php
declare(strict_types=1);
/**
*+------------------
* madong
*+------------------
* Copyright (c) https://gitee.com/motion-code All rights reserved.
*+------------------
* Official Website: http://www.madong.tech
*/
namespace app\service\core\enum;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use ReflectionException;
/**
* 枚举服务类
*
* @author Mr.April
* @since 1.0
*/
final class EnumService
{
/**
* 缓存的插件名称列表
*/
private array $pluginNames = [];
/**
* 获取所有插件名称
*
* @return array
*/
private function getPluginNames(): array
{
if (!empty($this->pluginNames)) {
return $this->pluginNames;
}
$pluginDir = base_path('plugin');
if (!is_dir($pluginDir)) {
$this->pluginNames = [];
return $this->pluginNames;
}
$this->pluginNames = [];
$dirs = scandir($pluginDir);
foreach ($dirs as $dir) {
if ($dir !== '.' && $dir !== '..' && is_dir($pluginDir . DIRECTORY_SEPARATOR . $dir)) {
$this->pluginNames[] = $dir;
}
}
return $this->pluginNames;
}
/**
* 获取所有枚举目录(递归扫描)
* '支持扫描 app/enum 和 plugin//app/enum 目录
* @return array
*/
public function getEnumDirectories(): array
{
$directories = [];
// 扫描 app/enum 目录
$appEnumDir = app_path('enum');
if (is_dir($appEnumDir)) {
$directories = array_merge($directories, $this->scanDirectory($appEnumDir));
}
// 扫描插件目录 plugin/*/app/enum
$pluginDir = base_path('plugin');
if (is_dir($pluginDir)) {
foreach ($this->getPluginNames() as $pluginName) {
$pluginEnumDir = $pluginDir . DIRECTORY_SEPARATOR . $pluginName . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'enum';
if (is_dir($pluginEnumDir)) {
$directories = array_merge($directories, $this->scanDirectory($pluginEnumDir, $pluginName));
}
}
}
return $directories;
}
/**
* 扫描指定目录并返回目录映射
*
* @param string $baseDir 基础目录
* @param string $pluginName 插件名称(可选)
* @return array
*/
private function scanDirectory(string $baseDir, string $pluginName = ''): array
{
$directories = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($baseDir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if ($file->isDir() && $file->getFilename() !== '.' && $file->getFilename() !== '..') {
$relativePath = str_replace($baseDir . DIRECTORY_SEPARATOR, '', $file->getPathname());
$relativePath = str_replace(DIRECTORY_SEPARATOR, '.', $relativePath);
// 如果是插件,则分类以插件名开头
$category = $pluginName ? ($pluginName . '.' . $relativePath) : $relativePath;
$directories[$category] = $file->getPathname();
}
}
return $directories;
}
/**
* 扫描所有枚举类(支持多级目录)
*
* @return array
*/
public function scanEnums(): array
{
$result = [];
$directories = $this->getEnumDirectories();
foreach ($directories as $category => $directory) {
if (!is_dir($directory)) {
continue;
}
$files = scandir($directory);
foreach ($files as $file) {
if ($file === '.' || $file === '..' || !str_ends_with($file, '.php')) {
continue;
}
$className = pathinfo($file, PATHINFO_FILENAME);
$namespace = $this->buildNamespace($category, $className);
if ($this->isEnumClass($namespace)) {
$result[] = [
'category' => $category,
'namespace' => $namespace,
'name' => $className,
'code' => strtolower($category . '.' . $className),
];
}
}
}
return $result;
}
/**
* 根据分类路径构建命名空间
*
* @param string $category 分类路径(如:system 或 official)
* @param string $className 类名
* @return string
*/
private function buildNamespace(string $category, string $className): string
{
$parts = explode('.', $category);
$firstPart = $parts[0];
// 判断第一部分是否为插件名
if (in_array($firstPart, $this->getPluginNames())) {
// 插件枚举:official → plugin\official\app\enum\PermissionType
$subPath = count($parts) > 1 ? implode('\\', array_slice($parts, 1)) : '';
$namespace = "plugin\\{$firstPart}\\app\\enum";
if ($subPath) {
$namespace .= "\\{$subPath}";
}
$namespace .= "\\{$className}";
} else {
// 系统枚举:system → app\enum\system\PermissionType
$subPath = str_replace('.', '\\', $category);
$namespace = "app\\enum\\{$subPath}\\{$className}";
}
return $namespace;
}
/**
* 根据枚举code获取枚举数据(支持多级目录)
*
* @param string $enumCode 枚举代码,格式如:system.permissiontype 或 official.permissiontype
* @return array
*/
public function getEnumByCode(string $enumCode): array
{
$parts = explode('.', $enumCode);
if (count($parts) < 2) {
return [];
}
$enumName = array_pop($parts);
$category = implode('.', $parts);
$namespace = $this->buildNamespace($category, $enumName);
if (!$this->isEnumClass($namespace)) {
return [];
}
return $this->getEnumOptions($namespace);
}
/**
* 获取枚举的选项数据
*
* @param string $enumClass 枚举类名
* @return array
*/
private function getEnumOptions(string $enumClass): array
{
try {
$reflectionClass = new ReflectionClass($enumClass);
$cases = $reflectionClass->getConstants();
$options = [];
// 预定义颜色数组,用于随机分配
$defaultColors = ['#1890ff', '#52c41a', '#faad14', '#f5222d', '#722ed1', '#fa541c', '#13c2c2', '#eb2f96'];
foreach ($cases as $caseName => $case) {
$option = [
'label' => method_exists($case, 'label') ? $case->label() : $caseName,
'value' => $case->value ?? $caseName,
];
// 添加颜色信息 - 优先使用枚举的color方法,否则使用随机颜色
if (method_exists($case, 'color')) {
$option['color'] = $case->color();
} else {
// 使用随机颜色,确保相同caseName总是得到相同颜色
$colorIndex = crc32($caseName) % count($defaultColors);
$option['color'] = $defaultColors[$colorIndex];
}
// 添加其他扩展信息 - 总是设置ext字段,没有方法时返回空数组
$option['ext'] = method_exists($case, 'ext') ? $case->ext() : [];
$options[] = $option;
}
return $options;
} catch (ReflectionException $e) {
return [];
}
}
/**
* 检查是否为枚举类
*
* @param string $className 类名
* @return bool
*/
private function isEnumClass(string $className): bool
{
try {
$reflectionClass = new ReflectionClass($className);
return $reflectionClass->isEnum();
} catch (ReflectionException $e) {
return false;
}
}
/**
* 获取所有枚举的完整列表(支持多级目录,一维结构)
*
* @return array
*/
public function getAllEnums(): array
{
$result = [];
$directories = $this->getEnumDirectories();
foreach ($directories as $category => $directory) {
if (!is_dir($directory)) {
continue;
}
$files = scandir($directory);
foreach ($files as $file) {
if ($file === '.' || $file === '..' || !str_ends_with($file, '.php')) {
continue;
}
$className = pathinfo($file, PATHINFO_FILENAME);
$namespace = $this->buildNamespace($category, $className);
if ($this->isEnumClass($namespace)) {
$options = $this->getEnumOptions($namespace);
$result[] = [
'name' => $className,
'namespace' => $namespace,
'category' => $category,
'category_label' => $this->getCategoryLabel($category),
'code' => strtolower($category . '.' . $className),
'options' => $options,
'count' => count($options),
];
}
}
}
return $result;
}
/**
* 获取分类标签(支持多级目录)
*
* @param string $category 分类
* @return string
*/
private function getCategoryLabel(string $category): string
{
$categoryEnum = \app\enum\common\CategoryEnum::fromDirectory($category);
if ($categoryEnum !== null) {
return $categoryEnum->label();
}
$labels = [
'common' => '通用枚举',
'system' => '系统枚举',
];
return $labels[$category] ?? $this->generateCategoryLabel($category);
}
/**
* 生成分类标签(用于未映射的分类)
*
* @param string $category 分类路径
* @return string
*/
private function generateCategoryLabel(string $category): string
{
$parts = explode('.', $category);
$lastPart = end($parts);
return ucfirst($lastPart) . '枚举';
}
/**
* 获取所有可用的枚举代码列表
*
* @return array
*/
public function getAvailableEnumCodes(): array
{
$enums = $this->scanEnums();
return array_column($enums, 'code');
}
/**
* 检查枚举代码是否有效
*
* @param string $enumCode 枚举代码
* @return bool
*/
public function isValidEnumCode(string $enumCode): bool
{
$parts = explode('.', $enumCode);
if (count($parts) < 2) {
return false;
}
$enumName = array_pop($parts);
$category = implode('.', $parts);
$namespace = $this->buildNamespace($category, $enumName);
return $this->isEnumClass($namespace);
}
/**
* 批量获取多个枚举数据
*
* @param array $enumCodes 枚举代码数组
* @return array
*/
public function getBatchEnums(array $enumCodes): array
{
$result = [];
foreach ($enumCodes as $enumCode) {
if ($this->isValidEnumCode($enumCode)) {
$result[$enumCode] = $this->getEnumByCode($enumCode);
}
}
return $result;
}
/**
* 获取枚举的详细信息
*
* @param string $enumCode 枚举代码
* @return array
*/
public function getEnumInfo(string $enumCode): array
{
if (!$this->isValidEnumCode($enumCode)) {
return [];
}
$parts = explode('.', $enumCode);
$enumName = array_pop($parts);
$category = implode('.', $parts);
$namespace = $this->buildNamespace($category, $enumName);
try {
$reflectionClass = new ReflectionClass($namespace);
$cases = $reflectionClass->getConstants();
return [
'code' => $enumCode,
'name' => $enumName,
'category' => $category,
'namespace' => $namespace,
'case_count' => count($cases),
'cases' => array_keys($cases),
];
} catch (ReflectionException $e) {
return [];
}
}
/**
* 获取枚举列表-支持搜索和分类筛选,不分页,一维结构
*
* @param string $search 搜索关键词
* @param string $category 分类筛选
* @return array
*/
public function getEnumsList(string $search = '', string $category = ''): array
{
$allEnums = $this->getAllEnums();
$filteredEnums = [];
foreach ($allEnums as $enum) {
// 分类筛选
if ($category && $enum['category'] !== $category) {
continue;
}
// 搜索筛选
if ($search && !str_contains(strtolower($enum['name']), strtolower($search))) {
continue;
}
$filteredEnums[] = $enum;
}
return $filteredEnums;
}
/**
* 获取枚举总数(用于分页计数)
*
* @param string $search 搜索关键词
* @param string $category 分类筛选
* @return int
*/
public function getEnumsCount(string $search = '', string $category = ''): int
{
return count($this->getEnumsList($search, $category));
}
/**
* 分页获取枚举列表(以枚举类为计数单位,一维结构)
*
* @param int $page 页码
* @param int $limit 每页数量
* @param string $search 搜索关键词
* @param string $category 分类筛选
* @return array
*/
public function getEnumsWithPagination(int $page = 1, int $limit = 10, string $search = '', string $category = ''): array
{
$filteredEnums = $this->getEnumsList($search, $category);
// 分页处理
$total = count($filteredEnums);
$offset = ($page - 1) * $limit;
$pagedEnums = array_slice($filteredEnums, $offset, $limit);
return [
'items' => $pagedEnums,
'total' => $total,
'page' => $page,
'limit' => $limit,
'total_page' => ceil($total / $limit),
];
}
/**
* 根据分类获取枚举列表
*
* @param string $category 分类
* @return array
*/
public function getEnumsByCategory(string $category): array
{
return $this->getEnumsList('', $category);
}
/**
* 搜索枚举
*
* @param string $keyword 关键词
* @param int $limit 限制数量
* @return array
*/
public function searchEnums(string $keyword, int $limit = 20): array
{
$allEnums = $this->getAllEnums();
$result = [];
foreach ($allEnums as $enum) {
if (str_contains(strtolower($enum['name']), strtolower($keyword))) {
$result[] = $enum;
if (count($result) >= $limit) {
break;
}
}
}
return $result;
}
/**
* 获取枚举统计信息
*
* @return array
*/
public function getEnumStatistics(): array
{
$allEnums = $this->getAllEnums();
$statistics = [
'total_enums' => count($allEnums),
'total_options' => 0,
'categories' => [],
];
$categoryStats = [];
foreach ($allEnums as $enum) {
$statistics['total_options'] += $enum['count'];
if (!isset($categoryStats[$enum['category']])) {
$categoryStats[$enum['category']] = [
'category' => $enum['category'],
'label' => $enum['categoryLabel'],
'enum_count' => 0,
'option_count' => 0,
];
}
$categoryStats[$enum['category']]['enum_count']++;
$categoryStats[$enum['category']]['option_count'] += $enum['count'];
}
$statistics['categories'] = array_values($categoryStats);
$statistics['total_categories'] = count($categoryStats);
return $statistics;
}
/**
* 获取所有分类列表
*
* @return array
*/
public function getCategoryList(): array
{
$allEnums = $this->getAllEnums();
$categories = [];
$seenCategories = [];
foreach ($allEnums as $enum) {
if (!in_array($enum['category'], $seenCategories)) {
$seenCategories[] = $enum['category'];
$categories[] = [
'value' => $enum['category'],
'label' => $enum['categoryLabel'],
];
}
}
return $categories;
}
/**
* 检查枚举是否存在
*
* @param string $enumCode 枚举代码
* @return bool
*/
public function enumExists(string $enumCode): bool
{
return $this->isValidEnumCode($enumCode);
}
/**
* 获取枚举的选项数量
*
* @param string $enumCode 枚举代码
* @return int
*/
public function getEnumOptionCount(string $enumCode): int
{
$options = $this->getEnumByCode($enumCode);
return count($options);
}
/**
* 批量检查枚举代码有效性
*
* @param array $enumCodes 枚举代码数组
* @return array
*/
public function validateEnumCodes(array $enumCodes): array
{
$result = [
'valid' => [],
'invalid' => [],
];
foreach ($enumCodes as $enumCode) {
if ($this->isValidEnumCode($enumCode)) {
$result['valid'][] = $enumCode;
} else {
$result['invalid'][] = $enumCode;
}
}
return $result;
}
/**
* 获取枚举的默认值(第一个选项的值)
*
* @param string $enumCode 枚举代码
* @return mixed
*/
public function getEnumDefaultValue(string $enumCode): mixed
{
$options = $this->getEnumByCode($enumCode);
if (empty($options)) {
return null;
}
return $options[0]['value'] ?? null;
}
/**
* 根据值获取枚举标签
*
* @param string $enumCode 枚举代码
* @param mixed $value 枚举值
* @return string|null
*/
public function getLabelByValue(string $enumCode, mixed $value): ?string
{
$options = $this->getEnumByCode($enumCode);
foreach ($options as $option) {
if ($option['value'] == $value) {
return $option['label'];
}
}
return null;
}
/**
* 根据标签获取枚举值
*
* @param string $enumCode 枚举代码
* @param string $label 枚举标签
* @return mixed
*/
public function getValueByLabel(string $enumCode, string $label): mixed
{
$options = $this->getEnumByCode($enumCode);
foreach ($options as $option) {
if ($option['label'] === $label) {
return $option['value'];
}
}
return null;
}
/**
* 获取枚举的颜色信息
*
* @param string $enumCode 枚举代码
* @param mixed $value 枚举值
* @return string|null
*/
public function getColorByValue(string $enumCode, mixed $value): ?string
{
$options = $this->getEnumByCode($enumCode);
foreach ($options as $option) {
if ($option['value'] == $value) {
return $option['color'] ?? null;
}
}
return null;
}
/**
* 重新扫描枚举
*
* @return array
*/
public function rescanEnums(): array
{
// 这里可以添加缓存清除逻辑
return $this->getAllEnums();
}
}