visuddhinanda 9 месяцев назад
Родитель
Сommit
152d175096

+ 28 - 0
api-v8/app/Providers/TemplateServiceProvider.php

@@ -0,0 +1,28 @@
+<?php
+
+// ================== 服务提供者 ==================
+
+namespace App\Providers;
+
+use Illuminate\Support\ServiceProvider;
+use App\Services\Template\TemplateService;
+
+class TemplateServiceProvider extends ServiceProvider
+{
+    public function register(): void
+    {
+        $this->app->singleton(TemplateService::class, function ($app) {
+            return new TemplateService(
+                config('template.cache_enabled', true),
+                config('template.cache_ttl', 3600)
+            );
+        });
+    }
+
+    public function boot(): void
+    {
+        $this->publishes([
+            __DIR__ . '/../config/template.php' => config_path('template.php'),
+        ], 'template-config');
+    }
+}

+ 13 - 0
api-v8/app/Services/Template/ContentNode.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Services\Template;
+
+
+abstract class ContentNode
+{
+    public string $type;
+    public string $content;
+    public array $position = [];
+
+    abstract public function toArray(): array;
+}

+ 15 - 0
api-v8/app/Services/Template/Contracts/ParserInterface.php

@@ -0,0 +1,15 @@
+<?php
+
+// ================== 契约接口 ==================
+
+namespace App\Services\Template\Contracts;
+
+interface ParserInterface
+{
+    public function parse(string $content): \App\Services\Template\ParsedDocument;
+}
+
+interface RendererInterface
+{
+    public function render(\App\Services\Template\ParsedDocument $document): string;
+}

+ 25 - 0
api-v8/app/Services/Template/MarkdownNode.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Services\Template;
+
+
+class MarkdownNode extends ContentNode
+{
+    public string $content;
+
+    public function __construct(string $content, array $position = [])
+    {
+        $this->type = 'markdown';
+        $this->content = $content;
+        $this->position = $position;
+    }
+
+    public function toArray(): array
+    {
+        return [
+            'type' => $this->type,
+            'content' => $this->content,
+            'position' => $this->position
+        ];
+    }
+}

+ 51 - 0
api-v8/app/Services/Template/ParameterResolver.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace App\Services\Template;
+
+
+// ================== 参数解析器 ==================
+
+class ParameterResolver
+{
+    private TemplateRegistry $registry;
+
+    public function __construct(TemplateRegistry $registry)
+    {
+        $this->registry = $registry;
+    }
+
+    public function resolveParameters(string $templateName, array $rawParams): array
+    {
+        $template = $this->registry->getTemplate($templateName);
+        if (!$template) {
+            return $rawParams;
+        }
+
+        $resolved = [];
+        $paramMapping = $template['paramMapping'] ?? [];
+        $defaultValues = $template['defaultValues'] ?? [];
+
+        // 处理位置参数
+        $positionIndex = 0;
+        foreach ($rawParams as $key => $value) {
+            if (is_numeric($key)) {
+                // 位置参数
+                $paramName = $paramMapping[$positionIndex] ?? $positionIndex;
+                $resolved[$paramName] = $value;
+                $positionIndex++;
+            } else {
+                // 命名参数
+                $resolved[$key] = $value;
+            }
+        }
+
+        // 应用默认值
+        foreach ($defaultValues as $key => $value) {
+            if (!isset($resolved[$key])) {
+                $resolved[$key] = $value;
+            }
+        }
+
+        return $resolved;
+    }
+}

+ 21 - 0
api-v8/app/Services/Template/ParsedDocument.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App\Services\Template;
+
+use App\Services\Template\Contracts\RendererInterface;
+use App\Services\Template\Contracts\ParserInterface;
+
+// ================== 数据结构 ==================
+
+class ParsedDocument
+{
+    public string $type = 'document';
+    public array $content = [];
+    public array $meta = [];
+
+    public function __construct(array $content = [], array $meta = [])
+    {
+        $this->content = $content;
+        $this->meta = $meta;
+    }
+}

+ 141 - 0
api-v8/app/Services/Template/Renderers/HtmlRenderer.php

