diff --git a/app/api/controller/Articles.php b/app/api/controller/Articles.php index ccf7677..e454520 100644 --- a/app/api/controller/Articles.php +++ b/app/api/controller/Articles.php @@ -8,6 +8,7 @@ use app\BaseController; use app\model\Post; use app\model\PostCategory; use app\model\PostTag; +use League\CommonMark\CommonMarkConverter; use think\Request; class Articles extends BaseController @@ -105,6 +106,21 @@ class Articles extends BaseController if (!isset($post_data['status'])) { $post_data['status'] = 0; } + if (!isset($post_data['content_type'])) { + $post_data['content_type'] = 'html'; + } + + // content_type 白名单校验 + $allowed_content_types = ['html', 'markdown']; + if (!in_array($post_data['content_type'], $allowed_content_types)) { + return json_message('无效的content_type', 500); + } + + // Markdown→HTML 自动转换 + if ($post_data['content_type'] === 'markdown' && !empty($post_data['content'])) { + $converter = new CommonMarkConverter(['html_input' => 'strip', 'allow_unsafe_links' => false]); + $post_data['content_html'] = $converter->convert($post_data['content']); + } $model_post = Post::create($post_data); @@ -201,6 +217,12 @@ class Articles extends BaseController } } + // Markdown→HTML 自动转换 + if (isset($post_data['content_type']) && $post_data['content_type'] === 'markdown' && !empty($post_data['content'])) { + $converter = new CommonMarkConverter(['html_input' => 'strip', 'allow_unsafe_links' => false]); + $post_data['content_html'] = $converter->convert($post_data['content']); + } + $model_post->save($post_data); return json_message([], 0, '更新成功'); diff --git a/app/model/Post.php b/app/model/Post.php index eac90ed..725d560 100644 --- a/app/model/Post.php +++ b/app/model/Post.php @@ -156,6 +156,30 @@ class Post extends Base return trim($value); } + public function getContentTypeAttr($value) + { + return $value ?: 'html'; + } + + public function setContentTypeAttr($value) + { + if (!in_array($value, ['html', 'markdown'], true)) { + throw new \InvalidArgumentException('Invalid content_type: ' . $value); + } + + return $value; + } + + public function setContentAttr($value) + { + return trim($value); + } + + public function getContentAttr($value) + { + return $value; + } + public function getContentMarkdownAttr() { $content_html = $this->getAttr('content_html'); diff --git a/composer.json b/composer.json index f0a81df..aca6d53 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ "topthink/think-filesystem": "^2.0", "mibe/feedwriter": "^1.1", "matomo/device-detector": "^6.1", - "intervention/image": "^2.7" + "intervention/image": "^2.7", + "league/commonmark": "^2.8" }, "require-dev": { "symfony/var-dumper": "^4.2" diff --git a/database/migrations/20260429180238_add_content_type_to_post.php b/database/migrations/20260429180238_add_content_type_to_post.php new file mode 100644 index 0000000..0bc7934 --- /dev/null +++ b/database/migrations/20260429180238_add_content_type_to_post.php @@ -0,0 +1,36 @@ +table('post')->addColumn( + Column::make('content_type', 'string', ['limit' => 20, 'default' => 'html', 'comment' => '内容类型: html/markdown']) + )->update(); + } +} diff --git a/view/index/api_doc/index.html b/view/index/api_doc/index.html index 47ace2e..a18e6f5 100644 --- a/view/index/api_doc/index.html +++ b/view/index/api_doc/index.html @@ -196,6 +196,7 @@ X-API-Key: {api_key} "id": 1, "title": "文章标题", "content": "文章内容", + "content_type": "html", "type": "1", "status": 1, "source": "api", @@ -238,6 +239,7 @@ X-API-Key: {api_key} "title": "文章标题", "content": "文章内容", "content_html": "<p>HTML内容</p>", + "content_type": "html", "desc": "摘要", "poster": "/uploads/poster.jpg", "type": "1", @@ -268,12 +270,13 @@ X-API-Key: {api_key}