'redirect','url'=>...] 或 ['type'=>'file','path'=>...] * @param int $id 资源ID * @param string $clientIp 客户端IP(用于防刷) * @return array * @throws \think\Exception */ public static function dispatch(int $id, string $clientIp): array { $resource = Resource::find($id); if (!$resource || $resource->status != 1 || $resource->is_audit != 1) { throw new \think\Exception('资源不存在或已下架'); } // 防刷:同 IP + 同资源 5 秒内不重复计数 $lockKey = 'dl_lock_' . md5($clientIp . '_' . $id); if (!Cache::get($lockKey)) { Db::name('download_resource') ->where('id', $id) ->inc('downloads', 1) ->update(); Cache::set($lockKey, 1, 5); } if ((int)$resource->is_local === 1) { // 本地文件:返回服务器绝对路径,由控制器做流式输出 $root = app()->getRootPath() . 'public'; $path = $root . $resource->file_url; if (!is_file($path)) { throw new \think\Exception('文件不存在'); } return ['type' => 'file', 'path' => $path, 'name' => $resource->title]; } if (empty($resource->file_url)) { throw new \think\Exception('下载地址为空'); } return ['type' => 'redirect', 'url' => $resource->file_url]; } /** * 查看计数(详情页调用) */ public static function incClicks(int $id): void { Db::name('download_resource') ->where('id', $id) ->inc('clicks', 1) ->update(); } }