@@ -0,0 +1,141 @@
+<?php
+
+namespace App\Services\Template\Renderers;
+
+use Illuminate\Support\Str;
+
+use App\Services\Template\Contracts\RendererInterface;
+use App\Services\Template\ParsedDocument;
+use App\Services\Template\ContentNode;
+use App\Services\Template\TextNode;
+use App\Services\Template\MarkdownNode;
+use App\Services\Template\TemplateNode;
+
+
+
+// ================== HTML 渲染器 ==================
+
+class HtmlRenderer implements RendererInterface
+{
+    private array $templateMappings = [];
+
+    public function __construct()
+    {
+        $this->initializeTemplateMappings();
+    }
+
+    private function initializeTemplateMappings(): void
+    {
+        $this->templateMappings = [
+            'note' => function ($params) {
+                $type = $params['type'] ?? 'info';
+                $text = $params['text'] ?? '';
+                $title = $params['title'] ?? '';
+
+                $typeClass = match ($type) {
+                    'warning' => 'alert-warning',
+                    'error' => 'alert-danger',
+                    'success' => 'alert-success',
+                    default => 'alert-info'
+                };
+
+                $titleHtml = $title ? "<h6 class='alert-heading'>$title</h6>" : '';
+                return "<div class='alert $typeClass' role='alert'>$titleHtml$text</div>";
+            },
+
+            'info' => function ($params) {
+                $content = $params['content'] ?? '';
+                $title = $params['title'] ?? '';
+
+                $titleHtml = $title ? "<h6 class='alert-heading'>$title</h6>" : '';
+                return "<div class='alert alert-info' role='alert'>$titleHtml$content</div>";
+            },
+
+            'warning' => function ($params) {
+                $message = $params['message'] ?? '';
+                $title = $params['title'] ?? '';
+
+                $titleHtml = $title ? "<h6 class='alert-heading'>$title</h6>" : '';
+                return "<div class='alert alert-warning' role='alert'>$titleHtml$message</div>";
+            }
+        ];
+    }
+
+    public function render(ParsedDocument $document): string
+    {
+        $html = '';
+
+        foreach ($document->content as $node) {
+            $html .= $this->renderNode($node);
+        }
+
+        return Str::markdown($html);
+    }
+
+    private function renderNode(ContentNode $node): string
+    {
+        switch ($node->type) {
+            case 'text':
+                return $this->renderText($node);
+            case 'markdown':
+                return $this->renderMarkdown($node);
+            case 'template':
+                return $this->renderTemplate($node);
+            default:
+                return '';
+        }
+    }
+
+    public function renderText(TextNode $text): string
+    {
+        return htmlspecialchars($text->content, ENT_QUOTES, 'UTF-8');
+    }
+
+    private function renderMarkdown(MarkdownNode $markdown): string
+    {
+        return Str::markdown($markdown->content);
+        // 简单的 Markdown 渲染,实际项目中可以使用 CommonMark 等库
+        $html = $markdown->content;
+
+        // 处理粗体
+        $html = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $html);
+        $html = preg_replace('/\*(.*?)\*/', '<em>$1</em>', $html);
+
+        // 处理链接
+        $html = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '<a href="$2">$1</a>', $html);
+
+        return $html;
+    }
+
+    public function renderTemplate(TemplateNode $template): string
+    {
+        if (!isset($this->templateMappings[$template->name])) {
+            // 未知模板,返回原始内容
+            return htmlspecialchars($template->raw, ENT_QUOTES, 'UTF-8');
+        }
+
+        $renderer = $this->templateMappings[$template->name];
+
+        // 处理参数中的嵌套内容
+        $processedParams = [];
+        foreach ($template->parameters as $key => $value) {
+            if (is_array($value)) {
+                // 嵌套内容,递归渲染
+                $nestedHtml = '';
+                foreach ($value as $childNode) {
+                    $nestedHtml .= $this->renderNode($childNode);
+                }
+                $processedParams[$key] = $nestedHtml;
+            } else {
+                $processedParams[$key] = $value;
+            }
+        }
+
+        return $renderer($processedParams);
+    }
+
+    public function registerTemplate(string $name, callable $renderer): void
+    {
+        $this->templateMappings[$name] = $renderer;
+    }
+}

