tp/app/service/ThemeService.php
2026-03-09 16:35:17 +08:00

280 lines
7.8 KiB
PHP

<?php
declare(strict_types=1);
namespace app\service;
use think\facade\Db;
use think\facade\Config;
/**
* 模板服务类
* 负责扫描 public/themes 目录,管理模板配置
*/
class ThemeService
{
private string $themesPath;
public function __construct()
{
$this->themesPath = root_path() . 'public' . DIRECTORY_SEPARATOR . 'themes';
}
/**
* 获取所有可用模板列表
* @return array
*/
public function getThemeList(): array
{
$themes = [];
$dirs = $this->scanThemeDirs();
foreach ($dirs as $dir) {
$config = $this->readThemeConfig($dir);
$preview = $this->getThemePreview($dir);
$themes[] = [
'key' => $dir,
'name' => $config['name'] ?? $dir,
'description'=> $config['description'] ?? '',
'version' => $config['version'] ?? '1.0.0',
'author' => $config['author'] ?? '',
'preview' => $preview,
'path' => '/themes/' . $dir . '/index.html',
'fields' => $config['fields'] ?? [],
];
}
return $themes;
}
/**
* 扫描模板目录
* @return array
*/
private function scanThemeDirs(): array
{
$dirs = [];
if (!is_dir($this->themesPath)) {
return $dirs;
}
$items = scandir($this->themesPath);
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$fullPath = $this->themesPath . DIRECTORY_SEPARATOR . $item;
// 检查是否有 index.html 或 index.php
if (is_dir($fullPath) && (is_file($fullPath . DIRECTORY_SEPARATOR . 'index.html') || is_file($fullPath . DIRECTORY_SEPARATOR . 'index.php'))) {
$dirs[] = $item;
}
}
return $dirs;
}
/**
* 读取模板配置文件
* @param string $themeDir
* @return array
*/
private function readThemeConfig(string $themeDir): array
{
$configPath = $this->themesPath . DIRECTORY_SEPARATOR . $themeDir . DIRECTORY_SEPARATOR . 'config.json';
if (!is_file($configPath)) {
return [];
}
$content = file_get_contents($configPath);
$config = json_decode($content, true);
return $config ?? [];
}
/**
* 获取模板预览图
* @param string $themeDir
* @return string
*/
private function getThemePreview(string $themeDir): string
{
$previewPath = '/themes/' . $themeDir . '/preview.png';
// 如果 preview.png 不存在,使用默认占位图
$fullPath = $this->themesPath . DIRECTORY_SEPARATOR . $themeDir . DIRECTORY_SEPARATOR . 'preview.png';
if (!is_file($fullPath)) {
return 'https://picsum.photos/300/200?random=' . ord($themeDir[0]);
}
return $previewPath;
}
/**
* 获取当前激活的模板Key
* @param int $tid 租户ID
* @return string
*/
public function getCurrentTheme(int $tid = 0): string
{
try {
$where = [['key', '=', 'current_theme'], ['delete_time', '=', null]];
if ($tid > 0) {
$where[] = ['tid', '=', $tid];
}
$config = Db::name('mete_template_site_config')
->where($where)
->find();
return $config['value'] ?? 'default';
} catch (\Exception $e) {
return 'default';
}
}
/**
* 切换当前模板
* @param int $tid 租户ID
* @param string $themeKey
* @return bool
*/
public function switchTheme(int $tid, string $themeKey): bool
{
// 验证模板是否存在
$themes = $this->getThemeList();
$exists = false;
foreach ($themes as $theme) {
if ($theme['key'] === $themeKey) {
$exists = true;
break;
}
}
if (!$exists) {
return false;
}
try {
// 查找或创建配置记录
$where = [['key', '=', 'current_theme'], ['delete_time', '=', null]];
if ($tid > 0) {
$where[] = ['tid', '=', $tid];
}
$config = Db::name('mete_template_site_config')
->where($where)
->find();
$now = date('Y-m-d H:i:s');
if ($config) {
Db::name('mete_template_site_config')->where('id', $config['id'])->update([
'value' => $themeKey,
'update_time' => $now
]);
} else {
Db::name('mete_template_site_config')->insert([
'key' => 'current_theme',
'value' => $themeKey,
'create_time' => $now,
'update_time' => $now
]);
}
return true;
} catch (\Exception $e) {
return false;
}
}
/**
* 获取模板数据(用于前端渲染)
* @param int $tid 租户ID
* @param string|null $themeKey
* @return array
*/
public function getThemeData(int $tid = 0, ?string $themeKey = null): array
{
$themeKey = $themeKey ?? $this->getCurrentTheme($tid);
try {
$where = [['theme_key', '=', $themeKey], ['delete_time', '=', null]];
if ($tid > 0) {
$where[] = ['tid', '=', $tid];
}
$themeData = Db::name('mete_template_theme_data')
->where($where)
->select()
->toArray();
$data = [];
foreach ($themeData as $item) {
$data[$item['field_key']] = $item['field_value'];
}
return [
'theme_key' => $themeKey,
'theme_path' => '/themes/' . $themeKey . '/index.html',
'data' => $data
];
} catch (\Exception $e) {
return [
'theme_key' => $themeKey,
'theme_path' => '/themes/' . $themeKey . '/index.html',
'data' => []
];
}
}
/**
* 保存模板字段数据
* @param int $tid 租户ID
* @param string $themeKey
* @param string $fieldKey
* @param mixed $fieldValue
* @return bool
*/
public function saveThemeField(int $tid, string $themeKey, string $fieldKey, $fieldValue): bool
{
try {
$where = [
['theme_key', '=', $themeKey],
['field_key', '=', $fieldKey],
['delete_time', '=', null]
];
if ($tid > 0) {
$where[] = ['tid', '=', $tid];
}
$existing = Db::name('mete_template_theme_data')
->where($where)
->find();
$value = is_array($fieldValue) ? json_encode($fieldValue, JSON_UNESCAPED_UNICODE) : $fieldValue;
$now = date('Y-m-d H:i:s');
if ($existing) {
Db::name('mete_template_theme_data')
->where('id', $existing['id'])
->update([
'field_value' => $value,
'update_time' => $now
]);
} else {
Db::name('mete_template_theme_data')->insert([
'tid' => $tid,
'theme_key' => $themeKey,
'field_key' => $fieldKey,
'field_value' => $value,
'create_time' => $now,
'update_time' => $now
]);
}
return true;
} catch (\Exception $e) {
return false;
}
}
}