修改帖子

Change-Id: I467b35242bce8b27d612eb96f2710b27aa03d1d2
diff --git a/src/pages/AuthPage/AuthPage.jsx b/src/pages/AuthPage/AuthPage.jsx
index 4c51ff9..64f3378 100644
--- a/src/pages/AuthPage/AuthPage.jsx
+++ b/src/pages/AuthPage/AuthPage.jsx
@@ -67,8 +67,9 @@
     if (data.msg === "登录成功" && data.user) {
       // 保存用户信息到 localStorage
       localStorage.setItem("user", JSON.stringify(data.user));
-      localStorage.setItem("userId", data.user.id); // 如果有 id 字段
-      window.location.href = '/forum';
+      localStorage.setItem("userId", data.user.userId); // 如果有 id 字段
+      debugger
+      // window.location.href = '/forum';
     } else {
       throw new Error(data.msg || "登录失败");
     }
diff --git a/src/pages/Forum/posts-detail/PostDetailPage.jsx b/src/pages/Forum/posts-detail/PostDetailPage.jsx
index a505d88..159bdfa 100644
--- a/src/pages/Forum/posts-detail/PostDetailPage.jsx
+++ b/src/pages/Forum/posts-detail/PostDetailPage.jsx
@@ -1,9 +1,9 @@
-import React, { useEffect, useState } from 'react';
+import React, { useContext, useEffect, useState } from 'react';
 import { useParams } from 'wouter';
 import { GoodTwo, Star } from '@icon-park/react';
 import { getPostDetail, getPostComments, likePost, unlikePost, addCommentToPost, collectPost, uncollectPost } from './api'; // 引入你的 API 函数
 import './PostDetailPage.css';