+ 36 - 0
api-v8/app/Services/Template/Renderers/JsonRenderer.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Services\Template\Renderers;
+
+use App\Services\Template\Contracts\RendererInterface;
+use App\Services\Template\ParsedDocument;
+use App\Services\Template\ContentNode;
+use App\Services\Template\TextNode;
+use App\Services\Template\MarkdownNode;
+use App\Services\Template\TemplateNode;
+
+// ================== JSON 渲染器 ==================
+
+class JsonRenderer implements RendererInterface
+{
+    public function render(ParsedDocument $document): string
+    {
+        $data = [
+            'type' => $document->type,
+            'content' => array_map(fn($node) => $node->toArray(), $document->content),
+            'meta' => $document->meta
+        ];
+
+        return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
+    }
+
+    public function renderTemplate(TemplateNode $template): string
+    {
+        return json_encode($template->toArray(), JSON_UNESCAPED_UNICODE);
+    }
+
+    public function renderText(TextNode $text): string
+    {
+        return json_encode($text->toArray(), JSON_UNESCAPED_UNICODE);
+    }
+}

+ 70 - 0
api-v8/app/Services/Template/Renderers/MarkdownRenderer.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Services\Template\Renderers;
+
+use App\Services\Template\Contracts\RendererInterface;
+use App\Services\Template\ParsedDocument;
+use App\Services\Template\ContentNode;
+use App\Services\Template\TextNode;
+use App\Services\Template\MarkdownNode;
+use App\Services\Template\TemplateNode;
+
+
+
+
+// ================== Markdown 渲染器 ==================
+
+class MarkdownRenderer implements RendererInterface
+{
+    public function render(ParsedDocument $document): string
+    {
+        $markdown = '';
+
+        foreach ($document->content as $node) {
+            $markdown .= $this->renderNode($node);
+        }
+
+        return $markdown;
+    }
+
+    private function renderNode(ContentNode $node): string
+    {
+        switch ($node->type) {
+            case 'text':
+                return $this->renderText($node);
+            case 'markdown':
+                return $node->content;
+            case 'template':
+                return $this->renderTemplate($node);
+            default:
+                return '';
+        }
+    }
+
+    public function renderText(TextNode $text): string
+    {
+        return $text->content;
+    }
+
+    public function renderTemplate(TemplateNode $template): string
+    {
+        // 将模板转换回 Markdown 格式
+        $params = [];
+
+        foreach ($template->parameters as $key => $value) {
+            if (is_array($value)) {
+                // 嵌套内容,递归渲染
+                $nestedMarkdown = '';
+                foreach ($value as $childNode) {
+                    $nestedMarkdown .= $this->renderNode($childNode);
+                }
+                $params[] = "$key=$nestedMarkdown";
+            } else {
+                $params[] = is_numeric($key) ? $value : "$key=$value";
+            }
+        }
+
+        $paramString = implode('|', $params);
+        return "{{" . $template->name . ($paramString ? "|$paramString" : "") . "}}";
+    }
+}

+ 32 - 0
api-v8/app/Services/Template/Renderers/RendererFactory.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Services\Template\Renderers;
+
+use App\Services\Template\Contracts\RendererInterface;
+
+// ================== 渲染器工厂 ==================
+
+class RendererFactory
+{
+    private static array $renderers = [];
+
+    public static function create(string $format): RendererInterface
+    {
+        if (!isset(self::$renderers[$format])) {
+            self::$renderers[$format] = match ($format) {
+                'json' => new JsonRenderer(),
+                'html' => new HtmlRenderer(),
+                'markdown' => new MarkdownRenderer(),
+                'text' => new TextRenderer(),
+                default => throw new \InvalidArgumentException("Unsupported format: $format")
+            };
+        }
+
+        return self::$renderers[$format];
+    }
+
+    public static function getSupportedFormats(): array
+    {
+        return ['json', 'html', 'markdown', 'text'];
+    }
+}

+ 150 - 0
api-v8/app/Services/Template/Renderers/TextRenderer.php

