mirror of
https://gitee.com/ulthon/ulthon_information.git
synced 2026-07-01 14:42:48 +08:00
T3: Add cover_text textarea to post edit form T4: Update Post controller - content copy + cover_text passing T5: Refactor JS engine - remove old APIs, add forced breaks, page numbers, per-page alignment T8: Add cover_text to API default_fields, apidoc (4 places), AGENTS.md
338 lines
11 KiB
PHP
338 lines
11 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\api\controller;
|
||
|
||
use app\BaseController;
|
||
use app\model\Post;
|
||
use app\model\PostCategory;
|
||
use app\model\PostTag;
|
||
use League\CommonMark\Environment\Environment;
|
||
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
|
||
use League\CommonMark\Extension\Table\TableExtension;
|
||
use League\CommonMark\MarkdownConverter;
|
||
use think\Request;
|
||
|
||
class Articles extends BaseController
|
||
{
|
||
protected $middleware = [\app\middleware\ApiKeyAuth::class];
|
||
|
||
/**
|
||
* 文章列表
|
||
*/
|
||
public function index(Request $request)
|
||
{
|
||
$page = $request->param('page', 1, 'intval');
|
||
$limit = $request->param('limit', 15, 'intval');
|
||
$type = $request->param('type', '');
|
||
$category_id = $request->param('category_id', 0, 'intval');
|
||
$keyword = $request->param('keyword', '');
|
||
|
||
$model_list = Post::with(['categorys.category', 'tags.tag'])
|
||
->order('id desc');
|
||
|
||
if (!empty($type)) {
|
||
$model_list = $model_list->where('type', $type);
|
||
}
|
||
if (!empty($category_id)) {
|
||
$model_list = $model_list->where('category_id', $category_id);
|
||
}
|
||
if (!empty($keyword)) {
|
||
$model_list = $model_list->where('title', 'like', "%{$keyword}%");
|
||
}
|
||
|
||
$list = $model_list->paginate([
|
||
'page' => $page,
|
||
'list_rows' => $limit,
|
||
]);
|
||
|
||
return json_message([
|
||
'list' => $list->items(),
|
||
'total' => $list->total(),
|
||
'page' => $page,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 文章详情
|
||
*/
|
||
public function read(Request $request)
|
||
{
|
||
$id = $request->param('id', 0, 'intval');
|
||
if ($id <= 0) {
|
||
return json_message('参数错误');
|
||
}
|
||
|
||
$post = Post::with(['categorys.category', 'tags.tag'])->find($id);
|
||
if (empty($post)) {
|
||
return json_message('文章不存在');
|
||
}
|
||
|
||
return json_message(['post' => $post]);
|
||
}
|
||
|
||
/**
|
||
* 创建文章
|
||
*/
|
||
public function save(Request $request)
|
||
{
|
||
if ($request->can_write_own != 1) {
|
||
return json_message('无权操作', 403);
|
||
}
|
||
|
||
$post_data = $request->post();
|
||
|
||
if (empty($post_data['title'])) {
|
||
return json_message('标题必填');
|
||
}
|
||
|
||
$categorys = [];
|
||
$tags = [];
|
||
if (isset($post_data['categorys'])) {
|
||
$categorys = $post_data['categorys'];
|
||
unset($post_data['categorys']);
|
||
}
|
||
if (isset($post_data['tags'])) {
|
||
$tags = $post_data['tags'];
|
||
unset($post_data['tags']);
|
||
}
|
||
|
||
// 禁止客户端设置的时间字段
|
||
unset($post_data['create_time'], $post_data['update_time']);
|
||
|
||
$post_data['uid'] = uniqid();
|
||
$post_data['source'] = 'api';
|
||
$post_data['create_time'] = time();
|
||
$post_data['update_time'] = time();
|
||
|
||
if (!isset($post_data['type'])) {
|
||
$post_data['type'] = '3';
|
||
}
|
||
if (!isset($post_data['status'])) {
|
||
$post_data['status'] = 0;
|
||
}
|
||
if (!isset($post_data['content_type'])) {
|
||
$post_data['content_type'] = 'html';
|
||
}
|
||
|
||
// 统一处理 publish_time:空字符串按未传处理;发布状态未传时自动补当前时间
|
||
if (array_key_exists('publish_time', $post_data)) {
|
||
if (is_string($post_data['publish_time'])) {
|
||
$post_data['publish_time'] = trim($post_data['publish_time']);
|
||
}
|
||
if ($post_data['publish_time'] === '') {
|
||
unset($post_data['publish_time']);
|
||
}
|
||
}
|
||
if (isset($post_data['publish_time'])) {
|
||
if (!preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $post_data['publish_time'])) {
|
||
return json_message('publish_time 格式错误,应为 Y-m-d H:i:s,如 2024-01-15 14:30:00');
|
||
}
|
||
} elseif ((string) $post_data['status'] === '1') {
|
||
$post_data['publish_time'] = date('Y-m-d H:i:s');
|
||
}
|
||
|
||
// 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'])) {
|
||
$environment = Environment::createCommonMarkEnvironment();
|
||
$environment->addExtension(new TableExtension());
|
||
$converter = new MarkdownConverter($environment);
|
||
$post_data['content_html'] = $converter->convert($post_data['content'])->getContent();
|
||
}
|
||
|
||
// 提供字段默认值,避免数据库严格模式报错
|
||
$default_fields = [
|
||
'jump_to_btn_title' => '',
|
||
'jump_to_url' => '',
|
||
'jump_to_url_status' => 0,
|
||
'poster' => '',
|
||
'desc' => '',
|
||
'cover_text' => '',
|
||
'author_name' => '',
|
||
'hits' => 0,
|
||
'is_top' => 0,
|
||
'sort' => 0,
|
||
'files' => '',
|
||
'pictures' => '',
|
||
'tpl_name' => '',
|
||
];
|
||
foreach ($default_fields as $field => $default) {
|
||
if (!isset($post_data[$field])) {
|
||
$post_data[$field] = $default;
|
||
}
|
||
}
|
||
|
||
$model_post = Post::create($post_data);
|
||
|
||
foreach ($categorys as $category) {
|
||
PostCategory::create([
|
||
'post_id' => $model_post->id,
|
||
'category_id' => $category,
|
||
]);
|
||
}
|
||
foreach ($tags as $tag) {
|
||
PostTag::create([
|
||
'post_id' => $model_post->id,
|
||
'tag_id' => $tag,
|
||
]);
|
||
}
|
||
|
||
return json_message(['id' => $model_post->id, 'uid' => $model_post->uid], 0, '创建成功');
|
||
}
|
||
|
||
/**
|
||
* 编辑文章
|
||
*/
|
||
public function update(Request $request)
|
||
{
|
||
$id = $request->param('id', 0, 'intval');
|
||
if ($id <= 0) {
|
||
return json_message('参数错误');
|
||
}
|
||
|
||
$model_post = Post::find($id);
|
||
if (empty($model_post)) {
|
||
return json_message('文章不存在');
|
||
}
|
||
|
||
$source = $model_post->getData('source');
|
||
if ($source === 'api') {
|
||
if ($request->can_write_own != 1) {
|
||
return json_message('无权操作', 403);
|
||
}
|
||
} else {
|
||
if ($request->can_write_other != 1) {
|
||
return json_message('无权操作', 403);
|
||
}
|
||
}
|
||
|
||
$post_data = $request->post();
|
||
unset($post_data['id']);
|
||
|
||
// 禁止客户端设置的时间字段
|
||
unset($post_data['create_time'], $post_data['update_time']);
|
||
|
||
// publish_time 处理:未传则不更新;仅草稿首次发布且原值为 0 时自动补当前时间
|
||
$has_publish_time = array_key_exists('publish_time', $post_data);
|
||
if ($has_publish_time && is_string($post_data['publish_time'])) {
|
||
$post_data['publish_time'] = trim($post_data['publish_time']);
|
||
}
|
||
if ($has_publish_time && $post_data['publish_time'] === '') {
|
||
unset($post_data['publish_time']);
|
||
$has_publish_time = false;
|
||
}
|
||
if ($has_publish_time) {
|
||
if (!preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $post_data['publish_time'])) {
|
||
return json_message('publish_time 格式错误,应为 Y-m-d H:i:s,如 2024-01-15 14:30:00');
|
||
}
|
||
} elseif (
|
||
array_key_exists('status', $post_data)
|
||
&& (string) $model_post->getData('status') === '0'
|
||
&& (string) $post_data['status'] === '1'
|
||
&& (int) $model_post->getData('publish_time') === 0
|
||
) {
|
||
$post_data['publish_time'] = date('Y-m-d H:i:s');
|
||
}
|
||
|
||
// categorys diff update
|
||
if (isset($post_data['categorys'])) {
|
||
$categorys = $post_data['categorys'];
|
||
unset($post_data['categorys']);
|
||
|
||
$old_category_list = PostCategory::where('post_id', $id)->select();
|
||
$old_category_ids = array_column($old_category_list->toArray(), 'category_id');
|
||
|
||
// old has, new doesn't -> delete
|
||
foreach ($old_category_list as $model_category) {
|
||
if (!in_array($model_category->category_id, $categorys)) {
|
||
$model_category->delete();
|
||
}
|
||
}
|
||
// new has, old doesn't -> create
|
||
foreach ($categorys as $category) {
|
||
if (!in_array($category, $old_category_ids)) {
|
||
PostCategory::create([
|
||
'post_id' => $id,
|
||
'category_id' => $category,
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
|
||
// tags diff update
|
||
if (isset($post_data['tags'])) {
|
||
$tags = $post_data['tags'];
|
||
unset($post_data['tags']);
|
||
|
||
$old_tag_list = PostTag::where('post_id', $id)->select();
|
||
$old_tag_ids = array_column($old_tag_list->toArray(), 'tag_id');
|
||
|
||
foreach ($old_tag_list as $model_tag) {
|
||
if (!in_array($model_tag->tag_id, $tags)) {
|
||
$model_tag->delete();
|
||
}
|
||
}
|
||
foreach ($tags as $tag) {
|
||
if (!in_array($tag, $old_tag_ids)) {
|
||
PostTag::create([
|
||
'post_id' => $id,
|
||
'tag_id' => $tag,
|
||
]);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Markdown→HTML 自动转换(启用表格扩展)
|
||
if (isset($post_data['content_type']) && $post_data['content_type'] === 'markdown' && !empty($post_data['content'])) {
|
||
$environment = Environment::createCommonMarkEnvironment();
|
||
$environment->addExtension(new TableExtension());
|
||
$converter = new MarkdownConverter($environment);
|
||
$post_data['content_html'] = $converter->convert($post_data['content'])->getContent();
|
||
}
|
||
|
||
$model_post->save($post_data);
|
||
|
||
return json_message([], 0, '更新成功');
|
||
}
|
||
|
||
/**
|
||
* 删除文章
|
||
*/
|
||
public function delete(Request $request)
|
||
{
|
||
$id = $request->param('id', 0, 'intval');
|
||
if ($id <= 0) {
|
||
return json_message('参数错误');
|
||
}
|
||
|
||
$model_post = Post::find($id);
|
||
if (empty($model_post)) {
|
||
return json_message('文章不存在');
|
||
}
|
||
|
||
$source = $model_post->getData('source');
|
||
if ($source === 'api') {
|
||
if ($request->can_delete < 1) {
|
||
return json_message('无权删除', 403);
|
||
}
|
||
} else {
|
||
if ($request->can_delete != 2) {
|
||
return json_message('无权删除', 403);
|
||
}
|
||
}
|
||
|
||
$model_post->delete();
|
||
PostCategory::where('post_id', $id)->delete();
|
||
PostTag::where('post_id', $id)->delete();
|
||
|
||
return json_message([], 0, '删除成功');
|
||
}
|
||
}
|