重构初始化

This commit is contained in:
技术老胡
2026-04-15 11:45:46 +08:00
parent 72d72d735b
commit 7612026773
381 changed files with 28287 additions and 14717 deletions

View File

@@ -0,0 +1,242 @@
<?php
namespace app\service\system\config;
use app\common\base\BaseService;
use RuntimeException;
class SystemConfigDefinitionService extends BaseService
{
protected const VIRTUAL_FIELD_PREFIX = '__';
/**
* 已解析的标签页缓存。
*/
protected ?array $tabCache = null;
/**
* 标签页键到定义的缓存。
*/
protected ?array $tabMapCache = null;
public function tabs(): array
{
if ($this->tabCache !== null) {
return $this->tabCache;
}
$definitions = (array) config('system_config', []);
$tabs = [];
$seenKeys = [];
$seenFields = [];
foreach ($definitions as $groupCode => $definition) {
if (!is_array($definition)) {
continue;
}
$tab = $this->normalizeTab((string) $groupCode, $definition);
if ($tab === null) {
continue;
}
$key = $tab['key'];
if (isset($seenKeys[$key])) {
throw new RuntimeException(sprintf('系统配置标签 key 重复:%s', $key));
}
foreach ($tab['rules'] as $rule) {
$field = (string) ($rule['field'] ?? '');
if ($field === '' || $this->isVirtualField($field)) {
continue;
}
if (isset($seenFields[$field])) {
throw new RuntimeException(sprintf('系统配置项 key 重复:%s', $field));
}
$seenFields[$field] = true;
}
$seenKeys[$key] = true;
$tabs[] = $tab;
}
usort($tabs, static function (array $left, array $right): int {
$leftSort = (int) ($left['sort'] ?? 0);
$rightSort = (int) ($right['sort'] ?? 0);
return $leftSort <=> $rightSort;
});
$this->tabCache = $tabs;
$this->tabMapCache = [];
foreach ($tabs as $tab) {
$key = (string) ($tab['key'] ?? '');
if ($key !== '') {
$this->tabMapCache[$key] = $tab;
}
}
return $this->tabCache;
}
public function tab(string $groupCode): ?array
{
$groupCode = strtolower(trim($groupCode));
if ($groupCode === '') {
return null;
}
$this->tabs();
return $this->tabMapCache[$groupCode] ?? null;
}
public function hydrateRules(array $tab, array $values): array
{
$rules = [];
foreach ((array) ($tab['rules'] ?? []) as $rule) {
if (!is_array($rule)) {
continue;
}
$field = (string) ($rule['field'] ?? '');
if ($field === '') {
continue;
}
if (!$this->isVirtualField($field)) {
$rule['value'] = array_key_exists($field, $values) ? $values[$field] : ($rule['value'] ?? '');
}
$rules[] = $rule;
}
return $rules;
}
public function extractFormData(array $tab, array $values): array
{
$data = [];
foreach ((array) ($tab['rules'] ?? []) as $rule) {
if (!is_array($rule)) {
continue;
}
$field = (string) ($rule['field'] ?? '');
if ($field === '' || $this->isVirtualField($field)) {
continue;
}
$data[$field] = array_key_exists($field, $values) ? $values[$field] : ($rule['value'] ?? '');
}
return $data;
}
public function requiredFieldMessages(array $tab): array
{
$messages = [];
foreach ((array) ($tab['rules'] ?? []) as $rule) {
if (!is_array($rule)) {
continue;
}
$field = strtolower(trim((string) ($rule['field'] ?? '')));
if ($field === '' || $this->isVirtualField($field)) {
continue;
}
foreach ((array) ($rule['validate'] ?? []) as $validateRule) {
if (!is_array($validateRule)) {
continue;
}
if (!empty($validateRule['required'])) {
$messages[$field] = (string) ($validateRule['message'] ?? sprintf('%s 不能为空', (string) ($rule['title'] ?? $field)));
break;
}
}
}
return $messages;
}
private function normalizeTab(string $groupCode, array $definition): ?array
{
$key = strtolower(trim((string) ($definition['key'] ?? $groupCode)));
if ($key === '') {
return null;
}
$rules = [];
foreach ((array) ($definition['rules'] ?? []) as $rule) {
$normalizedRule = $this->normalizeRule($rule);
if ($normalizedRule !== null) {
$rules[] = $normalizedRule;
}
}
return [
'key' => $key,
'title' => (string) ($definition['title'] ?? $key),
'icon' => (string) ($definition['icon'] ?? ''),
'description' => (string) ($definition['description'] ?? ''),
'sort' => (int) ($definition['sort'] ?? 0),
'disabled' => (bool) ($definition['disabled'] ?? false),
'submitText' => (string) ($definition['submitText'] ?? '保存配置'),
'refreshAfterSubmit' => (bool) ($definition['refreshAfterSubmit'] ?? true),
'rules' => $rules,
];
}
private function normalizeRule(mixed $rule): ?array
{
if (!is_array($rule)) {
return null;
}
$field = strtolower(trim((string) ($rule['field'] ?? '')));
if ($field === '') {
return null;
}
$options = [];
foreach ((array) ($rule['options'] ?? []) as $option) {
if (!is_array($option)) {
continue;
}
$options[] = [
'label' => (string) ($option['label'] ?? ''),
'value' => (string) ($option['value'] ?? ''),
];
}
$validate = [];
foreach ((array) ($rule['validate'] ?? []) as $validateRule) {
if (!is_array($validateRule)) {
continue;
}
$validate[] = $validateRule;
}
$normalized = $rule;
$normalized['type'] = (string) ($rule['type'] ?? 'input');
$normalized['field'] = $field;
$normalized['title'] = (string) ($rule['title'] ?? $field);
$normalized['value'] = (string) ($rule['value'] ?? '');
$normalized['props'] = is_array($rule['props'] ?? null) ? $rule['props'] : [];
$normalized['options'] = $options;
$normalized['validate'] = $validate;
return $normalized;
}
private function isVirtualField(string $field): bool
{
return str_starts_with($field, self::VIRTUAL_FIELD_PREFIX);
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace app\service\system\config;
use app\common\base\BaseService;
use app\exception\ValidationException;
use app\repository\system\config\SystemConfigRepository;
use Webman\Event\Event;
class SystemConfigPageService extends BaseService
{
public function __construct(
protected SystemConfigRepository $systemConfigRepository,
protected SystemConfigDefinitionService $systemConfigDefinitionService
) {
}
public function tabs(): array
{
$tabs = [];
foreach ($this->systemConfigDefinitionService->tabs() as $tab) {
unset($tab['rules']);
$tabs[] = $tab;
}
$defaultKey = '';
foreach ($tabs as $tab) {
if (!empty($tab['disabled'])) {
continue;
}
$defaultKey = (string) ($tab['key'] ?? '');
if ($defaultKey !== '') {
break;
}
}
return [
'defaultKey' => $defaultKey !== '' ? $defaultKey : (string) ($tabs[0]['key'] ?? ''),
'tabs' => $tabs,
];
}
public function detail(string $groupCode): array
{
$tab = $this->systemConfigDefinitionService->tab($groupCode);
if (!$tab) {
throw new ValidationException('系统配置标签不存在');
}
$keys = [];
foreach ((array) ($tab['rules'] ?? []) as $rule) {
if (!is_array($rule)) {
continue;
}
$field = strtolower(trim((string) ($rule['field'] ?? '')));
if ($field !== '' && !str_starts_with($field, '__')) {
$keys[] = $field;
}
}
$keys = array_values(array_unique($keys));
if ($keys === []) {
$rowMap = [];
} else {
$rows = $this->systemConfigRepository->query()
->whereIn('config_key', $keys)
->get(['config_key', 'config_value']);
$rowMap = [];
foreach ($rows as $row) {
$rowMap[strtolower((string) $row->config_key)] = (string) ($row->config_value ?? '');
}
}
$tab['rules'] = $this->systemConfigDefinitionService->hydrateRules($tab, $rowMap);
$tab['formData'] = $this->systemConfigDefinitionService->extractFormData($tab, $rowMap);
return $tab;
}
public function save(string $groupCode, array $values): array
{
$tab = $this->systemConfigDefinitionService->tab($groupCode);
if (!$tab) {
throw new ValidationException('系统配置标签不存在');
}
$formData = $this->systemConfigDefinitionService->extractFormData($tab, $values);
$this->validateRequiredValues($tab, $formData);
$this->transaction(function () use ($tab, $formData): void {
foreach ((array) ($tab['rules'] ?? []) as $rule) {
if (!is_array($rule)) {
continue;
}
$field = strtolower(trim((string) ($rule['field'] ?? '')));
if ($field === '' || str_starts_with($field, '__')) {
continue;
}
$value = $this->stringifyValue($formData[$field] ?? '');
$this->systemConfigRepository->updateOrCreate(
['config_key' => $field],
[
'group_code' => (string) $tab['key'],
'config_value' => $value,
]
);
}
});
Event::emit('system.config.changed', [
'group_code' => (string) $tab['key'],
]);
return $this->detail((string) $tab['key']);
}
protected function validateRequiredValues(array $tab, array $values): void
{
$messages = $this->systemConfigDefinitionService->requiredFieldMessages($tab);
foreach ($messages as $field => $message) {
$value = $values[$field] ?? '';
if ($this->isEmptyValue($value)) {
throw new ValidationException($message);
}
}
}
protected function isEmptyValue(mixed $value): bool
{
if (is_array($value)) {
return $value === [];
}
if (is_string($value)) {
return trim($value) === '';
}
return $value === null || $value === '';
}
protected function stringifyValue(mixed $value): string
{
if (is_bool($value)) {
return $value ? '1' : '0';
}
if (is_array($value) || is_object($value)) {
throw new ValidationException('系统配置值暂不支持复杂类型');
}
return (string) $value;
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace app\service\system\config;
use app\common\base\BaseService;
use app\repository\system\config\SystemConfigRepository;
use support\Cache;
use Throwable;
class SystemConfigRuntimeService extends BaseService
{
protected const CACHE_KEY = 'system_config:all';
public function __construct(
protected SystemConfigRepository $systemConfigRepository,
protected SystemConfigDefinitionService $systemConfigDefinitionService
) {
}
public function all(bool $refresh = false): array
{
if (!$refresh) {
$cached = $this->readCache();
if ($cached !== null) {
return $cached;
}
}
return $this->refresh();
}
public function get(string $configKey, mixed $default = '', bool $refresh = false): string
{
$configKey = strtolower(trim($configKey));
if ($configKey === '') {
return (string) $default;
}
$values = $this->all($refresh);
return (string) ($values[$configKey] ?? $default);
}
public function refresh(): array
{
$values = $this->buildValueMap();
$this->writeCache($values);
return $values;
}
protected function buildValueMap(): array
{
$values = [];
$tabs = $this->systemConfigDefinitionService->tabs();
$keys = [];
foreach ($tabs as $tab) {
foreach ((array) ($tab['rules'] ?? []) as $rule) {
if (!is_array($rule)) {
continue;
}
$field = strtolower(trim((string) ($rule['field'] ?? '')));
if ($field !== '' && !str_starts_with($field, '__')) {
$keys[] = $field;
}
}
}
$keys = array_values(array_unique($keys));
if ($keys === []) {
return [];
}
$rows = $this->systemConfigRepository->query()
->whereIn('config_key', $keys)
->get(['config_key', 'config_value']);
$rowMap = [];
foreach ($rows as $row) {
$rowMap[strtolower((string) $row->config_key)] = (string) ($row->config_value ?? '');
}
foreach ($tabs as $tab) {
foreach ((array) ($tab['rules'] ?? []) as $rule) {
if (!is_array($rule)) {
continue;
}
$field = strtolower(trim((string) ($rule['field'] ?? '')));
if ($field === '' || str_starts_with($field, '__')) {
continue;
}
$values[$field] = array_key_exists($field, $rowMap)
? (string) $rowMap[$field]
: (string) ($rule['value'] ?? '');
}
}
return $values;
}
protected function readCache(): ?array
{
try {
$raw = Cache::get(self::CACHE_KEY);
} catch (Throwable) {
return null;
}
return is_array($raw) ? $raw : null;
}
protected function writeCache(array $values): void
{
try {
Cache::set(self::CACHE_KEY, $values);
} catch (Throwable) {
// Redis 不可用时不阻塞主流程。
}
}
}