@@ -0,0 +1,150 @@
+<?php
+
+namespace App\Services\Template\Renderers;
+
+use App\Services\Template\Contracts\RendererInterface;
+use App\Services\Template\ParsedDocument;
+use App\Services\Template\ContentNode;
+use App\Services\Template\TextNode;
+use App\Services\Template\MarkdownNode;
+use App\Services\Template\TemplateNode;
+
+
+
+// ================== 纯文本渲染器 ==================
+
+class TextRenderer implements RendererInterface
+{
+    private array $templateTexts = [];
+
+    public function __construct()
+    {
+        $this->initializeTemplateTexts();
+    }
+
+    private function initializeTemplateTexts(): void
+    {
+        $this->templateTexts = [
+            'note' => function ($params) {
+                $type = $params['type'] ?? 'info';
+                $text = $params['text'] ?? '';
+                $title = $params['title'] ?? '';
+
+                $prefix = match ($type) {
+                    'warning' => '[警告]',
+                    'error' => '[错误]',
+                    'success' => '[成功]',
+                    default => '[信息]'
+                };
+
+                return $prefix . ($title ? " $title: " : ' ') . $text;
+            },
+
+            'info' => function ($params) {
+                $content = $params['content'] ?? '';
+                $title = $params['title'] ?? '';
+
+                return '[信息]' . ($title ? " $title: " : ' ') . $content;
+            },
+
+            'warning' => function ($params) {
+                $message = $params['message'] ?? '';
+                $title = $params['title'] ?? '';
+
+                return '[警告]' . ($title ? " $title: " : ' ') . $message;
+            }
+        ];
+    }
+
+    public function render(ParsedDocument $document): string
+    {
+        $text = '';
+
+        foreach ($document->content as $node) {
+            $text .= $this->renderNode($node);
+        }
+
+        return $text;
+    }
+
+    private function renderNode(ContentNode $node): string
+    {
+        switch ($node->type) {
+            case 'text':
+                return $this->renderText($node);
+            case 'markdown':
+                return $this->renderMarkdownAsText($node);
+            case 'template':
+                return $this->renderTemplate($node);
+            default:
+                return '';
+        }
+    }
+
+    public function renderText(TextNode $text): string
+    {
+        return $text->content;
+    }
+
+    private function renderMarkdownAsText(MarkdownNode $markdown): string
+    {
+        // 移除 Markdown 标记,返回纯文本
+        $text = $markdown->content;
+
+        // 移除粗体和斜体标记
+        $text = preg_replace('/\*\*(.*?)\*\*/', '$1', $text);
+        $text = preg_replace('/\*(.*?)\*/', '$1', $text);
+
+        // 移除链接标记,保留链接文本
+        $text = preg_replace('/\[([^\]]+)\]\([^)]+\)/', '$1', $text);
+
+        return $text;
+    }
+
+    public function renderTemplate(TemplateNode $template): string
+    {
+        if (!isset($this->templateTexts[$template->name])) {
+            // 未知模板,返回简化的文本表示
+            $text = $template->name;
+            if (!empty($template->parameters)) {
+                $paramTexts = [];
+                foreach ($template->parameters as $key => $value) {
+                    if (is_array($value)) {
+                        $nestedText = '';
+                        foreach ($value as $childNode) {
+                            $nestedText .= $this->renderNode($childNode);
+                        }
+                        $paramTexts[] = is_numeric($key) ? $nestedText : "$key: $nestedText";
+                    } else {
+                        $paramTexts[] = is_numeric($key) ? $value : "$key: $value";
+                    }
+                }
+                $text .= '(' . implode(', ', $paramTexts) . ')';
+            }
+            return $text;
+        }
+
+        $renderer = $this->templateTexts[$template->name];
+
+        // 处理参数中的嵌套内容
+        $processedParams = [];
+        foreach ($template->parameters as $key => $value) {
+            if (is_array($value)) {
+                $nestedText = '';
+                foreach ($value as $childNode) {
+                    $nestedText .= $this->renderNode($childNode);
+                }
+                $processedParams[$key] = $nestedText;
+            } else {
+                $processedParams[$key] = $value;
+            }
+        }
+
+        return $renderer($processedParams);
+    }
+
+    public function registerTemplate(string $name, callable $renderer): void
+    {
+        $this->templateTexts[$name] = $renderer;
+    }
+}

+ 34 - 0
api-v8/app/Services/Template/TemplateNode.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace App\Services\Template;
+
+
+class TemplateNode extends ContentNode
+{
+    public string $name;
+    public array $parameters = [];
+    public array $children = [];
+    public string $raw = '';
+
+    public function __construct(string $name, array $parameters = [], array $children = [], string $raw = '', array $position = [])
+    {
+        $this->type = 'template';
+        $this->name = $name;
+        $this->parameters = $parameters;
+        $this->children = $children;
+        $this->raw = $raw;
+        $this->position = $position;
+    }
+
+    public function toArray(): array
+    {
+        return [
+            'type' => $this->type,
+            'name' => $this->name,
+            'parameters' => $this->parameters,
+            'children' => array_map(fn($child) => $child->toArray(), $this->children),
+            'raw' => $this->raw,
+            'position' => $this->position
+        ];
+    }
+}

