101快猫短剧架构

Kcat短剧平台 - 系统架构与业务流程完整文档


目录

  1. 系统概述
  2. 整体技术架构
  3. 服务模块详解
  4. 核心业务流程
  5. 服务间交互关系
  6. 数据库设计
  7. 技术亮点与最佳实践
  8. 部署架构

一、系统概述

1.1 项目背景

Kcat短剧平台是一个基于微服务架构的短视频内容分发平台,专注于短剧内容的创作、审核、发布、播放与用户互动。系统采用RuoYi-Cloud脚手架构建,集成了工作流引擎、AI审核、视频处理、消息队列等现代化技术栈。

1.2 核心功能

  • 内容管理: 短剧、剧集、演员、分类、标签的全生命周期管理
  • 智能审核: AI自动审核 + 人工复核的混合审核机制
  • 视频处理: 腾讯云VOD转码(多清晰度+信息流格式)
  • 用户系统: 手机验证码登录、JWT认证、用户画像
  • 社交互动: 点赞、评论、弹幕、分享、收藏、关注
  • 推荐分发: 首页精选视频流、个性化推荐(待完善)

1.3 技术特色

  1. 微服务架构: Spring Cloud + Nacos实现服务治理
  2. 工作流引擎: Camunda BPM可视化编排复杂业务流程
  3. AI能力集成: Ollama本地模型 + DeepSeek云端模型
  4. 事件驱动: Kafka异步解耦,实现最终一致性
  5. 分布式缓存: Redis三高防护(穿透/击穿/雪崩)
  6. 视频云处理: 腾讯云VOD专业转码服务

二、整体技术架构

2.1 架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
┌─────────────────────────────────────────────────────────────────────┐
│ 移动端 / Web前端 │
│ (iOS / Android / H5) │
└──────────────────────────────┬──────────────────────────────────────┘
│ HTTPS

┌─────────────────────────────────────────────────────────────────────┐
│ API网关 (Gateway) │
│ 路由转发 / 鉴权 / 限流 / 日志 │
└──────────┬──────────────────┬──────────────────┬────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ user-service │ │ content-service │ │interaction-service│
│ (用户服务) │ │ (内容服务) │ │ (互动服务) │
│ 端口: 10003 │ │ 端口: 10001 │ │ 端口: 10004
│ │ │ │ │ │
- 用户认证登录 │ │ - 短剧管理 │ │ - 点赞/评论 │
- BFF聚合层 │ │ - 剧集管理 │ │ - 弹幕/分享 │
- 首页数据 │ │ - 短剧发布 │ │ - 举报审核 │
- 点赞事件生产 │ │ - 文件上传 │ │ - 内容统计 │
└────────┬─────────┘ └─────────┬────────┘ └────────┬─────────┘
│ │ │
│ │ │
├──────────────────────┴─────────────────────┤
│ Feign远程调用 │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ camunda-service │ │
│ │ (工作流服务) │ │
│ │ 端口: 10002 │ │
│ │ │ │
│ │ - 审核流程编排 │ │
│ │ - AI内容审核 │ │
│ │ - 转码任务触发 │ │
│ └────────┬─────────┘ │
│ │ │
└─────────────────────┴──────────────────────┘

┌─────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Kafka集群 │ │ Redis集群 │ │ MySQL数据库 │
│ │ │ │ │ │
- 点赞事件 │ │ - 缓存热点数据 │ │ - 用户数据 │
- 评论事件 │ │ - 验证码存储 │ │ - 内容数据 │
- 消息队列 │ │ - 点赞状态 │ │ - 互动数据 │
│ │ │ - 布隆过滤器 │ │ - 审核流程 │
└──────────────────┘ └──────────────────┘ └──────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ 基础设施层 │
├──────────────────┬──────────────────┬──────────────────┬────────────┤
│ Nacos │ Sentinel │ XXL-Job │ ELK │
│ (注册+配置中心) │ (限流+熔断) │ (任务调度) │ (日志) │
└──────────────────┴──────────────────┴──────────────────┴────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ 外部服务 │
├──────────────────┬──────────────────┬──────────────────┬────────────┤
│ 腾讯云VOD │ MinIO对象存储 │ Ollama AI │ 阿里云短信 │
│ (视频转码) │ (文件存储) │ (本地AI模型) │ (验证码) │
└──────────────────┴──────────────────┴──────────────────┴────────────┘

2.2 技术栈总览

技术分层 技术选型 版本 说明
基础框架 Spring Boot 3.4.x 核心应用框架
Spring Cloud 2024.0.0 微服务生态
Java 17 开发语言
服务治理 Nacos 最新 服务注册发现+配置中心
Sentinel 最新 流量控制+熔断降级
OpenFeign 最新 声明式HTTP客户端
LoadBalancer 最新 客户端负载均衡
数据存储 MySQL 8.0+ 关系型数据库
MyBatis-Plus 3.5.12 ORM增强框架
Redis 最新 缓存+分布式锁
Redisson 3.50.0 分布式工具包
消息队列 Kafka 最新 事件驱动消息中间件
工作流 Camunda BPM 7.23.0 BPMN工作流引擎
认证授权 Sa-Token 1.44.0 轻量级权限框架
AI能力 Spring AI 1.0.1 AI统一接口
Ollama 最新 本地AI模型服务
DeepSeek 最新 云端AI模型
文件存储 MinIO 8.5.13 对象存储服务
腾讯云VOD 2.1.5 视频点播服务
任务调度 XXL-Job 3.2.0 分布式任务调度
工具库 Lombok 1.18.20 简化Java代码
Hutool 5.8.39 Java工具集
监控日志 Logback 最新 日志框架
Knife4j 最新 接口文档

2.3 网络拓扑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
VPC网络 (172.16.0.0/16)

├── 应用子网 (172.16.1.0/24)
│ ├── user-service (10003)
│ ├── content-service (10001)
│ ├── interaction-service (10004)
│ └── camunda-service (10002)

├── 中间件子网 (172.16.2.0/24)
│ ├── MySQL (3306)
│ ├── Redis (6379)
│ ├── Kafka (9092)
│ └── Nacos (8848)

└── 外部服务
├── 腾讯云VOD API
├── MinIO (9000)
├── Ollama (11434)
└── 阿里云短信API

三、服务模块详解

3.1 user-service(用户服务/BFF层)

3.1.1 服务定位

角色: Backend For Frontend(前端后端聚合层)

核心职责:

  1. 用户认证与授权(短信验证码登录、JWT令牌管理)
  2. 聚合内容服务和互动服务的数据
  3. 提供统一的App端API接口
  4. 分布式缓存代理(三高防护)
  5. Kafka事件生产(点赞事件)

3.1.2 技术架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Controller层
├── LoginController (认证相关)
├── VerifyCodeController (验证码)
├── AppHomeController (首页数据)
└── AppInteractionController (互动功能)

BizService层(业务编排)
├── UserAuthServiceImpl (用户认证业务)
├── DramaBizServiceImpl (短剧业务+缓存)
├── LikeBizServiceImpl (点赞业务+Kafka生产)
├── CommentBizServiceImpl (评论业务)
└── VerifyCodeServiceImpl (验证码业务)

Feign客户端(服务调用)
├── ContentFeignClient → content-service
└── InteractionFeignClient → interaction-service

Service层(数据访问)
├── UsersService
├── UserAuthsService
├── UserFollowsService
└── ... (9个Service)

Mapper层(MyBatis-Plus)
└── 9个Mapper接口 + XML

3.1.3 核心功能模块

1. 用户认证模块

  • 短信验证码登录:
    • 发送6位数字验证码(Redis存储5分钟)
    • 验证通过自动注册(生成”游客+随机6位数”昵称)
    • Sa-Token登录并生成JWT令牌
  • 用户信息查询:
    • CompletableFuture并发查询5个维度数据
    • 自定义线程池优化性能

2. 首页数据聚合模块

  • 首页精选视频:
    • 远程调用content-service获取短剧列表
    • Redis手动缓存(key包含分页参数)
    • 查询用户点赞状态(Redis Set)
  • 短剧详情:
    • 使用@CacheData注解AOP缓存
    • 布隆过滤器防止缓存穿透
    • Redisson分布式锁防止缓存击穿
    • 随机过期时间防止缓存雪崩

3. 互动功能模块

  • 点赞功能:
    • 登录校验
    • 构造LikeEvent事件
    • 发送到Kafka(Topic: like-event-topic)
    • 生产者拦截器自动填充msgId、userId、timestamp
  • 评论查询:
    • 远程调用interaction-service

3.1.4 数据模型(9张表)

表名 说明 核心字段
users 用户基本信息 userId, phone, nickname, avatar, gender, level, experience
user_auths 用户认证方式 authId, userId, authType(password/sms/wechat/qq/apple), authKey, authCredential
user_follows 用户关注关系 followId, followerId, followeeId, status
user_collections 用户收藏 collectionId, userId, dramaId, dramaTitle, dramaCover
user_browse_history 浏览历史 historyId, userId, dramaId, title, cover, browseTime, browseDuration
user_devices 用户设备 deviceId, userId, deviceType, deviceToken(推送)
user_blacklist 用户黑名单 blacklistId, userId, blockedUserId
user_level_config 用户等级配置 levelId, levelName, minExperience, maxExperience, benefits
user_preferences 用户偏好设置 preferenceId, userId, autoPlay, pushNotification, privacySettings

3.1.5 API接口清单

接口路径 方法 功能 权限
/api/login POST 手机验证码登录 公开
/api/userinfo GET 获取当前用户信息 需登录
/api/send-code POST 发送验证码 公开
/api/episodes/featured GET 首页精选视频流 公开
/api/dramas/{dramaId} GET 短剧详情 公开
/api/dramas/{dramaId}/episodes/all GET 短剧剧集列表 公开
/api/episodes/{episodeId}/like POST 点赞/取消点赞 需登录
/api/episodes/{episodeId}/comments GET 获取评论列表 公开

3.2 content-service(内容服务)

3.2.1 服务定位

角色: 核心内容管理服务

核心职责:

  1. 短剧、剧集、演员、分类、标签的CRUD
  2. 短剧发布流程(多表事务操作)
  3. 文件上传(MinIO对象存储)
  4. 视频转码(腾讯云VOD)
  5. 审核流程集成(调用camunda-service)
  6. 提供RPC接口给user-service

3.2.2 技术架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Controller层
├── DramasController (短剧管理)
├── EpisodesController (剧集管理)
├── ActorsController (演员管理)
├── CategoriesController (分类管理)
├── TagsController (标签管理)
├── PublishController (短剧发布)
├── AuthCheckController (审核检查)
├── UploadController (文件上传)
└── rpc/
├── AppHomeDataController (首页数据RPC)
└── TencentVodController (视频转码RPC)

