前端封装是否点赞的api

Change-Id: I69e915f6c9aa1a8f721ff38a4df58dd8fdfc6e0a
diff --git a/Merge/back_wzy/routes/posts.py b/Merge/back_wzy/routes/posts.py
index 105900c..4d8be1e 100644
--- a/Merge/back_wzy/routes/posts.py
+++ b/Merge/back_wzy/routes/posts.py
@@ -1,10 +1,10 @@
 # routes/posts.py
 
 from flask import Blueprint, request, jsonify, abort
-from extensions    import db
-from models.post   import Post
+from extensions      import db
+from models.post     import Post
 from models.behavior import Behavior
-from utils.Fpost import Fpost
+from utils.Fpost     import Fpost
 import json
 
 posts_bp = Blueprint('posts', __name__)
@@ -12,25 +12,22 @@
 @posts_bp.route('', methods=['POST'])
 def create_post():
     try:
-        # 获取文本字段
-        user_id = request.form.get('user_id')
-        title = request.form.get('title')
-        content = request.form.get('content')
-        status = request.form.get('status', 'published')
-        topic_id = request.form.get('topic_id')
+        user_id     = request.form.get('user_id')
+        title       = request.form.get('title')
+        content     = request.form.get('content')
+        status      = request.form.get('status', 'published')
+        topic_id    = request.form.get('topic_id')
         media_count = int(request.form.get('media_count', 0))
-        
+
         if not user_id or not title or not content:
             return jsonify({'error': '缺少必要字段'}), 400
-        
-        # 获取上传的文件
+
         files = []
         for i in range(media_count):
-            file_key = f'media_{i}'
-            if file_key in request.files:
-                files.append(request.files[file_key])
-        
-        # 使用 Fpost 创建帖子
+            key = f'media_{i}'
+            if key in request.files:
+                files.append(request.files[key])
+
         fpost = Fpost(db.session)
         new_post = fpost.create_post_with_files(
             user_id=int(user_id),
@@ -40,18 +37,18 @@
             status=status,
             files=files
         )
-        
+
         return jsonify({'id': new_post.id}), 201
-        
+
     except Exception as e:
         return jsonify({'error': str(e)}), 500
 
+
 @posts_bp.route('', methods=['GET'])
 def list_posts():
     """
-    获取帖子列表,支持:
-      - GET /posts              返回所有已发布帖子
-      - GET /posts?user_id=123  返回指定用户 user_id 的所有帖子
+    GET /posts            -> 全部已发布帖子
+    GET /posts?user_id=xx -> 指定用户的所有帖子
     """
     user_id = request.args.get('user_id', type=int)
     query = Post.query
@@ -61,94 +58,90 @@
         query = query.filter_by(status='published')
 
     posts = query.all()
-
     return jsonify([{
-        'id': p.id,
-        'title': p.title,
-        'status': p.status,                     # 新增 status 字段
-        'heat': p.heat,
-        'created_at': p.created_at.isoformat()
+        'id'         : p.id,
+        'title'      : p.title,
+        'status'     : p.status,
+        'heat'       : p.heat,
+        'created_at' : p.created_at.isoformat()
     } for p in posts])
 
+
 @posts_bp.route('/<int:post_id>', methods=['GET'])
 def get_post(post_id):
     post = Post.query.get_or_404(post_id)
     return jsonify({
-        'id': post.id,
-        'user_id': post.user_id,
-        'topic_id': post.topic_id,
-        'title': post.title,
-        'content': post.content,
-        'media_urls': post.media_urls,
-        'status': post.status,
-        'heat': post.heat,
-        'created_at': post.created_at.isoformat(),
-        'updated_at': post.updated_at.isoformat()
+        'id'          : post.id,
+        'user_id'     : post.user_id,
+        'topic_id'    : post.topic_id,
+        'title'       : post.title,
+        'content'     : post.content,
+        'media_urls'  : post.media_urls,
+        'status'      : post.status,
+        'heat'        : post.heat,
+        'created_at'  : post.created_at.isoformat(),
+        'updated_at'  : post.updated_at.isoformat()
     })
 