+ 159 - 0
api-v8/app/Services/Template/TemplateParser.php

@@ -0,0 +1,159 @@
+<?php
+
+// ================== 主解析器 ==================
+
+namespace App\Services\Template;
+
+use App\Services\Template\Contracts\RendererInterface;
+use App\Services\Template\Contracts\ParserInterface;
+
+class TemplateParser implements ParserInterface
+{
+    private TemplateRegistry $registry;
+    private ParameterResolver $parameterResolver;
+
+    public function __construct()
+    {
+        $this->registry = new TemplateRegistry();
+        $this->parameterResolver = new ParameterResolver($this->registry);
+    }
+
+    public function parse(string $content): ParsedDocument
+    {
+        $tokenizer = new TemplateTokenizer($content);
+        $tokens = $tokenizer->tokenize();
+
+        $nodes = [];
+        $templatesUsed = [];
+
+        foreach ($tokens as $token) {
+            if ($token['type'] === 'text') {
+                $nodes[] = new TextNode($token['content'], $token['position']);
+            } elseif ($token['type'] === 'template') {
+                $templateNode = $this->parseTemplateContent($token);
+                if ($templateNode) {
+                    $nodes[] = $templateNode;
+                    $templatesUsed[] = $templateNode->name;
+                } else {
+                    // 解析失败,当作文本处理
+                    $nodes[] = new TextNode($token['raw'], $token['position']);
+                }
+            }
+        }
+
+        return new ParsedDocument($nodes, [
+            'templates_used' => array_unique($templatesUsed),
+            'total_templates' => count($templatesUsed)
+        ]);
+    }
+
+    private function parseTemplateContent(array $token): ?TemplateNode
+    {
+        $content = $token['content'];
+        $parts = $this->splitTemplateParts($content);
+
+        if (empty($parts)) {
+            return null;
+        }
+
+        $templateName = array_shift($parts);
+        $rawParams = $this->parseParameters($parts);
+
+        // 递归解析参数中的嵌套模板
+        $processedParams = [];
+        foreach ($rawParams as $key => $value) {
+            if (strpos($value, '{{') !== false) {
+                // 参数值包含模板,递归解析
+                $subDocument = $this->parse($value);
+                $processedParams[$key] = $subDocument->content;
+            } else {
+                $processedParams[$key] = $value;
+            }
+        }
+
+        $resolvedParams = $this->parameterResolver->resolveParameters($templateName, $processedParams);
+
+        return new TemplateNode(
+            $templateName,
+            $resolvedParams,
+            [],
+            $token['raw'],
+            $token['position']
+        );
+    }
+
+    private function splitTemplateParts(string $content): array
+    {
+        $parts = [];
+        $current = '';
+        $braceLevel = 0;
+        $inQuotes = false;
+        $quoteChar = '';
+
+        for ($i = 0; $i < strlen($content); $i++) {
+            $char = $content[$i];
+            $nextChar = $i + 1 < strlen($content) ? $content[$i + 1] : '';
+
+            if (!$inQuotes && ($char === '"' || $char === "'")) {
+                $inQuotes = true;
+                $quoteChar = $char;
+                $current .= $char;
+            } elseif ($inQuotes && $char === $quoteChar) {
+                $inQuotes = false;
+                $quoteChar = '';
+                $current .= $char;
+            } elseif (!$inQuotes && $char === '{' && $nextChar === '{') {
+                $braceLevel++;
+                $current .= '{{';
+                $i++; // 跳过下一个字符
+            } elseif (!$inQuotes && $char === '}' && $nextChar === '}') {
+                $braceLevel--;
+                $current .= '}}';
+                $i++; // 跳过下一个字符
+            } elseif (!$inQuotes && $char === '|' && $braceLevel === 0) {
+                $parts[] = trim($current);
+                $current = '';
+            } else {
+                $current .= $char;
+            }
+        }
+
+        if ($current !== '') {
+            $parts[] = trim($current);
+        }
+
+        return $parts;
+    }
+
+    private function parseParameters(array $parts): array
+    {
+        $params = [];
+        $positionalIndex = 0;
+
+        foreach ($parts as $part) {
+            if (strpos($part, '=') !== false && !$this->isInNestedTemplate($part)) {
+                // 命名参数
+                [$key, $value] = explode('=', $part, 2);
+                $params[trim($key)] = trim($value);
+            } else {
+                // 位置参数
+                $params[$positionalIndex] = trim($part);
+                $positionalIndex++;
+            }
+        }
+
+        return $params;
+    }
+
+    private function isInNestedTemplate(string $content): bool
+    {
+        $openBraces = substr_count($content, '{{');
+        $closeBraces = substr_count($content, '}}');
+        return $openBraces > 0 && $openBraces >= $closeBraces;
+    }
+
+    public function getRegistry(): TemplateRegistry
+    {
+        return $this->registry;
+    }
+}