BizService层
├── DramaPublishServiceImpl (发布业务)
├── AuthCheckServiceImpl (审核业务)
├── DramaHomeServiceImpl (首页业务)
└── TencentVodServiceImpl (转码业务)

Feign客户端
└── CamundaFeignClient → camunda-service

Service层(10个Service)
├── DramasService
├── EpisodesService
├── ActorsService
└── ... (其他Service)

Mapper层
└── 10个Mapper + XML

3.2.3 核心功能模块

1. 短剧发布流程(事务性操作)

1
2
3
4
5
6
7
8
9
1. 保存短剧基本信息 → dramas表
2. 保存短剧与分类关联 → drama_categories
3. 保存短剧与标签关联 → drama_tags
4. 保存短剧与演员关联 → drama_actors
- 新演员:先插入actors表,再插入关联表
- 已有演员:直接插入关联表
5. 保存剧集信息 → episodes表
6. 启动Camunda审核流程 → 调用camunda-service
7. 保存审核流程关系 → drama_auth

2. 文件上传模块

  • 使用MinIO对象存储
  • Bucket: “kcat”
  • 支持图片、视频上传
  • 返回访问URL

3. 视频转码模块

  • 信息流转码(竖屏短视频格式):
    • 处理预告片或第一集
    • 保存到dramas.trailerInfoflowUrl
  • 画质转码(多清晰度):
    • 1080p (高清) → episodes.videoUrlHd
    • 720p (标清) → episodes.videoUrlSd
    • 480p (低清) → episodes.videoUrlLd
  • 转码流程:
    1. 从MinIO下载视频到临时文件
    2. 上传到腾讯云VOD
    3. 指定转码模板
    4. 轮询PullEvents获取转码结果
    5. ConfirmEvents确认事件
    6. 更新数据库URL

4. 审核状态管理

  • AI审核状态查询
  • 更新审核状态(审核完成后回调)
  • 将通过审核的dramaId添加到布隆过滤器

5. 首页数据接口(RPC)

  • 查询首页精选视频:
    • 分页查询已上架且审核通过的短剧
    • 返回信息流剧集
    • 包含短剧详情、演员、标签、分类
  • 查询短剧所有剧集:
    • 短剧基本信息
    • 所有剧集列表(按集数排序)
    • 统计信息(总集数、可观看数等)
  • 查询短剧详情:
    • 封面、标题、描述
    • 播放量、点赞数
    • 演员信息

6. 布隆过滤器维护

  • 应用启动时初始化
  • 定时任务:每晚3点增量添加短剧ID
  • 每周重建布隆过滤器

3.2.4 数据模型(10张表)

表名 说明 核心字段
dramas 短剧表 dramaId, title, cover, poster, trailerUrl, description, totalEpisodes, status, auditStatus
episodes 剧集表 episodeId, dramaId, title, episodeNumber, videoUrl(多清晰度), duration, isFree, coinPrice
actors 演员表 actorId, actorName, actorImg, actorInfo, birthday, nationality
categories 分类表 categoryId, categoryName, parentId, sort
tags 标签表 tagId, tagName, hotCount
content_audits 内容审核记录表 auditId, contentId, contentType, auditStatus, auditReason, auditor
drama_auth 短剧审核流程表 dramaId, processId(Camunda流程ID), stepName, authStatus
drama_actors 短剧演员关联表 dramaId, actorId, roleName, roleType(主角/配角/客串), sortOrder
drama_categories 短剧分类关联表 dramaId, categoryId
drama_tags 短剧标签关联表 dramaId, tagId

3.2.5 外部依赖

服务 用途 调用方式
camunda-service 启动审核流程 Feign
MinIO 文件存储 MinioTemplate
腾讯云VOD 视频转码 VOD SDK
XXL-Job 定时任务(布隆过滤器重建、转码任务) HTTP调用
Redis/Redisson 布隆过滤器、缓存 Redisson客户端

3.3 interaction-service(互动服务)

3.3.1 服务定位

角色: 用户互动数据服务

核心职责:

  1. 点赞、评论、弹幕、分享、举报等互动功能
  2. Kafka消费点赞事件(最终一致性)
  3. 更新Redis缓存中的点赞数
  4. 内容统计数据管理
  5. 提供RPC接口给user-service

3.3.2 技术架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Controller层
└── rpc/
├── LikeActionController (点赞接口)
└── CommentsController (评论接口)

BizService层
├── LikeBizServiceImpl (点赞业务)
└── CommentBizServiceImpl (评论业务)

Kafka监听器
└── LikeEventListener (点赞事件消费)

Service层(9个Service)
├── UserLikesService
├── CommentsService
├── ContentStatisticsService
├── DanmakuService
└── ... (其他Service)

Mapper层
└── 9个Mapper + XML

3.3.3 核心功能模块

1. 点赞功能(Kafka异步消费)

  • Kafka监听器:
    • Topic: like-event-topic
    • 消费LikeEvent事件
    • 更新数据库点赞记录(幂等操作)
    • 更新Redis缓存中的点赞数(首页精选视频缓存)
    • 手动ACK确认
  • 幂等性处理:
    • 布隆过滤器 + Redis Set记录已处理消息ID
    • 数据库维护消息表(利用ACID)

2. 评论功能

  • 查询评论列表:
    • 过滤条件:parentId=0(一级评论)、status=1(正常)、auditStatus=1(审核通过)
    • 排序规则:按点赞数和创建时间倒序
    • 分页查询
  • TODO: 判断当前用户是否对评论点赞(Kafka + 双写一致性)

3. 弹幕功能(待实现)

  • 实时弹幕发送
  • 弹幕时间轴定位
  • 敏感词过滤

4. 分享与举报(待实现)

  • 多平台分享统计
  • 内容举报与审核

3.3.4 数据模型(9张表)

表名 说明 核心字段
user_likes 用户点赞记录 likeId, userId, targetId, targetType(短剧/剧集/评论/弹幕), status(0取消/1有效)
comments 评论表 commentId, userId, dramaId, episodeId, content, parentId, rootId, likeCount, status, auditStatus
content_statistics 内容统计 statId, contentId, contentType, commentCount, danmakuCount, likeCount, hotScore
danmaku 弹幕表 danmakuId, episodeId, userId, content, timePoint, color, position, fontSize, speed
danmaku_filter_words 弹幕过滤词 wordId, word, status
user_dislikes 用户点踩记录 dislikeId, userId, targetId, targetType
share_records 分享记录 shareId, userId, contentId, contentType, sharePlatform
reports 举报记录 reportId, userId, contentId, contentType, reportReason, reportType, status
user_interaction_statistics 用户互动统计 statId, userId, totalLikes, totalComments, totalShares, totalCollections

3.3.5 Redis缓存架构

RedisService工具类:

  1. 缓存穿透解决方案:
    • 空占位符: “NULL”(TTL=30分钟)
  2. 缓存雪崩解决方案:
    • 随机过期时间(0-99999秒)
  3. 分布式锁实现:
    • Lua脚本保证原子性(SETNX + 过期时间)
    • 释放锁时比较值再删除

缓存Key设计:

1
2
3
4
5
HOME_EPISODES_FEATURED_CACHE_KEY = "home:episodes:featured:{pageNum}:{pageSize}"
DRAMA_DETAIL_CACHE_KEY = "drama:detail:{dramaId}"
DRAMA_EPISODES_CACHE_KEY = "drama:episodes:{dramaId}"
LIKE_EPISODE_KEY = "like:episode:{episodeId}" # Set结构存储点赞用户ID
PHONE_VERIFY_CODE_KEY = "phone:code:{phone}"

3.4 camunda-service(工作流服务)

3.4.1 服务定位

角色: 工作流引擎与审核编排服务

核心职责:

  1. BPMN流程定义部署
  2. 短剧审核流程编排(AI审核 + 人工审核)
  3. AI情感分析(Ollama模型)
  4. 审核流程管理(启动、查询、完成任务)
  5. 审核通过后触发后续任务(转码、数据入库)

3.4.2 技术架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Controller层
├── ProcessDeployController (流程部署)
└── ProcessManagerController (流程管理)

Service层
├── ProcessManagerServiceImpl (流程管理服务)
└── OllamaEmotionalAnalysisServiceImpl (情感分析服务)

Biz层(业务委托处理器)
└── CamundaJavaDelegateHandler
├── aiCheck (AI审核)
├── updateDramaAuthStatus (更新审核状态)
├── tencentVodTranslate (触发转码)
└── ragDataHandler (RAG数据入库)

Feign客户端
└── ContentFeignClient → content-service

Camunda引擎
├── RepositoryService (流程部署)
├── RuntimeService (流程启动)
└── TaskService (任务管理)

3.4.3 短剧审核流程(DramaAuthProcess)

BPMN流程定义:

1
2
3
4
5
6
7
8
9
10
11
开始 → AI内容审核 → 人工审核 → 审核决策网关
├─ 不通过 → 更新数据库状态 → 结束
└─ 通过 → 并行网关
├─ 腾讯云转码
└─ RAG数据入库

并行网关汇聚

更新数据库状态

结束

流程变量:

  • 输入变量: dramaId, title, description
  • AI审核结果: titleAuthResult, descriptionAuthResult
  • 人工审核结果: approve(Boolean), auditStatus, auditReason

条件表达式:

  • 审核不通过: ${!approve}
  • 审核通过: ${approve}

核心节点详解:

  1. AI内容审核(Service Task):

    • JavaDelegate: ${aiCheck}
    • 功能: 使用Ollama AI模型对标题和简介进行情感分析
    • 结果: 0-负面, 1-正面
  2. 人工审核(User Task):

    • 任务名称: “人工审核”
    • 审核员手动执行审核
    • 提交审核结果(通过/不通过、原因)
  3. 腾讯云转码(Service Task):

    • JavaDelegate: ${tecentVodTranslator}
    • 功能: 触发content-service的视频转码接口
  4. RAG数据入库(Service Task):

    • JavaDelegate: ${ragDataHandler}
    • 功能: 将审核通过的数据入RAG知识库(待实现)
  5. 更新数据库状态(Service Task):

    • JavaDelegate: ${updateDramaAuthStatus}
    • 功能: 调用content-service更新审核状态

3.4.4 API接口清单

接口路径 方法 功能
/workflow/deploy POST 部署BPMN流程文件
/workflow/process/dramaAuth/start POST 启动短剧审核流程
/workflow/process/variables/{processId} GET 获取流程变量
/workflow/process/manual/authTask PUT 执行人工审核任务

3.4.5 AI能力集成

