Files
YwxAppThink/addon/download/controller/Index.php
T
ywxapp 1d49e6f5ee feat: 公共表更名 common/member 前缀 + 插件命令自动加载 + 版本 1.1.1
- 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
2026-08-20 20:19:15 +08:00

202 lines
6.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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),
]);
}
}