+ 83 - 0
api-v8/app/Services/Template/TemplateRegistry.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Services\Template;
+
+
+// ================== 模板注册表 ==================
+
+class TemplateRegistry
+{
+    private array $templates = [];
+
+    public function __construct()
+    {
+        $this->loadDefaultTemplates();
+    }
+
+    private function loadDefaultTemplates(): void
+    {
+        $this->templates = [
+            'note' => [
+                'defaultParams' => ['text', 'type'],
+                'paramMapping' => [
+                    '0' => 'text',
+                    '1' => 'type'
+                ],
+                'defaultValues' => [
+                    'type' => 'info'
+                ],
+                'validation' => [
+                    'required' => ['text'],
+                    'optional' => ['type', 'title']
+                ]
+            ],
+            'info' => [
+                'defaultParams' => ['content'],
+                'paramMapping' => [
+                    '0' => 'content'
+                ],
+                'defaultValues' => [],
+                'validation' => [
+                    'required' => ['content'],
+                    'optional' => ['title']
+                ]
+            ],
+            'warning' => [
+                'defaultParams' => ['message'],
+                'paramMapping' => [
+                    '0' => 'message'
+                ],
+                'defaultValues' => [],
+                'validation' => [
+                    'required' => ['message'],
+                    'optional' => ['title']
+                ]
+            ]
+        ];
+    }
+
+    public function registerTemplate(string $name, array $config): void
+    {
+        $this->templates[$name] = $config;
+    }
+
+    public function getTemplate(string $name): ?array
+    {
+        return $this->templates[$name] ?? null;
+    }
+
+    public function hasTemplate(string $name): bool
+    {
+        return isset($this->templates[$name]);
+    }
+
+    public function getParamMapping(string $templateName): array
+    {
+        return $this->templates[$templateName]['paramMapping'] ?? [];
+    }
+
+    public function getDefaultValues(string $templateName): array
+    {
+        return $this->templates[$templateName]['defaultValues'] ?? [];
+    }
+}

+ 209 - 0
api-v8/app/Services/Template/TemplateService.php