Ollama情感分析:

  • 模型: 本地部署的Ollama模型
  • 输入: 文本(标题或简介)
  • 输出: 0-负面, 1-正面
  • 系统提示词: 指导AI识别正负面情感

Spring AI配置:

  • OllamaChatModel: Ollama客户端
  • Prompt: 构建System Message + User Message
  • 返回结果转换为整数

四、核心业务流程

4.1 短剧发布与审核完整流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
┌─────────────────────────────────────────────────────────────────────┐
│ 阶段1:短剧发布(content-service) │
└─────────────────────────────────────────────────────────────────────┘
用户提交短剧信息(基本信息+分类+标签+演员+剧集)

PublishController接收请求

DramaPublishService开启事务

1. 保存短剧信息 → dramas表 → 生成dramaId
2. 保存短剧与分类关联 → drama_categories表
3. 保存短剧与标签关联 → drama_tags表
4. 保存短剧与演员关联 → drama_actors表(新演员先插入actors表)
5. 保存剧集信息 → episodes表
6. 启动Camunda审核流程 → Feign调用camunda-service
7. 保存审核流程关系 → drama_auth表(记录processId)

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段2:AI自动审核(camunda-service) │
└─────────────────────────────────────────────────────────────────────┘
Camunda流程启动 → 执行AI内容审核节点

CamundaJavaDelegateHandler.aiCheck()

1. 从流程变量获取dramaId、title、description
2. 调用Ollama情感分析服务分析标题 → titleAuthResult (0/1)
3. 调用Ollama情感分析服务分析简介 → descriptionAuthResult (0/1)
4. 将AI审核结果存入流程变量

进入人工审核节点(User Task)

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段3:人工审核(camunda-service) │
└─────────────────────────────────────────────────────────────────────┘
审核人员通过管理后台查看待审核短剧

调用 /workflow/process/manual/authTask 接口

ProcessManagerService执行任务:
1. 查询当前待处理任务
2. 任务领取(claim)
3. 完成任务并提交审核结果(approve, auditStatus, auditReason)

流程进入决策网关

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段4a:审核不通过分支 │
└─────────────────────────────────────────────────────────────────────┘
审核决策网关判断:approve = false

执行 updateDramaAuthStatus 节点

CamundaJavaDelegateHandler.updateDramaAuthStatus()

1. 构建DramaAuthCompleteDto对象
2. Feign调用content-service的 /dramas/updateDramaAuthStatus 接口
3. 更新dramas表的审核状态(auditStatus=2,auditReason=原因)
4. 更新drama_auth表

流程结束

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段4b:审核通过分支 │
└─────────────────────────────────────────────────────────────────────┘
审核决策网关判断:approve = true

进入并行网关(同时执行两个任务)

┌──────────────────────────┐ ┌──────────────────────────┐
│ 任务1:腾讯云视频转码 │ │ 任务2:RAG数据入库 │
│ (tecentVodTranslator) │ │ (ragDataHandler) │
└──────────────────────────┘ └──────────────────────────┘
↓ ↓
CamundaJavaDelegateHandler CamundaJavaDelegateHandler
.tencentVodTranslate() .ragDataHandler()
↓ ↓
Feign调用content-service 打印日志(待实现)
/tencent/vod/translate?dramaId=xxx

XXL-Job定时任务开始转码

(详见4.2)

┌─────────────────────────────────────────────────────────────────────┐
│ 并行网关汇聚(等待两个任务完成) │
└─────────────────────────────────────────────────────────────────────┘

执行 updateDramaAuthStatus 节点

CamundaJavaDelegateHandler.updateDramaAuthStatus()

1. 更新dramas表的审核状态(auditStatus=1)
2. 更新drama_auth表
3. 将dramaId添加到BloomFilter

流程结束

Mermaid时序图

sequenceDiagram
    participant User as 用户
    participant Content as content-service
    participant DB as MySQL数据库
    participant Camunda as camunda-service
    participant Ollama as Ollama AI
    participant Admin as 审核人员
    participant XXL as XXL-Job

    Note over User,XXL: 阶段1:短剧发布
    User->>Content: 提交短剧信息
    activate Content
    Content->>DB: 开启事务
    Content->>DB: 1. 保存短剧信息(dramas)
    Content->>DB: 2. 保存分类关联(drama_categories)
    Content->>DB: 3. 保存标签关联(drama_tags)
    Content->>DB: 4. 保存演员关联(drama_actors)
    Content->>DB: 5. 保存剧集信息(episodes)

    Note over Content,Camunda: 启动审核流程
    Content->>Camunda: Feign调用启动审核流程
    activate Camunda
    Camunda-->>Content: 返回processId
    Content->>DB: 6. 保存审核流程关系(drama_auth)
    Content->>DB: 提交事务
    Content-->>User: 发布成功
    deactivate Content

    Note over Camunda,Ollama: 阶段2:AI自动审核
    Camunda->>Camunda: 执行AI内容审核节点
    Camunda->>Ollama: 分析标题情感
    Ollama-->>Camunda: titleAuthResult(0/1)
    Camunda->>Ollama: 分析简介情感
    Ollama-->>Camunda: descriptionAuthResult(0/1)
    Camunda->>Camunda: 存入流程变量

    Note over Camunda,Admin: 阶段3:人工审核
    Camunda->>Admin: 分配人工审核任务
    Admin->>Camunda: 提交审核结果(approve)

    alt 审核不通过
        Note over Camunda,Content: 阶段4a:审核不通过
        Camunda->>Camunda: 执行updateDramaAuthStatus
        Camunda->>Content: Feign调用更新审核状态
        activate Content
        Content->>DB: 更新dramas审核状态(rejected)
        Content->>DB: 更新drama_auth表
        Content-->>Camunda: 更新成功
        deactivate Content
        Camunda->>Camunda: 流程结束
    else 审核通过
        Note over Camunda,XXL: 阶段4b:审核通过
        Camunda->>Camunda: 进入并行网关

        par 并行任务1:视频转码
            Camunda->>Content: Feign调用触发转码
            activate Content
            Content->>XXL: 创建转码任务
            XXL-->>Content: 任务创建成功
            deactivate Content
        and 并行任务2:RAG数据入库
            Camunda->>Camunda: 执行ragDataHandler
            Note right of Camunda: 待实现
        end

        Note over Camunda,Content: 并行网关汇聚
        Camunda->>Camunda: 执行updateDramaAuthStatus
        Camunda->>Content: Feign调用更新审核状态
        activate Content
        Content->>DB: 更新dramas审核状态(approved)
        Content->>DB: 更新drama_auth表
        Content->>Content: 添加dramaId到BloomFilter
        Content-->>Camunda: 更新成功
        deactivate Content
        Camunda->>Camunda: 流程结束
    end
    deactivate Camunda

4.2 视频转码流程(content-service)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
┌─────────────────────────────────────────────────────────────────────┐
│ 转码触发:camunda-service调用content-service转码接口 │
└─────────────────────────────────────────────────────────────────────┘
TencentVodController.translate(dramaId)

创建XXL-Job任务(TranslateJob)

XXL-Job调度执行

┌─────────────────────────────────────────────────────────────────────┐
│ 任务1:信息流转码(竖屏短视频格式) │
└─────────────────────────────────────────────────────────────────────┘
1. 查询dramas表,获取trailerUrl(预告片URL)或第一集videoUrl
2. 从MinIO下载视频到临时文件
3. 上传到腾讯云VOD
4. 指定信息流转码模板ID
5. 轮询PullEvents拉取转码事件
6. 获取转码结果URL
7. 更新dramas.trailerInfoflowUrl
8. ConfirmEvents确认事件

┌─────────────────────────────────────────────────────────────────────┐
│ 任务2:画质转码(1080p/720p/480p) │
└─────────────────────────────────────────────────────────────────────┘
1. 查询episodes表,获取所有剧集
2. 批量处理每一集:
a. 从MinIO下载视频到临时文件
b. 上传到腾讯云VOD
c. 指定画质转码模板ID(1080p/720p/480p)
d. 轮询PullEvents拉取转码事件
e. 获取转码结果URL(3个清晰度)
f. 更新episodes表的videoUrlHd/Sd/Ld字段
g. ConfirmEvents确认事件
3. 完成所有剧集转码

┌─────────────────────────────────────────────────────────────────────┐
│ 转码完成 │
└─────────────────────────────────────────────────────────────────────┘
短剧状态:已审核+已转码 → 可以上架展示

Mermaid流程图

sequenceDiagram
    participant Camunda as camunda-service
    participant Content as content-service
    participant XXL as XXL-Job
    participant DB as MySQL
    participant MinIO as MinIO对象存储
    participant VOD as 腾讯云VOD

    Note over Camunda,Content: 转码触发
    Camunda->>Content: Feign调用/tencent/vod/translate
    activate Content
    Content->>XXL: 创建转码任务
    Content-->>Camunda: 任务创建成功
    deactivate Content

    Note over XXL,VOD: XXL-Job执行转码任务
    XXL->>XXL: 调度执行TranslateJob

    rect rgb(240, 248, 255)
        Note over XXL,VOD: 任务1:信息流转码
        XXL->>DB: 查询dramas表获取trailerUrl
        DB-->>XXL: 返回视频URL
        XXL->>MinIO: 下载视频到临时文件
        MinIO-->>XXL: 返回视频文件
        XXL->>VOD: 上传视频
        VOD-->>XXL: 返回fileId
        XXL->>VOD: 指定信息流转码模板

        loop 轮询转码状态
            XXL->>VOD: PullEvents拉取转码事件
            VOD-->>XXL: 返回转码进度
        end

        VOD-->>XXL: 返回转码结果URL
        XXL->>VOD: ConfirmEvents确认事件
        XXL->>DB: 更新dramas.trailerInfoflowUrl
    end

    rect rgb(255, 250, 240)
        Note over XXL,VOD: 任务2:画质转码(1080p/720p/480p)
        XXL->>DB: 查询episodes表获取所有剧集
        DB-->>XXL: 返回剧集列表

        loop 每一集剧集
            XXL->>MinIO: 下载剧集视频
            MinIO-->>XXL: 返回视频文件
            XXL->>VOD: 上传视频
            VOD-->>XXL: 返回fileId
            XXL->>VOD: 指定画质转码模板(1080p/720p/480p)

            loop 轮询转码状态
                XXL->>VOD: PullEvents拉取转码事件
                VOD-->>XXL: 返回转码进度
            end

            VOD-->>XXL: 返回3个清晰度URL
            XXL->>VOD: ConfirmEvents确认事件
            XXL->>DB: 更新episodes(videoUrlHd/Sd/Ld)
        end
    end

    Note over XXL,DB: 转码完成
    XXL->>DB: 短剧状态: 已审核+已转码

