# 短视频应用开发文档 - 版本:v1.0 - 日期:2026-10-14 - 适用端:Flutter App(iOS / Android)、Python 后端 --- ## 1. 项目介绍 ### 1.1 项目概述 一款竖屏沉浸式短视频应用(产品形态参考抖音/快手)。核心商业模式为: **免费额度 + 积分付费观看 + 会员订阅** - 所有用户(含游客)每天可免费观看 10 个视频 - 超出免费额度后,注册用户每观看 1 个视频消耗 1 积分,游客需注册/登录 - 积分通过充值获得(支付宝 / 微信 / USDT),**1 元 = 10 积分** - 积分可开通会员,会员享有**无限观看**和**优先上传**权益 - 用户可通过评论赚取积分(每评论 10 个视频奖励 1 积分),形成内容互动激励闭环 ### 1.2 项目组成 ``` video/ # 项目根目录(当前目录) ├── app/ # Flutter 手机端(前端) ├── server/ # Python 后端服务 ├── admin/ # 管理后台 Web(Vue3,一期包含) └── docs/ # 项目文档 ``` ### 1.3 名词定义 | 名词 | 定义 | |---|---| | 游客 | 未注册/未登录用户,以设备 ID(device_id)标识 | | 注册用户 | 通过手机号或邮箱注册并登录的用户 | | 会员 | 开通体验/月卡/年卡/终身卡且在有效期内的注册用户 | | 积分 | 平台虚拟货币,1 元 = 10 积分,用于观看扣费、开通会员 | | 免费额度 | 每个计费主体(游客=设备,注册用户=账号)每天可免费观看的视频数,默认 10 个 | | 计费主体 | 观看计费的统计单位:注册用户为 user_id,游客为 device_id | | 资产 | 创作者激励体系的虚拟货币,**10 资产 = 1 元**,存放于资产钱包,可提现 | | 推荐人/被推荐人 | 通过邀请码绑定的邀请关系双方;推荐人须为会员方可获得奖励 | --- ## 2. 功能介绍 ### 2.1 用户模块 #### 2.1.1 游客模式 - 无需注册登录,打开 App 即可观看视频 - App 首次启动生成唯一 device_id 并本地持久化,之后所有请求携带 `X-Device-ID` 头 - 游客每天可免费观看 10 个视频,第 11 个视频弹出登录/注册引导 - 游客不可点赞、收藏、评论、上传 #### 2.1.2 注册 - 注册页**手机号 / 邮箱 Tab 切换**:手机号 + 短信验证码 + 密码,或 邮箱 + 邮箱验证码 + 密码 - 邮箱验证码由服务端**真实 SMTP 发送**(`POST /auth/email/send`),验证码写 Redis(`email:code:{email}`,600s),与短信验证码同一套校验机制 - 注册即登录,返回 Token - 约束:手机号/邮箱全局唯一;密码 ≥ 8 位,含字母和数字 #### 2.1.3 登录 - 支持**手机号或邮箱**作为账号:手机号 + 密码 / 手机号 + 短信验证码 / 邮箱 + 密码 - 登录成功后,游客时期的设备 ID 与账号可关联(观看记录合并,可选) #### 2.1.4 资料管理 - 昵称(默认"用户+尾号")、头像、性别、个人简介 - 修改手机号/邮箱需验证码验证 #### 2.1.5 找回密码与账号注销 - **找回密码**:已验证的手机号/邮箱 + 验证码 + 新密码完成重置(`POST /auth/password/reset`),重置后旧 Token 全部失效 - **账号注销**:用户主动发起 → 7 天冻结期(期间登录即撤销)→ 到期匿名化:手机号/邮箱置空释放、昵称头像清空、作品全部下架、钱包余额清零作废、Token 立即失效 ### 2.2 观看与计费模块(核心业务) #### 2.2.1 计费规则 1. 会员:观看不限量,不消耗免费额度与积分 2. 非会员(含游客):每个计费主体每天前 10 个不同视频免费 3. 免费额度用完后:注册用户每看 1 个视频扣 1 积分;游客拦截并引导登录 4. 所有"每日"口径均按 Asia/Shanghai(服务器时区)0 点切分 5. 积分余额不足:拦截播放,引导充值或开通会员 #### 2.2.2 计费判定流程 ``` 用户开始播放视频 │ ▼ App 调用「观看上报」接口 │ ▼ 是否会员? ──是──► 直接放行 │否 ▼ 该视频今天看过? ──是──► 直接放行(同日去重,不重复扣费) │否 ▼ 今日免费额度未用完? ──是──► 占用 1 次免费额度,放行 │否 ▼ 是游客? ──是──► 返回"需登录",App 弹登录引导 │否 ▼ 积分 ≥ 1? ──是──► 扣 1 积分(写积分流水),放行 │否 ▼ 返回"积分不足",App 引导充值/开会员 ``` #### 2.2.3 补充规则(对需求的完善) - **同日去重**:同一视频当天重复观看,不重复占用免费额度、不重复扣积分 - **自己的视频免费**:观看自己上传的视频不计费、不占额度 - **扣费时点**:开始播放即扣费(播放页加载完成上报),简单可解释;退款场景(视频违规下架)通过后台人工处理 - **额度重置**:每日 00:00(服务器时区)重置,免费额度不可累积 - **观看记录**:所有观看(免费/扣费)均记录,用于历史列表与风控分析 ### 2.3 积分模块 #### 2.3.1 积分获取 | 途径 | 规则 | |---|---| | 充值 | 支付宝/微信/USDT 支付成功到账,1 元 = 10 积分 | | 评论奖励 | 每评论满 10 个**不同**视频,奖励 1 积分(循环累计,进度清零重新计) | | 推荐奖励 | 会员邀请的新用户激活后,推荐人获 N 积分(默认 10,可配置),见 2.8 | | 活动赠送 | 后台手动发放(含体验会员赠送活动) | #### 2.3.2 积分消耗 | 场景 | 规则 | |---|---| | 观看视频 | 免费额度用完后,1 积分/个 | | 开通会员 | 按套餐价扣减(见 2.4) | #### 2.3.3 评论奖励防刷规则(完善项) - 评论内容长度 ≥ 5 字才计入进度 - 同一视频仅**首次**有效评论计入进度 - 评论被删除(自删/违规)时,已计入的进度回退;已发放奖励的扣回(余额可为负,后续充值抵扣) - 每日评论奖励上限 5 积分(可配置) - 同一用户短时间高频评论触发限流(如 10 条/分钟) #### 2.3.4 积分流水 - 所有积分变动必须写入积分流水表(变动值、变动后余额、业务类型、关联单据) - 流水是积分对账、客服处理、风控审计的唯一依据 ### 2.4 会员模块 #### 2.4.1 会员类型(价格/时长均可在配置文件中修改) | 类型 | 时长 | 价格(积分) | 获取方式 | |---|---|---|---| | 体验会员 | 1 天 | 默认 5 积分(可配置) | 活动赠送 / 积分开通 | | 月卡 | 30 天 | 300 | 积分开通 | | 年卡 | 365 天 | 3650 | 积分开通 | | 终身卡 | 永久 | 10000 | 积分开通 | > 注:按 1 元 = 10 积分折算,即月卡=30 元、年卡=365 元、终身卡=1000 元。各套餐时长与价格均为默认值,可在配置文件中调整。 #### 2.4.2 会员权益 1. **无限观看**:不消耗免费额度和积分 2. **优先上传**:转码队列优先、审核队列优先 #### 2.4.3 会员规则(完善项) - 必须注册登录才能开通和使用会员 - 有效期内重复开通:有效期**叠加**(如月卡剩余 10 天再开月卡 → 40 天) - 已是终身卡:不可再开通其它套餐(前端置灰) - 体验会员与付费卡互斥逻辑:体验会员期间开通付费卡,按付费卡生效(体验剩余时间不累加,避免活动滥用) - 到期自动失效,不自动续费(当前无连续包月需求) - **新注册赠送**:新用户注册成功即赠送体验会员(默认 1 天),管理后台可配置开关与天数(system_configs:`register_gift_trial.enabled` / `register_gift_trial.days`),关闭后注册不再赠送,实时生效 ### 2.5 视频模块 #### 2.5.1 视频上传 - 主流程:App 选择本地视频(mp4 等)→ 上传至服务端/对象存储 → 服务端 **FFmpeg 转码为 HLS(m3u8 + ts 分片)** → 生成封面 → 提交审核 → 发布 - 兼容流程:也支持直接上传已转码好的 m3u8 + ts 文件包(服务端校验分片完整性后直接进入审核) - 上传限制:单文件 ≤ 500MB、时长 5 秒 ~ 10 分钟(可配置) - 会员"优先上传":转码任务进入高优先级队列,优先被消费 #### 2.5.2 视频转码 - 统一转 HLS:1080p 源 → 输出 720p(默认),可配置多码率(1080/720/480)自适应 - 从视频第 1 秒截取封面图 - 转码失败自动重试 3 次,仍失败则标记失败并通知作者 #### 2.5.3 视频审核 - 状态机:`转码中 → 待审核 → 已发布 / 审核拒绝 / 已下架` - 一期采用**管理后台人工审核**:审核员在后台在线预览视频,执行 通过 / 拒绝(填写原因),通过后自动发布上架 - 仅"已发布"状态进入推荐流 #### 2.5.4 视频播放 - m3u8(HLS)流媒体播放,App 竖屏全屏沉浸式播放 - 上滑/下滑切换视频;**3 槽播放器池**预加载:当前页播放 + 下一页预缓冲,划走即 stop(断音 + 中断缓存下载) - 切到"我的"等页面时播放器池**挂起(suspend)静音断流**,回到 Feed 且首页 Tab 可见才恢复(resume),修复"切页仍出声"问题 - 网络 m3u8 播放地址改写为 **App 内嵌本地媒体代理**(127.0.0.1 HTTP 服务):m3u8 每次回源保证签名时效,ts 分片落盘缓存(1GB LRU),支持拖动进度条(Range/206);ts 流量直连源站 CDN,不经过业务服务器 - 进度条拖动时显示**时间气泡**(x时xx分xx秒),标题后带**时长角标** - 记录播放量(进入播放即 +1,Redis 计数异步落库) #### 2.5.5 视频标签(Tag) - 发布视频时可填写 0~5 个标签,每个标签 1~20 字(字母/数字/中文/下划线) - 播放页在**标题后展示标签**(#标签 样式) - 点击标签进入"标签视频列表页":展示该标签下全部已发布视频(支持 热度/最新 排序) - 标签是推荐策略中个性化加权的主要特征 - 存量视频标签由**离线脚本批量生成**(server/scripts/build_tags_from_titles.py:jieba TF-IDF topK=6 + 番号/书名号提取 + 噪音过滤,幂等可重跑;已生成 2.2 万+ 标签、9.9 万+ 关联),后台提供"标签管理"页维护 #### 2.5.6 视频推荐策略 Feed 支持五种策略,客户端通过接口参数切换(App 顶部 Tab:推荐 / 最新 / 热度 / 口碑 / 热播,各策略独立保活分页状态): | 策略 | 说明 | |---|---| | recommend(默认) | 个性化推荐 | | latest | 最新发布倒序 | | hot | 热度倒序:热度分 = (播放×1 + 点赞×5 + 评论×10) / (1 + 发布天数) | | liked(口碑) | 按点赞量倒序 | | viewed(热播) | 按播放量倒序 | > liked / viewed 为纯数值排序,游标采用(值, id)双键避免同值翻页丢失。 **个性化推荐(recommend)规则:** 1. 候选池:全部"已发布"视频 2. 个性化打分:统计用户近 7 天观看/点赞/收藏视频的标签,得出偏好标签及权重,候选视频命中偏好标签加权;游客与无行为新用户退化为热度分 3. **每日重排**:以"当天日期 + 用户/设备"为随机种子对候选池确定性洗牌,保证**同一用户每天的推荐顺序都不一样**,且同一天内多次刷新顺序稳定(翻页不混乱) 4. **当日去重**:当天已观看过的视频**尽量不再出现**在推荐流中;候选池耗尽时放开过滤兜底,避免 Feed 刷空 #### 2.5.7 作品管理(作者) - **编辑**:作者可修改自己视频的标题/描述/分类/标签,仅元信息变更,不影响已发布状态、不重新触发审核(视频文件不可替换) - **删除**:作者可删除自己视频 → 软删除(status=5 已删除),立即从 Feed/搜索/标签页下架;播放/点赞等历史数据保留用于对账,资产结算即刻停止 - 两个操作仅作者本人可用(非作者返回 2107) #### 2.5.8 搜索 - 一期:按标题、标签做关键词匹配(MySQL LIKE / FULLTEXT),支持 最新/热度 排序;二期接 Elasticsearch - 热搜词:近 24h 搜索频次 Top10(Redis ZSet 统计,每小时截断) - 搜索历史:App 端本地存储最近 10 条,不上报服务端 - 搜索词先过敏感词过滤,命中直接返回空结果 #### 2.5.9 视频下载(离线缓存) - **入口**:Feed 页 / 播放页长按弹出菜单 →「下载」(已下载/下载中置灰) - HLS 视频:4 并发下载 ts 分片与 AES-128 密钥,改写为本地 index.m3u8;mp4 整文件直接下载 - 下载过程展示进度,支持取消;失败自动清理半成品文件 - 下载列表在「我的 → 已下载」;离线播放走本地媒体代理,支持拖动进度条 - 已下载视频离线观看**不重复扣积分**(线上播放时已按正常规则计费) ### 2.6 互动模块 | 功能 | 规则 | |---|---| | 点赞 | 仅登录用户;同一视频重复点击为取消;视频表冗余 like_count;唯一约束防重 | | 收藏 | 仅登录用户;同上;个人中心可查看收藏列表 | | 评论 | 仅登录用户;支持一级评论和楼中楼回复(二级);作者可删除自己视频下的评论;评论计数入奖励进度 | #### 2.6.4 消息通知 - 类型:审核结果(视频通过/拒绝)、提现进度(通过打款/拒绝退回)、系统公告、评论回复 - 触发即写 notifications 表,Redis 维护未读数;App 我的页消息入口带红点 - 通知为站内信,一期不做 App 推送(push 二期接厂商通道) #### 2.6.5 举报 - 举报对象:视频、评论;原因分类:色情低俗/暴力血腥/侵权/广告营销/其他 - 同一用户对同一对象仅可举报一次(唯一约束,重复返回 2106) - 后台处理动作:下架视频 / 删除评论 / 驳回;处理结果通过消息通知告知举报人 - 举报不自动处置内容,全部人工决策 #### 2.6.6 敏感词过滤 - 评论、昵称、视频标题/描述/标签、搜索词均先过敏感词库(DFA 算法),命中拒绝并返回 2105 - 词库由运营在管理后台维护(sensitive_words 表),服务端 Redis 缓存 60s ### 2.7 支付模块 #### 2.7.1 充值套餐(管理后台可在线调整) - 默认档位:**10 / 30 / 100 / 200 元**,另开放**自定义金额**(1~10000 元) - 到账规则:1 元 = 10 积分;档位可单独配置"赠送积分"(如充 100 元赠 50 积分) - 档位存储于数据库 `recharge_packages` 表,**管理后台实时增删改、上下架、排序**,无需改配置、无需重启服务 #### 2.7.2 支付方式 | 方式 | 方案 | 说明 | |---|---|---| | 支付宝 | 支付宝开放平台 App 支付 | 服务端下单生成 orderString → App 调起 SDK → 异步回调到账 | | 微信支付 | 微信支付 V3 App 支付 | 服务端下单 prepay → App 调起 SDK → 异步回调验签到账 | | USDT | TRC20 链上收款 | 配置固定收款地址 + 汇率;订单生成"唯一金额"(如 30 USDT + 0.001~0.999 随机尾差),服务端轮询 TronGrid 匹配金额与时间窗,满 19 个确认后入账 | #### 2.7.3 支付通用规则(完善项) - 订单有效期 30 分钟,超时自动关闭 - 到账必须**幂等**:以第三方流水号/tx_hash 唯一约束防重复入账 - 到账在数据库事务内完成:更新订单状态 + 钱包加积分 + 写积分流水,三步原子 - 回调验签失败、金额不匹配一律拒绝并告警 - 所有支付参数(商户号、密钥、证书、USDT 地址/汇率)走配置文件/环境变量,不入库、不进代码仓库 #### 2.7.4 退款规则 - 发起:仅运营/客服在支付平台商户后台(支付宝/微信)人工发起退款;USDT 订单不支持自动退款(人工链上退回并线下登记) - 系统处理(退款回调或后台人工登记后): 1. 订单状态置为 refunded 2. 扣回该订单已发放积分(余额**允许为负**,后续充值先抵扣负数;负余额用户禁止积分消费) 3. 扣回推荐人已获得的该笔充值返利资产(biz_type=6,资产允许为负) - 退款不改变已发生的观看/会员事实(会员不回收,防止复杂对穿,运营层面约束) ### 2.8 推荐与创作者激励(资产体系) 平台在积分之外设有**资产钱包**:资产可提现,与人民币固定换算 **10 资产 = 1 元**(与积分同量纲,比例可配置)。所有资产变动写资产流水,变更规范与积分一致(行锁 + 流水 + 乐观锁)。 #### 2.8.1 推荐奖励(会员邀请新用户) - 每位用户注册自动生成邀请码/邀请链接;**推荐人须为会员**才产生奖励(发放时点校验会员身份) - 新用户注册时通过邀请码/分享口令/推广链接自动归因完成绑定(归因方案见 2.8.2),注册后 24h 内可补绑,每人仅能绑定 1 个推荐人 - 被推荐人"激活"(注册 72h 内累计观看 ≥3 个视频,且次日有登录)后,推荐人获得 **N 积分**(默认 10,可配置),**不限量** - **充值返利**:被推荐人每次充值成功,推荐人获得"充值积分 × 5%"的**资产**(不限量,比例可配置);订单退款时对应返利扣回(资产余额允许为负,后续收益抵扣) #### 2.8.2 推广归因(邀请链接 / 分享口令 / 模糊匹配) 用户通过推广链接下载 App,安装注册后自动识别推荐人。采用三段式自建归因方案(不依赖第三方 SDK): | 优先级 | 路径 | 说明 | |---|---|---| | 1(主路径) | 分享口令 | 推广文案内含 `#短视频邀请码XXXXXXXX#` 口令,被邀请人复制后首次打开 App,客户端解析剪贴板 → 注册页自动预填邀请码,准确率 100% | | 2(辅助路径) | H5 落地页点击埋点 + 模糊匹配 | 推广链接 `/r/{code}` 打开 H5 落地页时自动上报点击(code+IP+UA 存 Redis 24h);App 首启无口令时上报 device_id,服务端按 IP 就近匹配点击记录,命中结果按 device_id 服务端缓存(7 天),注册时自动带出 | | 3(兜底) | 注册页手填邀请码 | 注册表单邀请码选填;注册后 24h 内还可在"邀请好友"页补绑(`/referral/bind`) | - 归因结果**由服务端按 device_id 缓存并一次性消费**,不信任客户端回传,防止伪造归因 - 绑定优先级:显式 invite_code(口令/手填)> 模糊归因缓存;绑定沿用 2.8.3 全部风控规则 - 落地页展示邀请人昵称、复制口令按钮与 App 下载入口(下载地址由 system_configs `app.download_url` 驱动,后台可配) #### 2.8.3 推荐防作弊规则 - 不可绑定自己;同 device_id 已存在账号的设备注册 → 绑定直接无效(防开小号) - 注册 IP 与推荐人常用 IP 一致、或同 IP 24h 内注册 >5 个账号 → 标记风险 - 推荐人 24h 内新增绑定 >10 人 → 超出部分冻结 - 命中风控的绑定状态置为"风控冻结",奖励暂停发放,管理后台人工审核后可解冻或判无效 - 未激活的绑定不发放奖励;激活判定由定时任务执行 #### 2.8.4 会员视频创作收益 仅**会员**发布的视频参与资产结算(结算时点校验作者会员身份;非会员期间数据持续累计,成为会员后按增量结算): | 收益项 | 规则 | 说明 | |---|---|---| | 完整观看收益 | 每累计 1000 次完整观看 → 1 资产 | 完整观看=播放进度 ≥95% 或看到结尾;同一用户/设备同视频每日最多计 1 次;作者本人不计 | | 点赞收益 | 每累计 100 次点赞 → 1 资产 | 以累计新增点赞计数;取消点赞回退计数,但已结算发放的资产不扣回 | - 结算由定时任务每 5 分钟按增量执行,按视频独立保留余数(如 2500 次完整观看 → 发 2 资产,余 500 继续累计) #### 2.8.5 资产提现 - 满 100 资产(=10 元)可申请提现(门槛可配置) - 渠道:支付宝 / 微信 / USDT;管理后台**人工审核**后打款 - 审核拒绝 → 资产原路退回资产钱包;同一时间仅允许一笔审核中的提现 ### 2.9 管理后台(一期包含,Web 端) 独立 Web 管理后台,管理员账号体系与 App 用户完全隔离,分角色控制权限。 | 模块 | 功能 | |---|---| | 仪表盘 | 今日/累计:DAU、新增注册、充值金额、积分发放与消耗、播放量、待审核数 | | 视频审核(人工) | 待审核列表(在线预览 m3u8)、通过、拒绝(必填原因)、已发布视频下架 | | 用户管理 | 搜索(ID/手机号/邮箱)、用户详情(钱包/会员/作品/流水)、封禁与解封、**修改密码**(重置后 `auth:epoch` 递增,该用户全端 Token 立即失效) | | 积分管理 | 用户积分流水查询、手动加/减积分(原因必填 + 二次确认,写流水 biz_type=6) | | 订单管理 | 充值订单查询(状态/渠道/时间筛选)、异常订单人工补单(核实已收款后手动入账) | | 提现审核 | 提现申请列表、审核通过(线下打款后确认)/ 拒绝(自动退回资产) | | 推荐风控 | 推荐关系查询(状态/风控标记筛选)、冻结记录解冻或判无效 | | 平台配置 | 注册赠送体验会员开关与天数等运营参数(system_configs 键值对,实时生效) | | 充值套餐管理 | 档位与赠送积分增删改、上下架、排序,实时生效 | | 活动运营 | 赠送体验会员/积分(支持单个或批量用户) | | 标签管理 | 标签列表查询(关联视频数)、重命名、删除(联动清理 video_tags) | | 采集源管理 | 采集源 CRUD(分类名 + 列表 URL 模板)、批量添加、单源/全部同步(后台异步采集外部单视频入库) | | 管理员管理 | 管理员账号增删、角色分配(超级管理员/审核员/运营)、操作日志查询 | 角色权限:超级管理员(全部)、审核员(仅视频审核)、运营(套餐/活动/看板)。 ### 2.10 二期规划清单 1. **关注/粉丝**:预留,二期开放 2. **App 推送**:厂商通道(评论回复、审核结果、提现进度等通知的 push) 3. **搜索升级**:Elasticsearch、搜索联想词 4. **分享**:视频分享链接/海报生成 5. **趋势看板**:管理后台 7/30 天数据曲线、订单导出 CSV 6. **黑名单**:用户拉黑、内容"不感兴趣"负反馈,深化个性化推荐 7. **游客数据合并**:注册后游客观看记录正式合并入账号 8. **实名认证提示**:充值环节按支付合规要求引导实名(依赖支付渠道商户资质) 9. **运维**:上传临时文件与拒绝视频分片的定时清理、SLO 指标(Feed <300ms、起播 <1.5s)、客户端埋点上报 ### 2.11 合规提示 - 短视频平台运营涉及《网络文化经营许可证》《信息网络传播视听节目许可证》或备案要求,上架应用市场需提供对应资质,需提前规划 - 必须接内容审核,否则存在下架风险 - USDT 收款存在地区合规风险,请评估主体与运营地区法规 - **iOS 虚拟支付合规**:苹果要求 App 内虚拟商品(积分/会员)走 IAP,使用支付宝/微信支付存在拒审风险。对策:预留"iOS 支付开关"(system_configs:`ios_payment.enabled`),审核期/风险期切换为引导用户到 H5 网页端充值;安卓端不受限 - **注册协议**:注册页必须勾选《用户协议》与《隐私政策》后方可提交 --- ## 3. 数据库设计 数据库选型:**MySQL 8.0**(InnoDB / utf8mb4),缓存与计数:**Redis 7**。 详细 DDL、索引策略见《02-技术文档》第 3 章,此处给出表结构定义。 ### 3.1 表清单 现共 **30 张业务表**。Alembic 迁移链:`init_27_tables` → `add_collection_records` → `add_collection_sources`;线上数据量约 1.8 万视频。 | 表 | 说明 | |---|---| | users | 用户表 | | user_wallets | 用户钱包(积分余额) | | user_membership | 用户当前会员状态 | | membership_orders | 会员开通记录 | | videos | 视频表 | | categories | 视频分类表 | | comments | 评论表 | | likes | 点赞表 | | favorites | 收藏表 | | watch_records | 观看记录表 | | point_transactions | 积分流水表 | | recharge_orders | 充值订单表 | | transcode_tasks | 转码任务表 | | tags | 标签表 | | video_tags | 视频-标签关联表 | | recharge_packages | 充值套餐表(后台可配) | | admin_users | 管理员表 | | admin_logs | 后台操作日志表 | | referrals | 推荐关系表 | | asset_wallets | 资产钱包表 | | asset_transactions | 资产流水表 | | withdrawals | 提现记录表 | | video_earnings | 视频收益结算表 | | notifications | 消息通知表 | | reports | 举报表 | | sensitive_words | 敏感词表 | | series | 系列(合集)表:多条视频组织成集数列表 | | collection_sources | 采集源配置表(分类名 + 列表 URL 模板) | | collection_records | 采集记录表(外部内容源 → 本站系列/视频映射,幂等导入) | ### 3.2 用户域 #### users 用户表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK AUTO_INCREMENT | 用户 ID | | phone | VARCHAR(20) NULL | 手机号,**唯一索引**,与 email 至少其一非空 | | email | VARCHAR(100) NULL | 邮箱,**唯一索引** | | password_hash | VARCHAR(255) | 密码哈希(bcrypt) | | nickname | VARCHAR(50) | 昵称 | | avatar_url | VARCHAR(500) | 头像 URL | | gender | TINYINT DEFAULT 0 | 0未知 1男 2女 | | bio | VARCHAR(200) | 简介 | | status | TINYINT DEFAULT 1 | 1正常 2封禁 | | register_type | TINYINT | 1手机 2邮箱 | | invite_code | VARCHAR(10) | 我的邀请码,注册时自动生成,**唯一索引** | | register_ip | VARCHAR(45) | 注册 IP(风控) | | last_login_at | DATETIME NULL | 最近登录时间(推荐激活判定用) | | last_login_ip | VARCHAR(45) | 最近登录 IP | | created_at / updated_at | DATETIME | 创建/更新时间 | 索引:`uk_phone(phone)`、`uk_email(email)`、`uk_invite_code(invite_code)`、`idx_status_created(status, created_at)` #### user_wallets 钱包表 | 字段 | 类型 | 说明 | |---|---|---| | user_id | BIGINT PK FK→users.id | 用户 ID | | points | INT UNSIGNED DEFAULT 0 | 当前积分余额 | | total_recharge | INT UNSIGNED DEFAULT 0 | 累计充值积分 | | total_earned | INT UNSIGNED DEFAULT 0 | 累计奖励积分 | | total_consumed | INT UNSIGNED DEFAULT 0 | 累计消耗积分 | | version | INT DEFAULT 0 | 乐观锁版本号 | | updated_at | DATETIME | 更新时间 | > 余额变更必须走"行锁/乐观锁 + 流水",禁止直接 update 余额。 #### user_membership 当前会员状态表(冗余加速查询) | 字段 | 类型 | 说明 | |---|---|---| | user_id | BIGINT PK FK | 用户 ID | | is_member | TINYINT DEFAULT 0 | 是否会员(冗余,以 expire_at 判定为准) | | current_plan | VARCHAR(20) | 当前套餐:trial/month/year/lifetime | | is_lifetime | TINYINT DEFAULT 0 | 是否终身卡 | | expire_at | DATETIME NULL | 到期时间(终身卡为 NULL) | | updated_at | DATETIME | | #### membership_orders 会员开通记录表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | order_no | VARCHAR(32) | 业务单号,**唯一索引** | | user_id | BIGINT FK | | | plan_code | VARCHAR(20) | trial/month/year/lifetime | | points_cost | INT | 消耗积分(赠送为 0) | | duration_days | INT | 时长(0=终身) | | start_at / end_at | DATETIME | 生效/到期(终身 end_at 为 NULL) | | source | TINYINT | 1积分购买 2活动赠送 | | created_at | DATETIME | | 索引:`idx_user(user_id, created_at)` ### 3.3 视频域 #### videos 视频表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | user_id | BIGINT FK | 作者 | | category_id | INT | 分类 | | title | VARCHAR(100) | 标题 | | description | VARCHAR(500) | 描述 | | cover_url | VARCHAR(500) | 封面 URL | | video_url | VARCHAR(500) | m3u8 播放地址 | | duration | INT | 时长(秒) | | file_size | BIGINT | 源文件大小 | | width / height | INT | 分辨率 | | status | TINYINT | 0转码中 1待审核 2已发布 3审核拒绝 4已下架 5转码失败 | | reject_reason | VARCHAR(200) | 拒绝/下架原因 | | reviewer_id | BIGINT NULL | 审核人(admin_users.id) | | reviewed_at | DATETIME NULL | 审核时间 | | is_priority | TINYINT DEFAULT 0 | 会员优先标记 | | view_count | BIGINT DEFAULT 0 | 播放量 | | like_count | INT DEFAULT 0 | 点赞数(冗余) | | comment_count | INT DEFAULT 0 | 评论数(冗余) | | favorite_count | INT DEFAULT 0 | 收藏数(冗余) | | published_at | DATETIME NULL | 发布时间 | | created_at / updated_at | DATETIME | | 索引:`idx_user_status(user_id, status)`、`idx_feed(status, published_at DESC)`、`idx_category(category_id, status)` #### categories 分类表 | 字段 | 类型 | 说明 | |---|---|---| | id | INT PK | | | name | VARCHAR(50) | 分类名 | | sort | INT DEFAULT 0 | 排序 | | status | TINYINT DEFAULT 1 | 1启用 0停用 | #### transcode_tasks 转码任务表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | video_id | BIGINT FK | | | source_url | VARCHAR(500) | 源文件地址 | | task_status | TINYINT | 0排队 1转码中 2成功 3失败 | | priority | TINYINT DEFAULT 0 | 0普通 1优先(会员) | | retry_count | INT DEFAULT 0 | 重试次数 | | error_msg | VARCHAR(500) | 失败原因 | | created_at / updated_at | DATETIME | | 索引:`idx_pick(task_status, priority DESC, created_at)`(消费者取任务用) #### tags 标签表 | 字段 | 类型 | 说明 | |---|---|---| | id | INT PK AUTO_INCREMENT | | | name | VARCHAR(20) | 标签名,**唯一索引**(大小写不敏感) | | video_count | INT DEFAULT 0 | 关联视频数(冗余) | | created_at | DATETIME | | #### video_tags 视频-标签关联表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | video_id | BIGINT FK | | | tag_id | INT FK | | 约束:唯一索引 `uk_video_tag(video_id, tag_id)`;索引 `idx_tag(tag_id, video_id)`(标签视频列表页用) ### 3.4 互动域 #### comments 评论表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | video_id | BIGINT FK | | | user_id | BIGINT FK | | | parent_id | BIGINT DEFAULT 0 | 0=一级评论,否则为父评论 ID | | reply_to_user_id | BIGINT NULL | 被回复人 | | content | VARCHAR(500) | 内容 | | like_count | INT DEFAULT 0 | | | status | TINYINT DEFAULT 1 | 1正常 2已删除 3审核中 | | reward_counted | TINYINT DEFAULT 0 | 是否已计入评论奖励进度 | | created_at | DATETIME | | 索引:`idx_video(video_id, status, created_at)`、`idx_user_video(user_id, video_id)`(查首评用) #### likes 点赞表 / favorites 收藏表(结构相同) | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | user_id | BIGINT FK | | | video_id | BIGINT FK | | | created_at | DATETIME | | 约束:**唯一索引 `uk_user_video(user_id, video_id)`** 防重复点赞/收藏;另建 `idx_video(video_id)`。 ### 3.5 计费域 #### watch_records 观看记录表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | subject_type | TINYINT | 1注册用户 2游客 | | subject_id | VARCHAR(64) | user_id 或 device_id | | video_id | BIGINT | | | watch_date | DATE | 观看日期(用于当日去重) | | cost_type | TINYINT | 0免费额度 1积分扣减 2会员免费 3作者本人 | | cost_points | INT DEFAULT 0 | 本次消耗积分 | | watch_duration | INT DEFAULT 0 | 观看时长(秒) | | position | INT DEFAULT 0 | 最新播放进度(秒,用于续播) | | created_at | DATETIME | | 约束:**唯一索引 `uk_dedup(subject_type, subject_id, video_id, watch_date)`**,是"同日同视频不重复扣费"的底层保证。 #### point_transactions 积分流水表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | user_id | BIGINT FK | | | change_amount | INT | 变动值(正=增,负=减) | | balance_after | INT | 变动后余额 | | biz_type | TINYINT | 1充值 2观看消耗 3评论奖励 4开通会员 5活动赠送 6后台调整 7奖励扣回 8推荐奖励 | | biz_id | VARCHAR(64) | 关联单据号(订单号/视频ID等) | | remark | VARCHAR(200) | 备注 | | created_at | DATETIME | | 索引:`idx_user_created(user_id, created_at)`、`idx_biz(biz_type, biz_id)` #### recharge_orders 充值订单表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | order_no | VARCHAR(32) | 平台订单号,**唯一索引** | | user_id | BIGINT FK | | | amount_fen | INT | 人民币金额(分) | | points | INT | 应到账积分 | | pay_method | TINYINT | 1支付宝 2微信 3USDT | | status | TINYINT | 0待支付 1已支付 2已关闭 3已退款 | | third_trade_no | VARCHAR(64) NULL | 第三方流水号/tx_hash,**唯一索引**(幂等关键) | | usdt_amount | DECIMAL(18,6) NULL | USDT 金额(含随机尾差) | | usdt_rate | DECIMAL(10,4) NULL | 下单时 USDT 汇率 | | paid_at | DATETIME NULL | 支付时间 | | expire_at | DATETIME | 过期时间 | | created_at / updated_at | DATETIME | | 索引:`idx_user_status(user_id, status)` ### 3.6 Redis Key 设计(配合数据库) | Key | 类型 | TTL | 用途 | |---|---|---|---| | `sms:code:{phone}` | String | 5min | 短信验证码 | | `email:code:{email}` | String | 10min | 邮箱验证码 | | `free:watch:{subject}:{yyyyMMdd}` | Set(video_id) | 48h | 当日免费观看去重与计数 | | `comment:prog:{user_id}` | Hash {count, date, daily} | 7d | 评论奖励进度/日上限 | | `comment:counted:{user_id}:{video_id}` | String | 永久(或随评论删除) | 同视频首评去重 | | `view:video:{video_id}` | String(计数) | 异步落库 | 播放量缓冲 | | `token:blacklist:{jti}` | String | token剩余有效期 | 登出拉黑 | | `watched:{subject}:{yyyyMMdd}` | Set(video_id) | 48h | 当日已看视频集合(推荐去重) | | `rec:seed:{yyyyMMdd}` | String | 48h | 当日推荐重排随机种子 | | `cview:{subject}:{video_id}:{yyyyMMdd}` | String | 48h | 完整观看当日去重 | | `video:cview:{video_id}` | String(计数) | 异步落库 | 完整观看计数缓冲 | | `ref:device:{device_id}` | String | 永久 | 设备已注册标记(防小号绑定) | | `ref:risk:{inviter_id}:{yyyyMMdd}` | String(计数) | 48h | 邀请人当日新增绑定计数(风控) | | `invite:ip:{ip}` | ZSet(code→点击时间戳) | 24h | 推广点击埋点(按 IP 的归因候选) | | `invite:clk:{code}:{ip}` | String(JSON) | 24h | 点击详情(UA/时间戳) | | `invite:dup:{ip}:{code}` | String | 60s | 同 IP+code 重复埋点去重 | | `invite:attr:{device_id}` | String(邀请码) | 7d | 首启归因结果缓存,注册时一次性消费 | | `wd:pending:{user_id}` | String | 审核期间 | 提现在途唯一锁 | | `search:hot` | ZSet(word→次数) | 25h | 热搜统计(小时截断) | | `notif:unread:{user_id}` | String(计数) | 随读清零 | 未读通知数 | | `words:sensitive` | String(词库) | 60s | 敏感词缓存 | | `config:system` | Hash | 60s | system_configs 缓存 | ### 3.7 管理后台域 #### admin_users 管理员表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | username | VARCHAR(50) | 登录名,**唯一索引** | | password_hash | VARCHAR(255) | bcrypt | | name | VARCHAR(50) | 姓名 | | role | VARCHAR(20) | super / reviewer / operator | | status | TINYINT DEFAULT 1 | 1正常 0禁用 | | last_login_at | DATETIME NULL | | | created_at | DATETIME | | #### admin_logs 操作日志表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | admin_id | BIGINT FK | 操作人 | | action | VARCHAR(50) | 动作(review/adjust_points/ban...) | | target | VARCHAR(100) | 操作对象 | | detail | VARCHAR(1000) | 参数与原因 | | ip | VARCHAR(45) | 操作 IP | | created_at | DATETIME | | 索引:`idx_admin_created(admin_id, created_at)` #### system_configs 平台配置表 | 字段 | 类型 | 说明 | |---|---|---| | config_key | VARCHAR(50) PK | 配置键 | | config_value | VARCHAR(500) | 配置值 | | description | VARCHAR(200) | 说明 | | updated_by | BIGINT NULL | 最后修改人(admin_users.id) | | updated_at | DATETIME | | 预置键:`register_gift_trial.enabled`(注册赠送体验会员开关:1/0,默认 1)、`register_gift_trial.days`(赠送天数,默认 1) #### recharge_packages 充值套餐表 | 字段 | 类型 | 说明 | |---|---|---| | id | INT PK | | | amount_yuan | INT | 充值金额(元) | | points | INT | 基础到账积分(= 金额 × 汇率) | | bonus_points | INT DEFAULT 0 | 赠送积分 | | sort | INT DEFAULT 0 | 排序(越大越靠前) | | status | TINYINT DEFAULT 1 | 1上架 0下架 | | created_at / updated_at | DATETIME | | ### 3.8 激励域 #### referrals 推荐关系表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | inviter_id | BIGINT FK | 推荐人 | | invitee_id | BIGINT | 被推荐人,**唯一索引**(一人仅一次绑定) | | invite_code | VARCHAR(10) | 使用的邀请码 | | status | TINYINT | 0待激活 1有效(已发奖) 2无效 3风控冻结 | | risk_flag | VARCHAR(50) | 风控标记:same_device/same_ip/batch/inviter_burst | | register_ip | VARCHAR(45) | 被推荐人注册 IP | | activated_at | DATETIME NULL | 激活时间 | | created_at | DATETIME | 绑定时间 | 索引:`idx_inviter(inviter_id, created_at)`、`idx_status(status, created_at)` #### asset_wallets 资产钱包表 | 字段 | 类型 | 说明 | |---|---|---| | user_id | BIGINT PK FK | | | assets | DECIMAL(12,1) DEFAULT 0 | 当前资产(允许为负,返利扣回场景) | | total_earned | DECIMAL(14,1) DEFAULT 0 | 累计获得 | | total_withdrawn | DECIMAL(14,1) DEFAULT 0 | 累计提现 | | version | INT DEFAULT 0 | 乐观锁 | | updated_at | DATETIME | | #### asset_transactions 资产流水表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | user_id | BIGINT FK | | | change_amount | DECIMAL(12,1) | 变动值(正=增,负=减) | | balance_after | DECIMAL(12,1) | 变动后余额 | | biz_type | TINYINT | 1充值返利 2完整观看收益 3点赞收益 4提现 5提现退回 6返利扣回 7后台调整 | | biz_id | VARCHAR(64) | 关联单据(订单号/视频ID/提现单号) | | remark | VARCHAR(200) | | | created_at | DATETIME | | 索引:`idx_user_created(user_id, created_at)`、`idx_biz(biz_type, biz_id)` #### withdrawals 提现记录表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | withdraw_no | VARCHAR(32) | 提现单号,**唯一索引** | | user_id | BIGINT FK | | | assets | DECIMAL(12,1) | 申请资产 | | amount_fen | INT | 折合人民币(分) | | channel | TINYINT | 1支付宝 2微信 3USDT | | account | VARCHAR(200) | 收款账号 | | status | TINYINT | 0待审核 1已打款 2已拒绝(已退回) | | reason | VARCHAR(200) | 拒绝原因 | | admin_id | BIGINT NULL | 审核人 | | created_at / processed_at | DATETIME | 申请/处理时间 | 索引:`idx_user(user_id, created_at)`、`idx_status(status, created_at)` #### video_earnings 视频收益结算表 | 字段 | 类型 | 说明 | |---|---|---| | video_id | BIGINT PK | | | author_id | BIGINT | 作者(冗余,结算时查会员) | | complete_views | INT DEFAULT 0 | 累计完整观看次数 | | views_settled | INT DEFAULT 0 | 已结算的完整观看刻度 | | likes_accrued | INT DEFAULT 0 | 累计点赞(取消会回退) | | likes_settled | INT DEFAULT 0 | 已结算的点赞刻度 | | updated_at | DATETIME | | 索引:`idx_author(author_id)` ### 3.9 通用域 #### notifications 消息通知表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | user_id | BIGINT FK | 接收人 | | type | TINYINT | 1审核结果 2提现进度 3系统公告 4评论回复 | | title | VARCHAR(100) | | | content | VARCHAR(500) | | | biz_id | VARCHAR(64) | 关联单据(视频ID/提现单号/评论ID) | | is_read | TINYINT DEFAULT 0 | 0未读 1已读 | | created_at | DATETIME | | 索引:`idx_user_read(user_id, is_read, id)` #### reports 举报表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK | | | user_id | BIGINT FK | 举报人 | | target_type | TINYINT | 1视频 2评论 | | target_id | BIGINT | 对象 ID | | reason_type | TINYINT | 1色情低俗 2暴力血腥 3侵权 4广告营销 5其他 | | remark | VARCHAR(200) | 补充说明 | | status | TINYINT | 0待处理 1已处理 2已驳回 | | handle_result | VARCHAR(50) | 处理动作(offline_video/delete_comment) | | admin_id | BIGINT NULL | 处理人 | | created_at / handled_at | DATETIME | | 约束:唯一索引 `uk_user_target(user_id, target_type, target_id)`;索引 `idx_status(status, created_at)` #### sensitive_words 敏感词表 | 字段 | 类型 | 说明 | |---|---|---| | id | INT PK AUTO_INCREMENT | | | word | VARCHAR(50) | 敏感词,**唯一索引** | | created_at | DATETIME | | ### 3.10 采集域 #### series 系列(合集)表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK AUTO_INCREMENT | | | title | VARCHAR(100) | 系列标题 | | cover_url | VARCHAR(500) | 封面 | | description | VARCHAR(500) | 简介 | | author_id | BIGINT NULL | 作者 | | category_id | INT | 分类 | | total_episodes | INT DEFAULT 0 | 总集数 | | view_count | BIGINT DEFAULT 0 | 播放量 | | status | SMALLINT DEFAULT 1 | 1正常 0隐藏 | | created_at / updated_at | DATETIME | | 索引:`idx_series_cat_status(category_id, status)`、`idx_series_status_created(status, created_at)`;videos 表以 `series_id` 关联所属系列 #### collection_sources 采集源配置表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK AUTO_INCREMENT | | | name | VARCHAR(50) | 分类名(添加时自动建/找本站分类) | | list_url | VARCHAR(500) | 列表 URL 模板,`{{page}}` 为页码占位 | | category_id | INT UNSIGNED | 本站分类 id | | status | SMALLINT DEFAULT 1 | 1启用 0停用 | | last_sync_at | DATETIME NULL | 最近同步时间 | | created_at / updated_at | DATETIME | | #### collection_records 采集记录表 | 字段 | 类型 | 说明 | |---|---|---| | id | BIGINT PK AUTO_INCREMENT | | | source_key | VARCHAR(50) | 内容源标识 | | source_id | BIGINT | 源站内容 id | | series_id | BIGINT NULL | 本站系列 id | | video_id | BIGINT NULL | 本站视频 id(单视频类) | | created_at | DATETIME | | 约束:唯一索引 `uk_source(source_key, source_id)` 保证幂等导入(重复同步不产生重复视频);索引 `idx_record_series(series_id)`、`idx_record_video(video_id)` --- ## 4. 接口设计(概览) - 统一前缀:`/api/v1` - 认证:`Authorization: Bearer `,游客请求携带 `X-Device-ID` - 统一响应:`{ "code": 0, "message": "ok", "data": {...} }` - 完整接口定义见《03-接口文档》 | 分组 | 接口数 | 说明 | |---|---|---| | 认证 | 7 | 验证码、注册、登录、找回密码、刷新、登出 | | 用户 | 8 | 资料、头像、钱包、积分明细、注销、公开主页、他人作品 | | 视频 | 13 | Feed、详情、观看/完整观看上报、上传、发布、编辑、删除、我的作品、分类、标签视频、搜索、热搜 | | 互动 | 7 | 点赞、收藏、评论增删查、收藏/历史列表 | | 会员 | 3 | 套餐、我的会员、积分开通 | | 支付 | 4 + 2回调 | 套餐、下单、查单、取消;支付宝/微信回调(USDT 为服务端轮询) | | 激励 | 6 | 邀请信息、绑定邀请码、资产钱包/流水、提现申请/记录 | | 推广归因 | 2 + 1 落地页 | 点击埋点、首启归因匹配、H5 推广落地页(/r/{code}) | | 消息与通用 | 5 | 版本检查、通知列表/未读数/标记已读、举报 | | 后台管理 | 32 | 审核、视频/评论管理、用户、积分、订单、套餐配置、赠送、提现审核、推荐风控、举报处理、敏感词、平台配置、看板、日志(/admin 前缀) | --- ## 5. 前端设计 技术选型:Flutter(一套代码出 iOS/Android)。技术细节见《02-技术文档》第 5 章。 ### 5.1 页面清单与路由 | 路由 | 页面 | 说明 | |---|---|---| | /splash | 启动页 | 初始化 device_id、Token 续期、配置拉取 | | /home | 首页 | 沉浸式视频 Feed(默认页) | | /video/:id | 视频播放详情 | 全屏播放 + 互动栏 | | /login | 登录/注册 | 手机号 Tab(默认)/ 邮箱 Tab | | /mine | 我的 | 个人中心 | | /mine/edit | 编辑资料 | | | /mine/wallet | 我的钱包 | 积分余额、明细、充值入口 | | /mine/recharge | 充值页 | 套餐选择 + 支付方式 | | /mine/recharge/usdt | USDT 支付页 | 金额/地址/二维码/倒计时 | | /mine/referral | 邀请好友 | 邀请码/链接、累计奖励、推荐列表 | | /mine/assets | 资产钱包 | 资产余额、流水、提现入口 | | /mine/assets/withdraw | 提现页 | 金额/渠道/账号,提交后查进度 | | /mine/vip | 会员中心 | 套餐卡片、权益、开通 | | /mine/videos | 我的作品 | 含审核状态 | | /mine/favorites | 我的收藏 | | | /mine/history | 观看历史 | | | /upload | 上传视频 | 选片→剪辑信息→发布 | | /search | 搜索 | | | /category | 分类页 | 按分类浏览视频 | | /tags/:name | 标签视频页 | 某标签下的视频列表 | | /mine/downloads | 已下载 | 离线视频列表(进度/删除/播放) | | /notifications | 消息通知 | 通知列表、未读标记 | | /settings | 设置 | 退出登录等 | ### 5.2 核心页面布局 #### 5.2.1 首页(视频 Feed) - 全屏竖向 PageView,每个页面一个视频播放器(3 槽播放器池:当前页播放 + 下一页预缓冲,划走即停) - 顶部:Feed 策略 Tab(推荐 / 最新 / 热度 / 口碑 / 热播)+ 搜索图标;各策略分页状态独立保活,切 Tab 不丢进度 - 右侧互动栏(自上而下):作者头像(+关注预留)、点赞(红心+计数)、评论(气泡+计数)、收藏(星标+计数)、分享 - 左下:作者昵称、标题(标题后展示 #标签,点击跳转标签视频页;并带时长角标)、描述(展开/收起) - 底部(游客/非会员场景):今日免费剩余次数提示条(剩余 ≤ 3 次时显示) #### 5.2.2 评论页(底部弹层) - BottomSheet,高度 2/3 屏;顶部评论总数;列表(头像/昵称/内容/时间/点赞);底部输入框 - 二级回复:点击评论展开子回复 #### 5.2.3 我的 - 顶部:头像、昵称、ID、会员标识(金色卡片入口) - 中部数据行:作品 / 收藏 / 历史 - 功能列表:我的钱包(积分余额)、资产钱包(可提现)、邀请好友、会员中心、上传视频、设置 #### 5.2.4 充值页 - 积分余额展示;充值套餐网格(后台配置的档位,角标"赠 x 积分")+ 自定义金额输入框 - 支付方式单选:支付宝 / 微信 / USDT(带图标) - 底部"立即支付"按钮;支付结果页(成功/失败/查询中轮询) #### 5.2.5 会员中心 - 当前会员状态卡片(类型、到期时间) - 四张套餐卡:体验/月卡/年卡/终身卡(价格=积分,终身卡高亮"永久") - 权益说明列表:无限观看、优先上传 - 开通按钮 → 积分余额确认弹窗(余额不足跳充值) #### 5.2.6 上传页 - 三步:选择视频(系统相册/拍摄)→ 填写信息(封面选择、标题、描述、分类)→ 发布 - 发布后进入"转码中"状态页/ Toast 提示,可在"我的作品"查看进度 ### 5.3 核心交互设计 | 场景 | 交互 | |---|---| | 切换视频 | 垂直滑动,松手吸附;滑出即暂停,滑入自动播放并**上报观看计费** | | 单击屏幕 | 暂停 / 继续播放(暂停时显示播放图标) | | 双击屏幕 | 点赞动画(大爱心飘出) | | 长按屏幕 | 弹出操作菜单:下载视频 / 倍速 / 分享(已下载或下载中置灰) | | 拖动进度条 | 跟随 thumb 显示时间气泡(x时xx分xx秒,两端自动夹在屏内) | | 游客超限 | 播放第 11 个视频时弹出登录引导弹窗("注册解锁更多视频,今日已看 10/10") | | 用户积分不足 | 弹窗"积分不足":去充值 / 开通会员 两个按钮 | | 扣费提示 | 每次扣积分播放时顶部 Toast"本次消耗 1 积分" | | 会员标识 | 昵称旁金色 VIP 角标;会员 Center 显示到期时间 | | 上传结果 | 转码/审核状态在我的作品实时刷新(轮询或推送) | ### 5.4 UI 样式规范 | 项 | 规范 | |---|---| | 主题 | 深色为主(#121212 背景),保证视频沉浸感;浅色可选(二期);**弹窗/底部菜单统一白底亮色**(局部包 `ThemeData.light()`,避免深色背景下文字看不清) | | 主色 | #FE2C55(红),辅助色 #25F4EE(青) | | 会员色 | 金色渐变 #F7D794 → #D4A017 | | 文字 | 主 #FFFFFF,次 #B0B0B0,弱 #6E6E6E | | 字体 | 系统默认(中文 PingFang/思源),标题 17sp 加粗,正文 15sp,辅助 12sp | | 间距 | 4 的倍数体系:4/8/12/16/24 | | 圆角 | 按钮 8dp,卡片 12dp,头像圆形 | | 组件 | 统一封装:按钮、弹窗、Toast、空态、骨架屏 | ### 5.5 域名发现(API 入口) App 不内置固定 API 域名,启动时按以下流程动态获取: 1. **优先:DNS TXT 解析**。解析 `txt.256kb.top` 的 TXT 记录,记录内容即真实 API 域名(如 `api.xxx.com`),拼接 `https://` 作为 Base URL 2. **兜底:计算域名**。TXT 解析失败或内容为空时,取当前"年月"字符串(格式 `yyyyMM`,如 `202610`)计算 MD5(32 位小写),拼接为 `{md5(yyyyMM)}_jsnbhh.vip`(例如 `d8578ef...9c_jsnbhh.vip`) 3. 获取成功后写入本地缓存;后续启动优先使用缓存域名,同时后台异步刷新;缓存域名连续请求失败达到阈值时重新走完整发现流程 4. 所有网络请求统一从该入口获取 Base URL,业务代码中禁止硬编码任何 API 域名 5. **兜底域名配套**:`jsnbhh.vip` 需配置 DNS 泛解析 + 通配符证书(`*.jsnbhh.vip`)并接入 CDN/网关回源,保证任意 md5 前缀域名开箱可用;每月 1 日前运维确认泛解析与证书有效 --- ### 5.6 设备指纹与公共请求头 - **device_id**:App 首次启动生成 UUID(v4),Android 存 Keystore 加密的 SharedPreferences,iOS 存 Keychain(卸载重装保留);即游客计费与风控的设备指纹,客户端禁止采集 IMEI/OAID 等系统标识 - 所有请求携带公共 Header:`X-Device-ID`(必带)、`X-App-Version`(如 1.0.0)、`X-Platform`(android/ios) ### 5.7 版本检查与强制更新 - App 启动及回到前台时调用 `GET /app/version`:返回最新版本号、是否强制更新、下载地址、维护公告 - 强制更新:弹窗阻断使用并跳转商店/下载页;非强制:可跳过的提示弹窗;维护公告:全 App 只读提示 ### 5.8 工程结构(与当前代码对齐) ``` app/lib/ ├── screens/ # 页面:splash / home(HomeShell+IndexedStack) / feed / search / category │ # / player / membership / payment / wallet / assets / upload │ # / profile / auth / settings / notification / downloads ├── providers/ # Riverpod 状态(12 个):auth / feed / comment / wallet / member │ # / payment / profile / search / upload / asset / notification │ # / tag_videos └── core/ # 子系统:config(环境配置)/ media(local_media_server 本地代理、 # download_manager 下载)/ network(api_client、域名发现)/ # player(feed_player_pool 播放器池)/ storage(token、本地存储)/ # theme / utils ``` - Feed 策略状态按策略保活:`feedProviderFamily(strategy)` 每个策略一个独立 FeedNotifier;`activeFeedStrategyProvider` + `feedTabVisibleProvider` 配合 HomeShell(IndexedStack)在切页/回前台时挂起与恢复正确的播放器池 - 401 兜底:无 refresh token 或刷新被拒时清登录态并跳登录页,防止"僵尸登录态" ## 6. 后端设计 ### 6.1 框架推荐:**FastAPI** | 对比项 | FastAPI(推荐) | Django REST Framework | Flask | |---|---|---|---| | 异步支持 | 原生 async/await(IO 密集接口吞吐高) | 3.x 起部分支持 | 需额外方案 | | API 文档 | 自动生成 OpenAPI/Swagger | 需 drf-spectacular | 需 flasgger 等 | | 数据校验 | Pydantic 类型声明即校验 | Serializer 较繁琐 | 无内置 | | 性能 | 高(Starlette + Uvicorn) | 中 | 中 | | 学习/维护 | 类型提示友好,适合中小团队快速迭代 | 全家桶偏重 | 灵活但需自行拼装 | 本项目接口以高并发短请求 + 文件流转为主,FastAPI 的异步 IO、自动文档(与《03-接口文档》可互验)和 Pydantic 校验最匹配。 **技术栈**:Python 3.11 + FastAPI + SQLAlchemy 2.0(异步)+ Alembic + Pydantic v2 + Celery(转码/异步任务)+ MySQL 8 + Redis 7 + FFmpeg + Nginx。 ### 6.2 模块划分 ``` server/ ├── app/ │ ├── main.py # 应用入口、中间件、路由注册、lifespan(settle 周期任务启动) │ ├── core/ # 配置、日志、异常、安全(JWT/密码) │ ├── api/v1/ # 路由层(11 个文件):admin / auth / common / incentives │ │ # / interactions / invite / media / membership / payments │ │ # / users / videos(管理后台同文件 /admin 前缀、独立管理员鉴权) │ ├── schemas/ # Pydantic 请求/响应模型 │ ├── models/ # SQLAlchemy ORM 模型(30 张表) │ ├── services/ # 业务逻辑(17 个):user/auth/wallet/video/interaction/membership │ │ # /payment/incentive/invite/admin/common/config/sensitive │ │ # /storage/transcode/settle/collection │ ├── payment/ # 支付适配层:alipay / wechat / usdt │ ├── tasks/ # Celery 任务:转码、播放量落库、订单关单 │ └── utils/ # 短信、邮件、OSS、设备指纹 ├── scripts/ # 离线脚本:build_tags_from_titles.py(标签生成)、采集脚本 ├── alembic/ # 数据库迁移(init_27_tables → collection_records → collection_sources) ├── config.yaml # 业务参数配置(积分/会员/支付/上传限制) ├── .env # 密钥类配置(不入库) └── docker-compose.yml ``` ### 6.3 核心流程设计 #### 6.3.1 观看计费(watch 接口内部逻辑) 1. 解析身份:有 Token → 用户;无 Token → device_id 游客 2. 会员校验:`user_membership` 终身卡或 `expire_at > now()` → 记录观看(cost_type=2)返回放行 3. 同日去重:尝试插入 `watch_records`(唯一键冲突 = 今日已看过)→ 放行返回 4. 免费额度:Redis `SADD free:watch:{subject}:{date} video_id`,若返回 1 且 `SCARD ≤ 10` → 免费占用,落库返回 5. 超额度:游客 → 返回错误码 `FREE_LIMIT_EXCEEDED`;用户 → 事务内扣积分(锁钱包行 + 写流水 + 落观看记录)→ 返回成功及剩余积分 6. 积分不足 → 返回错误码 `INSUFFICIENT_POINTS` > 关键:步骤 3-5 的"去重 + 占额度"用 Redis Lua 脚本保证原子性;扣积分用数据库事务 + 行锁保证原子性。 #### 6.3.2 支付到账(回调逻辑,三渠道统一) 1. 验签/验来源(支付宝公钥、微信 V3 平台证书、USDT 链上数据核对) 2. 按订单号查订单,校验状态为"待支付"且金额一致 3. 幂等校验:`third_trade_no`/`tx_hash` 唯一索引冲突 → 直接返回成功(重复回调) 4. 事务:订单置已支付 → 钱包加积分(行锁)→ 写积分流水 → 提交 5. 返回支付平台要求的成功响应;失败进入重试/告警 #### 6.3.3 转码流水线 1. 上传完成 → 创建 videos(status=0) + transcode_tasks(priority=会员?1:0) 2. Celery 两个队列:`transcode.high` / `transcode.normal`,Worker 优先消费 high 3. FFmpeg 转 HLS(输出 m3u8 + ts)→ 上传 OSS → 更新 video_url、时长、封面 4. 状态流转:转码成功 → 待审核;失败重试 3 次 → 转码失败 #### 6.3.4 评论奖励结算 1. 发表评论(≥5 字、视频有效、同视频首评)→ 写评论 + `comment:counted` 去重标记 2. Redis `HINCRBY comment:prog:{uid} count 1`;达 10 → 扣减 10 并事务发放 1 积分(写流水,biz_type=3) 3. 日上限校验:`daily` 字段达 5 则当日不再发放 4. 评论删除:若 `reward_counted=1` → 进度 -1;若奖励已发放 → 发放 -1 积分流水(biz_type=7) ### 6.4 配置文件样例(config.yaml) ```yaml points: points_per_yuan: 10 # 1元=10积分 watch_cost: 1 # 每视频消耗积分 daily_free_count: 10 # 每日免费观看数 comment_reward: per_count: 10 # 每评论10个视频 reward: 1 # 奖励1积分 min_length: 5 daily_limit: 5 membership: trial: { days: 1, points: 5, enabled: true } monthly: { days: 30, points: 300, enabled: true } yearly: { days: 365, points: 3650, enabled: true } lifetime: { days: 0, points: 10000, enabled: true } # days=0 表示永久 recharge: # 充值档位在管理后台维护(recharge_packages 表),实时生效,此处不配置 usdt: { rate: 7.2, network: TRC20, address: "T...", confirmations: 19, order_expire_min: 30 } referral: register_reward_points: 10 # 邀请有效新用户奖励积分(N),不限量 rebate_percent: 5 # 被推荐人充值积分返利比例→资产 activate_watch_count: 3 # 激活条件:72h内观看≥3个视频 activate_hours: 72 risk_max_bind_per_day: 10 # 邀请人单日新增绑定上限(超限冻结审核) risk_max_register_per_ip: 5 # 同IP单日注册上限(超限标记风险) asset: assets_per_yuan: 10 # 10资产=1元 complete_views_per_asset: 1000 # 每1000次完整观看=1资产 likes_per_asset: 100 # 每100次点赞=1资产 complete_progress: 0.95 # 完整观看判定进度 withdraw: min_assets: 100 # 最低提现资产(=10元) fee_percent: 0 # 提现手续费比例 channels: [alipay, wechat, usdt] upload: max_size_mb: 500 min_duration_s: 5 max_duration_s: 600 hls_profiles: ["720p"] # 可配 ["1080p","720p","480p"] ``` ### 6.5 安全设计 - 密码 bcrypt 哈希;登录失败 5 次锁定 15 分钟(Redis 计数) - JWT 双 Token:access 2 小时 + refresh 30 天;登出/改密加入黑名单 - 支付回调只信验签结果,金额以服务端订单为准 - 接口限流:验证码 1 条/分钟、评论 10 条/分钟、观看上报 60 次/分钟 - 上传文件类型白名单校验(mp4/mov/m3u8/ts),大小校验 - SQL 注入防护:ORM 参数化;XSS:评论内容转义输出