2025-11-07 09:56:20 +08:00
|
|
|
<?php
|
|
|
|
|
namespace app\api\controller;
|
|
|
|
|
|
|
|
|
|
use app\model\Archives as ArchivesModel;
|
2026-04-06 03:10:44 +08:00
|
|
|
use app\model\Content;
|
2025-11-07 09:56:20 +08:00
|
|
|
use support\Request;
|
|
|
|
|
use hg\apidoc\annotation as Apidoc;
|
2026-04-06 03:10:44 +08:00
|
|
|
use support\Jwt\JwtToken;
|
|
|
|
|
use support\think\Db;
|
2025-11-07 09:56:20 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 文章模块
|
|
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
class ArticleController extends BaseController
|
|
|
|
|
{
|
2025-11-07 09:56:20 +08:00
|
|
|
public $noNeedLogin = ['*'];
|
2026-04-06 03:10:44 +08:00
|
|
|
|
|
|
|
|
private const CACHE_PREFIX_READ = 'article_read_';
|
|
|
|
|
private const CACHE_TTL = 86400;
|
|
|
|
|
|
2025-11-07 09:56:20 +08:00
|
|
|
/**
|
|
|
|
|
* 列表
|
2026-03-02 15:59:46 +08:00
|
|
|
* @Apidoc\Method("GET")
|
2026-04-06 03:10:44 +08:00
|
|
|
* @Apidoc\Query("category_id", type="int", require=true, desc="分类ID", default=10)
|
|
|
|
|
* @Apidoc\Query("page", type="int", require=true, desc="页码", default=1)
|
|
|
|
|
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小", default=10)
|
2025-11-07 09:56:20 +08:00
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
public function list()
|
|
|
|
|
{
|
|
|
|
|
$limit = (int)input('limit', 10);
|
|
|
|
|
$category_id = (int)input('category_id', 0);
|
2025-11-07 09:56:20 +08:00
|
|
|
|
2026-04-06 03:10:44 +08:00
|
|
|
$model = ArchivesModel::where('status', 'normal')->where('type', 'article');
|
|
|
|
|
if ($category_id) {
|
|
|
|
|
$model = $model->where('category_id', $category_id);
|
2025-11-07 09:56:20 +08:00
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
$list = $model->order('id', 'desc')->paginate($limit);
|
|
|
|
|
|
|
|
|
|
$user_id = $this->getCurrentUserId();
|
|
|
|
|
$list->each(function ($item) use ($user_id) {
|
|
|
|
|
$item->is_read = $user_id ? $this->getReadStatus($item->id, $user_id) : 0;
|
2025-11-07 09:56:20 +08:00
|
|
|
return $item;
|
|
|
|
|
});
|
2026-04-06 03:10:44 +08:00
|
|
|
|
|
|
|
|
return $this->success(__('successful'), $list->toArray());
|
2025-11-07 09:56:20 +08:00
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
2025-11-07 09:56:20 +08:00
|
|
|
/**
|
|
|
|
|
* faq
|
2026-03-02 15:59:46 +08:00
|
|
|
* @Apidoc\method("GET")
|
2026-04-06 03:10:44 +08:00
|
|
|
* @Apidoc\Query("page", type="int", require=true, desc="页码", default=1)
|
|
|
|
|
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小", default=10)
|
2025-11-07 09:56:20 +08:00
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
public function faq()
|
|
|
|
|
{
|
|
|
|
|
$limit = (int)input('limit', 10);
|
|
|
|
|
$list = ArchivesModel::alias('a')
|
2025-11-07 09:56:20 +08:00
|
|
|
->join('content c', 'a.id = c.id')
|
2026-04-06 03:10:44 +08:00
|
|
|
->where('a.status', 'normal')
|
|
|
|
|
->where('a.type', 'article')
|
|
|
|
|
->where('a.category_id', 9)
|
|
|
|
|
->field('a.title, a.id, c.content')
|
|
|
|
|
->order('a.id', 'desc')
|
|
|
|
|
->paginate($limit);
|
|
|
|
|
|
|
|
|
|
return $this->success(__('successful'), $list->toArray());
|
2025-11-07 09:56:20 +08:00
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
2025-11-07 09:56:20 +08:00
|
|
|
/**
|
|
|
|
|
* 详情
|
2026-03-02 15:59:46 +08:00
|
|
|
* @Apidoc\Method("GET")
|
2025-11-07 09:56:20 +08:00
|
|
|
* @Apidoc\Query("id", type="int", require=true, desc="ID")
|
|
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
public function detail()
|
|
|
|
|
{
|
|
|
|
|
$id = (int)input('id');
|
|
|
|
|
if (!$id) {
|
2026-04-08 10:05:25 +08:00
|
|
|
return $this->error(__("Invalid parameters"));
|
2026-04-06 03:10:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$vo = ArchivesModel::where('id', $id)->find();
|
|
|
|
|
if (!$vo) {
|
2025-11-07 09:56:20 +08:00
|
|
|
return $this->error(__("Article does not exist"));
|
|
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
|
|
|
|
$this->appendContent($vo);
|
|
|
|
|
|
|
|
|
|
$user_id = $this->getCurrentUserId();
|
|
|
|
|
if ($user_id) {
|
|
|
|
|
$this->markAsRead($vo->id, $user_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->success(__('successful'), $vo->toArray());
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 15:59:46 +08:00
|
|
|
/**
|
|
|
|
|
* 获取最新公告
|
|
|
|
|
* @Apidoc\Method("GET")
|
|
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
public function last_notie()
|
|
|
|
|
{
|
|
|
|
|
$vo = ArchivesModel::where('type', 'article')
|
|
|
|
|
->where('status', 'normal')
|
|
|
|
|
->order('id', 'desc')
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
if (!$vo) {
|
|
|
|
|
return $this->success(__("successful"), []);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->appendContent($vo);
|
|
|
|
|
|
|
|
|
|
$user_id = $this->getCurrentUserId();
|
|
|
|
|
if ($user_id) {
|
|
|
|
|
$this->markAsRead($vo->id, $user_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->success(__('successful'), $vo->toArray());
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-07 09:56:20 +08:00
|
|
|
/**
|
|
|
|
|
* 单页详情
|
2026-03-02 15:59:46 +08:00
|
|
|
* @Apidoc\Method("GET")
|
2025-11-07 09:56:20 +08:00
|
|
|
* @Apidoc\Query("id", type="int", require=true, desc="ID")
|
|
|
|
|
* @Apidoc\Query("name", type="string", require=true, desc="二选1")
|
|
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
public function singpage()
|
|
|
|
|
{
|
|
|
|
|
$id = (int)input('id');
|
2025-11-07 09:56:20 +08:00
|
|
|
$name = input('name');
|
2026-04-06 03:10:44 +08:00
|
|
|
|
|
|
|
|
$vo = null;
|
|
|
|
|
if ($name) {
|
|
|
|
|
$vo = ArchivesModel::where('name', $name)->find();
|
|
|
|
|
} elseif ($id) {
|
|
|
|
|
$vo = ArchivesModel::where('id', $id)->find();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$vo) {
|
2025-11-07 09:56:20 +08:00
|
|
|
return $this->error(__("Article does not exist"));
|
|
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
|
|
|
|
$this->appendContent($vo);
|
|
|
|
|
|
|
|
|
|
return $this->success(__('successful'), $vo->toArray());
|
2025-11-07 09:56:20 +08:00
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
2025-11-07 09:56:20 +08:00
|
|
|
/**
|
|
|
|
|
* 幻灯片
|
|
|
|
|
* @Apidoc\Query("id", type="int", require=true, desc="ID")
|
|
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
public function slide()
|
|
|
|
|
{
|
2025-11-07 09:56:20 +08:00
|
|
|
$list = [
|
2026-04-06 03:10:44 +08:00
|
|
|
['image' => domain() . '/storage/slide/1.jpg', 'title' => ''],
|
|
|
|
|
['image' => domain() . '/storage/slide/2.webp', 'title' => ''],
|
|
|
|
|
['image' => domain() . '/storage/slide/3.webp', 'title' => ''],
|
|
|
|
|
['image' => domain() . '/storage/slide/4.jpg', 'title' => ''],
|
2025-11-07 09:56:20 +08:00
|
|
|
];
|
2026-04-06 03:10:44 +08:00
|
|
|
return $this->success(__('successful'), $list);
|
2025-11-07 09:56:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 设为已读
|
|
|
|
|
* @Apidoc\Query("id", type="int", require=true, desc="ID,多个逗号隔开")
|
|
|
|
|
*/
|
2026-04-06 03:10:44 +08:00
|
|
|
public function mask_as_read()
|
|
|
|
|
{
|
2025-11-07 09:56:20 +08:00
|
|
|
$ids = input('id');
|
2026-04-06 03:10:44 +08:00
|
|
|
$user_id = $this->getCurrentUserId();
|
|
|
|
|
|
|
|
|
|
if (!$user_id) {
|
2025-11-07 09:56:20 +08:00
|
|
|
return $this->success(__('successful'));
|
|
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
|
|
|
|
$ids = array_filter(explode(',', $ids));
|
2025-11-07 09:56:20 +08:00
|
|
|
foreach ($ids as $id) {
|
2026-04-06 03:10:44 +08:00
|
|
|
$this->markAsRead((int)$id, $user_id);
|
2025-11-07 09:56:20 +08:00
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
2025-11-07 09:56:20 +08:00
|
|
|
return $this->success(__('successful'));
|
|
|
|
|
}
|
2026-04-06 03:10:44 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取当前用户ID
|
|
|
|
|
*/
|
|
|
|
|
protected function getCurrentUserId(): int
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
return (int)JwtToken::getCurrentId();
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取阅读状态
|
|
|
|
|
*/
|
|
|
|
|
protected function getReadStatus(int $articleId, int $userId): int
|
|
|
|
|
{
|
|
|
|
|
return (int)(cache_get($this->getCacheKey($articleId, $userId)) ?: 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 标记为已读
|
|
|
|
|
*/
|
|
|
|
|
protected function markAsRead(int $articleId, int $userId = null): void
|
|
|
|
|
{
|
|
|
|
|
if (!$articleId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$userId) {
|
|
|
|
|
$userId = $this->getCurrentUserId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$userId) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$cacheKey = $this->getCacheKey($articleId, $userId);
|
|
|
|
|
if (!cache_get($cacheKey)) {
|
|
|
|
|
Db::name('archives_read')->insert([
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
'source_id' => $articleId,
|
|
|
|
|
'value' => 1
|
|
|
|
|
]);
|
|
|
|
|
cache($cacheKey, 1, self::CACHE_TTL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 追加内容到文章
|
|
|
|
|
*/
|
|
|
|
|
protected function appendContent(ArchivesModel $article): void
|
|
|
|
|
{
|
|
|
|
|
$content = Content::where('id', $article->id)->find();
|
|
|
|
|
if ($content) {
|
|
|
|
|
$article->setAddonData($content->toArray());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 生成缓存键
|
|
|
|
|
*/
|
|
|
|
|
protected function getCacheKey(int $articleId, int $userId): string
|
|
|
|
|
{
|
|
|
|
|
return self::CACHE_PREFIX_READ . $articleId . '_' . $userId;
|
|
|
|
|
}
|
|
|
|
|
}
|