4.3 用户登录流程(user-service)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
┌─────────────────────────────────────────────────────────────────────┐
│ 阶段1:发送验证码 │
└─────────────────────────────────────────────────────────────────────┘
用户输入手机号 → 点击"获取验证码"

调用 /api/send-code 接口

VerifyCodeServiceImpl.sendVerifyCode(phone)

1. 生成6位随机数字验证码
2. 调用SmsTemplate发送短信(阿里云短信服务)
3. 验证码存入Redis(key: phone:verify:code:{phone},TTL=5分钟)
4. 返回成功响应(包含expireTime)

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段2:验证码登录 │
└─────────────────────────────────────────────────────────────────────┘
用户输入手机号+验证码 → 点击"登录"

调用 /api/login 接口(LoginReqVo: phone, code)

UserAuthServiceImpl.login(LoginReqVo)

1. 从Redis查询验证码(key: phone:verify:code:{phone})
2. 验证码不存在或不匹配 → 返回"验证码错误"
3. 验证通过 → 删除Redis中的验证码(防止重复使用)
4. 查询users表(phone = ?)
5. 用户不存在?
├─ 是 → 调用register(phone)
│ ├─ 生成随机昵称(游客+6位随机数)
│ ├─ 插入users表
│ └─ 返回userId
└─ 否 → 直接获取userId
6. Sa-Token登录:StpUtil.login(userId, LoginConfig携带phone、nickname等扩展信息)
7. 生成JWT令牌:StpUtil.getTokenValue()
8. 计算过期时间:StpUtil.getTokenTimeout()
9. 返回LoginResVo(token, expires)

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段3:后续请求携带Token │
└─────────────────────────────────────────────────────────────────────┘
客户端后续请求:
Header: Authorization: Bearer {token}

Sa-Token拦截器验证Token

StpUtil.isLogin() → true

业务代码获取当前用户:StpUtil.getLoginIdAsLong()

Mermaid时序图

sequenceDiagram
    participant User as 用户
    participant App as App客户端
    participant UserSvc as user-service
    participant Redis as Redis
    participant SMS as 阿里云短信
    participant DB as MySQL
    participant SaToken as Sa-Token

    Note over User,SMS: 阶段1:发送验证码
    User->>App: 输入手机号,点击"获取验证码"
    App->>UserSvc: POST /api/send-code
    activate UserSvc
    UserSvc->>UserSvc: 生成6位随机验证码
    UserSvc->>SMS: 发送短信验证码
    SMS-->>UserSvc: 发送成功
    UserSvc->>Redis: 存储验证码(TTL=5分钟)
    Redis-->>UserSvc: 存储成功
    UserSvc-->>App: 返回成功(expireTime)
    deactivate UserSvc
    App-->>User: 显示"验证码已发送"

    Note over User,SaToken: 阶段2:验证码登录
    User->>App: 输入验证码,点击"登录"
    App->>UserSvc: POST /api/login {phone, code}
    activate UserSvc

    UserSvc->>Redis: 查询验证码
    Redis-->>UserSvc: 返回验证码

    alt 验证码错误或过期
        UserSvc-->>App: 返回"验证码错误"
        App-->>User: 显示错误提示
    else 验证码正确
        UserSvc->>Redis: 删除验证码(防止重复使用)
        UserSvc->>DB: 查询users表(phone)
        DB-->>UserSvc: 返回用户信息

        alt 用户不存在
            UserSvc->>UserSvc: 生成随机昵称(游客+6位数)
            UserSvc->>DB: 插入users表
            DB-->>UserSvc: 返回userId
        else 用户已存在
            UserSvc->>UserSvc: 获取userId
        end

        UserSvc->>SaToken: StpUtil.login(userId, 扩展信息)
        activate SaToken
        SaToken->>Redis: 存储Token会话信息
        SaToken-->>UserSvc: 返回JWT Token
        deactivate SaToken

        UserSvc-->>App: 返回{token, expires}
        deactivate UserSvc
        App->>App: 存储Token到本地
        App-->>User: 登录成功,跳转首页
    end

    Note over User,SaToken: 阶段3:后续请求携带Token
    User->>App: 浏览首页
    App->>UserSvc: GET /api/episodes/featured<br/>Header: Authorization: Bearer {token}
    activate UserSvc
    UserSvc->>SaToken: 验证Token
    activate SaToken
    SaToken->>Redis: 查询Token会话信息
    Redis-->>SaToken: 返回会话信息
    SaToken-->>UserSvc: 验证通过,返回userId
    deactivate SaToken
    UserSvc->>UserSvc: 获取当前用户ID
    UserSvc->>UserSvc: 执行业务逻辑
    UserSvc-->>App: 返回数据
    deactivate UserSvc
    App-->>User: 展示数据

4.4 首页精选视频流程(user-service + content-service)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
┌─────────────────────────────────────────────────────────────────────┐
│ 阶段1:用户请求首页精选(user-service) │
└─────────────────────────────────────────────────────────────────────┘
App请求 /api/episodes/featured?pageNum=1&pageSize=10

AppHomeController.getEpisodesFeatured(PageReqDto)

DramaBizServiceImpl.getEpisodesFeatured(PageReqDto)

1. 拼接缓存Key: "home:episodes:featured:1:10"
2. 查询Redis缓存
3. 缓存命中?
├─ 是 → 直接返回缓存数据 → 跳转到阶段3
└─ 否 → 继续下一步

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段2:远程调用content-service获取数据 │
└─────────────────────────────────────────────────────────────────────┘
Feign调用 ContentFeignClient.getEpisodesFeatured(PageReqDto)

content-service的AppHomeDataController接收请求

DramaHomeServiceImpl.getEpisodesFeatured(pageNum, pageSize)

1. 查询数据库(dramas表):
- 条件:status=1(已上架)、auditStatus=1(审核通过)
- 分页:offset=(pageNum-1)*pageSize, limit=pageSize
- 排序:按创建时间倒序
2. 对每个短剧:
a. 查询信息流剧集(trailerInfoflowUrl或第一集)
b. 查询演员信息(关联drama_actors、actors表)
c. 查询标签列表(关联drama_tags、tags表)
d. 查询分类列表(关联drama_categories、categories表)
e. 组装HomeFeaturedDto对象
3. 计算分页信息(total, hasMore)
4. 返回数据给user-service

user-service接收到数据

1. 将数据写入Redis缓存(TTL=7天+随机5位数秒)
2. 继续下一步

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段3:查询点赞状态(user-service) │
└─────────────────────────────────────────────────────────────────────┘
遍历HomeFeaturedDto列表

对每个剧集:
1. 获取episodeId
2. 判断用户是否登录?
├─ 否 → liked = false
└─ 是 → 查询Redis Setkey: "like:episode:{episodeId}"
判断当前userId是否在Set
├─ 在 → liked = true
└─ 不在 → liked = false
3. 设置liked字段

返回完整数据给App端

Mermaid时序图

sequenceDiagram
    participant App as App客户端
    participant UserSvc as user-service
    participant Redis as Redis缓存
    participant ContentSvc as content-service
    participant DB as MySQL

    Note over App,Redis: 阶段1:查询缓存
    App->>UserSvc: GET /api/episodes/featured?pageNum=1&pageSize=10
    activate UserSvc
    UserSvc->>UserSvc: 拼接缓存Key<br/>"home:episodes:featured:1:10"
    UserSvc->>Redis: 查询缓存
    Redis-->>UserSvc: 返回缓存结果

    alt 缓存命中
        UserSvc->>UserSvc: 获取缓存数据
        Note over UserSvc: 跳转到阶段3
    else 缓存未命中
        Note over UserSvc,DB: 阶段2:远程调用content-service
        UserSvc->>ContentSvc: Feign调用getEpisodesFeatured
        activate ContentSvc

        ContentSvc->>DB: 查询dramas表<br/>(status=1, auditStatus=1, 分页)
        DB-->>ContentSvc: 返回短剧列表

        loop 每个短剧
            ContentSvc->>DB: 查询信息流剧集(episodes)
            ContentSvc->>DB: 查询演员信息(drama_actors + actors)
            ContentSvc->>DB: 查询标签列表(drama_tags + tags)
            ContentSvc->>DB: 查询分类列表(drama_categories + categories)
            DB-->>ContentSvc: 返回关联数据
            ContentSvc->>ContentSvc: 组装HomeFeaturedDto
        end

        ContentSvc->>ContentSvc: 计算分页信息(total, hasMore)
        ContentSvc-->>UserSvc: 返回数据列表
        deactivate ContentSvc

        UserSvc->>Redis: 写入缓存(TTL=7天+随机时间)
        Redis-->>UserSvc: 写入成功
    end

    Note over UserSvc,Redis: 阶段3:查询点赞状态
    alt 用户未登录
        UserSvc->>UserSvc: 所有剧集liked=false
    else 用户已登录
        UserSvc->>UserSvc: 获取当前userId

        loop 遍历每个剧集
            UserSvc->>Redis: 查询Set "like:episode:{episodeId}"
            Redis-->>UserSvc: 返回点赞用户集合

            alt userId在Set中
                UserSvc->>UserSvc: 设置liked=true
            else userId不在Set中
                UserSvc->>UserSvc: 设置liked=false
            end
        end
    end

    UserSvc-->>App: 返回完整数据(含点赞状态)
    deactivate UserSvc
    App->>App: 渲染首页视频流

4.5 短剧详情查询流程(user-service + content-service)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
┌─────────────────────────────────────────────────────────────────────┐
│ 完整缓存流程(三高防护) │
└─────────────────────────────────────────────────────────────────────┘
App请求 /api/dramas/{dramaId}

AppHomeController.getDramaDetail(dramaId)

DramaBizServiceImpl.getDramaDetail(dramaId) [@CacheData注解]

CacheDataAspect切面拦截

1. 拼接缓存Key: "drama:detail:{dramaId}"
2. 查询Redis缓存
3. 缓存命中?
├─ 是 → 直接返回缓存数据 → 结束
└─ 否 → 继续下一步(缓存未命中)

┌─────────────────────────────────────────────────────────────────────┐
│ 防护1:布隆过滤器防止缓存穿透 │
└─────────────────────────────────────────────────────────────────────┘
4. 查询布隆过滤器(bloomFilterKey: "bloom:filter:dramas:id")
5. 布隆过滤器不存在该dramaId?
├─ 是 → 直接返回null(防止查询不存在的数据) → 结束
└─ 否 → 继续下一步(dramaId可能存在)