@@ -0,0 +1,209 @@
+<?php
+
+namespace App\Services\Template;
+
+use App\Services\Template\TemplateParser;
+use App\Services\Template\Renderers\RendererFactory;
+use App\Services\Template\TemplateRegistry;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Log;
+
+class TemplateService
+{
+    private TemplateParser $parser;
+    private bool $cacheEnabled;
+    private int $cacheTtl;
+
+    public function __construct(bool $cacheEnabled = true, int $cacheTtl = 3600)
+    {
+        $this->parser = new TemplateParser();
+        $this->cacheEnabled = $cacheEnabled;
+        $this->cacheTtl = $cacheTtl;
+    }
+
+    /**
+     * 解析并渲染内容
+     */
+    public function parseAndRender(string $content, string $format = 'json'): array
+    {
+        try {
+            // 生成缓存键
+            $cacheKey = $this->generateCacheKey($content, $format);
+
+            if ($this->cacheEnabled && Cache::has($cacheKey)) {
+                return Cache::get($cacheKey);
+            }
+
+            // 解析内容
+            $document = $this->parser->parse($content);
+
+            // 渲染内容
+            $renderer = RendererFactory::create($format);
+            $renderedContent = $renderer->render($document);
+
+            $result = [
+                'data' => $format === 'json' ? json_decode($renderedContent, true) : $renderedContent,
+                'meta' => $document->meta
+            ];
+
+            // 缓存结果
+            if ($this->cacheEnabled) {
+                Cache::put($cacheKey, $result, $this->cacheTtl);
+            }
+
+            return $result;
+        } catch (\Exception $e) {
+            Log::error('Template parsing failed', [
+                'content' => substr($content, 0, 200),
+                'format' => $format,
+                'error' => $e->getMessage()
+            ]);
+
+            throw $e;
+        }
+    }
+
+    /**
+     * 仅解析,不渲染
+     */
+    public function parse(string $content): ParsedDocument
+    {
+        return $this->parser->parse($content);
+    }
+
+    /**
+     * 仅渲染已解析的文档
+     */
+    public function render(ParsedDocument $document, string $format): string
+    {
+        $renderer = RendererFactory::create($format);
+        return $renderer->render($document);
+    }
+
+    /**
+     * 注册新模板
+     */
+    public function registerTemplate(string $name, array $config): void
+    {
+        $registry = $this->parser->getRegistry();
+        $registry->registerTemplate($name, $config);
+
+        // 清除相关缓存
+        if ($this->cacheEnabled) {
+            $this->clearTemplateCache($name);
+        }
+    }
+
+    /**
+     * 获取可用模板列表
+     */
+    public function getAvailableTemplates(): array
+    {
+        $registry = $this->parser->getRegistry();
+        $templates = [];
+
+        // 这里需要添加一个方法来获取所有模板
+        // 为了示例,我们返回一些基本信息
+        $basicTemplates = ['note', 'info', 'warning'];
+
+        foreach ($basicTemplates as $templateName) {
+            $template = $registry->getTemplate($templateName);
+            if ($template) {
+                $templates[$templateName] = [
+                    'name' => $templateName,
+                    'config' => $template,
+                    'example' => $this->generateTemplateExample($templateName, $template)
+                ];
+            }
+        }
+
+        return $templates;
+    }
+
+    /**
+     * 生成模板使用示例
+     */
+    private function generateTemplateExample(string $name, array $config): string
+    {
+        $params = [];
+        $defaultParams = $config['defaultParams'] ?? [];
+
+        foreach ($defaultParams as $index => $paramName) {
+            $params[] = $paramName . '=示例值' . ($index + 1);
+        }
+
+        return '{{' . $name . '|' . implode('|', $params) . '}}';
+    }
+
+    /**
+     * 生成缓存键
+     */
+    private function generateCacheKey(string $content, string $format): string
+    {
+        return 'template_' . $format . '_' . md5($content);
+    }
+
+    /**
+     * 清除模板相关缓存
+     */
+    private function clearTemplateCache(string $templateName): void
+    {
+        // 这里可以实现更精确的缓存清除逻辑
+        Cache::flush(); // 简单起见,清除所有缓存
+    }
+
+    /**
+     * 批量处理内容
+     */
+    public function batchProcess(array $contents, string $format = 'json'): array
+    {
+        $results = [];
+
+        foreach ($contents as $index => $content) {
+            try {
+                $results[$index] = $this->parseAndRender($content, $format);
+            } catch (\Exception $e) {
+                $results[$index] = [
+                    'success' => false,
+                    'error' => $e->getMessage()
+                ];
+            }
+        }
+
+        return $results;
+    }
+
+    /**
+     * 验证模板语法
+     */
+    public function validateSyntax(string $content): array
+    {
+        $errors = [];
+
+        try {
+            $document = $this->parser->parse($content);
+
+            // 检查是否有未知模板
+            foreach ($document->content as $node) {
+                if ($node instanceof TemplateNode) {
+                    $registry = $this->parser->getRegistry();
+                    if (!$registry->hasTemplate($node->name)) {
+                        $errors[] = [
+                            'type' => 'unknown_template',
+                            'template' => $node->name,
+                            'position' => $node->position,
+                            'message' => "Unknown template: {$node->name}"
+                        ];
+                    }
+                }
+            }
+        } catch (\Exception $e) {
+            $errors[] = [
+                'type' => 'parse_error',
+                'message' => $e->getMessage()
+            ];
+        }
+
+        return $errors;
+    }
+}

+ 114 - 0
api-v8/app/Services/Template/TemplateTokenizer.php

