271 lines
7.4 KiB
PHP
271 lines
7.4 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\service;
|
||
|
||
use think\facade\Db;
|
||
use think\facade\Config;
|
||
use app\model\Cms\TemplateSiteConfig;
|
||
|
||
/**
|
||
* 模板服务类
|
||
* 负责扫描 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)
|
||
{
|
||
// 直接通过 tid 查询对应的 value
|
||
$value = TemplateSiteConfig::where('tid', $tid)->value('value');
|
||
|
||
return $value;
|
||
}
|
||
|
||
/**
|
||
* 切换当前模板
|
||
* @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 = TemplateSiteConfig::where($where)
|
||
->find();
|
||
|
||
$now = date('Y-m-d H:i:s');
|
||
|
||
if ($config) {
|
||
TemplateSiteConfig::where('id', $config['id'])->update([
|
||
'value' => $themeKey,
|
||
'update_time' => $now
|
||
]);
|
||
} else {
|
||
TemplateSiteConfig::insert([
|
||
'tid' => $tid,
|
||
'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 {
|
||
if ($tid > 0) {
|
||
$where[] = ['tid', '=', $tid];
|
||
}
|
||
$configData = TemplateSiteConfig::where($where)
|
||
->select()
|
||
->toArray();
|
||
|
||
$data = [];
|
||
foreach ($configData as $item) {
|
||
// 去掉 field_ 前缀
|
||
$fieldKey = substr($item['key'], 6);
|
||
$data[$fieldKey] = $item['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 {
|
||
// 使用 TemplateSiteConfig 表,key 格式为 field_xxx
|
||
$configKey = 'field_' . $fieldKey;
|
||
$where = [
|
||
['key', '=', $configKey],
|
||
['delete_time', '=', null]
|
||
];
|
||
if ($tid > 0) {
|
||
$where[] = ['tid', '=', $tid];
|
||
}
|
||
$existing = TemplateSiteConfig::where($where)
|
||
->find();
|
||
|
||
$value = is_array($fieldValue) ? json_encode($fieldValue, JSON_UNESCAPED_UNICODE) : $fieldValue;
|
||
$now = date('Y-m-d H:i:s');
|
||
|
||
if ($existing) {
|
||
TemplateSiteConfig::where('id', $existing['id'])
|
||
->update([
|
||
'value' => $value,
|
||
'update_time' => $now
|
||
]);
|
||
} else {
|
||
TemplateSiteConfig::insert([
|
||
'tid' => $tid,
|
||
'key' => $configKey,
|
||
'value' => $value,
|
||
'create_time' => $now,
|
||
'update_time' => $now
|
||
]);
|
||
}
|
||
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|