- 18 张公共表更名(addon/attachment/configure/links/spider_log/spider_stat/sms/notice/ad/task/prop/medal/help/card -> common_*,member_wallets->member_wallet,score_rule/score_log -> member_*,addon_config->common_addonconf),模型全部对齐新表名,Db 直引用清零 - Attachment 模型补 表名绑定,修复富文本上传查 wxapp_attachment 1146 隐患 - install.sql + 迁移 SQL:backend_admin/backend_role delete_at 默认 0,修复软删除(NULL != 0)误过滤导致后台菜单为空 - AppService::boot() 支持插件 info.php 声明 commands 自动注册插件命令(psr-4 自动加载,坏类名自动跳过) - 各插件(haonav/mqttbroker/wxchat/articles/blog/forum 等)字段与配置同步调整 - 框架版本 1.1.0 -> 1.1.1
202 lines
6.5 KiB
PHP
202 lines
6.5 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace addon\download\controller;
|
||
|
||
use think\facade\Config;
|
||
use think\facade\Lang;
|
||
use think\facade\Cookie;
|
||
use think\Response;
|
||
use ywxapp\controller\FrontendBase;
|
||
use addon\download\model\Category;
|
||
use addon\download\model\Resource;
|
||
use addon\download\service\DownloadService;
|
||
|
||
class Index extends FrontendBase
|
||
{
|
||
protected $noNeedLogin = ['*'];
|
||
protected $noNeedVerify = ['*'];
|
||
|
||
public function initialize()
|
||
{
|
||
// 多语言:支持 ?lang=zh-cn|en-us 切换并写 cookie;插件级 lang 显式加载兜底
|
||
$lang = $this->request->param('lang', '');
|
||
if (in_array($lang, ['zh-cn', 'en-us'], true)) {
|
||
Lang::setLangSet($lang);
|
||
Cookie::set('download_lang', $lang);
|
||
} elseif ($cookie = Cookie::get('download_lang')) {
|
||
Lang::setLangSet($cookie);
|
||
}
|
||
|
||
|
||
|
||
// 固定页面文案注入模板
|
||
$this->view->assign([
|
||
'site_name' => lang('site_name'),
|
||
'all_category_text' => lang('all_category'),
|
||
'hot_text' => lang('hot'),
|
||
'latest_text' => lang('latest'),
|
||
'list_text' => lang('list'),
|
||
'detail_text' => lang('detail'),
|
||
'search_text' => lang('search'),
|
||
'search_placeholder' => lang('search_placeholder'),
|
||
'download_btn_text' => lang('download_btn'),
|
||
]);
|
||
|
||
// 插件前台接入「全站动态布局」:视图只写正文,由核心 frontend 的
|
||
// common/layout.html 包裹全站 header / footer(与开发文档约定一致)。
|
||
// 注意:layout_name 必须带 .html 扩展名,否则 think-template 的
|
||
// parseTemplateFile() 会把无扩展名的绝对路径里的盘符冒号(D:)当成
|
||
// 模板分隔符替换成当前 viewPath,导致「模板文件不存在」。
|
||
$this->view->config([
|
||
'layout_on' => true,
|
||
'layout_name' => $this->app->getRootPath() . 'app' . DIRECTORY_SEPARATOR
|
||
. 'frontend' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR
|
||
. 'common' . DIRECTORY_SEPARATOR . 'layout.html',
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 读取本插件配置(运行时由 AppInit 注入 config('download'))
|
||
*/
|
||
protected function pluginConfig(): array
|
||
{
|
||
return Config::get('download', []);
|
||
}
|
||
|
||
/**
|
||
* 首页:分类导航 + 热门/最新(按 config 开关)
|
||
*/
|
||
public function index()
|
||
{
|
||
$cfg = $this->pluginConfig();
|
||
|
||
$categories = Category::with(['resources' => function ($q) {
|
||
$q->order('downloads', 'desc')->limit(8);
|
||
}])
|
||
->where('pid', 0)
|
||
->where('status', 1)
|
||
->order('sort', 'desc')
|
||
->select();
|
||
|
||
$hot = [];
|
||
$news = [];
|
||
if (!empty($cfg['show_hot'])) {
|
||
$hot = Resource::scope('visible')->order('downloads', 'desc')->limit(10)->select();
|
||
}
|
||
if (!empty($cfg['show_new'])) {
|
||
$news = Resource::scope('visible')->order('id', 'desc')->limit(10)->select();
|
||
}
|
||
|
||
$this->view->assign('categories', $categories);
|
||
$this->view->assign('hot', $hot);
|
||
$this->view->assign('news', $news);
|
||
$this->view->assign('config', $cfg);
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
/**
|
||
* 分类列表页
|
||
*/
|
||
public function list()
|
||
{
|
||
$cid = $this->request->param('cid/d', 0);
|
||
$page = $this->request->param('page/d', 1);
|
||
$cfg = $this->pluginConfig();
|
||
$size = (int)($cfg['page_size'] ?? 20);
|
||
|
||
$cat = $cid ? Category::find($cid) : null;
|
||
if ($cid && !$cat) {
|
||
$this->error('分类不存在');
|
||
}
|
||
|
||
$list = Resource::scope('visible');
|
||
if ($cid) {
|
||
$list = $list->where('cid', $cid);
|
||
}
|
||
$list = $list->order('downloads', 'desc')
|
||
->paginate(['page' => $page, 'list_rows' => $size]);
|
||
|
||
$this->view->assign('cat', $cat);
|
||
$this->view->assign('list', $list);
|
||
$this->view->assign('pager', $list->render());
|
||
$this->view->assign('config', $cfg);
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
/**
|
||
* 详情页
|
||
*/
|
||
public function detail()
|
||
{
|
||
$id = $this->request->param('id/d', 0);
|
||
$resource = Resource::scope('visible')->find($id);
|
||
if (!$resource) {
|
||
$this->error('资源不存在或已下架');
|
||
}
|
||
|
||
// 查看计数(防刷交给 service 内的简单锁,此处仅点击+1)
|
||
DownloadService::incClicks($id);
|
||
|
||
$cat = $resource['cid'] ? Category::find($resource['cid']) : null;
|
||
|
||
$this->view->assign('resource', $resource);
|
||
$this->view->assign('cat', $cat);
|
||
$this->view->assign('config', $this->pluginConfig());
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
/**
|
||
* 搜索
|
||
*/
|
||
public function search()
|
||
{
|
||
$q = $this->request->param('q', $this->request->param('kw', ''));
|
||
$page = $this->request->param('page/d', 1);
|
||
$cfg = $this->pluginConfig();
|
||
$size = (int)($cfg['page_size'] ?? 20);
|
||
|
||
$list = Resource::scope('visible');
|
||
if ($q) {
|
||
$list = $list->whereLike('title', "%{$q}%");
|
||
}
|
||
$list = $list->order('downloads', 'desc')
|
||
->paginate(['page' => $page, 'list_rows' => $size]);
|
||
|
||
$this->view->assign('keyword', $q);
|
||
$this->view->assign('list', $list);
|
||
$this->view->assign('pager', $list->render());
|
||
$this->view->assign('config', $cfg);
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
/**
|
||
* 触发下载:外链 302 跳转 / 本地文件流式输出
|
||
*/
|
||
public function down()
|
||
{
|
||
$id = $this->request->param('id/d', 0);
|
||
$ip = $this->request->ip();
|
||
try {
|
||
$ret = DownloadService::dispatch($id, $ip);
|
||
} catch (\think\Exception $e) {
|
||
$this->error($e->getMessage());
|
||
return;
|
||
}
|
||
|
||
if ($ret['type'] === 'redirect') {
|
||
return redirect($ret['url']);
|
||
}
|
||
|
||
// 本地文件流式输出(避免大文件占用内存)
|
||
$path = $ret['path'];
|
||
$name = ($ret['name'] ?? 'download') . '.' . pathinfo($path, PATHINFO_EXTENSION);
|
||
return Response::create()->data(file_get_contents($path))->header([
|
||
'Content-Type' => 'application/octet-stream',
|
||
'Content-Disposition' => 'attachment; filename="' . rawurlencode($name) . '"',
|
||
'Content-Length' => filesize($path),
|
||
]);
|
||
}
|
||
}
|