chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+274
View File
@@ -0,0 +1,274 @@
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace addon\haonav\controller;
use ywxapp\controller\FrontendBase;
use addon\haonav\model\Favorite as FavoriteModel;
use addon\haonav\model\FavoriteShare as FavoriteShareModel;
use addon\haonav\model\Configure as ConfigureModel;
/**
* 用户云端收藏(登录后「我的导航」跨端同步)
*
* 认证:复用框架前台 AuthAuthorization 头或 access_token Cookie)。
* 未登录时接口返回 code=401,前端自动回退 localStorage。
*
* @author ywxapp <admin@ywxapp.cn>
*/
class Favorite extends FrontendBase
{
protected $noNeedLogin = ['*'];
protected $noNeedVerify = ['*'];
protected function initialize()
{
}
/**
* 当前登录用户ID,未登录返回 0
*/
protected function uid(): int
{
if ($this->auth && $this->auth->isLogin) {
$info = $this->auth->info;
if (is_array($info)) {
return (int)($info['id'] ?? 0);
}
return (int)($info->id ?? 0);
}
return 0;
}
/**
* 登录态 + 收藏列表
* GET /haonav/favorite/list
*/
public function list()
{
$uid = $this->uid();
if (!$uid) {
return $this->result->error('未登录', 401);
}
$list = FavoriteModel::where('user_id', $uid)
->order('sort', 'asc')
->order('id', 'asc')
->select();
return $this->result->success($list);
}
/**
* 新增收藏
* POST /haonav/favorite/add {link_id?,title,url,icon?}
*/
public function add()
{
$uid = $this->uid();
if (!$uid) {
return $this->result->error('请先登录', 401);
}
$url = trim((string)$this->request->post('url', ''));
$title = trim((string)$this->request->post('title', ''));
if ($url === '' || $title === '') {
return $this->result->error('参数不完整');
}
if (!preg_match('#^https?://#i', $url) && strpos($url, '/') !== 0) {
return $this->result->error('网址格式不正确');
}
$exists = FavoriteModel::where('user_id', $uid)->where('url', $url)->find();
if ($exists) {
return $this->result->success([], '已在我的导航');
}
$maxSort = (int)FavoriteModel::where('user_id', $uid)->max('sort');
$model = new FavoriteModel();
$model->user_id = $uid;
$model->link_id = (int)$this->request->post('link_id', 0);
$model->title = mb_substr($title, 0, 100);
$model->url = mb_substr($url, 0, 500);
$model->icon = mb_substr((string)$this->request->post('icon', ''), 0, 200);
$model->sort = $maxSort + 1;
$model->save();
return $this->result->success(['id' => $model->id], '已收藏');
}
/**
* 移除收藏
* POST /haonav/favorite/remove {id} 或 {url}
*/
public function remove()
{
$uid = $this->uid();
if (!$uid) {
return $this->result->error('请先登录', 401);
}
$id = (int)$this->request->post('id', 0);
$url = trim((string)$this->request->post('url', ''));
$q = FavoriteModel::where('user_id', $uid);
if ($id) {
$q->where('id', $id);
} elseif ($url !== '') {
$q->where('url', $url);
} else {
return $this->result->error('参数不完整');
}
$q->delete();
return $this->result->success([], '已移除');
}
/**
* 保存排序
* POST /haonav/favorite/sort {ids:[3,1,2]}
*/
public function sort()
{
$uid = $this->uid();
if (!$uid) {
return $this->result->error('请先登录', 401);
}
$ids = $this->request->post('ids', []);
if (is_string($ids)) {
$ids = json_decode($ids, true);
}
if (!is_array($ids) || !$ids) {
return $this->result->error('参数不完整');
}
$sort = 1;
foreach ($ids as $id) {
FavoriteModel::where('user_id', $uid)->where('id', (int)$id)->update(['sort' => $sort++]);
}
return $this->result->success([], '已保存');
}
/**
* 一次性把本地收藏合并到云端(登录后同步)
* POST /haonav/favorite/merge {items:[{title,url,icon},...]}
*/
public function merge()
{
$uid = $this->uid();
if (!$uid) {
return $this->result->error('请先登录', 401);
}
$items = $this->request->post('items', []);
if (is_string($items)) {
$items = json_decode($items, true);
}
if (is_array($items)) {
$maxSort = (int)FavoriteModel::where('user_id', $uid)->max('sort');
foreach ($items as $it) {
$url = trim((string)($it['url'] ?? ''));
$title = trim((string)($it['title'] ?? ''));
if ($url === '' || $title === '') {
continue;
}
$exists = FavoriteModel::where('user_id', $uid)->where('url', $url)->find();
if ($exists) {
continue;
}
$model = new FavoriteModel();
$model->user_id = $uid;
$model->link_id = (int)($it['id'] ?? 0);
$model->title = mb_substr($title, 0, 100);
$model->url = mb_substr($url, 0, 500);
$model->icon = mb_substr((string)($it['icon'] ?? ''), 0, 200);
$model->sort = ++$maxSort;
$model->save();
}
}
$list = FavoriteModel::where('user_id', $uid)->order('sort', 'asc')->order('id', 'asc')->select();
return $this->result->success($list, '同步完成');
}
/**
* 获取当前用户的收藏夹分享配置(登录)
* GET /haonav/favorite/share
*/
public function share()
{
$uid = $this->uid();
if (!$uid) {
return $this->result->error('请先登录', 401);
}
$row = FavoriteShareModel::forUser($uid);
return $this->result->success($this->shareData($row));
}
/**
* 保存分享配置:开关 / 标题 / 重置令牌(登录)
* POST /haonav/favorite/sharesave {enabled,title?,reset?}
*/
public function shareSave()
{
$uid = $this->uid();
if (!$uid) {
return $this->result->error('请先登录', 401);
}
$row = FavoriteShareModel::forUser($uid);
$row->enabled = (int)$this->request->post('enabled', $row->enabled) ? 1 : 0;
$title = trim((string)$this->request->post('title', ''));
if ($title !== '') {
$row->title = mb_substr($title, 0, 100);
}
if ((int)$this->request->post('reset', 0) === 1) {
$row->token = FavoriteShareModel::genToken();
}
$row->save();
return $this->result->success($this->shareData($row), '已保存');
}
/**
* 组装分享配置返回数据(含完整分享链接)
*/
protected function shareData(FavoriteShareModel $row): array
{
return [
'enabled' => (int)$row->enabled,
'token' => (string)$row->token,
'title' => (string)$row->title,
'views' => (int)$row->views,
'url' => $this->request->domain() . '/haonav/favorite/shared/' . $row->token . '.html',
];
}
/**
* 公开的收藏夹分享页(无需登录,凭 token 访问)
* GET /haonav/favorite/shared/:token
*/
public function shared($token = '')
{
$token = trim((string)$token);
$row = $token !== '' ? FavoriteShareModel::where('token', $token)->find() : null;
$valid = $row && (int)$row->enabled === 1;
$list = [];
if ($valid) {
$list = FavoriteModel::where('user_id', $row->user_id)
->order('sort', 'asc')
->order('id', 'asc')
->select();
// 访问计数(不含所有者本人重复刷新的精确去重,简单自增即可)
FavoriteShareModel::where('id', $row->id)->inc('views')->update();
}
$title = $valid ? ($row->title ?: '收藏夹分享') : '收藏夹不存在或未公开';
$this->view->assign('valid', $valid);
$this->view->assign('shareTitle', $title);
$this->view->assign('list', $list);
$this->view->assign('count', is_countable($list) ? count($list) : 0);
$this->view->assign('seo', [
'title' => $title . ' - 网址导航',
'keywords' => '收藏夹,分享,网址导航',
'description' => (string)ConfigureModel::getVal('seo_description', '简洁实用的网址导航'),
]);
$this->view->assign('siteUrl', $this->request->domain());
return $this->view->fetch('favorite/shared');
}
}