@@ -0,0 +1,114 @@
+<?php
+
+namespace App\Services\Template;
+
+
+// ================== 词法分析器 ==================
+
+class TemplateTokenizer
+{
+    private string $content;
+    private int $position = 0;
+    private int $length;
+
+    public function __construct(string $content)
+    {
+        $this->content = $content;
+        $this->length = strlen($content);
+    }
+
+    public function tokenize(): array
+    {
+        $tokens = [];
+        $this->position = 0;
+
+        while ($this->position < $this->length) {
+            $token = $this->nextToken();
+            if ($token) {
+                $tokens[] = $token;
+            }
+        }
+
+        return $tokens;
+    }
+
+    private function nextToken(): ?array
+    {
+        // 寻找模板开始标记
+        $templateStart = strpos($this->content, '{{', $this->position);
+
+        if ($templateStart === false) {
+            // 没有更多模板,返回剩余文本
+            if ($this->position < $this->length) {
+                $text = substr($this->content, $this->position);
+                $this->position = $this->length;
+                return [
+                    'type' => 'text',
+                    'content' => $text,
+                    'position' => ['start' => $this->position - strlen($text), 'end' => $this->position]
+                ];
+            }
+            return null;
+        }
+
+        // 如果模板前有文本
+        if ($templateStart > $this->position) {
+            $text = substr($this->content, $this->position, $templateStart - $this->position);
+            $this->position = $templateStart;
+            return [
+                'type' => 'text',
+                'content' => $text,
+                'position' => ['start' => $this->position - strlen($text), 'end' => $this->position]
+            ];
+        }
+
+        // 解析模板
+        return $this->parseTemplate();
+    }
+
+    private function parseTemplate(): ?array
+    {
+        $start = $this->position;
+        $this->position += 2; // 跳过 '{{'
+
+        $braceCount = 1;
+        $templateContent = '';
+
+        while ($this->position < $this->length && $braceCount > 0) {
+            $char = $this->content[$this->position];
+            $nextChar = $this->position + 1 < $this->length ? $this->content[$this->position + 1] : '';
+
+            if ($char === '{' && $nextChar === '{') {
+                $braceCount++;
+                $templateContent .= '{{';
+                $this->position += 2;
+            } elseif ($char === '}' && $nextChar === '}') {
+                $braceCount--;
+                if ($braceCount > 0) {
+                    $templateContent .= '}}';
+                }
+                $this->position += 2;
+            } else {
+                $templateContent .= $char;
+                $this->position++;
+            }
+        }
+
+        if ($braceCount > 0) {
+            // 未闭合的模板,当作普通文本处理
+            $this->position = $start + 2;
+            return [
+                'type' => 'text',
+                'content' => '{{',
+                'position' => ['start' => $start, 'end' => $start + 2]
+            ];
+        }
+
+        return [
+            'type' => 'template',
+            'content' => trim($templateContent),
+            'raw' => '{{' . $templateContent . '}}',
+            'position' => ['start' => $start, 'end' => $this->position]
+        ];
+    }
+}

+ 25 - 0
api-v8/app/Services/Template/TextNode.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Services\Template;
+
+
+class TextNode extends ContentNode
+{
+    public string $content;
+
+    public function __construct(string $content, array $position = [])
+    {
+        $this->type = 'text';
+        $this->content = $content;
+        $this->position = $position;
+    }
+
+    public function toArray(): array
+    {
+        return [
+            'type' => $this->type,
+            'content' => $this->content,
+            'position' => $this->position
+        ];
+    }
+}

+ 49 - 0
api-v8/config/template.php

@@ -0,0 +1,49 @@
+<?php
+
+return [
+    /*
+    |--------------------------------------------------------------------------
+    | Template Cache Settings
+    |--------------------------------------------------------------------------
+    */
+    'cache_enabled' => env('TEMPLATE_CACHE_ENABLED', true),
+    'cache_ttl' => env('TEMPLATE_CACHE_TTL', 3600),
+
+    /*
+    |--------------------------------------------------------------------------
+    | Default Templates
+    |--------------------------------------------------------------------------
+    */
+    'default_templates' => [
+        'note' => [
+            'defaultParams' => ['text', 'type'],
+            'paramMapping' => [
+                '0' => 'text',
+                '1' => 'type'
+            ],
+            'defaultValues' => [
+                'type' => 'info'
+            ],
+            'validation' => [
+                'required' => ['text'],
+                'optional' => ['type', 'title']
+            ]
+        ],
+        // 更多模板定义...
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Renderer Settings
+    |--------------------------------------------------------------------------
+    */
+    'supported_formats' => ['json', 'html', 'markdown', 'text'],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Parser Settings
+    |--------------------------------------------------------------------------
+    */
+    'max_nesting_level' => 10,
+    'max_template_size' => 10000, // characters
+];