帖子模块设计文档

1. 数据库设计

1.1 Post表

class Post(models.Model):
    id = models.AutoField(primary_key=True)
    course = models.ForeignKey(Course, on_delete=models.CASCADE)
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    content = models.TextField()  # HTML内容
    mentioned_users = models.JSONField(default=list, blank=True)  # ['user_id1', 'user_id2']
    topic = models.CharField(max_length=50, blank=True)  # 单个话题标签
    likes_count = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

索引设计

class Meta:
    ordering = ['-created_at']
    indexes = [
        models.Index(fields=['topic']),  # 为话题搜索添加索引
        models.Index(fields=['course', 'created_at']),  # 为课程内帖子列表添加索引
    ]

1.2 关联表设计

  • Note表:用于存储用户笔记
  • Notification表:用于存储通知信息
  • Comment表:用于存储帖子评论

2. 服务器架构

2.1 服务器组成

  • Django后端服务器:处理业务逻辑
  • 远程文件服务器:存储帖子图片
  • Redis远程服务器:处理点赞缓存和临时数据

2.2 数据存储策略

  1. 文本内容:存储在MySQL数据库中
  2. 图片内容:存储在文件服务器,数据库仅存储URL
  3. 点赞数据:
    • 总数存储在数据库
    • 用户点赞关系存储在Redis
    • 定期同步Redis数据到数据库

3. 帖子创建流程

3.1 前端实现(Vue + TinyMCE)

  1. 用户交互:

    • 使用TinyMCE编辑器编写富文本内容
    • 上传图片获取临时URL
    • 添加@用户标记
    • 设置话题标签
  2. 数据收集:

    • HTML内容
    • 图片映射信息(临时URL到文件的映射)
    • @用户列表
    • 话题标签

3.2 后端处理(Django)

  1. 接收前端数据
  2. 图片处理:
    • 解析HTML提取临时URL
    • 上传图片至文件服务器
    • 获取永久URL
    • 替换HTML中的临时URL
  3. 保存帖子数据到数据库

4. 点赞机制

4.1 Redis键值设计

article:likes:{id}          - 存储帖子点赞总数
article:likes:update:{id}   - 存储待更新到数据库的点赞数
article:liked_users:{id}    - 存储已点赞用户集合

4.2 点赞流程

  1. 获取帖子列表:

    • 请求携带user_id
    • 通过Redis Pipeline获取点赞数和用户点赞状态
    • Redis无数据时从数据库获取并缓存
  2. 点赞操作:

    • 检查用户是否已点赞
    • Pipeline执行:
      • 添加用户到点赞集合
      • 增加点赞总数
      • 增加待更新数
    • 返回最新点赞数
  3. 取消点赞:

    • 检查用户是否已点赞
    • Pipeline执行:
      • 从点赞集合移除用户
      • 减少点赞总数
      • 减少待更新数
    • 返回最新点赞数

4.3 数据同步

  • 后台线程每60秒执行一次同步
  • 获取所有待更新的点赞数
  • 批量更新数据库
  • 清零Redis更新计数器

5. 优化策略

5.1 数据库优化

  • 为高频查询添加索引
  • 使用JSON字段存储非结构化数据

5.2 缓存优化

  • 使用Redis缓存点赞数据
  • Pipeline操作保证原子性
  • 异步同步减少数据库压力

5.3 性能优化

  • 图片与文本分离存储
  • 利用Redis减少数据库访问
  • 批量操作代替频繁单次操作
  • 合理设置索引提升查询效率

6. 扩展性考虑

  • 模块化设计便于功能扩展
  • 服务分离支持横向扩展
  • 预留字段支持未来功能
  • 统一的错误处理机制

敲代码过程中的经验

1,django后端中也要有回滚,若帖子创建过程中失败,则应删除数据库中的内容;

2,由于json在传输过程中会给双引号添加转译字符,因此存放帖子内容时应统一使用单引号;

    def process_post_images(content, image_map, post_id):
        """处理帖子中的图片,返回替换后的内容"""
        # 1. 首先统一将内容中的双引号替换为单引号
        content = content.replace('"', "'")

        # 2. 获取所有临时图片URL(同时支持单引号和双引号的匹配)
        temp_urls = re.findall(r"src=['\"]temp://([^'\"]+)['\"]", content)

        # 验证所有临时URL都有对应的文件
        missing_files = set(temp_urls) - set(
            key.replace('image_map[temp://', '').replace(']', '')
            for key in image_map.keys()
        )
        if missing_files:
            raise ValidationError(f"缺少图片文件: {', '.join(missing_files)}")

        # 3. 上传图片并替换URL(统一使用单引号)
        for temp_url in temp_urls:
            file_key = f"image_map[temp://{temp_url}]"
            if file_key in image_map:
                file = image_map[file_key]
                permanent_url = PostService.upload_post_image(file, post_id)
                content = content.replace(
                    f"temp://{temp_url}",
                    permanent_url
                )

        return content