增加文件系统存储功能,修复上传功能

Change-Id: I7b76c0a78fa0a02018fa76e932d283e29b35d4be
diff --git a/Merge/back_wzy/routes/__pycache__/posts.cpython-310.pyc b/Merge/back_wzy/routes/__pycache__/posts.cpython-310.pyc
index 7d61b05..45d3343 100644
--- a/Merge/back_wzy/routes/__pycache__/posts.cpython-310.pyc
+++ b/Merge/back_wzy/routes/__pycache__/posts.cpython-310.pyc
Binary files differ
diff --git a/Merge/back_wzy/routes/posts.py b/Merge/back_wzy/routes/posts.py
index 14ff64c..a016f69 100644
--- a/Merge/back_wzy/routes/posts.py
+++ b/Merge/back_wzy/routes/posts.py
@@ -4,16 +4,47 @@
 from extensions    import db
 from models.post   import Post
 from models.behavior import Behavior
+from utils.Fpost import Fpost
+import json
 
 posts_bp = Blueprint('posts', __name__)
 
 @posts_bp.route('', methods=['POST'])
 def create_post():
-    data = request.get_json() or {}
-    post = Post(**data)
-    db.session.add(post)
-    db.session.commit()
-    return jsonify({'id': post.id}), 201
+    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')
+        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 创建帖子
+        fpost = Fpost(db.session)
+        new_post = fpost.create_post_with_files(
+            user_id=int(user_id),
+            title=title,
+            content=content,
+            topic_id=int(topic_id) if topic_id else None,
+            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():
@@ -59,14 +90,64 @@
 def update_post(post_id):
     """
     修改帖子字段(可选字段:title, content, topic_id, media_urls, status)
+    支持FormData和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])
-    db.session.commit()
-    return '', 204
+    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')
+            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
+            existing_media_urls = None
+            if existing_media_urls_str:
+                try:
+                    existing_media_urls = json.loads(existing_media_urls_str)
+                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(
+                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,
+                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])
+            db.session.commit()
+            updated_post = post
+        
+        if not updated_post:
+            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):
diff --git a/Merge/back_wzy/utils/Fpost.py b/Merge/back_wzy/utils/Fpost.py
index c8b5f48..894a831 100644
--- a/Merge/back_wzy/utils/Fpost.py
+++ b/Merge/back_wzy/utils/Fpost.py
@@ -6,9 +6,22 @@
 from sqlalchemy.orm import Session
 from models.logs import Log
 from models.syscost import PerformanceData
+import os
+import requests
+from werkzeug.utils import secure_filename
+import uuid
+
 class Fpost:
     def __init__(self,session:Session):
         self.session=session
+        # 配置文件存储节点(docker容器的实际路径)
+        self.storage_nodes = [
+            '/home/tianruiming/docker02/static',
+            '/home/tianruiming/docker03/static', 
+            '/home/tianruiming/docker04/static'
+        ]
+        # 默认访问节点(LVS负载均衡器)
+        self.access_node = '192.168.5.231:8080'
         return
     
 
@@ -153,4 +166,131 @@
     
     def getsyscost(self):
         res= self.session.query(PerformanceData).all()
-        return res
\ No newline at end of file
+        return res
+    
+    def save_files_to_storage(self, files, post_id):
+        """
+        将文件保存到所有存储节点
+        :param files: 文件列表
+        :param post_id: 帖子ID
+        :return: 媒体URL列表
+        """
+        media_urls = []
+        
+        for file in files:
+            if file and file.filename:
+                # 生成安全的文件名
+                original_filename = secure_filename(file.filename)
+                # 生成唯一文件名避免冲突
+                unique_id = str(uuid.uuid4())
+                file_extension = os.path.splitext(original_filename)[1]
+                unique_filename = f"{unique_id}{file_extension}"
+                
+                # 读取文件内容
+                file_content = file.read()
+                file.seek(0)  # 重置文件指针
+                
+                # 保存到所有存储节点
+                success_count = 0
+                for node_path in self.storage_nodes:
+                    try:
+                        # 创建目录路径:/home/tianruiming/docker0X/static/{post_id}/
+                        node_dir = os.path.join(node_path, str(post_id))
+                        os.makedirs(node_dir, exist_ok=True)
+                        
+                        # 完整文件路径
+                        full_file_path = os.path.join(node_dir, unique_filename)
+                        
+                        # 写入文件到存储节点
+                        with open(full_file_path, 'wb') as f:
+                            f.write(file_content)
+                        
+                        success_count += 1
+                        print(f"Successfully saved file to {full_file_path}")
+                        
+                    except Exception as e:
+                        print(f"Failed to save file to node {node_path}: {str(e)}")
+                
+                if success_count > 0:
+                    # 生成访问URL,格式:http://192.168.5.231:8080/static/{post_id}/{filename}
+                    media_url = f"http://{self.access_node}/static/{post_id}/{unique_filename}"
+                    media_urls.append(media_url)
+                else:
+                    raise Exception(f"Failed to save file {original_filename} to any storage node")
+        
+        return media_urls
+
+    def create_post_with_files(self, user_id, title, content, topic_id, status, files):
+        """
+        创建带文件的帖子
+        :param user_id: 用户ID
+        :param title: 标题
+        :param content: 内容
+        :param topic_id: 话题ID
+        :param status: 状态
+        :param files: 文件列表
+        :return: 帖子对象
+        """
+        # 先创建帖子获取ID
+        new_post = post(
+            user_id=user_id,
+            title=title,
+            content=content,
+            topic_id=topic_id if topic_id else None,
+            status=status,
+            created_at=datetime.now(),
+            updated_at=datetime.now()
+        )
+        
+        self.session.add(new_post)
+        self.session.flush()  # 获取ID但不提交
+        
+        # 保存文件
+        if files:
+            media_urls = self.save_files_to_storage(files, new_post.id)
+            new_post.media_urls = media_urls
+        else:
+            new_post.media_urls = []
+        
+        self.session.commit()
+        return new_post
+
+    def update_post_with_files(self, post_id, title=None, content=None, topic_id=None, status=None, files=None, existing_media_urls=None):
+        """
+        更新带文件的帖子
+        :param post_id: 帖子ID
+        :param title: 标题
+        :param content: 内容
+        :param topic_id: 话题ID
+        :param status: 状态
+        :param files: 新文件列表
+        :param existing_media_urls: 现有媒体URL
+        :return: 更新后的帖子对象
+        """
+        post_obj = self.session.query(post).filter(post.id == post_id).first()
+        if not post_obj:
+            return None
+        
+        # 更新基本信息
+        if title is not None:
+            post_obj.title = title
+        if content is not None:
+            post_obj.content = content
+        if topic_id is not None:
+            post_obj.topic_id = topic_id
+        if status is not None:
+            post_obj.status = status
+        
+        post_obj.updated_at = datetime.now()
+        
+        # 处理文件
+        if files:
+            # 有新文件,保存新文件
+            media_urls = self.save_files_to_storage(files, post_id)
+            post_obj.media_urls = media_urls
+        elif existing_media_urls is not None:
+            # 保留现有媒体URL
+            post_obj.media_urls = existing_media_urls
+        
+        self.session.commit()
+        return post_obj
\ No newline at end of file
diff --git a/Merge/back_wzy/utils/__pycache__/Fpost.cpython-310.pyc b/Merge/back_wzy/utils/__pycache__/Fpost.cpython-310.pyc
index 92bc6b9..b261f17 100644
--- a/Merge/back_wzy/utils/__pycache__/Fpost.cpython-310.pyc
+++ b/Merge/back_wzy/utils/__pycache__/Fpost.cpython-310.pyc
Binary files differ