+
 @posts_bp.route('/<int:post_id>', methods=['PUT'])
 def update_post(post_id):
     """
-    修改帖子字段(可选字段:title, content, topic_id, media_urls, status)
-    支持FormData和JSON两种格式
+    支持 FormData 和 JSON 两种格式更新:
+      - multipart/form-data 时可上传新文件并保留 existing_media_urls
+      - application/json 时只修改字段
     """
     try:
         fpost = Fpost(db.session)
-        
-        # 检查是否是FormData请求
+
         if request.content_type and 'multipart/form-data' in request.content_type:
-            # FormData请求
-            title = request.form.get('title')
-            content = request.form.get('content')
-            status = request.form.get('status')
+            title    = request.form.get('title')
+            content  = request.form.get('content')
+            status   = request.form.get('status')
             topic_id = request.form.get('topic_id')
-            media_count = int(request.form.get('media_count', 0))
-            existing_media_urls_str = request.form.get('existing_media_urls')
-            
-            # 解析现有媒体URLs
+            count    = int(request.form.get('media_count', 0))
+            existing = request.form.get('existing_media_urls')
+
             existing_media_urls = None
-            if existing_media_urls_str:
+            if existing:
                 try:
-                    existing_media_urls = json.loads(existing_media_urls_str)
+                    existing_media_urls = json.loads(existing)
                 except:
                     existing_media_urls = None
-            
-            # 获取新上传的文件
+
             files = []