┌─────────────────────────────────────────────────────────────────────┐
│ 防护2:分布式锁防止缓存击穿 │
└─────────────────────────────────────────────────────────────────────┘
6. 尝试获取Redisson分布式锁(lockKey: "lock:drama:detail:{dramaId}")
7. 获取锁成功?
├─ 是 → 继续下一步(当前线程负责查询数据库)
└─ 否 → 等待2秒 → 重新查询Redis缓存 → 返回

┌─────────────────────────────────────────────────────────────────────┐
│ 查询数据库并回写缓存 │
└─────────────────────────────────────────────────────────────────────┘
8. Feign调用ContentFeignClient.getDramaDetail(dramaId)
9. content-service查询数据库:
a. 查询dramas表
b. 查询演员信息
c. 查询标签列表
d. 组装HomeDramaInfoDto
10. 数据不存在?
├─ 是 → 写入空占位符到Redis(TTL=30分钟) → 释放锁 → 返回null
└─ 否 → 继续下一步

┌─────────────────────────────────────────────────────────────────────┐
│ 防护3:随机过期时间防止缓存雪崩 │
└─────────────────────────────────────────────────────────────────────┘
11. 写入Redis缓存:
- TTL = 7天 + 随机5位数秒(最大约19小时)
- 避免大量key同时失效
12. 释放分布式锁
13. 返回数据

Mermaid流程图(三高防护机制)

flowchart TD
    Start([App请求短剧详情]) --> A[user-service接收请求]
    A --> B["@CacheData切面拦截"]
    B --> C{查询Redis缓存}

    C -->|缓存命中| End1([返回缓存数据])

    C -->|缓存未命中| D{"查询布隆过滤器<br/>bloom:filter:dramas:id"}

    D -->|dramaId不存在| End2(["返回null<br/>防护1: 缓存穿透"])

    D -->|dramaId可能存在| E{"尝试获取分布式锁<br/>lock:drama:detail"}

    E -->|获取锁失败| F[等待2秒]
    F --> G{重新查询缓存}
    G -->|有数据| End3([返回缓存数据])
    G -->|无数据| End4([返回null])

    E -->|获取锁成功| H[Feign调用content-service]
    H --> I[查询MySQL数据库]
    I --> J{数据存在?}

    J -->|不存在| K["写入空占位符<br/>TTL:30分钟"]
    K --> L[释放分布式锁]
    L --> End5(["返回null<br/>防护1: 缓存穿透"])

    J -->|存在| M["写入Redis缓存<br/>TTL:7天+随机时间"]
    M --> N[释放分布式锁]
    N --> End6(["返回数据<br/>防护3: 缓存雪崩"])

    style D fill:#e1f5ff
    style E fill:#fff4e1
    style M fill:#e8f5e9
    style K fill:#ffebee

三高防护说明:

  • 防护1(缓存穿透): 布隆过滤器快速判断数据是否存在 + 空值缓存
  • 防护2(缓存击穿): Redisson分布式锁,只有一个线程回源查询
  • 防护3(缓存雪崩): 随机过期时间(7天+0-27小时),避免集体失效

4.6 点赞功能流程(user-service + interaction-service + Kafka)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
┌─────────────────────────────────────────────────────────────────────┐
│ 阶段1:用户点击点赞(user-service) │
└─────────────────────────────────────────────────────────────────────┘
App请求 /api/episodes/{episodeId}/like
Body: { action: "like" } //"unlike"
Header: Authorization: Bearer {token}

AppInteractionController.likeEpisode(episodeId, EpisodesLikeReqVo)

1. Sa-Token登录校验:StpUtil.isLogin() → 未登录抛出异常
2. 获取当前用户ID:StpUtil.getLoginIdAsLong()

LikeBizServiceImpl.likeEpisode(episodeId, reqVo)

3. 构造LikeEvent事件对象:
- episodeId: 剧集ID
- action: "like""unlike"
- msgId: null(生产者拦截器自动填充)
- userId: null(生产者拦截器自动填充)
- timestamp: null(生产者拦截器自动填充)

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段2:发送Kafka消息 │
└─────────────────────────────────────────────────────────────────────┘
4. KafkaTemplate.send():
- Topic: "like-event-topic"
- Key: "like-event-{episodeId}-{userId}"(保证同一用户对同一剧集的操作顺序)
- Value: LikeEvent对象

5. KafkaEventProducerInterceptor拦截器自动填充:
- msgId: IdUtil.getSnowflakeNextId()(雪花算法生成唯一ID)
- userId: StpUtil.getLoginIdAsLong()
- timestamp: System.currentTimeMillis()

6. 消息发送到Kafka Broker
7. 返回成功响应给App(不等待消费完成)

