补充
Change-Id: I058b37511bf75593587009fabaebaa7b11ad7aed
diff --git a/Merge/front/src/api/recommend_rhj.js b/Merge/front/src/api/recommend_rhj.js
new file mode 100644
index 0000000..34d9164
--- /dev/null
+++ b/Merge/front/src/api/recommend_rhj.js
@@ -0,0 +1,20 @@
+// RHJ 深度推荐接口
+import { getAuthToken } from '../utils/auth'
+const RHJ_BASE_URL = 'http://10.126.59.25:8082' // 端口已修正
+
+export async function deepRecommend(userId, topk = 10) {
+ const token = getAuthToken()
+ const headers = {
+ 'Content-Type': 'application/json',
+ ...(token ? { 'Authorization': `Bearer ${token}` } : {})
+ }
+ const res = await fetch(`${RHJ_BASE_URL}/recommend`, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify({ user_id: userId, topk })
+ })
+ if (!res.ok) throw new Error('深度推荐接口调用失败')
+ const data = await res.json()
+ // 兼容接口返回格式
+ return data.data?.recommendations || []
+}
diff --git a/Merge/front/src/components/HomeFeed.jsx b/Merge/front/src/components/HomeFeed.jsx
index cc37642..1fb12a5 100644
--- a/Merge/front/src/components/HomeFeed.jsx
+++ b/Merge/front/src/components/HomeFeed.jsx
@@ -6,6 +6,7 @@
import { fetchPosts, fetchPost } from '../api/posts_wzy'
import { searchAPI } from '../api/search_jwlll'
import { getUserInfo } from '../utils/auth'
+import { deepRecommend } from '../api/recommend_rhj'
import '../style/HomeFeed.css'
const categories = [
@@ -15,7 +16,8 @@
const recommendModes = [
{ label: '标签推荐', value: 'tag' },
- { label: '协同过滤推荐', value: 'cf' }
+ { label: '协同过滤推荐', value: 'cf' },
+ { label: '深度推荐', value: 'deep' } // 新增
]
const DEFAULT_USER_ID = '3' // 确保数据库有此用户(作为回退值)
@@ -69,16 +71,35 @@
try {
const currentUserId = getCurrentUserId()
const data = await searchAPI.recommendByTags(currentUserId, tags)
- const formattedItems = (data.recommendations || []).map(item => ({
- id: item.id,
- title: item.title,
- author: item.author || '佚名',
- avatar: `https://i.pravatar.cc/40?img=${item.id}`,
- img: item.img || '',
- likes: item.heat || 0,
- content: item.content
- }))
- setItems(formattedItems)
+ // 新增:拉取详情,保证和原始数据一致
+ const detailed = await Promise.all(
+ (data.recommendations || []).map(async item => {
+ try {
+ const d = await fetchPost(item.id)
+ return {
+ id: d.id,
+ title: d.title,
+ author: `作者 ${d.user_id}`,
+ avatar: `https://i.pravatar.cc/40?img=${d.user_id}`,
+ img: d.media_urls?.[0] || '',
+ likes: d.heat,
+ content: d.content || ''
+ }
+ } catch {
+ // 拉详情失败时兜底
+ return {
+ id: item.id,
+ title: item.title,
+ author: item.author || '佚名',
+ avatar: `https://i.pravatar.cc/40?img=${item.id}`,
+ img: item.img || '',
+ likes: item.heat || 0,
+ content: item.content || ''
+ }
+ }
+ })
+ )
+ setItems(detailed)
} catch (e) {
console.error('标签推荐失败:', e)
setError('标签推荐失败')
@@ -93,16 +114,35 @@
try {
const currentUserId = getCurrentUserId()
const data = await searchAPI.userBasedRecommend(currentUserId, topN)
- const formattedItems = (data.recommendations || []).map(item => ({
- id: item.id,
- title: item.title,
- author: item.author || '佚名',
- avatar: `https://i.pravatar.cc/40?img=${item.id}`,
- img: item.img || '',
- likes: item.heat || 0,
- content: item.content
- }))
- setItems(formattedItems)
+ // 新增:拉取详情,保证和原始数据一致
+ const detailed = await Promise.all(
+ (data.recommendations || []).map(async item => {
+ try {
+ const d = await fetchPost(item.id)
+ return {
+ id: d.id,
+ title: d.title,
+ author: `作者 ${d.user_id}`,
+ avatar: `https://i.pravatar.cc/40?img=${d.user_id}`,
+ img: d.media_urls?.[0] || '',
+ likes: d.heat,
+ content: d.content || ''
+ }
+ } catch {
+ // 拉详情失败时兜底
+ return {
+ id: item.id,
+ title: item.title,
+ author: item.author || '佚名',
+ avatar: `https://i.pravatar.cc/40?img=${item.id}`,
+ img: item.img || '',
+ likes: item.heat || 0,
+ content: item.content || ''
+ }
+ }
+ })
+ )
+ setItems(detailed)
} catch (e) {
console.error('协同过滤推荐失败:', e)
setError('协同过滤推荐失败')
@@ -110,6 +150,47 @@
}
setLoading(false)
}, [recCFNum])
+ // 深度推荐
+ const fetchDeepRecommend = useCallback(async (topN = 20) => {
+ setLoading(true)
+ setError(null)
+ try {
+ const currentUserId = getCurrentUserId()
+ const recs = await deepRecommend(currentUserId, topN)
+ // 拉取详情,保证和原始数据一致
+ const detailed = await Promise.all(
+ (recs || []).map(async item => {
+ try {
+ const d = await fetchPost(item.id)
+ return {
+ id: d.id,
+ title: d.title,
+ author: `作者 ${d.user_id}`,
+ avatar: `https://i.pravatar.cc/40?img=${d.user_id}`,
+ img: d.media_urls?.[0] || '',
+ likes: d.heat,
+ content: d.content || ''
+ }
+ } catch {
+ return {
+ id: item.id,
+ title: item.title,
+ author: item.author || '佚名',
+ avatar: `https://i.pravatar.cc/40?img=${item.id}`,
+ img: item.img || '',
+ likes: item.heat || 0,
+ content: item.content || ''
+ }
+ }
+ })
+ )
+ setItems(detailed)
+ } catch (e) {
+ setError('深度推荐失败')
+ setItems([])
+ }
+ setLoading(false)
+ }, [])
// 获取用户兴趣标签后再推荐
const fetchUserTagsAndRecommend = useCallback(async () => {
setLoading(true)
@@ -124,11 +205,13 @@
}
if (recMode === 'tag') {
await fetchTagRecommend(tags)
- } else {
+ } else if (recMode === 'cf') {
await fetchCFRecommend()
+ } else if (recMode === 'deep') {
+ await fetchDeepRecommend()
}
setLoading(false)
- }, [recMode, fetchTagRecommend, fetchCFRecommend])
+ }, [recMode, fetchTagRecommend, fetchCFRecommend, fetchDeepRecommend])
useEffect(() => {
// 原始数据加载函数
diff --git a/Merge/front/src/components/Sidebar.jsx b/Merge/front/src/components/Sidebar.jsx
index e35db63..b0453e4 100644
--- a/Merge/front/src/components/Sidebar.jsx
+++ b/Merge/front/src/components/Sidebar.jsx
@@ -1,32 +1,15 @@
-import React, { useState, useEffect } from 'react'
+import React, { useState } from 'react'
import { NavLink, useLocation, useNavigate } from 'react-router-dom'
import {
Home,
BookOpen,
- BarChart3,
- Activity,
- Users,
ChevronDown,
- Search,
- Upload,
} from 'lucide-react'
import '../App.css'
const menuItems = [
{ id: 'home', label: '首页', icon: Home, path: '/home' },
{ id: 'notebooks', label: '笔记管理', icon: BookOpen, path: '/notebooks' },
- {
- id: 'dashboard',
- label: '数据看板',
- icon: BarChart3,
- path: '/dashboard',
- submenu: [
- { id: 'overview', label: '账号概况', path: '/dashboard/overview' },
- { id: 'content', label: '内容分析', path: '/dashboard/content' },
- { id: 'fans', label: '粉丝数据', path: '/dashboard/fans' },
- ]
- },
- { id: 'upload-jwlll', label: '智能发布', icon: Upload, path: '/upload-jwlll' },
// { id: 'activity', label: '活动中心', icon: Activity, path: '/activity' },
// { id: 'notes', label: '笔记灵感', icon: BookOpen, path: '/notes' },
// { id: 'creator', label: '创作学院', icon: Users, path: '/creator' },
@@ -38,13 +21,6 @@
const location = useLocation()
const navigate = useNavigate()
- // 打开 dashboard 下拉时保持展开
- useEffect(() => {
- if (location.pathname.startsWith('/dashboard')) {
- setExpandedMenu('dashboard')
- }
- }, [location.pathname])
-
const toggleMenu = item => {
if (item.submenu) {
setExpandedMenu(expandedMenu === item.id ? null : item.id)