-            for i in range(media_count):
-                file_key = f'media_{i}'
-                if file_key in request.files:
-                    files.append(request.files[file_key])
-            
-            # 更新帖子
-            updated_post = fpost.update_post_with_files(
+            for i in range(count):
+                key = f'media_{i}'
+                if key in request.files:
+                    files.append(request.files[key])
+
+            updated = fpost.update_post_with_files(
                 post_id=post_id,
                 title=title,
                 content=content,
                 topic_id=int(topic_id) if topic_id else None,
                 status=status,
-                files=files if files else None,
+                files=files or None,
                 existing_media_urls=existing_media_urls
             )
-            
         else:
-            # JSON请求(保持原有逻辑)
             post = Post.query.get_or_404(post_id)
             data = request.get_json() or {}
-            for key in ('title', 'content', 'topic_id', 'media_urls', 'status'):
-                if key in data:
-                    setattr(post, key, data[key])
+            for field in ('title','content','topic_id','media_urls','status'):
+                if field in data:
+                    setattr(post, field, data[field])
             db.session.commit()
-            updated_post = post
-        
-        if not updated_post:
+            updated = post
+
+        if not updated:
             return jsonify({'error': '帖子不存在'}), 404
-            
+
         return '', 204
-        
+
     except Exception as e:
         return jsonify({'error': str(e)}), 500
 
+
 @posts_bp.route('/<int:post_id>', methods=['DELETE'])
 def delete_post(post_id):
     post = Post.query.get_or_404(post_id)
@@ -156,89 +149,14 @@
     db.session.commit()
     return '', 204
 
-@posts_bp.route('/<int:post_id>/<action>', methods=['POST'])
-def post_action(post_id, action):
-    """
-    支持的 action: like, favorite, view, share
-    对于 like 和 favorite,保证每个用户每帖只做一次。
-    """
-    if action not in ('like', 'favorite', 'view', 'share'):
-        abort(400, 'Invalid action')
 
-    data = request.get_json() or {}
-    user_id = data.get('user_id')
-    if not user_id:
-        abort(400, 'user_id required')
-
-    # 对 like/favorite 做去重检查
-    if action in ('like', 'favorite'):
-        exists = Behavior.query.filter_by(
-            user_id=user_id,
-            post_id=post_id,
-            type=action
-        ).first()
-        if exists:
-            return jsonify({'error': f'already {action}d'}), 400
-
-    # 创建行为记录
-    beh = Behavior(user_id=user_id, post_id=post_id, type=action)
-    db.session.add(beh)
-
-    # 更新热度
-    post = Post.query.get_or_404(post_id)
-    post.heat += 1
-
-    db.session.commit()
-    return '', 201
-
-@posts_bp.route('/<int:post_id>/like', methods=['DELETE'])
-def unlike(post_id):
-    user_id = request.get_json(silent=True) and request.get_json().get('user_id')
-    if not user_id:
-        abort(400, 'user_id required')
-    # 查找已有的 like 行为
-    beh = Behavior.query.filter_by(
-        user_id=user_id,
-        post_id=post_id,
-        type='like'
-    ).first()
-    if not beh:
-        return jsonify({'error': 'not liked yet'}), 400
-
-    db.session.delete(beh)
-    # 更新热度,确保不降到负数
-    post = Post.query.get_or_404(post_id)
-    post.heat = max(post.heat - 1, 0)
-    db.session.commit()
-    return '', 204
-
-@posts_bp.route('/<int:post_id>/favorite', methods=['DELETE'])
-def unfavorite(post_id):
-    user_id = request.get_json(silent=True) and request.get_json().get('user_id')
-    if not user_id:
-        abort(400, 'user_id required')
-    # 查找已有的 favorite 行为
-    beh = Behavior.query.filter_by(
-        user_id=user_id,
-        post_id=post_id,
-        type='favorite'
-    ).first()
-    if not beh:
-        return jsonify({'error': 'not favorited yet'}), 400
-
-    db.session.delete(beh)
-    # 更新热度
-    post = Post.query.get_or_404(post_id)
-    post.heat = max(post.heat - 1, 0)
-    db.session.commit()
-    return '', 204
+# —— 显式的 like/favorite 删除和查询路由,放在泛用 action 路由之前 —— #
 
 @posts_bp.route('/<int:post_id>/like', methods=['GET'])
 def has_liked(post_id):
     """
-    检查指定 user_id 是否对 post_id 点过赞。
-    GET /posts/<post_id>/like?user_id=123
-    返回 { "liked": true } 或 { "liked": false }
+    GET /posts/<post_id>/like?user_id=xx
+    返回 { "liked": true/false }
     """
     user_id = request.args.get('user_id', type=int)
     if not user_id:
@@ -251,3 +169,85 @@
     ).first() is not None
 
     return jsonify({'liked': exists}), 200
+
+
+@posts_bp.route('/<int:post_id>/like', methods=['DELETE'])
+def unlike(post_id):
+    data    = request.get_json(silent=True) or {}
+    user_id = data.get('user_id')
+    if not user_id:
+        abort(400, 'user_id required')
+
+    beh = Behavior.query.filter_by(
+        user_id=user_id,
+        post_id=post_id,
+        type='like'
+    ).first()
+    if not beh:
+        return jsonify({'error': 'not liked yet'}), 400
+
+    db.session.delete(beh)
+    post = Post.query.get_or_404(post_id)
+    post.heat = max(post.heat - 1, 0)
+    db.session.commit()
+    return '', 204
+
+
+@posts_bp.route('/<int:post_id>/favorite', methods=['DELETE'])
+def unfavorite(post_id):
+    data    = request.get_json(silent=True) or {}
+    user_id = data.get('user_id')
+    if not user_id:
+        abort(400, 'user_id required')
+
+    beh = Behavior.query.filter_by(
+        user_id=user_id,
+        post_id=post_id,
+        type='favorite'
+    ).first()
+    if not beh:
+        return jsonify({'error': 'not favorited yet'}), 400
+
+    db.session.delete(beh)
+    post = Post.query.get_or_404(post_id)
+    post.heat = max(post.heat - 1, 0)
+    db.session.commit()
+    return '', 204
+
+
+# —— 泛用 action 路由,仅处理 POST /posts/<id>/(like|favorite|view|share) —— #
+
+@posts_bp.route('/<int:post_id>/<action>', methods=['POST'])
+def post_action(post_id, action):
+    """
+    支持 action: like, favorite, view, share,
+    对 like/favorite 做幂等去重检查。
+    """
+    if action not in ('like','favorite','view','share'):
+        abort(400, 'Invalid action')
+
+    data    = request.get_json() or {}
+    user_id = data.get('user_id')
+    if not user_id:
+        abort(400, 'user_id required')
+
+    # 幂等检查
+    if action in ('like','favorite'):
+        exists = Behavior.query.filter_by(
+            user_id=user_id,
+            post_id=post_id,
+            type=action
+        ).first()
+        if exists:
+            return jsonify({'error': f'already {action}d'}), 400
+
+    # 记录行为
+    beh = Behavior(user_id=user_id, post_id=post_id, type=action)
+    db.session.add(beh)
+
+    # 更新热度
+    post = Post.query.get_or_404(post_id)
+    post.heat += 1
+
+    db.session.commit()
+    return '', 201
diff --git a/Merge/front/src/api/search_jwlll.js b/Merge/front/src/api/search_jwlll.js
index 01b743f..e18efa3 100644
--- a/Merge/front/src/api/search_jwlll.js
+++ b/Merge/front/src/api/search_jwlll.js
@@ -79,6 +79,17 @@
     })
   },
 