-import { useUser } from '../../../context/UserContext'; // 注意路径
+import { UserContext, useUser } from '../../../context/UserContext'; // 注意路径
 import Header from '../../../components/Header';
 
 const PostDetailPage = () => {
@@ -20,7 +20,8 @@
   const [replyToUsername, setReplyToUsername] = useState(null);
 
   // 获取当前用户ID(假设从上下文中获取)
-  const { user } = useUser(); // 你需要从用户上下文获取用户 ID
+  const { user } = useContext(UserContext);
+  // const { user } = useUser(); // 你需要从用户上下文获取用户 ID
 
   useEffect(() => {
     const fetchPostDetail = async () => {
@@ -69,7 +70,7 @@
     try {
       if (isLiked) {
         // 取消点赞
-        await unlikePost(postId, user.id);
+        await unlikePost(postId, user.userId);
         setIsLiked(false);
         setPostDetail((prev) => ({
           ...prev,
@@ -77,7 +78,7 @@
         }));
       } else {
         // 点赞
-        await likePost(postId, user.id);
+        await likePost(postId, user.userId);
         setIsLiked(true);
         setPostDetail((prev) => ({
           ...prev,
@@ -100,7 +101,7 @@
     try {
         if (isCollected) {
             // 取消收藏 - 使用原有的collectPost函数,传递action: "cancel"
-            await collectPost(postId, user.id, "cancel");
+            await collectPost(postId, user.userId, "cancel");
             setIsCollected(false);
             setPostDetail((prev) => ({
                 ...prev,
@@ -108,7 +109,7 @@
             }));
         } else {
             // 收藏
-            await collectPost(postId, user.id, "collect");
+            await collectPost(postId, user.userId, "collect");
             setIsCollected(true);
             setPostDetail((prev) => ({
                 ...prev,
@@ -121,40 +122,57 @@
     }
 };
 
-  // 添加评论
+// 添加评论
 const handleAddComment = async () => {
+  // 直接使用组件顶层获取的 user
+  if (!user || !user.userId) {
+    alert('请先登录后再评论');
+    return;
+  }
+
   if (!newComment.trim()) {
     alert('评论内容不能为空');
     return;
   }
 
   try {
+    // 构建评论数据
     const commentPayload = {
       content: newComment,
-      userId: user.id,
+      userId: user.userId, // 使用已获取的用户ID
       isAnonymous: false,
       com_comment_id: replyToCommentId || null,
     };
 
+    // 发送评论请求
     const commentData = await addCommentToPost(postId, commentPayload);
 
-    setComments((prev) => [
-      ...prev,
-      {
-        commentId: (commentData && commentData.commentId) || Date.now(),
-        post_id: postId,
-        userId: user.id,
-        content: newComment,
-        commentTime: new Date().toISOString(),
-        comCommentId: replyToCommentId,
-      },
-    ]);
+    // 更新评论列表
+    const newCommentItem = {
+      commentId: commentData?.commentId || Date.now(),
+      post_id: postId,
+      userId: user.userId,
+      content: newComment,
+      commentTime: new Date().toISOString(),
+      comCommentId: replyToCommentId,
+    };
 
+    setComments((prevComments) => [newCommentItem, ...prevComments]);
+
+    // 重置表单
     setNewComment('');
     setReplyToCommentId(null);
-  } catch (err) {
-    console.error('评论添加失败:', err);
-    alert('评论失败,请稍后再试');
+
+    // alert('评论成功!');
+  } catch (error) {
+    console.error('评论失败:', error);
+    
+    const errorMessage = 
+      error.response?.data?.message || 
+      error.message || 
+      '评论失败,请稍后再试';
+      
+    alert(errorMessage);
   }
 };
 
diff --git a/src/pages/Forum/promotion-part/Promotion.jsx b/src/pages/Forum/promotion-part/Promotion.jsx
index 36d6319..49863e3 100644
--- a/src/pages/Forum/promotion-part/Promotion.jsx
+++ b/src/pages/Forum/promotion-part/Promotion.jsx
@@ -1,16 +1,153 @@
+// import React, { useEffect, useState, useRef } from 'react';
+// import './Promotion.css';
+
+// const Promotion = () => {
+//   const [promotions, setPromotions] = useState([]);
+//   const [coldResources, setColdResources] = useState([]);
+//   const [loading, setLoading] = useState(true);
+
+//   const [promoIndex, setPromoIndex] = useState(0);
+//   const [coldIndex, setColdIndex] = useState(0);
+
+//   const promoTimerRef = useRef(null);
+//   const coldTimerRef = useRef(null);
+
+//   useEffect(() => {
+//     fetchData();
+//   }, []);
+
+//   useEffect(() => {
+//     if (promotions.length === 0) return;
+//     clearInterval(promoTimerRef.current);
+//     promoTimerRef.current = setInterval(() => {
+//       setPromoIndex(prev => (prev + 1) % promotions.length);
+//     }, 5000);
+//     return () => clearInterval(promoTimerRef.current);
+//   }, [promotions]);
+
+//   useEffect(() => {
+//     if (coldResources.length === 0) return;
+//     clearInterval(coldTimerRef.current);
+//     coldTimerRef.current = setInterval(() => {
+//       setColdIndex(prev => (prev + 1) % coldResources.length);
+//     }, 5000);
+//     return () => clearInterval(coldTimerRef.current);
+//   }, [coldResources]);
+
+//   const fetchData = async () => {
+//     try {
+//       // ✅ 获取促销活动列表(新接口)
+//       const promoResponse = await fetch(`seeds/promotions`);
+//       const promoJson = await promoResponse.json();
+//       const promoData = Array.isArray(promoJson?.result) ? promoJson.result : [];
+//       setPromotions(promoData);
+
+//       // 冷门资源(接口保持不变,若已更换请提供文档)
+//       const coldResponse = await fetch(`/echo/resources/cold`);
+//       const coldData = await coldResponse.json();
+//       setColdResources(coldData);
+//     } catch (error) {
+//       console.error('获取数据失败:', error);
+//     } finally {
+//       setLoading(false);
+//     }
+//   };
+
+//   if (loading) {
+//     return <div className="promotion-container">加载中...</div>;
+//   }
+
+//   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
+//   const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
+//   const prevCold = () => setColdIndex((coldIndex - 1 + coldResources.length) % coldResources.length);
+//   const nextCold = () => setColdIndex((coldIndex + 1) % coldResources.length);
+
+//   const currentPromo = promotions[promoIndex];
+//   const currentCold = coldResources[coldIndex];
+
+//   return (
+//     <div className="promotion-container carousel-container">
+//       {/* 促销活动轮播 */}
+//       <section className="carousel-section">
+//         <h2>当前促销活动</h2>
+//         {promotions.length === 0 || !currentPromo ? (
+//           <div className="empty-state">暂无促销活动</div>
+//         ) : (
+//           <div
+//             className="carousel"
+//             onMouseEnter={() => clearInterval(promoTimerRef.current)}
+//             onMouseLeave={() => {
+//               promoTimerRef.current = setInterval(() => {
+//                 setPromoIndex(prev => (prev + 1) % promotions.length);
+//               }, 3000);
+//             }}
+//           >
+//             <button className="arrow left" onClick={prevPromo}>&lt;</button>
+//             <div className="slide">
+//               <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
+//               <div><strong>促销时间:</strong>
+//                 {currentPromo?.pStartTime && currentPromo?.pEndTime
+//                   ? `${new Date(currentPromo.pStartTime).toLocaleString()} ~ ${new Date(currentPromo.pEndTime).toLocaleString()}`
+//                   : '未知'}
+//               </div>
+//               <div><strong>上传奖励系数:</strong>{currentPromo?.uploadCoeff ?? '无'}</div>
+//               <div><strong>下载折扣系数:</strong>{currentPromo?.downloadCoeff ?? '无'}</div>
+//               {currentPromo?.description && (
+//                 <div><strong>描述:</strong>{currentPromo.description}</div>
+//               )}
+//             </div>
+//             <button className="arrow right" onClick={nextPromo}>&gt;</button>
+//           </div>
+//         )}
+//       </section>
+
+//       {/* 冷门资源轮播 */}
+//       <section className="carousel-section">
+//         <h2>冷门资源推荐</h2>
+//         {coldResources.length === 0 || !currentCold ? (
+//           <div className="empty-state">暂无冷门资源推荐</div>
+//         ) : (
+//           <div
+//             className="carousel"
+//             onMouseEnter={() => clearInterval(coldTimerRef.current)}
+//             onMouseLeave={() => {
+//               coldTimerRef.current = setInterval(() => {
+//                 setColdIndex(prev => (prev + 1) % coldResources.length);
+//               }, 3000);
+//             }}
+//           >
+//             <button className="arrow left" onClick={prevCold}>&lt;</button>
+//             <div className="slide cold-slide">
+//               <img src={currentCold.poster} alt={currentCold.title} className="resource-poster" />
+//               <div className="resource-info">
+//                 <div><strong>标题:</strong>{currentCold.title}</div>
+//                 <div><strong>下载量:</strong>{currentCold.download_count ?? 0} | <strong>种子数:</strong>{currentCold.seed_count ?? 0}</div>
+//                 <div><strong>激励:</strong>
+//                   {currentCold.incentives?.download_exempt && <span className="incentive-badge">免下载量</span>}
+//                   {currentCold.incentives?.extra_seed_bonus && <span className="incentive-badge">做种加成</span>}
+//                   {!currentCold.incentives?.download_exempt && !currentCold.incentives?.extra_seed_bonus && '无'}
+//                 </div>
+//               </div>
+//             </div>
+//             <button className="arrow right" onClick={nextCold}>&gt;</button>
+//           </div>
+//         )}
+//       </section>
+//     </div>
+//   );
+// };
+
+// export default Promotion;
+
 import React, { useEffect, useState, useRef } from 'react';
 import './Promotion.css';
 
 const Promotion = () => {
   const [promotions, setPromotions] = useState([]);
-  const [coldResources, setColdResources] = useState([]);
   const [loading, setLoading] = useState(true);
 
   const [promoIndex, setPromoIndex] = useState(0);
-  const [coldIndex, setColdIndex] = useState(0);
-
   const promoTimerRef = useRef(null);
-  const coldTimerRef = useRef(null);
 
   useEffect(() => {
     fetchData();
@@ -25,29 +162,16 @@
     return () => clearInterval(promoTimerRef.current);
   }, [promotions]);
 
-  useEffect(() => {
-    if (coldResources.length === 0) return;
-    clearInterval(coldTimerRef.current);
-    coldTimerRef.current = setInterval(() => {
-      setColdIndex(prev => (prev + 1) % coldResources.length);
-    }, 5000);
-    return () => clearInterval(coldTimerRef.current);
-  }, [coldResources]);
-
   const fetchData = async () => {
     try {
       // ✅ 获取促销活动列表(新接口)
-      const promoResponse = await fetch(`seeds/promotions`);
+      const promoResponse = await fetch(`/seeds/promotions`);
       const promoJson = await promoResponse.json();
+      console.log('接口返回数据:', promoJson);
       const promoData = Array.isArray(promoJson?.result) ? promoJson.result : [];
       setPromotions(promoData);
-
-      // 冷门资源(接口保持不变,若已更换请提供文档)
-      const coldResponse = await fetch(`/echo/resources/cold`);
-      const coldData = await coldResponse.json();
-      setColdResources(coldData);
     } catch (error) {
-      console.error('获取数据失败:', error);
+      console.error('获取促销活动失败:', error);
     } finally {
       setLoading(false);
     }
@@ -59,11 +183,7 @@
 
   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
   const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
-  const prevCold = () => setColdIndex((coldIndex - 1 + coldResources.length) % coldResources.length);
-  const nextCold = () => setColdIndex((coldIndex + 1) % coldResources.length);
-
   const currentPromo = promotions[promoIndex];
-  const currentCold = coldResources[coldIndex];
 
   return (
     <div className="promotion-container carousel-container">
@@ -100,39 +220,6 @@
           </div>
         )}
       </section>
-
-      {/* 冷门资源轮播 */}
-      <section className="carousel-section">
-        <h2>冷门资源推荐</h2>
-        {coldResources.length === 0 || !currentCold ? (
-          <div className="empty-state">暂无冷门资源推荐</div>
-        ) : (
-          <div
-            className="carousel"
-            onMouseEnter={() => clearInterval(coldTimerRef.current)}
-            onMouseLeave={() => {
-              coldTimerRef.current = setInterval(() => {
-                setColdIndex(prev => (prev + 1) % coldResources.length);
-              }, 3000);
-            }}
-          >
-            <button className="arrow left" onClick={prevCold}>&lt;</button>
-            <div className="slide cold-slide">
-              <img src={currentCold.poster} alt={currentCold.title} className="resource-poster" />
-              <div className="resource-info">
-                <div><strong>标题:</strong>{currentCold.title}</div>
-                <div><strong>下载量:</strong>{currentCold.download_count ?? 0} | <strong>种子数:</strong>{currentCold.seed_count ?? 0}</div>
-                <div><strong>激励:</strong>
-                  {currentCold.incentives?.download_exempt && <span className="incentive-badge">免下载量</span>}
-                  {currentCold.incentives?.extra_seed_bonus && <span className="incentive-badge">做种加成</span>}
-                  {!currentCold.incentives?.download_exempt && !currentCold.incentives?.extra_seed_bonus && '无'}
-                </div>
-              </div>
-            </div>
-            <button className="arrow right" onClick={nextCold}>&gt;</button>
-          </div>
-        )}
-      </section>
     </div>
   );
 };
diff --git a/src/pages/FriendMoments/FriendMoments.jsx b/src/pages/FriendMoments/FriendMoments.jsx
index 6d9def6..fd017b2 100644
--- a/src/pages/FriendMoments/FriendMoments.jsx
+++ b/src/pages/FriendMoments/FriendMoments.jsx
@@ -4,69 +4,66 @@
 import Header from '../../components/Header';
 import { Edit } from '@icon-park/react';
 
-
-const USER_ID = 456;
-
 const FriendMoments = () => {
   const [feeds, setFeeds] = useState([]);
   const [filteredFeeds, setFilteredFeeds] = useState([]);
   const [query, setQuery] = useState('');
+  const [userId, setUserId] = useState(456); // 从状态管理或登录信息获取
 
   // Modal state & form fields
   const [showModal, setShowModal] = useState(false);
   const [title, setTitle] = useState('');
   const [content, setContent] = useState('');
-  const [previewUrls, setPreviewUrls] = useState([]);
-  const [images, setImages] = useState([]);
+  const [selectedImages, setSelectedImages] = useState([]);
+  const [previewUrls, setPreviewUrls] = useState([]); // 新增:图片预览URLs
 
   // 拉取好友动态列表
   const fetchFeeds = async () => {
     try {
-      const res = await axios.get(`/echo/users/${USER_ID}/feeds`);
-      setFeeds(res.data.feeds);
-      setFilteredFeeds(res.data.feeds);
+      // 修改为新的API路径
+      const res = await axios.get(`/echo/dynamic/${userId}/getAllDynamics`);
+      setFeeds(res.data.posts || []);
+      setFilteredFeeds(res.data.posts || []);
     } catch (err) {
       console.error('获取动态列表失败:', err);
+      alert('获取动态列表失败,请稍后重试');
     }
   };
 
   useEffect(() => {
     fetchFeeds();
-  }, []);
+  }, [userId]);
 
   // 搜索处理
   const handleSearch = () => {
     const q = query.trim().toLowerCase();
-    if (!q) return;
+    if (!q) {
+      setFilteredFeeds(feeds);
+      return;
+    }
     setFilteredFeeds(
-      feeds.filter(f => (f.title || '').toLowerCase().includes(q))
+      feeds.filter(f => 
+        (f.title || '').toLowerCase().includes(q) || 
+        (f.postContent || '').toLowerCase().includes(q)
+      )
     );
   };
+
   const handleReset = () => {
     setQuery('');
     setFilteredFeeds(feeds);
   };
 
-  // 对话框内:本地预览 & 上传
-  const handleImageChange = async (e) => {
+  // 对话框内:处理图片选择
+  const handleImageChange = (e) => {
     const files = Array.from(e.target.files);
     if (!files.length) return;
-    setPreviewUrls(files.map(f => URL.createObjectURL(f)));
-    try {
-      const uploaded = await Promise.all(files.map(f => uploadImageToServer(f)));
-      setImages(uploaded);
-    } catch (err) {
-      console.error('图片上传失败', err);
-      alert('图片上传失败,请重试');
-    }
-  };
-  const uploadImageToServer = async (file) => {
-    const fd = new FormData();
-    fd.append('file', file);
-    const res = await axios.post(`/upload`, fd, {
-      headers: {'Content-Type':'multipart/form-data'}
-    });
-    return res.data.url;
+    
+    // 生成预览URLs
+    const previewUrls = files.map(file => URL.createObjectURL(file));
+    
+    setSelectedImages(files);
+    setPreviewUrls(previewUrls); // 更新预览URLs
   };
 
   // 对话框内:提交新动态
@@ -75,18 +72,33 @@
       alert('内容不能为空');
       return;
     }
+
     try {
-      await axios.post(
-        `/echo/users/${USER_ID}/createFeed`,
-        { title: title.trim() || undefined, friend_content: content.trim(), images }
-      );
+      const formData = new FormData();
+      formData.append('user_id', userId);
+      formData.append('title', title.trim() || '');
+      formData.append('content', content.trim());
+      
+      // 添加图片文件
+      selectedImages.forEach((file, index) => {
+        formData.append('image_url', file);
+      });
+
+      // 修改为新的API路径
+      await axios.post(`/echo/dynamic/${userId}/createDynamic`, formData, {
+        headers: {
+          'Content-Type': 'multipart/form-data'
+        }
+      });
+
       // 重置表单
       setTitle('');
       setContent('');
-      setImages([]);
-      setPreviewUrls([]);
+      setSelectedImages([]);
+      setPreviewUrls([]); // 重置预览URLs
       setShowModal(false);
       fetchFeeds();
+      alert('发布成功');
     } catch (err) {
       console.error('发布失败', err);
       alert('发布失败,请稍后重试');
@@ -94,14 +106,75 @@
   };
 
   // 删除动态
-  const handleDelete = async (feedId) => {
+  const handleDelete = async (dynamicId) => {
     if (!window.confirm('确定要删除这条动态吗?')) return;
     try {
-      await axios.delete(`/echo/users/me/feed/${feedId}`);
+      // 修改为新的API路径
+      await axios.delete(`/echo/dynamic/me/deleteDynamic/${dynamicId}`);
       fetchFeeds();
+      alert('删除成功');
     } catch (err) {
       console.error('删除失败', err);
-      alert('删除失败');
+      alert('删除失败,请稍后重试');
+    }
+  };
+
+  // 点赞动态
+  const handleLike = async (dynamicId) => {
+    try {
+      // 调用新的点赞API
+      const res = await axios.post(`/echo/dynamic/like`, {
+        userId,
+        dynamicId
+      });
+      
+      if (res.status === 200) {
+        // 更新本地状态
+        setFeeds(feeds.map(feed => {
+          if (feed.postNo === dynamicId) {
+            return {
+              ...feed,
+              postLikeNum: (feed.postLikeNum || 0) + 1,
+              liked: true
+            };
+          }
+          return feed;
+        }));
+      } else {
+        alert(res.data.message || '点赞失败');
+      }
+    } catch (err) {
+      console.error('点赞失败', err);
+      alert('点赞失败,请稍后重试');
+    }
+  };
+
+  // 取消点赞
+  const handleUnlike = async (dynamicId) => {
+    try {
+      // 调用新的取消点赞API
+      const res = await axios.delete(`/echo/dynamic/unlike`, {
+        data: { userId, dynamicId }
+      });
+      
+      if (res.status === 200) {
+        // 更新本地状态
+        setFeeds(feeds.map(feed => {
+          if (feed.postNo === dynamicId) {
+            return {
+              ...feed,
+              postLikeNum: Math.max(0, (feed.postLikeNum || 0) - 1),
+              liked: false
+            };
+          }
+          return feed;
+        }));
+      } else {
+        alert(res.data.message || '取消点赞失败');
+      }
+    } catch (err) {
+      console.error('取消点赞失败', err);
+      alert('取消点赞失败,请稍后重试');
     }
   };
 
@@ -128,13 +201,13 @@
 
       <div className="feed-list">
         {filteredFeeds.map(feed => (
-          <div className="feed-item" key={feed.feed_id}>
+          <div className="feed-item" key={feed.postNo}>
             {feed.title && <h4>{feed.title}</h4>}
-            <p>{feed.friend_content}</p>
+            <p>{feed.postContent}</p>
 
-            {feed.images?.length > 0 && (
+            {feed.imageUrl && feed.imageUrl.split(',').length > 0 && (
               <div className="feed-images">
-                {feed.images.map((url, i) => (
+                {feed.imageUrl.split(',').map((url, i) => (
                   <img key={i} src={url} alt={`动态图${i}`} />
                 ))}
               </div>
@@ -142,10 +215,21 @@
 
             <div className="feed-footer">
               <span className="feed-date">
-                {new Date(feed.created_at).toLocaleString()}
+                {new Date(feed.postTime).toLocaleString()}
               </span>
-              {feed.is_mine && (
-                <button className="delete-btn" onClick={() => handleDelete(feed.feed_id)}>
+              <div className="like-container">
+                {feed.liked ? (
+                  <button className="unlike-btn" onClick={() => handleUnlike(feed.postNo)}>
+                    已点赞 ({feed.postLikeNum || 0})
+                  </button>
+                ) : (
+                  <button className="like-btn" onClick={() => handleLike(feed.postNo)}>
+                    点赞 ({feed.postLikeNum || 0})
+                  </button>
+                )}
+              </div>
+              {feed.user_id === userId && (
+                <button className="delete-btn" onClick={() => handleDelete(feed.postNo)}>
                   删除
                 </button>
               )}
@@ -170,12 +254,6 @@
               value={content}
               onChange={e => setContent(e.target.value)}
             />
-            {/* <input
-              type="file"
-              accept="image/*"
-              multiple
-              onChange={handleImageChange}
-            /> */}
             <label className="file-label">
               选择图片
               <input
@@ -187,7 +265,7 @@
               />
             </label>
             <div className="cf-preview">
-              {previewUrls.map((url, i) => (
+              {previewUrls.map((url, i) => ( // 使用定义的previewUrls
                 <img key={i} src={url} alt={`预览${i}`} />
               ))}
             </div>
@@ -206,5 +284,4 @@
   );
 };
 
-export default FriendMoments;
-
+export default FriendMoments;
\ No newline at end of file