编写种子详情页
在前面的章节中,我们已经完成了种子数据的采集、存储以及搜索索引的构建。有了这些基础能力之后,一个完整的磁力搜索或资源聚合系统,必然还需要一个 种子详情页 ,用于展示单个种子的完整信息,并为用户提供下载入口和相关推荐。
本文将围绕「种子详情页」展开,介绍后端 Controller 的实现思路,以及前端 EJS 模板如何组织和渲染数据。整体实现基于 Egg.js + EJS 模板,力求结构清晰、逻辑直观。
1. 种子详情页 Controller
详情页的核心职责很明确:
- 根据 URL 中的 hash 参数查询对应的种子信息
- 如果数据不存在,返回 404
- 查询一定数量的随机种子,用于 相关链接 推荐
- 将数据传递给视图模板进行渲染
下面是 app/controller/torrent.js 中的实现代码:
// app/controller/torrent.js
'use strict';
const Controller = require('egg').Controller;
module.exports = class extends Controller {
async index() {
const torrent = await this.ctx.service.torrent.getByHash(this.ctx.params.hash);
if (!torrent.id) {
this.ctx.status = 404;
return;
}
const random = await this.ctx.service.torrent.random(10);
await this.ctx.helper.renderView('torrent.ejs', {
torrent,
random,
title: torrent.name,
});
}
async delete() {
const body = this.ctx.request.body;
// 从数据库删除
await this.ctx.service.torrent.delete(body.id);
// 从 manticore 中删除
await this.ctx.service.search.delete(body.id);
this.ctx.body = {
code: 0,
message: '删除成功',
success: true,
data: '',
};
}
};关键点说明
- 通过 hash 查询种子 :
getByHash通常是一个基于唯一索引的查询,这样可以保证访问/torrent/:hash时的性能。 - 404 处理 :
如果查询结果不存在,直接设置ctx.status = 404并返回,避免后续渲染报错。 - 随机推荐数据 :
random(10)用于生成 相关链接 ,即便不是真正意义上的相似推荐,也能有效提升页面的浏览深度。 - renderView 封装 :
通过ctx.helper.renderView统一渲染逻辑,通常可以在 helper 中处理公共变量(SEO、站点信息等)。 - 删除接口 :
删除操作不仅需要清理数据库记录,同时也要同步删除搜索引擎(如 Manticore / Sphinx)中的索引数据,保证搜索结果的准确性。
2. 种子详情页视图模板
详情页采用 EJS 模板进行渲染,主要包含以下几个区域:
- 种子基础信息(名称、大小、时间、热度等)
- 磁力链接展示与复制
- 文件列表
- 相关链接推荐
对应的模板文件为 app/view/torrent.ejs :
<!-- // app/view/torrent.ejs -->
<!DOCTYPE html>
<html lang="en">
<%-include('head.ejs')%>
<body>
<%-include('header.ejs')%>
<div class="main">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="torSingle">
<h1><%-torrent.name%></h1>
<div class="torMeta">
<span>大小:<%-ctx.helper.formatBytes(torrent.length)%></span>
<span>收录时间:<%-ctx.helper.date('Y-m-d H:i', torrent.add_date)%></span>
<span>文件数:<%-torrent.file_count%></span>
<span>热度:<%-torrent.download_count+1%>℃</span>
</div>
<h2>磁力链接</h2>
<textarea rows="6" class="form-control" readonly id="magnet">
magnet:?xt=urn:btih:<%-torrent.hash%>&dn=<%-torrent.name%>&xl=<%-torrent.length%></textarea
>
<div class="torBtns">
<a
class="btn btn-primary"
href="magnet:?xt=urn:btih:<%-torrent.hash%>&dn=<%-torrent.name%>&xl=<%-torrent.length%>"
>
下载资源
</a>
<a href="javascript:" class="btn btn-info" id="clipboard" data-clipboard-target="#magnet"> 复制链接 </a>
</div>
<h2>文件列表</h2>
<div class="torFiles">
<% torrent.files.forEach(function(file, index){ %>
<p>
<span class="torFileName"><%-file.name%></span>
<span class="torSize"><%-ctx.helper.formatBytes(file.length)%></span>
</p>
<% }) %>
<!-- -->
<% if(torrent.file_count>50){ %>
<p class="torMoreInfo">-----仅列出部分文件,更多信息请下载后查看-----</p>
<% } %>
</div>
<h1 style="margin-top: 30px">相关链接</h1>
<ul class="torRela">
<% random.forEach(function(item){ %>
<li>
<a title="<%-item.name%>" href="/torrent/<%-item.hash%>">
<span class="relTorName"><%-item.name%></span>
<span><%-ctx.helper.formatBytes(item.length)%></span>
<span><%-ctx.helper.date('Y-m-d H:i', item.add_date)%></span>
</a>
</li>
<% }) %>
</ul>
</div>
</div>
</div>
</div>
</div>
<%-include('footer.ejs')%>
<script>
new ClipboardJS('#clipboard');
</script>
</body>
</html>模板设计要点
- 使用
<%- %>输出 :
EJS 中<%- %>表示不进行 HTML 转义,适合输出已经可信的数据(如服务端生成的字段)。 - 磁力链接拼接 :
btih对应种子的 hash,dn表示显示名称,xl表示文件大小,这三个参数基本可以满足绝大多数下载器的识别需求。 - 文件列表展示优化 :
当文件数量过多时,仅展示部分列表,并给出提示,避免页面过长或渲染性能问题。 - 复制功能 :
通过ClipboardJS实现一键复制磁力链接,这是一个非常轻量但体验提升明显的功能。 - 相关链接 :
随机种子列表可以有效提高站内跳转率,为后续加入 相似度推荐 标签推荐 等功能预留空间。
3. 小结
至此,一个完整的种子详情页就实现了。从后端数据查询、404 处理,到前端模板渲染、磁力链接生成与复制,整个流程清晰且易于扩展。
在此基础上,你还可以继续优化:
- 增加访问统计与热度算法
- 对文件列表进行懒加载或折叠展示
- 引入真正的相似推荐(基于关键词或全文检索)
- 为搜索引擎添加结构化数据(SEO)
这些能力叠加起来,才能让一个磁力资源站点逐步从 能用 ,走向 好用 。




