推荐系统完成
Change-Id: I244590be01b1b4f37664a0e7f3103827e607ffbe
diff --git a/react-ui/src/pages/Torrent/service.ts b/react-ui/src/pages/Torrent/service.ts
index e526151..35d2312 100644
--- a/react-ui/src/pages/Torrent/service.ts
+++ b/react-ui/src/pages/Torrent/service.ts
@@ -19,10 +19,9 @@
// ================================
/** 查询种子列表 */
-export async function listBtTorrent(params?: Partial<BtTorrent>) {
- const queryString = params ? `?${new URLSearchParams(params as any)}` : '';
- return request(`/api/system/torrent/list${queryString}`, {
- method: 'get',
+export async function listBtTorrent(userId: string) {
+ return request(`/api/system/torrent/recommend`, {
+ method: 'post',
});
}
diff --git a/recommend/download_model.py b/recommend/download_model.py
index 1bb0c50..b9555dc 100644
--- a/recommend/download_model.py
+++ b/recommend/download_model.py
@@ -1,6 +1,6 @@
import os
import urllib.request
-from recommend import train_and_save_itemcf
+
MODEL_URL = "https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.zh.300.bin.gz"
MODEL_DIR = "./models"
MODEL_PATH = os.path.join(MODEL_DIR, "cc.zh.300.bin")
@@ -28,5 +28,4 @@
print("✅ 模型下载并解压完成!")
if __name__ == "__main__":
- train_and_save_itemcf()
download_model()
diff --git a/recommend/recommend.py b/recommend/recommend.py
index 25032a0..b216d52 100644
--- a/recommend/recommend.py
+++ b/recommend/recommend.py
@@ -15,7 +15,7 @@
engine = create_engine("mysql+pymysql://sy:sy_password@49.233.215.144:3306/pt_station")
# === ✅ 加载 fastText 模型 ===
-fasttext_model_path = 'E:\\course\\pt\\recommend\\models\\cc.zh.300.bin'
+fasttext_model_path = 'models\\cc.zh.300.bin'
if not os.path.exists(fasttext_model_path):
raise FileNotFoundError("fastText 模型文件不存在,请检查路径。")
print("加载 fastText 模型中...")
@@ -25,7 +25,7 @@
# === ✅ 用户标签行为矩阵构建 ===
def get_user_tag_matrix():
df = pd.read_sql("SELECT user_id, tag, score FROM user_tag_scores", engine)
- print(df)
+ #print(df)
df['user_id'] = df['user_id'].astype(str)
user_map = {u: i for i, u in enumerate(df['user_id'].unique())}
tag_map = {t: i for i, t in enumerate(df['tag'].unique())}
@@ -39,20 +39,27 @@
def semantic_recommend(user_id, topn=5):
print(f"正在为用户 {user_id} 生成推荐...")
- # 读取数据库中的用户标签数据
+ # 读取数据
df = pd.read_sql("SELECT user_id, tag, score FROM user_tag_scores", engine)
- print(f"总记录数: {len(df)}")
- print(f"数据示例:\n{df.head()}")
- print(df.dtypes)
- user_id = str(user_id) # 确保匹配
- # 获取该用户的所有标签(按分数从高到低排序)
+ # 统一类型转换
+ df['user_id'] = df['user_id'].astype(str) # 确保整个列转为字符串
+ user_id = str(user_id) # 要查询的ID也转为字符串
+
+ # 现在查询应该正常工作了
user_tags = df[df['user_id'] == user_id].sort_values(by="score", ascending=False)['tag'].tolist()
print(f"用户 {user_id} 的标签(按分数排序): {user_tags}")
if not user_tags:
print(f"用户 {user_id} 没有标签记录,返回空推荐结果。")
return []
+ else:
+ user_tags = user_tags[:3]
+ print(f"用户 {user_id} 的 Top 3 标签: {user_tags}")
+
+ if not user_tags:
+ print(f"用户 {user_id} 没有标签记录,返回空推荐结果。")
+ return []
# 截取前 3 个标签作为“兴趣标签”
user_tags = user_tags[:3]
@@ -85,8 +92,8 @@
# 排序并返回 topN 标签
sorted_tags = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:topn]
print(f"\n最终推荐标签(前 {topn}):")
- for tag, score in sorted_tags:
- print(f"{tag}: {score:.4f}")
+ #for tag, score in sorted_tags:
+ # print(f"{tag}: {score:.4f}")
return [tag for tag, _ in sorted_tags]
@@ -117,8 +124,6 @@
print(f"⚠️ 用户 {user_id} 没有任何标签评分记录。")
return []
- print(f"用户 {user_id} 的标签评分:\n{user_tags}")
-
scores = {}
for tag, val in user_tags.items():
if tag not in sim_df:
@@ -212,28 +217,32 @@
def get_torrent_ids_by_tags(tags, limit_per_tag=10):
if not tags:
tags = []
+ print(f"传递给 get_torrent_ids_by_tags 的标签: {tags}")
recommended_ids = set()
with engine.connect() as conn:
for tag in tags:
query = text("""
SELECT torrent_id
- FROM bt_torrent_tags
- WHERE tag = :tag
+ FROM bt_torrent_tags
+ WHERE tag = :tag
LIMIT :limit
""")
result = conn.execute(query, {"tag": tag, "limit": limit_per_tag})
+ print(f"标签 '{tag}' 的推荐结果:")
for row in result:
+ print(row[0]) # 打印每个torrent_id
recommended_ids.add(row[0])
# 获取数据库中所有 torrent_id
- all_query = text("SELECT DISTINCT torrent_id FROM bt_torrent_tags")
+ all_query = text("SELECT DISTINCT torrent_id FROM bt_torrent")
all_result = conn.execute(all_query)
all_ids = set(row[0] for row in all_result)
+ print("数据库中所有torrent_id:", all_ids)
# 剩下的(非推荐)种子 ID
remaining_ids = all_ids - recommended_ids
-
+ print(remaining_ids)
# 随机打乱推荐和剩下的 ID
recommended_list = list(recommended_ids)
remaining_list = list(remaining_ids)
diff --git a/recommend/requirements.txt b/recommend/requirements.txt
index 2efe47a..b609f83 100644
--- a/recommend/requirements.txt
+++ b/recommend/requirements.txt
@@ -3,7 +3,7 @@
huggingface_hub==0.31.2
jieba==0.42.1
mysql_connector_repackaged==0.3.1
-numpy==2.2.6
+numpy==1.26.4
pandas==2.2.3
scikit_learn==1.6.1
scikit_surprise==1.1.4
@@ -16,3 +16,4 @@
torch==2.7.0
transformers==4.51.3
waitress==3.0.2
+pymysql
\ No newline at end of file
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java
index 15dc9f3..559547b 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/controller/BtTorrentController.java
@@ -6,6 +6,7 @@
import java.nio.charset.StandardCharsets;
import java.util.*;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.ruoyi.common.utils.http.HttpUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
@@ -61,6 +62,33 @@
@Autowired
private IBtTorrentAnnounceService btTorrentAnnounceService;
private static final String RECOMMEND_API = "http://127.0.0.1:5000/recommend_torrents";
+ private String sendJsonPost(String url, String jsonBody) throws IOException {
+ HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Content-Type", "application/json");
+ connection.setRequestProperty("Accept", "application/json");
+
+ try (OutputStream os = connection.getOutputStream()) {
+ os.write(jsonBody.getBytes(StandardCharsets.UTF_8));
+ }
+
+ try (InputStream is = connection.getInputStream()) {
+ return new String(is.readAllBytes(), StandardCharsets.UTF_8);
+ }
+ }
+ public static class TorrentRecommendationRequest {
+ @JsonProperty("torrent_ids")
+ private List<Long> ids;
+
+ public List<Long> getIds() {
+ return ids;
+ }
+
+ public void setIds(List<Long> ids) {
+ this.ids = ids;
+ }
+ }
private String torrentPath= "torrents";
@@ -92,9 +120,6 @@
}
TorrentFileUtil.uploadFile(file,torrentPath+"/"+SecurityUtils.getUserId());
-
-
-
// Assuming the Flask server responds with JSON, parse the response
String responseBody = new String(connection.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
parseTorrentData(responseBody);
@@ -155,7 +180,6 @@
btTorrentFileService.insertBtTorrentFile(btFile);
}
);
-
// Convert btTorrentAnnounceNode to List<BtTorrentAnnounce>
List<BtTorrentAnnounce> btTorrentAnnounceList = new ArrayList<>();
if (btTorrentAnnounceNode.isArray()) {
@@ -170,18 +194,9 @@
btTorrentAnnounceService.insertBtTorrentAnnounce(btTorrentAnnounce);
}
);
-
-
-
-
-
-
-
-
} catch (Exception e) {
e.printStackTrace();
// Handle the error (e.g., return null or an error message)
-
}
}
@@ -227,22 +242,41 @@
*/
@PreAuthorize("@ss.hasPermi('system:torrent:list')")
@PostMapping("/recommend")
- public TableDataInfo recommendTorrents(@RequestParam("user_id") String userId) {
+ public TableDataInfo recommendTorrents() {
+ // 假设当前用户的 user_id 是通过 SecurityContext 或 session 中获取的
+ String userId = String.valueOf(getUserId()); // 获取当前用户的 user_id
+
+ if (userId == null || userId.isEmpty()) {
+ TableDataInfo error = new TableDataInfo();
+ error.setCode(400);
+ error.setMsg("用户ID无效或未登录");
+ return error;
+ }
+
+ System.out.println("当前用户ID: " + userId); // 打印调试信息
+
try {
- // 1. 构造 JSON 请求体
+ // 1. 构造 JSON 请求体,包含用户ID
String jsonRequest = "{\"user_id\": \"" + userId + "\"}";
- // 2. 向 Flask 服务发 POST 请求,返回 JSON 数组
- String jsonResponse = HttpUtils.sendPost(RECOMMEND_API, jsonRequest);
+ // 2. 向 Flask 服务发 POST 请求,返回 JSON 响应
+ String jsonResponse = sendJsonPost(RECOMMEND_API, jsonRequest);
- // 3. 使用 Jackson 解析 JSON 数组为 List<Long>
+ // 3. 使用 Jackson 解析 JSON 响应为 TorrentRecommendationRequest 对象
ObjectMapper mapper = new ObjectMapper();
- List<Long> idList = mapper.readValue(jsonResponse, new TypeReference<List<Long>>() {});
+ TorrentRecommendationRequest recommendationRequest = mapper.readValue(jsonResponse, TorrentRecommendationRequest.class);
- // 4. 根据 ID 查询完整种子信息,并保持推荐顺序
+ // 4. 提取 ids 字段
+ List<Long> idList = recommendationRequest.getIds();
+
+ if (idList == null || idList.isEmpty()) {
+ throw new RuntimeException("推荐的种子ID列表为空");
+ }
+
+ // 5. 根据种子ID查询完整的种子信息,保持推荐顺序
List<BtTorrent> resultList = btTorrentService.selectBtTorrentsByIdsOrdered(idList);
- // 5. 封装成 TableDataInfo 返回
+ // 6. 封装查询结果并返回
TableDataInfo rsp = new TableDataInfo();
rsp.setCode(200);
rsp.setMsg("推荐成功");
@@ -251,7 +285,9 @@
return rsp;
} catch (Exception e) {
- e.printStackTrace();
+ e.printStackTrace(); // 打印异常信息
+
+ // 如果出错,返回错误信息
TableDataInfo error = new TableDataInfo();
error.setCode(500);
error.setMsg("推荐失败:" + e.getMessage());
@@ -259,6 +295,7 @@
}
}
+
/**
* 导出种子主列表
*/
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/mapper/BtTorrentMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/mapper/BtTorrentMapper.java
index 529043c..05fc282 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/torrent/mapper/BtTorrentMapper.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/mapper/BtTorrentMapper.java
@@ -68,5 +68,5 @@
* @param ids 种子ID列表
* @return 种子列表
*/
- List<BtTorrent> selectBtTorrentsByIdsOrdered(@Param("list") List<Long> ids);
+ public List<BtTorrent> selectBtTorrentsByIdsOrdered(@Param("list") List<Long> ids);
}
diff --git a/ruoyi-admin/src/main/resources/mapper/system/BtTorrentMapper.xml b/ruoyi-admin/src/main/resources/mapper/system/BtTorrentMapper.xml
index 4f5cf3f..097d7b8 100644
--- a/ruoyi-admin/src/main/resources/mapper/system/BtTorrentMapper.xml
+++ b/ruoyi-admin/src/main/resources/mapper/system/BtTorrentMapper.xml
@@ -45,14 +45,14 @@
where torrent_id = #{torrentId}
</select>
- <select id="selectBtTorrentsByIdsOrdered" resultType="BtTorrent">
+ <select id="selectBtTorrentsByIdsOrdered" resultMap="BtTorrentResult">
SELECT * FROM bt_torrent
- WHERE id IN
- <foreach collection="list" item="id" open="(" separator="," close=")">
+ WHERE torrent_id IN <!-- 修改这里 -->
+ <foreach item="id" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
- ORDER BY FIELD(id
- <foreach collection="list" item="id" separator=",">
+ ORDER BY FIELD(torrent_id, <!-- 修改这里 -->
+ <foreach item="id" collection="list" separator=",">
#{id}
</foreach>
)