+  // 查看是否点赞
+  hasLiked: async (postId, userId) => {
+    const res = await request(
+      `${WZY_BASE_URL}/posts/${postId}/like?user_id=${userId}`,
+      {
+        method: 'GET'
+      }
+    )
+    return res.liked
+  },
+
   // 添加评论
   addComment: async (postId, userId, content) => {
     return await request(`${WZY_BASE_URL}/posts/${postId}/comments`, {
diff --git a/WZY/xhs_server/__pycache__/config.cpython-312.pyc b/WZY/xhs_server/__pycache__/config.cpython-312.pyc
index bd10f35..189ac81 100644
--- a/WZY/xhs_server/__pycache__/config.cpython-312.pyc
+++ b/WZY/xhs_server/__pycache__/config.cpython-312.pyc
Binary files differ
diff --git a/WZY/xhs_server/routes/__pycache__/posts.cpython-312.pyc b/WZY/xhs_server/routes/__pycache__/posts.cpython-312.pyc
index f301c84..2e58010 100644
--- a/WZY/xhs_server/routes/__pycache__/posts.cpython-312.pyc
+++ b/WZY/xhs_server/routes/__pycache__/posts.cpython-312.pyc
Binary files differ
diff --git a/WZY/xhs_server/routes/posts.py b/WZY/xhs_server/routes/posts.py
index cb7e266..d4994c1 100644
--- a/WZY/xhs_server/routes/posts.py
+++ b/WZY/xhs_server/routes/posts.py
@@ -1,26 +1,31 @@
 # routes/posts.py
 
 from flask import Blueprint, request, jsonify, abort
-from extensions    import db
-from models.post   import Post
+from extensions      import db
+from models.post     import Post
 from models.behavior import Behavior
 
 posts_bp = Blueprint('posts', __name__)
 
 @posts_bp.route('', methods=['POST'])
 def create_post():
+    """
+    POST /posts
+    接收 application/json 格式:
+        { "user_id": ..., "title": ..., "content": ..., "topic_id": ..., "media_urls": [...], "status": ... }
+    """
     data = request.get_json() or {}
     post = Post(**data)
     db.session.add(post)
     db.session.commit()
     return jsonify({'id': post.id}), 201
 
+
 @posts_bp.route('', methods=['GET'])
 def list_posts():
     """
-    获取帖子列表,支持:
-      - GET /posts              返回所有已发布帖子
-      - GET /posts?user_id=123  返回指定用户 user_id 的所有帖子
+    GET /posts             → 返回所有已发布帖子
+    GET /posts?user_id=xx  → 返回指定用户的所有帖子
     """
     user_id = request.args.get('user_id', type=int)
     query = Post.query
@@ -28,36 +33,43 @@
         query = query.filter_by(user_id=user_id)
     else:
         query = query.filter_by(status='published')
-    posts = query.all()
 
+    posts = query.all()
     return jsonify([{
-        'id': p.id,
-        'title': p.title,
-        'heat': p.heat,
-        'status': p.status,
-        'created_at': p.created_at.isoformat()
+        'id'         : p.id,
+        'title'      : p.title,
+        'status'     : p.status,
+        'heat'       : p.heat,
+        'created_at' : p.created_at.isoformat()
     } for p in posts])
 
+
 @posts_bp.route('/<int:post_id>', methods=['GET'])
 def get_post(post_id):
+    """
+    GET /posts/<post_id>
+    """
     post = Post.query.get_or_404(post_id)
     return jsonify({
-        'id': post.id,
-        'user_id': post.user_id,
-        'topic_id': post.topic_id,
-        'title': post.title,
-        'content': post.content,
-        'media_urls': post.media_urls,
-        'status': post.status,
-        'heat': post.heat,
-        'created_at': post.created_at.isoformat(),
-        'updated_at': post.updated_at.isoformat()
+        'id'          : post.id,
+        'user_id'     : post.user_id,
+        'topic_id'    : post.topic_id,
+        'title'       : post.title,
+        'content'     : post.content,
+        'media_urls'  : post.media_urls,
+        'status'      : post.status,
+        'heat'        : post.heat,
+        'created_at'  : post.created_at.isoformat(),
+        'updated_at'  : post.updated_at.isoformat()
     })
 
+
 @posts_bp.route('/<int:post_id>', methods=['PUT'])
 def update_post(post_id):
     """
-    修改帖子字段(可选字段:title, content, topic_id, media_urls, status)
+    PUT /posts/<post_id>
+    接收 application/json 格式,可选字段:
+        title, content, topic_id, media_urls, status
     """
     post = Post.query.get_or_404(post_id)
     data = request.get_json() or {}
@@ -67,28 +79,104 @@
     db.session.commit()
     return '', 204
 
+
 @posts_bp.route('/<int:post_id>', methods=['DELETE'])
 def delete_post(post_id):
+    """
+    DELETE /posts/<post_id>
+    """
     post = Post.query.get_or_404(post_id)
     db.session.delete(post)
     db.session.commit()
     return '', 204
 
-@posts_bp.route('/<int:post_id>/<action>', methods=['POST'])
-def post_action(post_id, action):
-    """
-    支持的 action: like, favorite, view, share
-    对于 like 和 favorite,保证每个用户每帖只做一次。
-    """
-    if action not in ('like', 'favorite', 'view', 'share'):
-        abort(400, 'Invalid action')
 
-    data = request.get_json() or {}
+@posts_bp.route('/<int:post_id>/like', methods=['GET'])
+def has_liked(post_id):
+    """
+    GET /posts/<post_id>/like?user_id=xx
+    返回 { "liked": true/false }
+    """
+    user_id = request.args.get('user_id', type=int)
+    if not user_id:
+        abort(400, 'user_id required')
+
+    exists = Behavior.query.filter_by(
+        user_id=user_id,
+        post_id=post_id,
+        type='like'
+    ).first() is not None
+
+    return jsonify({'liked': exists}), 200
+
+
+@posts_bp.route('/<int:post_id>/like', methods=['DELETE'])
+def unlike(post_id):
+    """
+    DELETE /posts/<post_id>/like
+    body: { "user_id": xx }
+    """
+    data    = request.get_json(silent=True) or {}
     user_id = data.get('user_id')
     if not user_id:
         abort(400, 'user_id required')
 
-    # 对 like/favorite 做去重检查
+    beh = Behavior.query.filter_by(
+        user_id=user_id,
+        post_id=post_id,
+        type='like'
+    ).first()
+    if not beh:
+        return jsonify({'error': 'not liked yet'}), 400
+
+    db.session.delete(beh)
+    post = Post.query.get_or_404(post_id)
+    post.heat = max(post.heat - 1, 0)
+    db.session.commit()
+    return '', 204
+
+
+@posts_bp.route('/<int:post_id>/favorite', methods=['DELETE'])
+def unfavorite(post_id):
+    """
+    DELETE /posts/<post_id>/favorite
+    body: { "user_id": xx }
+    """
+    data    = request.get_json(silent=True) or {}
+    user_id = data.get('user_id')
+    if not user_id:
+        abort(400, 'user_id required')
+
+    beh = Behavior.query.filter_by(
+        user_id=user_id,
+        post_id=post_id,
+        type='favorite'
+    ).first()
+    if not beh:
+        return jsonify({'error': 'not favorited yet'}), 400
+
+    db.session.delete(beh)
+    post = Post.query.get_or_404(post_id)
+    post.heat = max(post.heat - 1, 0)
+    db.session.commit()
+    return '', 204
+
+
+@posts_bp.route('/<int:post_id>/<action>', methods=['POST'])
+def post_action(post_id, action):
+    """
+    POST /posts/<post_id>/<action>
+    支持 action: like, favorite, view, share
+    """
+    if action not in ('like', 'favorite', 'view', 'share'):
+        abort(400, 'Invalid action')
+
+    data    = request.get_json() or {}
+    user_id = data.get('user_id')
+    if not user_id:
+        abort(400, 'user_id required')
+
+    # 幂等检查
     if action in ('like', 'favorite'):
         exists = Behavior.query.filter_by(
             user_id=user_id,
@@ -108,64 +196,3 @@
 
     db.session.commit()
     return '', 201
-
-@posts_bp.route('/<int:post_id>/like', methods=['DELETE'])
-def unlike(post_id):
-    user_id = request.get_json(silent=True) and request.get_json().get('user_id')
-    if not user_id:
-        abort(400, 'user_id required')
-    # 查找已有的 like 行为
-    beh = Behavior.query.filter_by(
-        user_id=user_id,
-        post_id=post_id,
-        type='like'
-    ).first()
-    if not beh:
-        return jsonify({'error': 'not liked yet'}), 400
-
-    db.session.delete(beh)
-    # 更新热度,确保不降到负数
-    post = Post.query.get_or_404(post_id)
-    post.heat = max(post.heat - 1, 0)
-    db.session.commit()
-    return '', 204
-
-@posts_bp.route('/<int:post_id>/favorite', methods=['DELETE'])
-def unfavorite(post_id):
-    user_id = request.get_json(silent=True) and request.get_json().get('user_id')
-    if not user_id:
-        abort(400, 'user_id required')
-    # 查找已有的 favorite 行为
-    beh = Behavior.query.filter_by(
-        user_id=user_id,
-        post_id=post_id,
-        type='favorite'
-    ).first()
-    if not beh:
-        return jsonify({'error': 'not favorited yet'}), 400
-
-    db.session.delete(beh)
-    # 更新热度
-    post = Post.query.get_or_404(post_id)
-    post.heat = max(post.heat - 1, 0)
-    db.session.commit()
-    return '', 204
-
-@posts_bp.route('/<int:post_id>/like', methods=['GET'])
-def has_liked(post_id):
-    """
-    检查指定 user_id 是否对 post_id 点过赞。
-    GET /posts/<post_id>/like?user_id=123
-    返回 { "liked": true } 或 { "liked": false }
-    """
-    user_id = request.args.get('user_id', type=int)
-    if not user_id:
-        abort(400, 'user_id required')
-
-    exists = Behavior.query.filter_by(
-        user_id=user_id,
-        post_id=post_id,
-        type='like'
-    ).first() is not None
-
-    return jsonify({'liked': exists}), 200