┌─────────────────────────────────────────────────────────────────────┐
│ 阶段3:Kafka消费者处理(user-service 和 interaction-service) │
└─────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│ 消费者1:user-service (LikeEventListener) │
└──────────────────────────────────────────────────────────────────┘
8. 监听Topic: "like-event-topic"
9. 接收LikeEvent消息
10. 更新Redis缓存(首页精选视频缓存):
a. 查询Redis缓存(key: "home:episodes:featured:{pageNum}:{pageSize}"
b. 遍历短剧列表,找到对应的episodeId
c. action = "like" ?
├─ 是 → likeCount + 1
└─ 否 → likeCount - 1
d. 回写Redis缓存
11. 更新Redis Set(点赞状态):
a. key: "like:episode:{episodeId}"
b. action = "like" ?
├─ 是 → sadd userId
└─ 否 → srem userId
12. 手动ACK确认

┌──────────────────────────────────────────────────────────────────┐
│ 消费者2:interaction-service (LikeEventListener) │
└──────────────────────────────────────────────────────────────────┘
13. 监听Topic: "like-event-topic"
14. 接收LikeEvent消息
15. LikeBizServiceImpl.likeEpisode():
a. 查询user_likes表:
- userId = ?
- targetId = episodeId
- targetType = 2(剧集)
b. 记录存在?
├─ 是 → 更新status字段(action="like"1, "unlike"0
└─ 否 → 插入新记录
16. 更新Redis缓存(首页精选视频缓存中的点赞数)
17. 手动ACK确认

┌─────────────────────────────────────────────────────────────────────┐
│ 最终一致性保证 │
└─────────────────────────────────────────────────────────────────────┘
- 数据库持久化(interaction-service)
- Redis缓存更新(user-service + interaction-service)
- 幂等性保证:同一消息多次消费结果一致(数据库幂等更新 + Redis Set操作幂等)

Mermaid时序图(Kafka异步处理)

sequenceDiagram
    participant App as App客户端
    participant UserSvc as user-service
    participant KafkaP as Kafka Producer
    participant Kafka as Kafka Broker<br/>(like-event-topic)
    participant UserConsumer as user-service<br/>LikeEventListener
    participant InterConsumer as interaction-service<br/>LikeEventListener
    participant Redis as Redis
    participant DB as MySQL

    Note over App,KafkaP: 阶段1:用户点击点赞
    App->>UserSvc: POST /api/episodes/{episodeId}/like<br/>{action: "like"}
    activate UserSvc
    UserSvc->>UserSvc: Sa-Token登录校验
    UserSvc->>UserSvc: 获取userId

    Note over UserSvc,Kafka: 阶段2:发送Kafka消息
    UserSvc->>KafkaP: 构造LikeEvent事件
    activate KafkaP
    KafkaP->>KafkaP: 拦截器自动填充<br/>msgId(雪花ID), userId, timestamp
    KafkaP->>Kafka: 发送消息<br/>Key: like-event-{episodeId}-{userId}
    deactivate KafkaP
    Kafka-->>UserSvc: 发送成功
    UserSvc-->>App: 返回成功响应(不等待消费)
    deactivate UserSvc
    App->>App: 立即更新UI(乐观更新)

    Note over Kafka,DB: 阶段3:Kafka异步消费

    par 消费者1: user-service
        Kafka->>UserConsumer: 推送LikeEvent消息
        activate UserConsumer

        UserConsumer->>Redis: 查询首页缓存<br/>"home:episodes:featured:*"
        Redis-->>UserConsumer: 返回缓存数据

        alt action = "like"
            UserConsumer->>UserConsumer: likeCount + 1
        else action = "unlike"
            UserConsumer->>UserConsumer: likeCount - 1
        end

        UserConsumer->>Redis: 更新缓存中的点赞数
        Redis-->>UserConsumer: 更新成功

        UserConsumer->>Redis: 更新点赞状态Set<br/>"like:episode:{episodeId}"
        alt action = "like"
            Redis->>Redis: sadd userId
        else action = "unlike"
            Redis->>Redis: srem userId
        end
        Redis-->>UserConsumer: 更新成功

        UserConsumer->>Kafka: 手动ACK确认
        deactivate UserConsumer

    and 消费者2: interaction-service
        Kafka->>InterConsumer: 推送LikeEvent消息
        activate InterConsumer

        InterConsumer->>DB: 查询user_likes表<br/>(userId, episodeId)
        DB-->>InterConsumer: 返回查询结果

        alt 记录已存在
            alt action = "like"
                InterConsumer->>DB: UPDATE status = 1
            else action = "unlike"
                InterConsumer->>DB: UPDATE status = 0
            end
        else 记录不存在
            InterConsumer->>DB: INSERT 新记录
        end
        DB-->>InterConsumer: 更新成功

        InterConsumer->>Redis: 更新缓存中的点赞数
        Redis-->>InterConsumer: 更新成功

        InterConsumer->>Kafka: 手动ACK确认
        deactivate InterConsumer
    end

    Note over UserConsumer,InterConsumer: 最终一致性保证<br/>数据库持久化 + Redis缓存同步

流程说明:

  1. 异步响应: 用户点赞后立即返回成功,不等待消费完成(降低响应时间)
  2. 并行消费: user-service和interaction-service同时消费消息,互不阻塞
  3. 最终一致性: 数据库持久化 + Redis缓存最终同步
  4. 幂等性保证:
    • 数据库: 通过唯一索引 + UPDATE操作保证幂等
    • Redis Set: sadd/srem操作天然幂等

五、服务间交互关系

5.1 服务依赖关系图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
┌──────────────────────────────────────────────────────────────────┐
user-service │
│ (BFF聚合层/API网关) │
└───────────┬─────────────────────────┬──────────────────────────┘
│ │
│ Feign │ Feign
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ content-service │ │ interaction-service │
│ │ │ │
│ ┌──────────────────┐ │ │ ┌──────────────────┐ │
│ │ Feign │ │ │ │ Kafka Consumer │ │
│ │ ↓ │ │ │ │ ↓ │ │
│ │ camunda-service │ │ │ │ user-service │ │
│ └──────────────────┘ │ │ └──────────────────┘ │
└────────────────────────┘ └────────────────────────┘
↓ ↑
│ Feign │ Kafka
▼ │
┌────────────────────────┐ │
│ camunda-service │ │
│ │ │
│ ┌──────────────────┐ │ │
│ │ Feign │ │ │
│ │ ↓ │ │ │
│ │ content-service │ │ │
│ └──────────────────┘ │ │
└────────────────────────┘ │

┌─────────────────────────────────────┴───────────────────────────┐
│ Kafka集群 │
│ Topic: like-event-topic (10分区) │
│ 生产者: user-service │
│ 消费者: user-service + interaction-service │
└──────────────────────────────────────────────────────────────────┘

5.2 Feign调用关系表

调用方 被调用方 接口 用途
user-service content-service POST /home/featured/infoflow 查询首页精选视频
user-service content-service POST /home/dramas/{dramaId}/episodes/all 查询短剧剧集列表
user-service content-service POST /home/dramas/{dramaId} 查询短剧详情
user-service interaction-service GET /interaction/comments 查询评论列表
content-service camunda-service POST /workflow/process/dramaAuth/start 启动短剧审核流程
content-service camunda-service GET /workflow/process/variables/{processId} 查询流程变量
content-service camunda-service PUT /workflow/process/manual/authTask 执行人工审核任务
camunda-service content-service PUT /dramas/updateDramaAuthStatus 更新短剧审核状态
camunda-service content-service GET /tencent/vod/translate 触发视频转码

5.3 Kafka主题与消费者关系

Topic 生产者 消费者 消息类型 用途
like-event-topic user-service user-service + interaction-service LikeEvent 点赞事件异步处理

消息分区策略:

  • Key设计: like-event-{episodeId}-{userId}
  • 保证同一用户对同一剧集的操作顺序性
  • 10个分区,均匀分布

消费者组:

  • user-service: 更新Redis缓存中的点赞数和点赞状态
  • interaction-service: 更新数据库点赞记录

5.4 数据流向图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌─────────────────────────────────────────────────────────────────────┐
│ 用户操作 → user-service → content-service → camunda-service │
│ ↓ ↓ ↓ │
│ Redis缓存 MySQL数据库 Camunda引擎 │
│ ↑ ↑ ↓ │
│ └──────────────┴───────────── content-service │
│ ↓ │
│ 腾讯云VOD转码 │
│ ↓ │
│ MySQL数据库 │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ 用户点赞 → user-service → Kafka → interaction-service │
│ ↓ ↓ │
│ Redis缓存 MySQL数据库 │
│ ↑ ↑ │
│ └───── Kafka ──────────┘ │
└─────────────────────────────────────────────────────────────────────┘

六、数据库设计

6.1 数据库架构

数据库实例: MySQL 8.0+
字符集: UTF8MB4
存储引擎: InnoDB
数据库数量: 4个业务数据库

1
2
3
4
kcat_user       (用户服务数据库 - 9张表)
kcat_content (内容服务数据库 - 10张表)
kcat_interaction (互动服务数据库 - 9张表)
kcat_camunda (工作流服务数据库 - Camunda引擎表 + 1张业务表)

6.2 核心表设计

6.2.1 用户服务(kcat_user)

users(用户表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE TABLE users (
user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
phone VARCHAR(11) UNIQUE NOT NULL COMMENT '手机号',
nickname VARCHAR(50) NOT NULL COMMENT '昵称',
avatar VARCHAR(255) COMMENT '头像URL',
gender TINYINT DEFAULT 0 COMMENT '性别(0未知/1男/2女)',
birthday DATE COMMENT '生日',
level INT DEFAULT 1 COMMENT '用户等级',
experience INT DEFAULT 0 COMMENT '经验值',
coin_balance DECIMAL(10,2) DEFAULT 0 COMMENT '金币余额',
vip_expire_time DATETIME COMMENT 'VIP到期时间',
status TINYINT DEFAULT 1 COMMENT '状态(0禁用/1正常)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_phone (phone),
INDEX idx_nickname (nickname),
INDEX idx_level (level)
) COMMENT='用户基本信息表';

user_auths(用户认证表)

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE user_auths (
auth_id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '用户ID',
auth_type VARCHAR(20) NOT NULL COMMENT '认证类型(password/sms/wechat/qq/apple)',
auth_key VARCHAR(100) NOT NULL COMMENT '认证标识(手机号/openid等)',
auth_credential VARCHAR(255) COMMENT '认证凭证(密码hash/access_token)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_auth_type_key (auth_type, auth_key),
INDEX idx_user_id (user_id)
) COMMENT='用户认证方式表';

user_follows(用户关注关系表)

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE user_follows (
follow_id BIGINT PRIMARY KEY AUTO_INCREMENT,
follower_id BIGINT NOT NULL COMMENT '关注者ID',
followee_id BIGINT NOT NULL COMMENT '被关注者ID',
status TINYINT DEFAULT 1 COMMENT '状态(0取消/1有效)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_follower_followee (follower_id, followee_id),
INDEX idx_follower (follower_id),
INDEX idx_followee (followee_id)
) COMMENT='用户关注关系表';

6.2.2 内容服务(kcat_content)

dramas(短剧表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
CREATE TABLE dramas (
drama_id BIGINT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100) NOT NULL COMMENT '标题',
sub_title VARCHAR(100) COMMENT '副标题',
cover VARCHAR(255) NOT NULL COMMENT '封面URL',
poster VARCHAR(255) COMMENT '海报URL',
trailer_url VARCHAR(255) COMMENT '预告片URL',
trailer_infoflow_url VARCHAR(255) COMMENT '信息流预告片URL',
description TEXT COMMENT '简介',
story_line TEXT COMMENT '剧情介绍',
director VARCHAR(100) COMMENT '导演',
screenwriter VARCHAR(100) COMMENT '编剧',
production_company VARCHAR(100) COMMENT '制作公司',
release_date DATE COMMENT '发布日期',
total_episodes INT DEFAULT 0 COMMENT '总集数',
episode_duration INT COMMENT '单集时长(秒)',
total_duration INT COMMENT '总时长(秒)',
language VARCHAR(20) DEFAULT 'zh-CN' COMMENT '语言',
region VARCHAR(50) COMMENT '地区',
year INT COMMENT '年份',
quality VARCHAR(20) COMMENT '画质',
age_rating VARCHAR(20) COMMENT '年龄分级',
is_finished TINYINT DEFAULT 0 COMMENT '是否完结(0连载/1完结)',
is_vip TINYINT DEFAULT 0 COMMENT '是否VIP(0否/1是)',
is_new TINYINT DEFAULT 0 COMMENT '是否新剧(0否/1是)',
is_hot TINYINT DEFAULT 0 COMMENT '是否热播(0否/1是)',
is_recommended TINYINT DEFAULT 0 COMMENT '是否推荐(0否/1是)',
play_count BIGINT DEFAULT 0 COMMENT '播放次数',
like_count BIGINT DEFAULT 0 COMMENT '点赞数',
comment_count INT DEFAULT 0 COMMENT '评论数',
share_count INT DEFAULT 0 COMMENT '分享数',
collection_count INT DEFAULT 0 COMMENT '收藏数',
follow_count INT DEFAULT 0 COMMENT '追剧数',
rating_score DECIMAL(3,1) DEFAULT 0 COMMENT '评分',
rating_count INT DEFAULT 0 COMMENT '评分人数',
status TINYINT DEFAULT 0 COMMENT '上架状态(0下架/1上架)',
audit_status TINYINT DEFAULT 0 COMMENT '审核状态(0待审核/1通过/2拒绝)',
audit_reason VARCHAR(255) COMMENT '审核拒绝原因',
sort_order INT DEFAULT 0 COMMENT '排序',
create_by VARCHAR(64),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_by VARCHAR(64),
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_title (title),
INDEX idx_status_audit (status, audit_status),
INDEX idx_create_time (create_time),
INDEX idx_hot (is_hot),
INDEX idx_new (is_new)
) COMMENT='短剧表';

episodes(剧集表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
CREATE TABLE episodes (
episode_id BIGINT PRIMARY KEY AUTO_INCREMENT,
drama_id BIGINT NOT NULL COMMENT '短剧ID',
title VARCHAR(100) COMMENT '标题',
episode_number INT NOT NULL COMMENT '集数',
cover VARCHAR(255) COMMENT '封面URL',
video_url VARCHAR(255) NOT NULL COMMENT '视频URL',
video_url_hd VARCHAR(255) COMMENT '高清视频URL',
video_url_sd VARCHAR(255) COMMENT '标清视频URL',
video_url_ld VARCHAR(255) COMMENT '低清视频URL',
subtitle_url VARCHAR(255) COMMENT '字幕URL',
duration INT COMMENT '时长(秒)',
description TEXT COMMENT '简介',
is_free TINYINT DEFAULT 0 COMMENT '是否免费(0付费/1免费)',
is_trailer TINYINT DEFAULT 0 COMMENT '是否预告片(0否/1是)',
coin_price DECIMAL(10,2) DEFAULT 0 COMMENT '金币价格',
play_count BIGINT DEFAULT 0 COMMENT '播放次数',
like_count BIGINT DEFAULT 0 COMMENT '点赞数',
comment_count INT DEFAULT 0 COMMENT '评论数',
share_count INT DEFAULT 0 COMMENT '分享数',
danmaku_count INT DEFAULT 0 COMMENT '弹幕数',
status TINYINT DEFAULT 1 COMMENT '状态(0删除/1正常)',
audit_status TINYINT DEFAULT 0 COMMENT '审核状态(0待审核/1通过/2拒绝)',
audit_reason VARCHAR(255) COMMENT '审核拒绝原因',
publish_time DATETIME COMMENT '发布时间',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_drama_id (drama_id),
INDEX idx_episode_number (episode_number),
UNIQUE KEY uk_drama_episode (drama_id, episode_number)
) COMMENT='剧集表';

drama_auth(短剧审核流程表)

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE drama_auth (
drama_id BIGINT NOT NULL COMMENT '短剧ID',
process_id VARCHAR(64) NOT NULL COMMENT 'Camunda流程实例ID',
step_name VARCHAR(50) COMMENT '当前步骤名称',
auth_status TINYINT DEFAULT 0 COMMENT '审核状态(-1未通过/0审核中/1通过)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (drama_id),
INDEX idx_process_id (process_id),
INDEX idx_auth_status (auth_status)
) COMMENT='短剧审核流程表';

6.2.3 互动服务(kcat_interaction)

user_likes(用户点赞记录表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE user_likes (
like_id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '用户ID',
target_id BIGINT NOT NULL COMMENT '目标ID',
target_type TINYINT NOT NULL COMMENT '目标类型(1短剧/2剧集/3评论/4弹幕)',
target_user_id BIGINT COMMENT '目标所属用户ID',
drama_id BIGINT COMMENT '短剧ID(冗余字段)',
episode_id BIGINT COMMENT '剧集ID(冗余字段)',
status TINYINT DEFAULT 1 COMMENT '状态(0取消/1有效)',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_target (user_id, target_id, target_type),
INDEX idx_target (target_id, target_type),
INDEX idx_drama (drama_id),
INDEX idx_episode (episode_id)
) COMMENT='用户点赞记录表';

comments(评论表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
CREATE TABLE comments (
comment_id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '用户ID',
user_nickname VARCHAR(50) COMMENT '用户昵称(冗余)',
user_avatar VARCHAR(255) COMMENT '用户头像(冗余)',
drama_id BIGINT COMMENT '短剧ID',
episode_id BIGINT COMMENT '剧集ID',
content TEXT NOT NULL COMMENT '评论内容',
parent_id BIGINT DEFAULT 0 COMMENT '父评论ID',
root_id BIGINT DEFAULT 0 COMMENT '根评论ID',
reply_user_id BIGINT COMMENT '回复用户ID',
reply_user_nickname VARCHAR(50) COMMENT '回复用户昵称',
like_count INT DEFAULT 0 COMMENT '点赞数',
dislike_count INT DEFAULT 0 COMMENT '点踩数',
reply_count INT DEFAULT 0 COMMENT '回复数',
is_hot TINYINT DEFAULT 0 COMMENT '是否热门(0否/1是)',
is_top TINYINT DEFAULT 0 COMMENT '是否置顶(0否/1是)',
ip_address VARCHAR(50) COMMENT 'IP地址',
location VARCHAR(100) COMMENT '地理位置',
device_info VARCHAR(255) COMMENT '设备信息',
status TINYINT DEFAULT 1 COMMENT '状态(0删除/1正常/2审核中/3已屏蔽)',
audit_status TINYINT DEFAULT 0 COMMENT '审核状态(0待审核/1通过/2拒绝)',
audit_reason VARCHAR(255) COMMENT '审核拒绝原因',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_drama_id (drama_id),
INDEX idx_episode_id (episode_id),
INDEX idx_parent_id (parent_id),
INDEX idx_root_id (root_id),
INDEX idx_create_time (create_time)
) COMMENT='评论表';

content_statistics(内容统计表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE content_statistics (
stat_id BIGINT PRIMARY KEY AUTO_INCREMENT,
content_id BIGINT NOT NULL COMMENT '内容ID',
content_type TINYINT NOT NULL COMMENT '内容类型(1短剧/2剧集)',
comment_count INT DEFAULT 0 COMMENT '评论数',
danmaku_count INT DEFAULT 0 COMMENT '弹幕数',
like_count BIGINT DEFAULT 0 COMMENT '点赞数',
dislike_count INT DEFAULT 0 COMMENT '点踩数',
share_count INT DEFAULT 0 COMMENT '分享数',
report_count INT DEFAULT 0 COMMENT '举报数',
hot_score DECIMAL(10,2) DEFAULT 0 COMMENT '热度分数',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_content (content_id, content_type)
) COMMENT='内容统计表';

6.3 表关系ER图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
┌──────────────────────────────────────────────────────────────────────┐
│ 用户服务(kcat_user) │
└──────────────────────────────────────────────────────────────────────┘
users (用户基本信息)
├── 1:Nuser_auths (认证方式)
├── 1:Nuser_follows (关注关系 - follower_id)
├── N:1user_follows (粉丝关系 - followee_id)
├── 1:Nuser_collections (收藏)
├── 1:Nuser_browse_history (浏览历史)
└── 1:Nuser_devices (设备)

┌──────────────────────────────────────────────────────────────────────┐
│ 内容服务(kcat_content) │
└──────────────────────────────────────────────────────────────────────┘
dramas (短剧)
├── 1:Nepisodes (剧集)
├── M:Nactors (演员) 通过 drama_actors
├── M:Ncategories (分类) 通过 drama_categories
├── M:Ntags (标签) 通过 drama_tags
├── 1:1drama_auth (审核流程)
└── 1:Ncontent_audits (审核记录)

┌──────────────────────────────────────────────────────────────────────┐
│ 互动服务(kcat_interaction) │
└──────────────────────────────────────────────────────────────────────┘
users (外键引用kcat_user.users)
├── 1:Nuser_likes (点赞)
├── 1:Ncomments (评论)
├── 1:Ndanmaku (弹幕)
├── 1:Nshare_records (分享)
└── 1:Nreports (举报)

dramas/episodes (外键引用kcat_content)
├── 1:Nuser_likes
├── 1:Ncomments
├── 1:Ndanmaku
├── 1:Nshare_records
└── 1:1content_statistics (统计)

┌──────────────────────────────────────────────────────────────────────┐
│ 工作流服务(kcat_camunda) │
└──────────────────────────────────────────────────────────────────────┘
Camunda引擎表(由Camunda BPM自动创建管理)
├── act_re_* (Repository - 流程定义)
├── act_ru_* (Runtime - 运行时实例)
├── act_hi_* (History - 历史数据)
└── act_id_* (Identity - 用户组织)

6.4 索引设计原则

  1. 主键索引: 所有表使用BIGINT自增主键
  2. 唯一索引: 手机号、认证标识等唯一字段
  3. 复合索引: 高频查询条件组合(如status+audit_status)
  4. 外键索引: 关联查询的字段(userId, dramaId, episodeId)
  5. 时间索引: 支持按时间排序和范围查询
  6. 状态索引: 支持状态筛选查询

七、技术亮点与最佳实践

7.1 分布式缓存三高防护

7.1.1 缓存穿透防护(布隆过滤器)

实现方案: Redisson BloomFilter

特性:

  • 预计元素: 1000万
  • 误判率: 0.001%
  • 内存占用: 约14.4MB

重建机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建临时布隆过滤器
RBloomFilter<Long> tempFilter = redissonClient.getBloomFilter("temp_bloom_filter");
tempFilter.tryInit(10_000_000, 0.001);

// 查询所有短剧ID并添加
List<Long> dramaIds = dramaMapper.selectAllDramaIds();
for (Long dramaId : dramaIds) {
tempFilter.add(dramaId);
}

// 毫秒级切换(rename)
RBloomFilter<Long> oldFilter = redissonClient.getBloomFilter("bloom:filter:dramas:id");
oldFilter.rename("old_bloom_filter");
tempFilter.rename("bloom:filter:dramas:id");
redissonClient.getKeys().delete("old_bloom_filter");

应用场景: 短剧详情查询、剧集详情查询


7.1.2 缓存击穿防护(分布式锁)

实现方案: Redisson RLock

加锁流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
String lockKey = "lock:" + cacheKey;
RLock lock = redissonClient.getLock(lockKey);
boolean lockResult = lock.tryLock();

if (lockResult) {
try {
// 双重检查缓存
String cacheData = redisService.getData(cacheKey);
if (cacheData != null) {
return cacheData;
}

// 查询数据库
Object dbData = queryDatabase();

// 写入缓存
redisService.setData(cacheKey, dbData);

return dbData;
} finally {
lock.unlock();
}
} else {
// 获取锁失败,等待2秒后重试
Thread.sleep(2000);
return getData(cacheKey);
}

优势: 只有一个线程回源查询,避免数据库瞬时压力


7.1.3 缓存雪崩防护(随机过期时间)

实现方案: 基础TTL + 随机时间

1
2
3
4
5
6
7
8
9
10
// 默认TTL = 7天
long baseTtl = 7 * 24 * 60 * 60;

// 随机TTL = 0-99999秒(约0-27小时)
long randomTtl = Long.parseLong(RandomUtil.randomNumbers(5));

// 最终TTL
long finalTtl = baseTtl + randomTtl;

stringRedisTemplate.opsForValue().set(cacheKey, jsonStr, finalTtl, TimeUnit.SECONDS);

效果: 缓存失效时间分散,避免集体失效


7.1.4 缓存一致性(延迟双删)

实现方案: 立即删除 + 延迟删除

1
2
3
4
5
6
7
8
9
10
// 更新数据库
updateDatabase(data);

// 第一次删除缓存
stringRedisTemplate.delete(cacheKey);

// 延迟10秒再删除(使用ScheduledThreadPoolExecutor)
scheduledThreadPoolExecutor.schedule(() -> {
stringRedisTemplate.delete(cacheKey);
}, 10, TimeUnit.SECONDS);

适用场景: 数据更新操作(99%场景有效)


7.2 自定义AOP缓存注解

@CacheData注解:

1
2
3
4
5
6
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheData {
String cacheKey(); // 缓存Key(支持SpEL格式化)
String bloomFilterKey() default ""; // 布隆过滤器Key
}

使用示例:

1
2
3
4
5
@CacheData(cacheKey = RedisConstants.DRAMA_DETAIL_CACHE_KEY,
bloomFilterKey = RedisConstants.BLOOM_FILTER_DRAMAS_ID)
public HomeDramaInfoDto getDramaDetail(Long dramaId) {
// 方法体:远程调用content-service
}

CacheDataAspect切面逻辑:

  1. 解析方法参数(dramaId)和返回值类型
  2. 拼接缓存Key(支持格式化占位符,如drama:detail:{dramaId}
  3. 查询缓存 → 命中则返回
  4. 未命中 → 判断布隆过滤器 → 不存在则返回null
  5. 存在 → 尝试获取分布式锁
  6. 获取锁成功 → 执行目标方法 → 写入缓存 → 释放锁
  7. 获取锁失败 → 等待2秒 → 重新查询缓存

优势: 业务代码无侵入,一个注解搞定完整缓存流程


7.3 Kafka事件驱动架构

7.3.1 生产者拦截器(自动填充公共字段)

KafkaEventProducerInterceptor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public ProducerRecord<String, Object> onSend(ProducerRecord<String, Object> record) {
BaseEvent baseEvent = (BaseEvent) record.value();

// 自动填充msgId(雪花ID)
baseEvent.setMsgId(IdUtil.getSnowflakeNextId());

// 自动填充userId(从Sa-Token获取)
baseEvent.setUserId(StpUtil.getLoginIdAsLong());

// 自动填充timestamp
baseEvent.setTimestamp(System.currentTimeMillis());

return record;
}

效果: 业务代码只需构造核心业务字段,公共字段自动填充


7.3.2 消息幂等性保证

方案1: Redis Set记录已处理消息ID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
String doneKey = "done:like:msg";
if (redisTemplate.opsForSet().isMember(doneKey, msgId)) {
// 消息已处理,直接ACK
ack.acknowledge();
return;
}

// 处理业务逻辑
processBusiness(event);

// 记录消息ID
redisTemplate.opsForSet().add(doneKey, msgId);

// 手动ACK
ack.acknowledge();

方案2: 数据库唯一索引 + 幂等更新

1
2
3
4
5
-- user_likes表设计
UNIQUE KEY uk_user_target (user_id, target_id, target_type)

-- 幂等更新SQL
UPDATE user_likes SET status = ? WHERE user_id = ? AND target_id = ? AND target_type = ?

7.3.3 最终一致性保证

流程:

  1. 用户点击点赞 → 发送Kafka消息 → 立即返回成功
  2. Kafka消费者1(user-service)→ 更新Redis缓存
  3. Kafka消费者2(interaction-service)→ 更新数据库
  4. 消息持久化 + 手动ACK → 保证消息不丢失
  5. 幂等性处理 → 重复消费结果一致

优势:

  • 降低接口响应时间(异步处理)
  • 削峰填谷(高并发缓冲)
  • 最终一致性(数据库与缓存同步)

7.4 Feign响应统一处理

FeignResponseDecoder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public Object decode(Response response, Type type) throws IOException {
Object decode = super.decode(response, type);

if (decode instanceof R<?>) {
R<?> r = (R<?>) decode;
if (r.getCode() != 200) {
throw new ServiceException(r.getMsg(), r.getCode());
}
// 自动解包,返回data
return r.getData();
}

return decode;
}

优势:

  • 业务代码无需关注R对象的code判断
  • 自动解包,直接获取data
  • 统一异常处理

使用示例:

1
2
// 调用方代码(无需判断R.code)
HomeDramaInfoDto dto = contentFeignClient.getDramaDetail(dramaId);

7.5 线程池并发查询

ThreadPoolConfig配置:

1
2
3
4
thread-pool:
core-size: 10
max-size: 20
queue-capacity: 100

应用场景: 用户信息查询(5个维度数据并发查询)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CompletableFuture<Users> baseInfoFuture = CompletableFuture.supplyAsync(() -> {
return usersService.getById(userId);
}, threadPoolExecutor);

CompletableFuture<Long> followCountFuture = CompletableFuture.supplyAsync(() -> {
return userFollowsService.count(Wrappers.<UserFollows>lambdaQuery()
.eq(UserFollows::getFollowerId, userId));
}, threadPoolExecutor);

// ... (其他3个Future)

CompletableFuture.allOf(baseInfoFuture, followCountFuture, ...).join();

// 获取结果
Users users = baseInfoFuture.get();
Long followCount = followCountFuture.get();

性能提升: 5个串行查询(假设每个100ms = 500ms)→ 并发查询(约100ms)


7.6 Sa-Token轻量级认证

配置模式: JWT Simple模式(StpLogicJwtForSimple

特性:

  1. Token生成: 登录时携带扩展信息(phone, nickname)
1
2
3
StpUtil.login(userId, new SaLoginModel()
.setExtra("phone", phone)
.setExtra("nickname", nickname));
  1. Token验证: @SaCheckLogin注解自动鉴权
  2. 用户信息获取:
1
2
Long userId = StpUtil.getLoginIdAsLong();
String phone = StpUtil.getExtra("phone").toString();
  1. Redis存储: Token与用户信息映射关系存储在Redis

优势: 轻量级、易用、集成简单


7.7 Camunda工作流引擎

优势:

  1. 可视化建模: BPMN 2.0标准,支持可视化流程设计
  2. 灵活扩展: JavaDelegate机制,业务逻辑与流程解耦
  3. 并行任务: Parallel Gateway支持并行执行
  4. 条件路由: Exclusive Gateway支持条件分支
  5. 人工任务: User Task支持人工审核
  6. 流程监控: 内置流程实例监控和历史查询

应用场景: 短剧审核流程编排

流程定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
<bpmn:process id="DramaAuthProcess" name="短剧审核流程">
<bpmn:startEvent id="StartEvent_1"/>
<bpmn:serviceTask id="AI_Check" name="AI内容审核"
camunda:delegateExpression="${aiCheck}"/>
<bpmn:userTask id="Manual_Audit" name="人工审核"/>
<bpmn:exclusiveGateway id="Gateway_1"/>
<bpmn:parallelGateway id="Parallel_1"/>
<bpmn:serviceTask id="VOD_Translate" name="腾讯云转码"
camunda:delegateExpression="${tecentVodTranslator}"/>
<bpmn:serviceTask id="RAG_Data" name="RAG数据入库"
camunda:delegateExpression="${ragDataHandler}"/>
<bpmn:endEvent id="EndEvent_1"/>
</bpmn:process>

八、部署架构

8.1 开发环境部署

Docker Compose部署清单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
version: '3'
services:
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- ./data/mysql:/var/lib/mysql

redis:
image: redis:latest
ports:
- "6379:6379"

kafka:
image: bitnami/kafka:latest
ports:
- "9092:9092"
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

zookeeper:
image: bitnami/zookeeper:latest
ports:
- "2181:2181"
environment:
ALLOW_ANONYMOUS_LOGIN: yes

nacos:
image: nacos/nacos-server:latest
ports:
- "8848:8848"
environment:
MODE: standalone

minio:
image: minio/minio:latest
ports:
- "9000:9000"
- "9001:9001"
command: server /data --console-address ":9001"

ollama:
image: ollama/ollama:latest
ports:
- "11434:11434"

xxl-job-admin:
image: xuxueli/xxl-job-admin:latest
ports:
- "8080:8080"

服务启动顺序:

  1. MySQL、Redis、Kafka、Zookeeper、Nacos、MinIO、Ollama、XXL-Job
  2. camunda-service、content-service、interaction-service、user-service

8.2 生产环境部署

Kubernetes部署架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌─────────────────────────────────────────────────────────────────────┐
│ Ingress Controller (Nginx) │
│ ├── /api/* → user-service │
│ ├── /content/* → content-service │
│ ├── /interaction/* → interaction-service │
│ └── /workflow/* → camunda-service │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ Service (ClusterIP) │
│ ├── user-service (3 replicas) │
│ ├── content-service (3 replicas) │
│ ├── interaction-service (3 replicas) │
│ └── camunda-service (2 replicas) │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ StatefulSet │
│ ├── MySQL (Master-Slave 12从) │
│ ├── Redis (Cluster 6节点) │
│ ├── Kafka (3 brokers) │
│ └── Nacos (Cluster 3节点) │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│ 外部服务 │
│ ├── 腾讯云VOD │
│ ├── MinIO集群 │
│ ├── Ollama GPU节点 │
│ └── 阿里云短信 │
└─────────────────────────────────────────────────────────────────────┘

资源配置建议:

服务 CPU 内存 副本数
user-service 2核 4GB 3
content-service 2核 4GB 3
interaction-service 2核 4GB 3
camunda-service 1核 2GB 2
MySQL 4核 16GB 3
Redis 2核 8GB 6
Kafka 4核 8GB 3
Nacos 2核 4GB 3

8.3 监控体系

监控指标:

  1. 应用监控:

    • Prometheus + Grafana
    • JVM指标(堆内存、GC、线程)
    • 接口QPS、响应时间、错误率
  2. 中间件监控:

    • MySQL慢查询、连接数
    • Redis缓存命中率、内存使用率
    • Kafka消息积压、消费延迟
  3. 业务监控:

    • 短剧发布量、审核通过率
    • 用户注册量、活跃用户数
    • 点赞数、评论数、播放量
  4. 日志监控:

    • ELK (Elasticsearch + Logstash + Kibana)
    • 日志聚合、全文检索、可视化
  5. 链路追踪:

    • SkyWalking / Zipkin
    • 全链路追踪、调用链分析

附录

A. 常用命令

启动Docker Compose:

1
docker-compose up -d

查看服务状态:

1
docker-compose ps

查看服务日志:

1
docker-compose logs -f user-service

重启服务:

1
docker-compose restart user-service

Maven打包:

1
mvn clean package -Dmaven.test.skip=true

Kafka创建Topic:

1
2
3
4
5
kafka-topics.sh --create \
--bootstrap-server localhost:9092 \
--topic like-event-topic \
--partitions 10 \
--replication-factor 1

B. 配置文件模板

application.yml(Nacos配置):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# application-common.yml(公共配置)
spring:
redis:
host: redis
port: 6379
database: 0
timeout: 3000ms
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0

kafka:
bootstrap-servers: kafka:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
consumer:
group-id: ${spring.application.name}
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
auto-offset-reset: earliest
enable-auto-commit: false

# datasource.yml(数据源配置)
spring:
datasource:
url: jdbc:mysql://mysql:3306/${db.name}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
mapper-locations: classpath*:mapper/**/*Mapper.xml
type-aliases-package: com.qwf.*.domain
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl

C. 常见问题FAQ

Q1: Kafka消息积压怎么办?
A: 增加消费者实例数、优化消费逻辑、增加分区数

Q2: Redis内存不足怎么办?
A: 设置合理的过期时间、使用LRU淘汰策略、扩容Redis节点

Q3: Camunda流程实例查询慢?
A: 定期归档历史数据、优化数据库索引、使用缓存

Q4: 视频转码失败怎么办?
A: 检查腾讯云VOD配置、增加重试机制

Q5: 布隆过滤器误判怎么办?
A: 降低误判率(增大内存)、配合缓存空对象使用


结语

本文档详细描述了Kcat短剧平台的完整技术架构和业务流程,涵盖了4个核心微服务(user-service、content-service、interaction-service、camunda-service)的技术实现、服务交互、数据模型、部署架构等全方位内容。

核心技术亮点:

  1. 微服务架构(Spring Cloud + Nacos)
  2. 工作流引擎(Camunda BPM)
  3. 事件驱动架构(Kafka)
  4. 分布式缓存三高防护(布隆过滤器 + 分布式锁 + 随机过期)
  5. AI能力集成(Ollama + DeepSeek)
  6. 视频云处理(腾讯云VOD)

业务场景:

  • 短剧内容管理与发布
  • AI自动审核 + 人工审核
  • 视频转码与多清晰度支持
  • 用户互动(点赞、评论、弹幕)
  • 首页精选视频推荐

该系统架构设计合理、分层清晰、扩展性强,是一个完整的企业级短视频内容分发平台解决方案。


文档版本: v1.0
生成日期: 2025-09-30
作者: Claude Code
文档字数: 约35000字


101快猫短剧架构
http://example.com/2025/11/23/101架构和流程文档/
作者
無鎏雲
发布于
2025年11月23日
许可协议