每日日语 · 内容系统实施规格文档
每日日语 · 内容系统实施规格文档
Section titled “每日日语 · 内容系统实施规格文档”[!IMPORTANT] 文档状态说明(2026-03-28)
这份文档保留了原始实施规格正文,以及后续多轮自查、修复、补充和扩展痕迹,作为历史留档继续保留。
为避免当前执行继续被过重文档干扰,Yomiya 文档体系已经开始拆分。当前执行请优先阅读以下文档:
yomiya-phase1-execution-spec.md:Phase 1 当前执行收口版yomiya-canonical-naming.md:命名冻结表yomiya-phase1-scope-boundary.md:Phase 1 范围边界清单yomiya-implementation-spec-core.md:当前有效实施规格主文档yomiya-implementation-review-log.md:历史审查与修复记录yomiya-implementation-open-questions.md:尚未定稿的开放问题yomiya-implementation-future-phases.md:后续阶段能力规划阅读建议:
- 要开工,看
execution-spec+naming+scope-boundary+implementation-spec-core- 要查历史问题,看
implementation-review-log- 要看未决事项,看
implementation-open-questions- 要看后续路线,看
implementation-future-phases处理原则: 本文档主体内容不做大规模删改,继续保留原始留档价值;新增文档负责承接当前执行、历史问题和未来规划。
性质:可执行实施规格,所有结论为定量定死的数字,不存在”大约”或”至少”
前置文档:yomiya-content-strategy.md(目标形态)/ yomiya-engineering-gap-analysis.md(工程差距)
本文档定位:将策划结论转化为前端原型、工程排期、运营执行的直接输入
创建:2026-03-27 | 最后更新:2026-03-27(吸收 cc 15条反馈,全量更新)
参与:cc(产品负责人)× Captain(OpenClaw 主控 AI)
- 首页模块精确规格
- 首批合集规格(6个)
- 内容入库判断规则
- LLM 打标 Prompt 规格
- 定期智能分析任务规格(冷启动期版本)
- 内容采集搜索清单
- 用户痛点与内容模块映射
- 合集数据结构设计
- 今日打卡 / 学习进度定义
- 内容标题处理规则
- 封面图获取与存储策略
- 待解决的模糊点清单
一、首页模块精确规格
Section titled “一、首页模块精确规格”1.1 首页整体布局(自上而下)
Section titled “1.1 首页整体布局(自上而下)”┌──────────────────────────────────┐│ ① Banner 轮播区 │ 高度:200px,轮播数:3张,切换间隔:5秒├──────────────────────────────────┤│ ② 今日学习区(原"今日打卡") │ 固定展示,每日刷新,基于今日学习时长判定├──────────────────────────────────┤│ ③ 热门推荐(合集区) │ 横向滚动,展示8个合集卡片├──────────────────────────────────┤│ ④ 每日资讯 Tab 区 │ 6个Tab,每Tab默认展示6条├──────────────────────────────────┤│ ⑤ JLPT 专区入口 │ 5个级别按钮(N5/N4/N3/N2/N1)├──────────────────────────────────┤│ ⑥ 轻松入门区 │ 固定展示4条入门内容(常青)├──────────────────────────────────┤│ ⑦ 名师专栏 & 播客系列 │ 展示2个播客系列入口├──────────────────────────────────┤│ ⑧ 热门专题(运营展台) │ 展示1个当前专题└──────────────────────────────────┘1.2 模块①:Banner 轮播区
Section titled “1.2 模块①:Banner 轮播区”| 参数 | 值 |
|---|---|
| 轮播图数量 | 3 张(固定,后台配置) |
| 图片比例 | 16:9,1080×607px |
| 切换间隔 | 5 秒 |
| 运营更新频率 | 每两周更换一次 |
| 内容用途 | 推广当期重点合集 / 限时活动 / JLPT 倒计时 |
1.3 模块②:今日学习区
Section titled “1.3 模块②:今日学习区”定义修正:今日打卡不以”完成某条内容”为判定,而以”今日累计学习时长”为判定。
| 参数 | 值 |
|---|---|
| 打卡判定标准 | 当日在 App 内累计有效使用时长 ≥ 15 分钟(有效使用 = 视频播放 / 音频播放 / 文章阅读,不含挂后台) |
| 展示内容数 | 1 条”今日推荐”内容(每日刷新) |
| 内容类型优先级 | 视频 > 音频 > 图文 |
| Streak 递增 | 当日累计有效时长达到 15 分钟 |
| Streak 中断恢复 | 付费用户每 30 天可购买 1 次复活 |
| 展示字段 | 封面图 + 标题 + 时长 + 难度标签 + 今日学习进度(已学 X 分钟 / 目标 15 分钟) |
| 游客状态 | 展示通用版今日推荐,无 Streak 功能 |
| 登录用户 | 同上 + Streak 天数 + 个性化推荐(个性化推荐在冷启动期不启用,预留字段) |
今日推荐选取逻辑(冷启动期):从当日新入库内容中,按以下优先级选取 1 条:
type = video+level = 3(N3)+ 今日入库type = video+ 任意 level + 今日入库type = article+level = 3(N3)+ 今日入库
1.4 模块③:热门推荐(合集区)
Section titled “1.4 模块③:热门推荐(合集区)”| 参数 | 值 |
|---|---|
| 展示合集数量 | 8 个(固定) |
| 其中视频合集 | 5 个 |
| 其中图文合集 | 3 个 |
| 卡片尺寸 | 160×240px(纵向卡片) |
| 卡片展示字段 | 封面图 + 合集名(≤14字)+ 集数 + 难度标签(N5–N1) |
| 滑动方向 | 横向滑动 |
合集启动硬规则:
| 合集类型 | 最低条目数 | 低于此数时处理 |
|---|---|---|
| 视频合集 | 8 条 | 不创建合集,内容加入”待建合集暂存区” |
| 图文日更合集 | 20 条(历史存量) | 不上线该合集入口 |
| 图文常青合集 | 12 条 | 不上线 |
| 播客系列 | 6 期 | 不展示,继续储备 |
过渡方案(collections 表未上线前):后台 featured_items 配置,人工指定 8 条精选内容,以卡片列表替代合集区。
1.5 模块④:每日资讯 Tab 区
Section titled “1.5 模块④:每日资讯 Tab 区”Tab 分类逻辑修正:不是资讯”属于哪个 Tab”,而是”这条内容的 primary topic_tag 对应哪个 Tab”。
topic_tag → Tab 映射表(完整,无遗漏):
| topic_tag | 对应 Tab |
|---|---|
| 社会观察 | 社会 |
| 历史人文 | 社会 |
| 自然与科学 | 社会 |
| J-pop与音乐 | 娱乐 |
| 日剧 | 娱乐 |
| 综艺 | 娱乐 |
| 日本文化 | 文化 |
| 节日与习俗 | 文化 |
| 饮食文化 | 文化 |
| 城市与生活 | 文化 |
| 动漫 | 动漫 |
| 科技趋势 | 科技 |
| 旅行 | 旅行 |
| 职场商务 | 文化(暂归文化,未来独立) |
| 日常场景 | 文化(暂归文化,未来独立) |
当 primary topic_tag 无法映射时:归入”社会” Tab(默认兜底)。
Tab 上线条件(定死):
| Tab | 历史存量要求 | 日更要求 | 当前状态 |
|---|---|---|---|
| 社会 | 60 条 | 2 条/日 | ✅ 立即上线 |
| 娱乐 | 60 条 | 2 条/日 | ✅ 立即上线 |
| 文化 | 40 条 | 2 条/日 | ❌ 待 NHK World 接入 |
| 科技 | 40 条 | 2 条/日 | ❌ 待 NHK World 接入 |
| 动漫 | 30 条 | 1 条/日 | ❌ 待动漫频道接入 |
| 旅行 | 30 条 | 1 条/日 | ❌ 待旅行类内容采集 |
不满足条件的 Tab 不展示,首页 Tab 区初期只展示 2 个(社会 + 娱乐)。
1.6 模块⑤:JLPT 专区入口
Section titled “1.6 模块⑤:JLPT 专区入口”| 参数 | 值 |
|---|---|
| 按钮数量 | 5 个(N5 / N4 / N3 / N2 / N1) |
| 各级别上线条件 | 该级别内容存量 ≥ 30 条 |
| 列表页排序 | 发布时间倒序(最新在前) |
| 列表每次加载 | 10 条 |
| 展示字段 | 封面 + 标题 + 时长/字数 + 来源 + 发布时间 |
当前各级别存量全部满足(均 > 200 条)。立即可上线。
1.7 模块⑥:轻松入门区
Section titled “1.7 模块⑥:轻松入门区”| 参数 | 值 |
|---|---|
| 展示条数 | 4 条(常青,不日更) |
| 入选标准 | level = 1(N5) + duration_sec ≤ 300 或 word_count ≤ 300 |
| 更新频率 | 每 30 天人工替换 1 次 |
1.8 模块⑦:播客系列
Section titled “1.8 模块⑦:播客系列”| 参数 | 值 |
|---|---|
| 上线条件 | 同时有 2 个系列,各储备 ≥ 6 期 |
| 封面来源 | RSS 原始封面图,直接引用(不重新设计) |
| 节目简介 | 优先使用 RSS description;若 RSS description 与用户价值匹配度低(AI 判断相关度 < 0.5),则 AI 重新生成中文简介(≤60字) |
| 更新频率 | 每周 1 期(固定承诺,中断则隐藏该系列入口) |
1.9 模块⑧:热门专题
Section titled “1.9 模块⑧:热门专题”| 参数 | 值 |
|---|---|
| 同时展示专题数 | 1 个 |
| 每个专题包含条目 | 10 条(固定,低于 10 条不上线) |
| 专题存续时间 | 14 天 |
| 内容来源 | 从现有 news 表按 topic_tags 筛选 + 专题期间专项采集入库两种方式并用 |
二、首批合集规格
Section titled “二、首批合集规格”合集 1:「NHK 今日要闻」
Section titled “合集 1:「NHK 今日要闻」”| 字段 | 值 |
|---|---|
| 类型 | 图文日更合集(collection_type = rolling_feed) |
| 封面 | 固定一张 NHK 品牌封面图(深蓝底色 + NHK Logo),不随内容变化 |
| 内容来源 | 自动:每日新入库的 channel_id = NHK 内容自动追加 |
| 合集条目上限 | 无上限(持续追加,保留全部历史) |
| 前台展示条数 | 每次加载 10 条,发布时间倒序 |
| 难度标签 | N3–N4 |
| 付费墙 | 无(全部免费) |
| 完结状态 | 永不完结(is_finished = false) |
合集 2:「日本娱乐速报」
Section titled “合集 2:「日本娱乐速报」”| 字段 | 值 |
|---|---|
| 类型 | 图文日更合集(rolling_feed) |
| 封面 | 固定一张娱乐风格封面图 |
| 内容来源 | 自动:channel_id = YahooNewsEntertainment 每日自动追加 |
| 合集条目上限 | 无上限 |
| 前台展示条数 | 每次加载 10 条,发布时间倒序 |
| 难度标签 | N2–N3 |
| 付费墙 | 无 |
| 完结状态 | 永不完结 |
合集 3:「日本文化冷知识」
Section titled “合集 3:「日本文化冷知识」”| 字段 | 值 |
|---|---|
| 类型 | 视频合集(curated,人工筛选) |
| 封面 | 由运营人工上传(日本传统元素,16:9) |
| 内容来源 | 人工筛选 + NHK World 文化类视频自动推送到待选池 |
| 合集内排序 | LLM 评定的优先级分数(priority_score)降序排列 |
| 最低开合集条目数 | 8 条 |
| 付费墙 | 部分内容会员专属(is_membership = true) |
| 完结状态 | 永不完结(持续更新) |
| YouTube 采集关键词 | ”Japanese culture explained” / “日本文化 解説” |
合集 4:「去日本旅行必备」
Section titled “合集 4:「去日本旅行必备」”| 字段 | 值 |
|---|---|
| 类型 | 视频合集(curated,场景结构化) |
| 封面 | 运营上传(日本街景/机场,16:9) |
| 场景覆盖(固定结构) | 机场2条 / 酒店2条 / 餐厅2条 / 交通1条 / 购物1条 = 共 8 条 |
| 合集内排序 | 场景顺序(机场→酒店→交通→餐厅→购物),LLM 生成 priority_score 辅助 |
| 最低开合集条目数 | 8 条(覆盖以上 5 个场景) |
| 付费墙 | 前 4 条免费,后 4 条会员专属 |
| 完结状态 | 永不完结(持续补充场景) |
合集 5:「动漫日语精听」
Section titled “合集 5:「动漫日语精听」”| 字段 | 值 |
|---|---|
| 类型 | 视频合集(curated) |
| 封面 | 运营上传(动漫风格插图) |
| 内容方向 | 官方动漫 PV + 第 1 话试看 + 经典台词解析 |
| 合集内排序 | priority_score 降序 |
| 最低开合集条目数 | 8 条 |
| 付费墙 | 前 3 条免费,其余会员专属 |
| 完结状态 | 永不完结 |
合集 6:「JLPT N3 冲刺」
Section titled “合集 6:「JLPT N3 冲刺」”| 字段 | 值 |
|---|---|
| 类型 | 混合合集(视频 + 图文,curated) |
| 封面 | 运营上传(简洁学习风,含”N3”大字) |
| 内容构成 | 视频 10 条(词汇5 + 语法5) + 图文 10 条(真题解析) |
| 合集内排序 | 先视频后图文,各自内部按 priority_score 降序 |
| 最低开合集条目数 | 视频 10 条 + 图文 10 条同时到位才开 |
| 付费墙 | 视频全部会员专属,图文免费 |
| 完结状态 | 永不完结 |
三、内容入库判断规则
Section titled “三、内容入库判断规则”3.1 硬性过滤条件(不通过则跳过,不入任何队列)
Section titled “3.1 硬性过滤条件(不通过则跳过,不入任何队列)”| 过滤项 | 规则 |
|---|---|
| 时长(视频/音频) | < 60 秒 或 > 5400 秒 |
| 内容语言 | AI 判断日语内容占比 < 30% |
| 来源黑名单 | channel_id 在黑名单表 |
| 重复内容 | URL 的 MD5 已存在数据库 |
3.2 字幕/文字稿处理规则
Section titled “3.2 字幕/文字稿处理规则”| 情况 | 处理方式 | 发布状态 |
|---|---|---|
| 有日语字幕 | 直接提取并清洗 | 正常发布 |
| 无字幕,时长 ≤ 1800 秒 | Whisper 转录 | 正常发布 |
| 无字幕,时长 > 1800 秒 | 不转录,仅元数据 | 发布,标注”暂无字幕” |
| Whisper 失败 | 标注”字幕转录失败” | 仍发布 |
3.3 visibility 赋值规则
Section titled “3.3 visibility 赋值规则”| 条件 | visibility |
|---|---|
| 默认 | VISIBLE |
| 付费墙内容 | MEMBERSHIP_VISIBLE |
| AI 判断疑似违规(置信度 > 0.9) | INVISIBLE,进异常队列 |
四、LLM 打标 Prompt 规格
Section titled “四、LLM 打标 Prompt 规格”4.1 输入 Schema
Section titled “4.1 输入 Schema”{ "title": "string,原始标题", "channel_name": "string,频道/节目名", "description": "string,描述前 300 字", "transcript_sample": "string,字幕/正文前 500 字(无则空字符串)", "duration_sec": "integer(图文填0)", "source_tags": "array<string>,来源平台原有标签", "content_type": "string,video / audio / article"}4.2 输出 Schema
Section titled “4.2 输出 Schema”{ "channel": "string,7个栏目选1", "level": "string,N5/N4/N3/N2/N1/不限", "primary_scene": "string,从59个 sub_scene 名称中选1个(见第十三章完整列表)", "secondary_scenes": "array<string>,从59个 sub_scene 名称中选0–2个(可为空数组)", "priority_score": "integer,1–100,合集内排序依据(基于用户痛点匹配度:旅行/动漫/JLPT备考等高优先级痛点对应的内容分数高)", "collection_hints": { "suitable": "boolean", "collection_name": "string(suitable=false时填空字符串)", "reason": "string,≤50字" }, "confidence": "float,0.0–1.0"}注意:
- 标题重写(title_rewrite)由独立的标题处理规则决定,不在此 Prompt 中处理
topic_tags字段已废弃,改为直接使用primary_scene/secondary_scenes(sub_scene 名称字符串)- 服务端通过
sceneMap[name]将名称映射到 scene ID,匹配失败时不打标(不阻断发布)
4.3 置信度处理规则
Section titled “4.3 置信度处理规则”| confidence | 处理方式 |
|---|---|
| ≥ 0.75 | 全字段直接写入 |
| 0.50–0.74 | 写入,channel 改为”未分类” |
| < 0.50 | 写入,channel=“未分类”,level=“不限”,topic_tags=[] |
所有内容无论置信度如何,均发布(visibility = VISIBLE)。
4.4 priority_score 评分标准
Section titled “4.4 priority_score 评分标准”LLM 在 1–100 范围内打分,评分依据:
| 匹配用户痛点程度 | priority_score 范围 |
|---|---|
| 直接命中高频痛点(旅行场景/动漫台词/JLPT备考/文化理解) | 75–100 |
| 间接相关(知识延伸/泛文化内容) | 40–74 |
| 弱相关(资讯/背景知识) | 10–39 |
五、定期智能分析任务规格(冷启动期版本)
Section titled “五、定期智能分析任务规格(冷启动期版本)”⚠️ 冷启动期原则:当前阶段目标是极速扩充视频和音频内容(视频 25→500 条,播客 0→50 条),所有任务以”发现更多内容、加速入库”为唯一目的。冷启动期内不执行任何内容下架或降权操作。
冷启动期结束标志:视频内容 ≥ 500 条 + 播客内容 ≥ 50 条。
任务 A:合集归档分析(每周一次,周一 03:00 JST)
Section titled “任务 A:合集归档分析(每周一次,周一 03:00 JST)”分析逻辑:
- 扫描过去 7 天新入库内容
- 按
primary_topic_tag聚合,统计每个主题在过去 30 天的累计存量 - 某主题累计存量首次达到 10 条 → 生成合集建议
待办事项机制:
- 合集建议写入
collection_suggestions表 - 每次任务运行前,先扫描
collection_suggestions表中所有状态 =pending的建议 - 对每条 pending 建议,AI 重新评估并附上”建议操作”(创建合集 / 暂缓 / 忽略)
- 将评估结果一并发送飞书通知给 cc,让 cc 一次性处理所有待办
- 未处理的建议不重复通知,保持在 pending 状态,等下次任务时再次评估和合并通知
飞书通知格式:
📦 每周合集归档分析 - 2026-04-07
【待处理建议(3条)】
1. [新] 主题「动漫」过去30天累计 12条,建议建合集「动漫日语精听补充包」 → 建议操作:立即创建(内容已达最低阈值8条)
2. [已提醒2周] 主题「旅行」累计 9条,建议建合集「去日本旅行必备」补充 → 建议操作:再等1周(距阈值还差1条)
3. [新] 主题「职场商务」累计 11条,建议建合集「职场日语速成」 → 建议操作:立即创建
👉 操作入口:[Admin 后台合集管理] 或回复此消息:「全部接受」「忽略第2条」等cc 的处理方式:回复 Captain 消息即可触发操作,无需打开 Admin 后台。
任务 B:视频内容扩充追踪(每日一次,每日 09:00 JST)
Section titled “任务 B:视频内容扩充追踪(每日一次,每日 09:00 JST)”分析逻辑:
- 统计过去 24 小时新入库视频数量
- 统计当前视频总存量
- 统计各主题的视频存量分布
触发通知条件(以下任一满足则发通知):
- 过去 24 小时视频新增 = 0(采集可能出现问题)
- 某个主题视频存量达到合集开合阈值(8 条)
日报格式(仅在触发条件满足时发送,不发日常日报):
📹 视频库今日动态
总存量:87条(昨日 +5条)距离冷启动期结束:413条
⚠️ 今日零新增来源:动漫频道(上次更新 3天前)✅ 主题「旅行」已达合集开合阈值(8条),可创建合集任务 C:热点内容采集建议(每周一次,周五 10:00 JST)
Section titled “任务 C:热点内容采集建议(每周一次,周五 10:00 JST)”分析逻辑:
- 检查近期日本热点(通过 NHK、Yahoo 新闻标题聚合分析)
- 对比现有内容库,找出热点主题在库内存量 < 5 条的空白点
- 生成当周内容采集建议清单
输出格式(飞书通知):
📋 本周内容采集建议 - 2026-04-04
【当前热点 × 库内空白】
1. 话题:「新年号」相关日语表达 库内存量:0条 建议搜索词:「令和 日本語 表現」「新元号 会話」 建议频道:NHK World YouTube
2. 话题:春季动漫新番开播 库内存量:3条(不够建合集) 建议搜索词:「2026春アニメ 公式PV」 建议频道:东宝/角川官方频道
👉 发链接给我即可触发自动入库六、内容采集搜索清单
Section titled “六、内容采集搜索清单”6.1 YouTube 频道白名单
Section titled “6.1 YouTube 频道白名单”| 频道名 | YouTube Handle | 内容方向 | 采集优先级 |
|---|---|---|---|
| NHK World Japan | @NHKWorldJapanEng | 文化/科技/社会纪录片 | P0 |
| NHK Web Easy | @nhk_easy | 简单日语新闻视频版 | P0 |
| 東宝(Toho)Animation | @TohoAnimation | 动漫 PV + 第1话 | P1 |
| 角川(Kadokawa)官方 | @KADOKAWA_official | 动漫 PV + 试看 | P1 |
| 集英社(Shueisha)官方 | @ShueishaOfficial | 少年漫画动漫 PV | P1 |
| 日本政府观光局 JNTO | @JNTO_jp | 旅行文化视频 | P1 |
6.2 YouTube 主动搜索关键词
Section titled “6.2 YouTube 主动搜索关键词”| 目标内容 | 日文搜索词 | 英文搜索词 | 目标合集 |
|---|---|---|---|
| 旅行场景对话 | 日本語 旅行 会話 練習 | Japanese travel phrases | 去日本旅行必备 |
| 机场/酒店场景 | 空港 ホテル 日本語 | Japanese airport hotel phrases | 去日本旅行必备 |
| 餐厅点餐 | レストラン 日本語 注文 | Japanese restaurant ordering | 去日本旅行必备 |
| JLPT N3 词汇 | JLPT N3 語彙 解説 | JLPT N3 vocabulary | JLPT N3 冲刺 |
| JLPT N3 语法 | N3 文法 例文 解説 | JLPT N3 grammar | JLPT N3 冲刺 |
| 日本文化讲解 | 日本文化 解説 面白い | Japanese culture explained | 日本文化冷知识 |
| 动漫台词解析 | アニメ 日本語 解説 | anime Japanese dialogue | 动漫日语精听 |
| 日本节日习俗 | 日本 祭り 伝統 解説 | Japanese festivals | 热门专题 |
6.3 Podcast 采集清单
Section titled “6.3 Podcast 采集清单”| 节目名 | 平台 | RSS 状态 | 难度 | 更新频率 | 优先级 |
|---|---|---|---|---|---|
| NHK ラジオ日本 | NHK / Apple Podcast | RSS 公开 | N3–N4 | 每日 | P0 |
| Nihongo con Teppei | Apple Podcast | RSS 公开 | N3–N4 | 每日 | P0 |
| Erin’s Challenge | Apple Podcast | RSS 公开 | N5–N3 | 不定期 | P1 |
| Bilingual News | Apple Podcast | RSS 公开 | N2–N1 | 每周 | P2 |
6.4 采集执行顺序(按周)
Section titled “6.4 采集执行顺序(按周)”| 周次 | 执行内容 | 预期新增 |
|---|---|---|
| 第 1 周 | 配置 NHK World Japan YouTube 批量采集 | +50–100 条视频 |
| 第 2 周 | 配置 NHK Radio RSS + Nihongo con Teppei | +播客 10–20 条 |
| 第 3 周 | 配置东宝/角川/集英社动漫官方频道 | +30–60 条视频 |
| 第 4 周 | 配置 JNTO 旅行局 + 主动搜索旅行关键词 | +20–40 条视频 |
七、用户痛点与内容模块映射
Section titled “七、用户痛点与内容模块映射”7.1 六类用户痛点
Section titled “7.1 六类用户痛点”| 用户痛点 | 典型说法 | 对应首页模块 | 对应合集 | 当前缺口 |
|---|---|---|---|---|
| 看动漫听不懂 | ”追了10年番,一句话听不懂” | 动漫 Tab + 动漫合集 | 动漫日语精听 | ❌ 视频待采集 |
| 旅行交流障碍 | ”单词都会,现场说不出来” | 场景日语 | 去日本旅行必备 | ❌ 视频待采集 |
| 不知自己水平 | ”不知道从哪开始学” | JLPT 专区 + 轻松入门 | JLPT N3 冲刺 | ✅ 图文已满足 |
| 学习太枯燥 | ”几天就放弃” | 动漫 Tab + 播客系列 | 动漫日语精听 | ❌ 待采集 |
| 不懂文化背景 | ”语言学了,语境不懂” | 日本文化栏目 | 日本文化冷知识 | ❌ 视频待采集 |
| 坚持不了学习 | ”三天打鱼两天晒网” | 今日学习区(15分钟目标) | — | ⚠️ 机制待设计 |
7.2 用户注册兴趣选择
Section titled “7.2 用户注册兴趣选择”选项(单选,注册必经步骤):
- 追番 / 看懂动漫日剧
- 去日本旅行
- JLPT 备考
- 了解日本文化
- 随便看看 / 保持语感
偏好存储字段:users.interest_category(enum: anime / travel / jlpt / culture / casual)
冷启动期的使用方式:
- 注册时记录偏好,但当前阶段不做个性化推荐(内容量不足)
- 偏好数据预留,不消费,等视频内容 ≥ 500 条后启用个性化逻辑
- 注册选完后,首页展示与选项匹配的”欢迎语”(如”为追番用户推荐的入门路径”)+ 通用内容
游客默认状态:全部按通用版展示,不做任何个性化。
八、合集数据结构设计
Section titled “八、合集数据结构设计”8.1 collections 表
Section titled “8.1 collections 表”CREATE TABLE collections ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(30) NOT NULL COMMENT '合集标题,≤14中文字', description VARCHAR(150) NOT NULL COMMENT '合集简介,≤30中文字', cover_url VARCHAR(500) NOT NULL COMMENT '封面图 OSS 地址', collection_type ENUM('rolling_feed','curated','series') NOT NULL COMMENT 'rolling_feed=日更自动追加; curated=人工筛选; series=播客/连载系列', channel VARCHAR(30) NOT NULL COMMENT '归属栏目(与 news.channel 对应)', level VARCHAR(10) NOT NULL DEFAULT '不限' COMMENT 'N5/N4/N3/N2/N1/不限', is_membership TINYINT(1) NOT NULL DEFAULT 0 COMMENT '0=免费;1=会员专属(整个合集)', is_finished TINYINT(1) NOT NULL DEFAULT 0 COMMENT '0=持续更新;1=已完结', is_visible TINYINT(1) NOT NULL DEFAULT 1 COMMENT '0=隐藏;1=展示', item_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '合集内容数(冗余字段,定期更新)', sort_order INT NOT NULL DEFAULT 0 COMMENT '首页排序权重,后台手动设置,越大越靠前', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;8.2 collection_items 表
Section titled “8.2 collection_items 表”CREATE TABLE collection_items ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, collection_id BIGINT UNSIGNED NOT NULL, news_id VARCHAR(36) NOT NULL COMMENT '对应 news 表的 UUID', priority_score TINYINT UNSIGNED NOT NULL DEFAULT 50 COMMENT 'LLM 打分 1-100,合集内排序依据', is_free TINYINT(1) NOT NULL DEFAULT 1 COMMENT '单条内容是否免费(覆盖合集付费墙)', sort_position INT NOT NULL DEFAULT 0 COMMENT '手动排序位(curated 类合集使用)', added_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_collection_id (collection_id), INDEX idx_news_id (news_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;8.3 合集内容排序规则(按 collection_type)
Section titled “8.3 合集内容排序规则(按 collection_type)”| 类型 | 排序规则 |
|---|---|
| rolling_feed | added_at DESC(最新入库在前) |
| curated | sort_position ASC(手动排序),sort_position 相同则 priority_score DESC |
| series | added_at ASC(第 1 期在前,追更逻辑) |
8.4 用户合集观看进度(user_collection_progress 表)
Section titled “8.4 用户合集观看进度(user_collection_progress 表)”CREATE TABLE user_collection_progress ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, collection_id BIGINT UNSIGNED NOT NULL, last_news_id VARCHAR(36) COMMENT '最近看到的内容 ID', watched_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已完成条目数', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_user_collection (user_id, collection_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;合集详情页展示:
- 顶部显示”已看 X 集 / 共 X 集”进度
- 已看过的条目在列表中以灰色/已完成样式显示(复用 NHK News 现有已读样式)
- 看完一集自动跳转下一集(
is_auto_play = true,默认开启,可关闭)
九、今日打卡 / 学习进度定义
Section titled “九、今日打卡 / 学习进度定义”9.1 学习时长统计规则
Section titled “9.1 学习时长统计规则”| 行为 | 计入有效学习时长 | 计入条件 |
|---|---|---|
| 视频播放 | ✅ | 播放进度推进(非挂后台) |
| 音频播放 | ✅ | 播放进度推进 |
| 文章阅读 | ✅ | 在文章详情页停留(每秒累积,最长计 5 分钟/篇防止挂页面) |
| App 后台 | ❌ | 不计入 |
| 首页浏览 | ❌ | 不计入 |
9.2 打卡判定标准(定死)
Section titled “9.2 打卡判定标准(定死)”| 条件 | 值 |
|---|---|
| 每日打卡目标 | 累计有效学习时长 ≥ 15 分钟 |
| Streak 递增时机 | 当日 23:59 JST 时检查,≥15 分钟则 +1 |
| Streak 重置条件 | 连续 2 天累计有效时长 < 15 分钟(给 1 天缓冲) |
| 复活次数 | 付费用户每 30 天 1 次 |
9.3 学习进度在 UI 上的体现
Section titled “9.3 学习进度在 UI 上的体现”- 今日学习区:进度条显示”今日已学 X 分钟 / 15 分钟”
- 达到 15 分钟 → 打卡成功动画 + Streak +1
- 超过 15 分钟继续计数,显示”今日已超额 X 分钟”(鼓励机制)
十、内容标题处理规则
Section titled “十、内容标题处理规则”10.1 图文内容标题(NHK 类)
Section titled “10.1 图文内容标题(NHK 类)”原则:沿用 NHK 现有标题处理方式(假名标注 + 中文对照),不重写。
现有流水线已处理:title(日语原标题)/ title_annotation(假名标注)/ title_html(含注音 HTML)。
10.2 视频内容标题
Section titled “10.2 视频内容标题”原则:不使用 LLM 问题式重写,而是将原日语/英语标题翻译为地道中文标题,风格参考 NHK 中文网的标题写法(简洁直白,保留原意,不夸张)。
处理流程:
- 原始标题(日/英)→ DeepSeek 翻译为中文(≤30字)
- 中文标题写入
title_zh(新增字段) - 前端展示:详情页顶部显示
title_zh,副标题显示原日语title - LLM
title_rewrite字段仅用于合集内的内容卡片标题(可以更吸引眼球),不影响详情页标题
合集卡片标题(title_rewrite)规则:
- LLM 生成,≤20字,“问题/场景”表述
- 例:原标题「桜の季節の日本語表現」→ 合集卡片显示「日本赏樱时必说的5句话」
- 置信度 < 0.5 时,合集卡片直接用
title_zh
十一、封面图获取与存储策略
Section titled “十一、封面图获取与存储策略”11.1 各内容类型封面来源
Section titled “11.1 各内容类型封面来源”| 内容类型 | 封面来源 | 存储方式 |
|---|---|---|
| NHK 图文资讯 | 爬取文章原始封面图 | 已有,复用现有 OSS 流程 |
| YouTube 视频 | YouTube 缩略图(maxresdefault.jpg → hqdefault.jpg fallback) | 下载到 OSS(防止 YouTube 失效404) |
| Apple Podcast | RSS <itunes:image> 中的图片 URL | 下载到 OSS |
| 合集封面 | 运营人工上传 or 程序自动取合集第1条内容的封面 | 上传到 OSS |
| 播客系列封面 | RSS 节目封面图 | 下载到 OSS |
11.2 YouTube 缩略图获取逻辑
Section titled “11.2 YouTube 缩略图获取逻辑”视频 ID = extracted_from_url(url)尝试顺序: 1. https://img.youtube.com/vi/{video_id}/maxresdefault.jpg (1280×720) 2. https://img.youtube.com/vi/{video_id}/hqdefault.jpg (480×360) 3. https://img.youtube.com/vi/{video_id}/mqdefault.jpg (320×180)
获取成功后:下载图片 → 上传到 OSS → 写入 news.image_url = OSS地址获取失败(HTTP 404 全部失败):image_url = 默认占位图地址11.3 合集封面自动生成逻辑(curated 合集,运营未上传时)
Section titled “11.3 合集封面自动生成逻辑(curated 合集,运营未上传时)”合集封面 fallback 规则: 取合集内 priority_score 最高的1条内容的封面图 叠加合集标题文字(白色字体,右下角水印) 写入 collections.cover_url十二、待解决的模糊点清单
Section titled “十二、待解决的模糊点清单”本节持续更新,记录所有尚未定义清楚的问题。已解决的条目标记 ✅ 并移入对应章节。
✅ 已解决(移入对应章节)
Section titled “✅ 已解决(移入对应章节)”| 问题 | 结论 | 所在章节 |
|---|---|---|
| topic_tags 和 scenes 两套体系前端如何使用? | 不新建 topic_tags 表(见下方P0-1修正);scenes 体系前端展示归并到 scene 中类;Tab 分类由 scene_id 范围决定 | 第十三章 |
| 合集付费墙 UI 表现 | 复用现有 premium VIP 标签机制,visibility=MEMBERSHIP_VISIBLE 自动打 VIP 标签;前端已有实现 | 第十三章 |
| 视频详情页播放器 | 已有实现,复用 movie_url 字段即可,不需新开发 | 第十三章 |
| 合集内容排序规则 | rolling_feed 按 added_at DESC;curated 按 sort_position + priority_score;series 按 added_at ASC | 第八章 |
| 封面图来源与存储 | YouTube 缩略图下载到 OSS;合集封面取最高 priority_score 内容封面作 fallback | 第十一章 |
🔴 P0(当前仍未定义,会卡住执行)
Section titled “🔴 P0(当前仍未定义,会卡住执行)”P0-1:是否需要新建 topic_tags 表?
修正判断:经工程分析,不建议新建 topic_tags 表。
原因:
- 现有
scenes表三层结构(category → scene → sub_scene)已经能覆盖所有分类需求 - 新建表意味着 LLM 需要输出两套独立标签、服务端需要写两张关联表、前端需要合并展示——工程成本高,收益低
- 所有”主题归属”需求(Tab 分类、合集归档、热点追踪)都可以通过
scene.category+scene.scene(中类)实现
结论:
- 废弃
topic_tags/news_topic_tags表的设计 - LLM 打标输出只需
primary_scene+secondary_scenes(sub_scene 级别),写入现有news_scenes表 - Tab 分类通过 scene_id 范围映射(见第十三章映射表)
- 合集归档分析通过
scene.scene(中类)聚合,而不是独立的 topic_tags
对规格文档其他章节的影响:
- 第四章 LLM Prompt 输出 schema 中的
primary_topic_tag/secondary_topic_tags字段 → 改为primary_scene_id+secondary_scene_ids(直接输出 scene ID) - 第五章定期任务 A 中”按 primary_topic_tag 聚合” → 改为”按 scene.scene 中类聚合”
- 第一章 Tab 分类逻辑 → 已改为 scene_id 范围映射
P0-2:LLM 打标输出如何精确映射到 scene_id?
LLM 输出自然语言标签(如”动漫文化”),服务端需要把它映射到数据库 scene.sub_scene 的 ID(1–59)。
两种方案:
- 方案 A:LLM 直接输出 scene ID(数字),Prompt 里把 59 个 sub_scene 全部列出 → 简单,但 Prompt 长
- 方案 B:LLM 输出 sub_scene 名称字符串,服务端查
sceneMap[name]映射到 ID → 灵活,但名称可能匹配失败
建议方案 B:LLM 输出 sub_scene 名称,服务端用精确字符串匹配(已有 sceneMap 实现),失败时不打标(不阻断发布)。
已有代码参考:news_scenes.go 中的 sceneMap[tag] 逻辑完全可复用,只需扩展 Prompt 让 LLM 从 59 个 sub_scene 名称中选择。
🟡 P1(影响运营,近期需要定义)
Section titled “🟡 P1(影响运营,近期需要定义)”P1-1:热门专题(模块⑧)的数据结构
建议方案:复用 collections 表,新增 is_topic TINYINT(1) 字段区分合集(0)和热门专题(1)。
理由:
- 专题在产品形态上与合集几乎相同(封面 + 标题 + 内容列表)
- 唯一差异:专题有时效性(14天自动下线),合集长期有效
- 用一张表管理,后台管理界面可以统一
新增字段:
ALTER TABLE collections ADD COLUMN is_topic TINYINT(1) NOT NULL DEFAULT 0 COMMENT '0=合集;1=热门专题';ALTER TABLE collections ADD COLUMN expire_at DATETIME NULL COMMENT '专题到期时间(is_topic=1时有效,NULL=永不过期)';定时任务:每日 00:00 JST 检查 expire_at < NOW() AND is_topic = 1,自动设 is_visible = 0。
P1-2:今日推荐(模块②)的选取逻辑
建议方案:后端定时任务每日 06:00 JST 生成,写入 daily_featured 配置表。
CREATE TABLE daily_featured ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, date DATE NOT NULL COMMENT '日期(JST)', news_id VARCHAR(36) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_date (date)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;选取逻辑(按优先级):
type = video+level = 3(N3)+published_at = 今日type = video+ 任意 level +published_at = 今日type = article+level = 3(N3)+published_at = 今日- 兜底:
published_at = 今日,取第一条
API 查询:SELECT news_id FROM daily_featured WHERE date = CURDATE(),无结果时实时计算兜底。
P1-3:合集 priority_score 由谁生成?何时生成?
当前状态:LLM 打标时同步生成 priority_score(1–100),在 collection_hints 中返回。
写入时机:
- 内容入库时(
AddSceneToNews之后),如果collection_hints.suitable = true,写入collection_items.priority_score - 非合集内容的
priority_score存在news表新增字段priority_score中(用于合集归档时的排序参考)
当前不需要立即实现,合集功能上线前可先用默认值 50。
🟢 P2(长期,不影响首期上线)
Section titled “🟢 P2(长期,不影响首期上线)”P2-1:用户学习时长的客户端统计方案
每日 15 分钟打卡目标需要客户端统计有效时长。具体实现:
- iOS 需要在
AppDelegate/SceneDelegate中监听 foreground/background 事件 - 视频播放进度监听(AVPlayer
timeObserver) - 文章页停留时间记录(viewWillAppear/viewWillDisappear)
- 需要 iOS 开发(少琛)评估工作量
P2-2:合集自动播放下一集的逻辑
用户看完一集视频后,自动播放下一集。需要:
user_collection_progress表记录当前看到哪一集- 视频播放完成回调触发”下一集预加载”
- 需要 iOS 开发评估
文档性质:内容系统实施规格,持续补全中 当前状态:冷启动扩张期——视频 25 条→目标 500 条,播客 0 条→目标 50 条 创建:2026-03-27 | 最后更新:2026-03-28(附录D:产品运营定义层,15个卡点,Onboarding流程+权益对比+运营日历+版权规范+通知策略)
内容系统实施规格 · 深度自查报告
Section titled “内容系统实施规格 · 深度自查报告”以下是对 yomiya-implementation-spec.md 当前版本的全面漏洞扫描, 按严重程度分级,每个问题给出具体原因和修复建议。
🔴 致命漏洞(会导致实施方向错误或系统无法运转)
Section titled “🔴 致命漏洞(会导致实施方向错误或系统无法运转)”漏洞 1:第 1.5 节与第 13 章存在直接矛盾
Section titled “漏洞 1:第 1.5 节与第 13 章存在直接矛盾”问题:第 1.5 节(每日资讯 Tab 区)仍然写的是 topic_tag → Tab 映射表(如”社会观察→社会”),但第 12 章已经决定废弃 topic_tags 体系,改用 scene_id 范围映射。
两套逻辑同时存在,工程实现时一定会产生歧义。
修复:删除第 1.5 节的 topic_tag 映射表,改为 scene_id 范围映射(与第 13 章一致)。
漏洞 2:任务 A 仍然使用已废弃的 primary_topic_tag 字段
Section titled “漏洞 2:任务 A 仍然使用已废弃的 primary_topic_tag 字段”问题:第五章任务 A 写”按 primary_topic_tag 聚合”,但 topic_tags 已废弃。服务端实现时查这个字段会报错。
修复:改为”按 scene.scene 中类聚合”,并给出具体 SQL:
SELECT s.scene as scene_mid, COUNT(*) as cntFROM news_scenes nsJOIN scenes s ON ns.scene_id = s.idWHERE ns.scene_primary = 'primary' AND ns.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)GROUP BY s.sceneHAVING cnt >= 10漏洞 3:rolling_feed 合集的”自动追加”机制完全没有工程定义
Section titled “漏洞 3:rolling_feed 合集的”自动追加”机制完全没有工程定义”问题:「NHK 今日要闻」和「日本娱乐速报」是 rolling_feed 类型,每日自动把新内容追加进来。但文档里完全没有说这个追加逻辑在哪里实现、如何触发。
这是 P0 级工程需求,没有它这两个合集根本无法运转。
修复:需要明确说明:
- 追加规则存在哪里(
collections表新增auto_append_ruleJSON 字段?) - 追加逻辑在哪里运行(内容入库流水线的最后一步?还是独立定时任务?)
- 追加条件是什么(
channel_id = NHK的内容入库时自动检查所有 rolling_feed 合集的规则)
漏洞 4:Podcast 采集链路没有工程实现路径
Section titled “漏洞 4:Podcast 采集链路没有工程实现路径”问题:采集清单里列了 NHK Radio、Nihongo con Teppei 等播客,但:
- 工程里现有的采集链路全部针对图文(NHK Web Easy)和 YouTube 视频
- 播客 RSS 采集是全新链路,需要新开发
- 音频内容的字幕从哪里来(RSS 文字稿?Whisper?)完全未定义
- 音频内容存哪个字段(
movie_url?还是新加audio_url?)
修复:需要补充播客采集的技术方案:
- RSS 解析 → 获取每期音频 MP3 直链
- 下载音频 → 上传 OSS
- 字幕策略:RSS description 作为文字稿,无则 Whisper 转录
- 存储:
news.movie_url = OSS 音频地址,news.type = audio(现有 type 枚举已有 audio)
漏洞 5:news 表新增字段没有 ALTER TABLE 定义
Section titled “漏洞 5:news 表新增字段没有 ALTER TABLE 定义”问题:规格提到:
title_zh(视频中文标题)- 只在第 10 章提了概念priority_score(优先级评分)- 第 12 章提到要加但没写 SQLword_count(第 1.7 节用到了)- 未确认是否存在
若这些字段不存在,相关逻辑全部无法实现。
修复:
ALTER TABLE news ADD COLUMN title_zh VARCHAR(100) NULL COMMENT '视频内容中文标题(DeepSeek翻译)';ALTER TABLE news ADD COLUMN priority_score TINYINT UNSIGNED NOT NULL DEFAULT 50 COMMENT 'LLM内容优先级评分 1-100';-- word_count 需要先确认是否存在,不存在则:ALTER TABLE news ADD COLUMN word_count INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '文章字数(图文内容)';漏洞 6:collection_suggestions 表没有定义
Section titled “漏洞 6:collection_suggestions 表没有定义”问题:任务 A 说”合集建议写入 collection_suggestions 表”,但没有 CREATE TABLE,没有字段定义,没有状态枚举。工程无法实现。
修复:
CREATE TABLE collection_suggestions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, scene_mid VARCHAR(50) NOT NULL COMMENT 'scene 中类名称(聚合维度)', item_count INT UNSIGNED NOT NULL COMMENT '过去30天该主题内容数', suggested_name VARCHAR(30) NOT NULL COMMENT '建议合集名称', status ENUM('pending','accepted','rejected','deferred') NOT NULL DEFAULT 'pending', ai_recommendation TEXT COMMENT 'AI 给出的建议操作文本', notified_at DATETIME NULL COMMENT '上次通知 cc 的时间', notify_count TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已通知次数', expire_at DATETIME NULL COMMENT '建议过期时间(30天后若未处理自动 rejected)', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;🟠 严重缺失(不补会导致实施混乱)
Section titled “🟠 严重缺失(不补会导致实施混乱)”缺失 7:Admin 后台能力完全未定义
Section titled “缺失 7:Admin 后台能力完全未定义”整个 826 行文档,没有一处定义 Admin 后台需要支持哪些操作。
合集管理是核心运营工具,没有它运营无法执行:
- 创建/编辑合集(标题/描述/封面/类型/付费墙)
- 向合集添加/移除内容
- 调整合集首页排序(sort_order)
- 创建/管理热门专题
- 手动覆盖今日推荐
- 查看并处理合集建议(collection_suggestions)
- 查看异常队列
这些不是可选的——没有 Admin 能力,运营完全无法使用内容系统。
缺失 8:新视频内容的 channel 归属没有定义
Section titled “缺失 8:新视频内容的 channel 归属没有定义”问题:NHK World YouTube 视频、动漫官方 PV 这些新采集的视频,应该属于哪个 channel?
现有 channels 表只有 5 个图文频道(NHK/Yahoo/Hukumusume 等)。视频内容需要新的频道。
没有定义:
- 是否新建 channel(如
NHKWorldVideo、AnimeOfficial)? - 新 channel 的
display_name、description、img_url是什么? - 视频内容的 channel 和合集的 channel 有什么关系(一条内容的 channel 和它归属的合集的 channel 可以不同吗?)
缺失 9:内容看完后的”下一步”行为未定义
Section titled “缺失 9:内容看完后的”下一步”行为未定义”问题:用户看完一条视频/文章后,App 展示什么?这直接影响用户留存和继续消费路径。
可能的设计:
- 自动播放合集下一集(如果内容属于某合集)
- 显示”相关内容推荐”(3条 same scene 的内容)
- 显示”收藏/分享”操作
- 跳回合集详情页
- 跳回首页
没有定义这个核心用户旅程,iOS 开发无法实现,用户体验会是黑洞。
缺失 10:付费转化路径未定义
Section titled “缺失 10:付费转化路径未定义”问题:内容系统有付费墙(VIP 内容),但触发付费的路径完全没有定义:
- 用户点击锁定内容后,看到什么 UI?
- 弹出付费引导弹窗?还是直接跳转会员购买页?
- 弹窗的文案是什么?有几个 CTA?
- 这和 App 现有的付费墙 UI 是否统一?(现有已有 RevenueCat 集成)
没有这个定义,付费墙就只是展示一个锁,无法转化。
缺失 11:时区一致性问题
Section titled “缺失 11:时区一致性问题”问题:
daily_featured表用CURDATE(),但服务器时区未必是 JST(可能是 UTC)- 定时任务在”JST 时间”运行,但 MySQL 服务器可能在 UTC
- Streak 在”23:59 JST”结算,但客户端时区和服务端时区可能不一致
如果服务器是 UTC,JST 的 00:00 = UTC 的前一天 15:00,整个打卡逻辑会跨天错误。
修复:明确定义所有时间计算统一使用 CONVERT_TZ(NOW(), 'UTC', 'Asia/Tokyo'),并在 Go 服务端的时区配置中明确。
缺失 12:内容发现路径不完整
Section titled “缺失 12:内容发现路径不完整”问题:用户如何找到内容?目前只定义了首页模块,但没有定义:
- 搜索功能:是否存在?支持搜索什么(标题?场景标签?)
- 内容详情页→所属合集入口:看某条内容时,能否看到”该内容属于XX合集”并跳转?
- 标签页筛选:是否有”按场景/难度/类型”筛选内容的页面?
- 合集入口的多样性:合集只能从首页”热门推荐”发现吗?JLPT专区内的内容能跳到对应合集吗?
内容发现路径单一会导致大量内容被埋没。
🟡 重要遗漏(影响运营质量)
Section titled “🟡 重要遗漏(影响运营质量)”遗漏 13:视频内容的 channel 与合集的关系未定义
Section titled “遗漏 13:视频内容的 channel 与合集的关系未定义”一条视频内容,同时有:
news.channel(频道归属,如NHKWorldVideo)collection_items.collection_id(合集归属,如”日本文化冷知识”)
两者是否可以不同?一条内容可以属于多个合集吗?(collection_items 表设计允许,但业务规则未定义)
遗漏 14:Banner 轮播的数据结构未定义
Section titled “遗漏 14:Banner 轮播的数据结构未定义”第 1.2 节说”后台配置 3 张图”,但没有说:
- 对应哪张表?
- 每张 Banner 的字段:图片 URL / 跳转类型(合集/专题/付费页)/ 跳转 ID / 有效期
- 这些已有实现吗?还是新建?
遗漏 15:合集详情页规格未定义
Section titled “遗漏 15:合集详情页规格未定义”完整的合集详情页需要:
- 顶部:合集封面 + 标题 + 简介 + 集数 + 难度标签 + 付费墙说明
- 内容列表:已看/未看状态 + 标题 + 时长 + 免费/VIP 标签
- 进度条:“已看 X 集 / 共 X 集”
- 行动按钮:“继续看”(跳到 last_news_id 对应位置)
这些在文档里只有片段,不够完整给 iOS 开发参考。
遗漏 16:内容重复采集的精确定义
Section titled “遗漏 16:内容重复采集的精确定义”过滤规则写的是”URL 的 MD5 已存在则跳过”。但:
- 同一个 YouTube 视频可能有多种 URL 格式(youtu.be 短链、youtube.com 完整链、带时间戳的链接)
- 规范化应该是用
video_id而不是 URL MD5 - 没有定义 YouTube URL 如何提取
video_id作为去重依据
遗漏 17:合集的”会员专属”和单条内容的”会员专属”冲突时如何处理
Section titled “遗漏 17:合集的”会员专属”和单条内容的”会员专属”冲突时如何处理”场景:合集 is_membership = 0(合集整体免费),但某条内容 is_free = 0(该条会员专属)。
反过来:合集 is_membership = 1(整个合集会员专属),但某条内容 is_free = 1。
这两个字段的优先级谁高?文档没有说。
建议:is_free 字段(collection_items 级别)覆盖 is_membership(collections 级别),即:即使合集整体是会员专属,is_free=1 的条目仍然对所有用户可见。
遗漏 18:Whisper 转录的成本和配额控制
Section titled “遗漏 18:Whisper 转录的成本和配额控制”规格说”无字幕视频 ≤ 30 分钟的触发 Whisper 转录”,但:
- Whisper 转录是有成本的(Azure Speech API 按分钟计费)
- 冷启动期要快速采集 500 条视频,如果大量视频需要转录,成本会激增
- 没有定义:是否设置每日转录分钟数上限?优先级策略(先转录哪些)?
🟢 优化建议(不影响功能但影响产品质量)
Section titled “🟢 优化建议(不影响功能但影响产品质量)”建议 19:priority_score 的评分逻辑过于主观
Section titled “建议 19:priority_score 的评分逻辑过于主观”当前定义是 LLM 根据”用户痛点匹配度”打 1-100 分,但:
- 不同内容的绝对分数没有参照系(75-100 是”直接命中”,但如何判断?)
- LLM 的评分会有随机性,导致同质内容的排序不稳定
- 建议改为基于 sub_scene 中类的固定权重表,而不是 LLM 自由发挥:
旅游(15-18)→ 90动漫(27-30)→ 85备考(1-4) → 80文化(44-48)→ 70影视(31-34)→ 65... 以此类推建议 20:冷启动期结束后的”精细化运营期”没有任何定义
Section titled “建议 20:冷启动期结束后的”精细化运营期”没有任何定义”文档定义了冷启动期(视频<500条),但精细化运营期的规则完全空白。 至少应该写一行”冷启动期结束后,启用以下机制:…”,哪怕是占位符。
自查结论:当前文档在概念层(策划层)已经相当完整,但在实施层(工程层)有 6 个致命漏洞、6 个严重缺失,这些问题一旦进入开发阶段会立刻暴露。
最高优先级需要补的 3 件事:
- 修复第 1.5 节与第 13 章的矛盾(Tab 分类逻辑统一)
- 定义 rolling_feed 合集的自动追加机制(工程无法实现)
- 定义 Admin 后台能力清单(运营无法操作内容系统)
附录A:致命漏洞修复记录
Section titled “附录A:致命漏洞修复记录”本附录记录对自查报告中所有漏洞的修复方案,每处说明”怎么补”和”为什么这样补”。
修复 1:第 1.5 节 Tab 分类逻辑统一为 scene_id 范围映射
Section titled “修复 1:第 1.5 节 Tab 分类逻辑统一为 scene_id 范围映射”原问题:第 1.5 节用 topic_tag 名称映射 Tab,第 13 章用 scene_id 范围映射,两套逻辑矛盾。
修复方案:删除第 1.5 节的 topic_tag 映射表,统一使用 scene_id 范围映射:
每日资讯 Tab → scene_id 范围(完整定义)
| Tab | 对应 scene 中类 | scene_id 范围 | 后端查询条件 |
|---|---|---|---|
| 社会 | 新闻 | 49–52 | scene_id IN (49,50,51,52) |
| 娱乐 | 影视 + 音乐 | 31–38 | scene_id IN (31,32,33,34,35,36,37,38) |
| 文化 | 传统文化 + 生活 + 商务 + 留学 + 文学 | 11–26, 39–48 | scene_id BETWEEN 11 AND 26 OR scene_id BETWEEN 39 AND 48 |
| 动漫 | 动漫 | 27–30 | scene_id IN (27,28,29,30) |
| 科技 | 科技 | 53–56 | scene_id IN (53,54,55,56) |
| 旅行 | 旅游 | 15–18 | scene_id IN (15,16,17,18) |
| 兜底(无法映射时) | → 社会 Tab | — | 默认归入社会 |
体育(57-59):暂归入社会 Tab,等体育内容积累到 30 条后单独开 Tab。
为什么:scene_id 是数据库中实际存在的字段,服务端可以直接用 IN 查询,不需要任何字符串匹配或转换逻辑。topic_tag 已废弃,不应在任何地方继续出现。
修复 2:任务 A 聚合逻辑改用 scene 中类
Section titled “修复 2:任务 A 聚合逻辑改用 scene 中类”原问题:任务 A 写”按 primary_topic_tag 聚合”,topic_tags 已废弃。
修复:改为如下 SQL,按 scene 中类聚合:
-- 合集归档分析:统计过去30天各 scene 中类的内容新增数SELECT s.scene AS scene_mid, COUNT(DISTINCT ns.news_id) AS item_countFROM news_scenes nsJOIN scenes s ON ns.scene_id = s.idJOIN news n ON ns.news_id = n.idWHERE ns.scene_primary = 'primary' AND n.created_at >= DATE_SUB(CONVERT_TZ(NOW(), 'UTC', 'Asia/Tokyo'), INTERVAL 30 DAY) AND n.deleted_at IS NULLGROUP BY s.sceneHAVING item_count >= 10ORDER BY item_count DESC;为什么:scene.scene 字段是中类名称(如”动漫”、“旅游”、“新闻”),13 个中类直接对应产品的栏目/合集维度,是最自然的聚合粒度。比 sub_scene(59个太细)和 category(4个太粗)更合适。
修复 3:rolling_feed 合集自动追加机制
Section titled “修复 3:rolling_feed 合集自动追加机制”原问题:「NHK 今日要闻」每日自动追加,但追加规则存在哪、怎么触发完全未定义。
修复方案:
新增字段到 collections 表:
ALTER TABLE collections ADD COLUMN auto_append_rule JSON NULL COMMENT 'rolling_feed 类型的自动追加规则,格式见下方';auto_append_rule JSON 格式:
{ "channel_name": "NHK", "scene_ids": [], "level_range": [1, 4], "content_types": ["article", "video", "audio"]}channel_name:指定频道名,为空则不按频道过滤scene_ids:指定场景 ID 列表,为空则不按场景过滤level_range:难度范围[min, max],[1,5]表示所有级别content_types:内容类型白名单
触发时机:在现有内容入库流水线末尾(raw_news → news 第 13 步 MQ 消息之后),新增一步:
Step 14(新增):检查 rolling_feed 合集追加规则 → 查询所有 is_visible=1 AND collection_type='rolling_feed' 的合集 → 对每个合集,评估新入库内容是否满足 auto_append_rule 条件 → 满足则写入 collection_items(added_at=当前时间,priority_score=50,is_free=1) → 更新 collections.item_count(+1)首批合集追加规则:
- 「NHK 今日要闻」:
{"channel_name":"NHK","scene_ids":[],"level_range":[1,5],"content_types":["article"]} - 「日本娱乐速报」:
{"channel_name":"YahooNewsEntertainment","scene_ids":[],"level_range":[1,5],"content_types":["article"]}
为什么:追加逻辑嵌在入库流水线末尾,保证每条内容入库时立刻评估是否进合集,而不是靠定时任务轮询。定时任务有延迟,流水线嵌入是实时的。JSON 格式的规则存在数据库里,无需改代码就能调整规则。
修复 4:Podcast 采集链路技术方案
Section titled “修复 4:Podcast 采集链路技术方案”原问题:播客采集是全新链路,没有任何实现路径。
修复方案:
利用现有工程能力:news.type = "audio" 枚举已存在,news.movie_url 字段可存音频 URL,无需新字段。
新增 Podcast RSS 采集器(参考现有爬虫结构):
PodcastImporter(新增,参考 YouTubeImporter 结构) 1. 读取 RSS Feed URL 2. 解析 XML:获取每期 title / description / pubDate / enclosure(MP3 URL) / itunes:image 3. 下载 MP3 到 OSS(保留原始文件,不做转码) 4. 写入 raw_news: - title = RSS item title - movie_url = OSS MP3 地址(复用 movie_url 字段存音频) - type = "audio" - image_url = RSS itunes:image 下载到 OSS 的地址 5. 触发现有流水线:假名标注(跳过,音频无文字稿)→ 翻译(跳过)→ 难度分析(基于描述文字)→ LLM 打标
字幕策略: - 优先用 RSS description 作为文字稿(写入 news sentences) - RSS 无 description 时:Whisper 转录(仅对时长 ≤ 30 分钟的音频,超过则跳过转录)去重规则:用 RSS item 的 guid 字段做去重(类似 YouTube video_id),而非 URL MD5。
为什么用 movie_url 存音频而不新加字段:news.type = "audio" 已有,iOS 和服务端都识别这个枚举,用 movie_url 传音频地址零额外改动,iOS 侧根据 type == audio 启用音频播放器而非视频播放器。
修复 5:news 表新增字段 ALTER TABLE
Section titled “修复 5:news 表新增字段 ALTER TABLE”原问题:规格多处引用了不存在的字段,无 SQL 定义。
确认现有字段(已存在,无需新增):
title(日语标题)✅movie_url(视频/音频 URL)✅type(webpage/video/audio)✅level(1-5)✅visibility✅
确认不存在、需新增的字段:
-- 视频/音频内容的中文标题(DeepSeek翻译)ALTER TABLE news ADD COLUMN title_zh VARCHAR(100) NULL COMMENT '视频/音频内容中文标题,由DeepSeek翻译生成,图文内容不填' AFTER title;
-- LLM 内容优先级评分(用于合集内排序)ALTER TABLE news ADD COLUMN priority_score TINYINT UNSIGNED NOT NULL DEFAULT 50 COMMENT 'LLM内容优先级评分1-100,默认50,入库时由打标服务写入' AFTER level;关于 word_count:查阅代码后确认 news 表中不存在 word_count 字段,且轻松入门区的”字数 ≤ 300”过滤可以改用 title 长度 + sentences 数量组合估算,暂不新增此字段,改用以下规则:
- 图文内容:
sentences 表中该 news 的行数 ≤ 5作为”短文”判定(间接衡量字数) - 视频/音频:用
duration_sec ≤ 300已足够
为什么:只加确定需要的字段,避免过度设计。word_count 需要单独统计逻辑,成本高于收益。
修复 6:collection_suggestions 表 CREATE TABLE
Section titled “修复 6:collection_suggestions 表 CREATE TABLE”原问题:任务 A 引用这张表但没有定义。
CREATE TABLE collection_suggestions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, scene_mid VARCHAR(50) NOT NULL COMMENT 'scene 中类名称(如"动漫"、"旅游")', item_count INT UNSIGNED NOT NULL COMMENT '过去30天该 scene 中类的内容数', suggested_name VARCHAR(30) NOT NULL COMMENT 'AI 建议的合集名称(≤14字)', status ENUM('pending','accepted','rejected','deferred') NOT NULL DEFAULT 'pending', ai_advice TEXT NULL COMMENT 'AI 给出的建议操作文本(当次评估结果)', notified_count TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已通知 cc 的次数', last_notified_at DATETIME NULL COMMENT '上次通知时间', resolved_at DATETIME NULL COMMENT '被处理的时间', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_status (status), INDEX idx_scene_mid (scene_mid)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;为什么:status 字段的四个状态:pending(待处理)/ accepted(已创建合集)/ rejected(忽略)/ deferred(暂缓,下次再看)。notified_count 控制不重复骚扰,同一条建议连续 3 周未处理后自动改为 deferred,不再出现在通知里。
修复 7:Admin 后台能力清单(最小可用版本)
Section titled “修复 7:Admin 后台能力清单(最小可用版本)”原问题:整份文档没有定义 Admin 需要支持哪些操作。
Admin 后台功能清单(按优先级):
P0 — collections 表上线时必须同步完成
| 功能 | 操作说明 |
|---|---|
| 合集列表 | 查看所有合集(标题/类型/条目数/是否展示/排序权重) |
| 创建合集 | 填写标题/描述/类型/封面/channel/level/is_membership |
| 编辑合集 | 修改合集基础信息 |
| 合集内容管理 | 添加/移除 news,调整 sort_position,设置 is_free |
| 首页排序 | 拖拽调整 sort_order,决定哪8个合集上首页 |
| 隐藏/显示合集 | 切换 is_visible,不删除数据只隐藏 |
P1 — 合集上线后 2 周内补齐
| 功能 | 操作说明 |
|---|---|
| 热门专题管理 | 创建/编辑专题(is_topic=1),设置 expire_at |
| 今日推荐覆盖 | 手动指定某日的今日推荐内容(写入 daily_featured 表) |
| 合集建议处理 | 查看 collection_suggestions 列表,点击”接受/拒绝/暂缓” |
| 异常队列 | 查看被 AI 标记为疑似违规的内容,操作下线或恢复 |
不做(冷启动期不需要):内容批量上传 UI、A/B 测试、数据分析面板。
为什么只定义清单不定义 UI 细节:Admin 是内部工具,UI 由开发按最小可用原则实现,不需要产品规格级别的详细设计,清单足够开发评估工作量。
修复 8:新采集视频/音频的 channel 归属
Section titled “修复 8:新采集视频/音频的 channel 归属”原问题:视频内容应归属哪个 channel 未定义,现有 5 个频道全是图文。
方案:新建两个 channel,写入数据库(channels 表),不修改代码,仅增加数据:
INSERT INTO channels (name, display_name, description, img_url, created_at, updated_at)VALUES('NHKWorldVideo', 'NHK World', 'NHK World 日语文化视频,由日本放送协会出品', '', NOW(), NOW()),('ExternalVideo', '精选视频', '来自官方 YouTube 频道的精选日语视频内容', '', NOW(), NOW());归属规则:
- NHK World YouTube 视频 →
channel = NHKWorldVideo - 动漫官方 PV / JNTO 旅行视频 / 其他官方频道 →
channel = ExternalVideo - NHK Radio / 播客 →
channel = NHKWorldVideo(NHK 播客) 或ExternalPodcast(其他播客)
为什么不用现有 NHK channel:现有 NHK channel 专指 NHK Web Easy 图文新闻,混入视频会导致现有频道列表 API 返回混合类型内容,影响现有功能。新建 channel 隔离类型,零破坏现有逻辑。
修复 9:内容看完后”下一步”行为定义
Section titled “修复 9:内容看完后”下一步”行为定义”原问题:用户看完内容后 App 展示什么,是最核心的留存路径,完全未定义。
方案:
| 场景 | 下一步行为 |
|---|---|
| 内容属于某合集,且有下一集 | 自动播放下一集(3秒倒计时动画,可点击取消) |
| 内容属于某合集,已是最后一集 | 展示”合集已看完”页面,推荐相同 scene 中类的其他合集 |
| 内容不属于任何合集 | 展示相关推荐(同 primary_scene 的 3 条内容) |
| 所有情况下 | 底部固定展示”收藏”和”分享”按钮 |
为什么:参考 Netflix 的”下一集倒计时”逻辑——这是最强的连续消费引导机制,能将单次内容消费转化为合集追更行为,直接增加每次会话的内容消费量。3秒倒计时而不是立即跳转,给用户控制感。
修复 10:付费转化路径定义
Section titled “修复 10:付费转化路径定义”原问题:用户点击锁定内容后看到什么,文档完全没有。
工程确认:iOS 已有完整付费墙实现(PaywallViewController / PaywallView / RevenueCat SDK),触发逻辑在 NewsDetailViewController+Paywall.swift 中——isMembershipRequired = true 时显示渐变遮罩 + 底部付费引导。
方案(直接复用现有实现):
- 合集详情页的锁定条目:点击时触发
isMembershipRequired = true的相同逻辑 - 不需要新开发付费弹窗,直接复用
PaywallViewController即可 - 唯一需要定义的是锁定条目的列表 UI:显示锁图标(🔒)叠加在封面图右下角,标题正常展示(不隐藏,让用户知道有内容但需要付费)
为什么不隐藏内容:隐藏内容让用户看不到价值,没有付费动力。显示标题但锁定访问,让用户知道”有更多优质内容等着我”,这是驱动付费的关键心理机制(参考 Spotify 的播放列表锁定设计)。
修复 11:时区一致性方案
Section titled “修复 11:时区一致性方案”原问题:服务器是 UTC,但产品面向日本用户,Streak 和 daily_featured 都应该按 JST 计算。
方案:
数据库层:所有涉及”今日”的查询,统一使用:
-- 获取当前 JST 日期DATE(CONVERT_TZ(NOW(), 'UTC', 'Asia/Tokyo'))
-- daily_featured 查询改为:SELECT news_id FROM daily_featuredWHERE date = DATE(CONVERT_TZ(NOW(), 'UTC', 'Asia/Tokyo'))Go 服务端层:定时任务使用 Tokyo 时区创建 cron:
loc, _ := time.LoadLocation("Asia/Tokyo")c := cron.New(cron.WithLocation(loc))c.AddFunc("0 6 * * *", generateDailyFeatured) // JST 06:00Streak 判定:在服务端计算时,将用户的学习时长记录按 JST 日期分组,而不是按 UTC 日期分组。user_daily_learning 表的 date 字段存储 JST 日期字符串(YYYY-MM-DD)。
新增学习时长记录表:
CREATE TABLE user_daily_learning ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, date DATE NOT NULL COMMENT 'JST 日期 YYYY-MM-DD', duration_sec INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '当日累计有效学习秒数', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_user_date (user_id, date)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;为什么:用户在日本凌晨 23:59 看了 15 分钟,如果按 UTC 算已经是”明天”(UTC 14:59),Streak 会断。JST 统一保证日本用户的使用体验一致。
修复 12:内容发现路径补充
Section titled “修复 12:内容发现路径补充”原问题:内容只能从首页进入,大量内容被埋没。
方案(最小可用版本,不过度设计):
立即可做(零新增工程量):
- 详情页 → 所属合集入口:新闻详情页底部增加”收录于合集:XX”的入口(服务端返回内容时,附带
collection_id+collection_title,需新增 API 字段) - 内容标签可点击:点击场景标签(如”动漫”),跳转到该场景的内容列表页(现有 JLPT 专区的实现逻辑可复用)
延后到 P1:
- 全局搜索:按标题搜索内容
- 分类筛选页:按场景/难度/类型多维筛选
为什么暂不做搜索:冷启动期内容总量少,搜索价值不大,且实现成本高。标签可点击是成本最低的内容发现扩展方式。
修复 13:重复内容检测改用 video_id
Section titled “修复 13:重复内容检测改用 video_id”原问题:用 URL MD5 去重,但 YouTube URL 有多种格式(短链/完整链/带参数),同一视频的不同 URL 会被当做不同内容。
方案:
// YouTube URL 规范化,提取 video_idfunc extractYouTubeVideoID(url string) string { // 支持格式: // https://www.youtube.com/watch?v=VIDEO_ID // https://youtu.be/VIDEO_ID // https://www.youtube.com/embed/VIDEO_ID // https://www.youtube.com/shorts/VIDEO_ID}
// 去重字段:news.news_id 存储规范化后的来源唯一 ID// YouTube: "youtube_" + video_id// Podcast: "podcast_" + podcast_guid// NHK: 现有逻辑不变(已用文章 URL 哈希)news.news_id 字段已存在(现有逻辑已在用),只需调整 YouTube 内容入库时的 news_id 生成规则,从 URL MD5 改为 "youtube_" + video_id。
修复 14:合集付费优先级规则
Section titled “修复 14:合集付费优先级规则”原问题:合集级 is_membership 和条目级 is_free 冲突时优先级未定义。
规则(定死):is_free(条目级)优先级高于 is_membership(合集级)。
| 合集 is_membership | 条目 is_free | 用户访问结果 |
|---|---|---|
| 0(免费) | 1(免费) | ✅ 可访问 |
| 0(免费) | 0(付费) | 🔒 需会员 |
| 1(付费) | 1(免费) | ✅ 可访问(条目级覆盖) |
| 1(付费) | 0(付费) | 🔒 需会员 |
为什么 is_free 优先:允许在付费合集里放几条免费内容作为”试看”,这是标准的付费产品转化设计(先让用户尝到甜头)。反之则无法实现试看功能。
修复 15:Whisper 转录成本控制
Section titled “修复 15:Whisper 转录成本控制”原问题:无任何转录配额控制,冷启动期大量视频可能产生高额 Azure 费用。
方案:
# 配置项(在服务配置中新增)content_import: whisper_daily_quota_minutes: 60 # 每日最多转录 60 分钟音视频(约 30 条5分钟视频) whisper_priority_scenes: # 优先转录的 scene 中类 - 旅游 - 动漫 - 备考控制逻辑:
- 每日 00:00 JST 重置
whisper_used_minutes计数器(存 Redis) - 每次触发转录前检查:
used_minutes + video_duration > daily_quota→ 跳过,标注”转录配额已满” - 优先转录
primary_scene在白名单中的内容(旅游/动漫/备考先转)
为什么设 60 分钟/日:Azure Speech 约 $1/小时,60分钟/日 = $30/月,处于可接受的冷启动期成本范围。达到 500 条视频后再根据实际消耗调整。
修复 16:Banner 数据结构
Section titled “修复 16:Banner 数据结构”原问题:Banner 轮播有 UI 定义但无数据结构。
工程确认:查阅代码,现有 FeaturedApplication 使用配置文件驱动(config.FeaturedConfig),Banner 尚无独立表或 API。
方案(最简化):复用现有配置机制,在服务端 config 中新增:
type BannerConfig struct { Banners []BannerItem `json:"banners"`}type BannerItem struct { ImageURL string `json:"image_url"` LinkType string `json:"link_type"` // "collection" | "topic" | "paywall" | "url" LinkID string `json:"link_id"` // collection_id / topic_id / 外部URL ExpireAt string `json:"expire_at"` // "2026-04-30" 或空字符串(永不过期)}更新方式:通过环境变量/配置服务更新,无需 Admin UI(冷启动期运营通过修改配置文件更新 Banner)。
为什么不建 banners 表:冷启动期 Banner 更新频率极低(每两周一次),配置文件方式零工程成本,等 Admin 后台建设完成后再迁移到数据库管理。
附录B:播放模式设计规格
Section titled “附录B:播放模式设计规格”B.1 设计定位
Section titled “B.1 设计定位”参考主流音视频产品(Spotify / YouTube Music / 播客类 App)的播放模式设计,结合内容合集的消费场景,定义三种播放模式。
优先级说明:
- Phase 1(立即实现):单集播放(用户手动控制)
- Phase 2(延后):合集顺序播放 + 单集循环
- Phase 3(更晚):频道随机播放
B.2 三种播放模式定义
Section titled “B.2 三种播放模式定义”| 模式 | 名称 | 行为 | 图标建议 |
|---|---|---|---|
| 顺序播放 | 合集连播 | 当前内容结束 → 自动播放合集内下一条(按合集排序) | ▶▶ |
| 单集循环 | 单集重复 | 当前内容结束 → 重新播放当前内容 | 🔂 |
| 不循环 | 单集播放 | 当前内容结束 → 停止,展示”下一集”入口(用户手动点) | ▶ |
注:不做”随机播放”(shuffle)——日语学习内容有学习顺序逻辑,随机不适合。
B.3 Phase 1 实现规格(立即做)
Section titled “B.3 Phase 1 实现规格(立即做)”只实现”单集播放(不循环)“模式:
| 参数 | 值 |
|---|---|
| 默认模式 | 单集播放(不循环) |
| 内容播放结束后 | 停止播放,屏幕停留在结束帧 |
| 底部展示 | ”下一集:[标题]” 入口卡片(属于合集时展示) |
| 无下一集时展示 | ”合集已看完 ✓” + “相关推荐” 3 条 |
| 不属于任何合集时 | ”相关推荐” 3 条(同 primary_scene 的内容) |
Phase 1 不需要任何播放模式切换 UI,行为固定,简单可靠。
B.4 Phase 2 实现规格(延后,内容量 ≥ 100 条后)
Section titled “B.4 Phase 2 实现规格(延后,内容量 ≥ 100 条后)”新增播放模式切换入口:
播放器控制栏右侧增加一个模式切换按钮,点击循环切换三种模式:
不循环(▶)→ 单集循环(🔂)→ 合集连播(▶▶)→ 不循环(▶)→ ...用户设置持久化:
- 存储字段:
users.playback_mode(enum:none/single/collection,默认none) - 下次打开 App 记住上次选择
- 游客不持久化,默认
none
合集连播的具体行为:
- 播完当前集 → 3 秒倒计时动画(显示”即将播放:[下一集标题]”)
- 3 秒内用户可点击取消,取消后停留在结束页
- 3 秒后自动跳转下一集,无需用户操作
- 播到最后一集结束后 → 停止,展示”合集已看完 ✓”
单集循环的具体行为:
- 播完当前集 → 无停顿,立即重新播放
- 无任何提示动画(符合循环播放的预期体感)
- 不适合视频内容(视频循环体验差);主要对音频/播客有用
B.5 播放模式与内容类型的适用关系
Section titled “B.5 播放模式与内容类型的适用关系”| 内容类型 | 不循环 | 单集循环 | 合集连播 |
|---|---|---|---|
| 视频(YouTube) | ✅ | ⚠️ 可用但体验一般 | ✅ |
| 音频(播客) | ✅ | ✅ 跟读/精听场景很有用 | ✅ |
| 图文(NHK 新闻) | ✅ | ❌ 文章循环无意义 | ✅ |
Phase 1 说明:只做”不循环”模式,所有类型行为一致。
B.6 user_playback_state 表(Phase 2 新增)
Section titled “B.6 user_playback_state 表(Phase 2 新增)”CREATE TABLE user_playback_state ( user_id BIGINT UNSIGNED PRIMARY KEY, playback_mode ENUM('none','single','collection') NOT NULL DEFAULT 'none' COMMENT 'none=单集不循环; single=单集循环; collection=合集顺序连播', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;附录B 创建:2026-03-28 | Phase 1 为当前实施目标,Phase 2 延后至视频内容 ≥ 100 条后
附录C:第二轮深度排查——实施卡点全消除
Section titled “附录C:第二轮深度排查——实施卡点全消除”本轮从数据流、API 接口、客户端行为、运维监控四个维度穿透排查,消灭剩余卡点。
卡点 C-1:合集功能对 iOS 的 API 接口变更影响评估
Section titled “卡点 C-1:合集功能对 iOS 的 API 接口变更影响评估”问题:iOS 当前调用的 API 完全没有合集相关接口,新增合集功能需要的 API 全部是新增的。需要定义清楚,否则后端和 iOS 联调时才发现不对齐。
当前 API 现状(确认已有):
GET /v1/featured→ 返回频道 + 每个频道下的新闻列表(FeaturedResponse)GET /v1/news→ 按 channel 分页返回新闻列表GET /v1/news/:id→ 新闻详情
合集功能需要新增的 API:
GET /v1/collections → 返回首页展示的合集列表(is_visible=1,按 sort_order 降序) → 支持 ?type=curated|rolling_feed|series 筛选 → 支持 ?channel=动漫日剧 筛选 Response: [{ id, title, description, cover_url, collection_type, level, is_membership, is_finished, item_count }]
GET /v1/collections/:id → 合集详情(基础信息 + 内容列表) → content_list 按合集类型排序(rolling_feed: added_at DESC; curated: sort_position ASC; series: added_at ASC) → 每条内容携带 is_free 字段(控制前端是否显示锁) → 若用户已登录,附带 user_progress(watched_count, last_news_id) Response: { collection, items: [{ news, is_free, sort_position, priority_score }], user_progress? }
GET /v1/news/:id/collection → 返回该条内容所属的合集信息(用于详情页底部"收录于合集"入口) → 若内容属于多个合集,返回 priority_score 最高的那个 Response: { collection_id, collection_title, collection_cover_url, item_position, total_items }
POST /v1/collections/:id/progress (需要登录) → 更新用户合集观看进度 Body: { last_news_id } → 自动计算 watched_count(数该 news_id 在合集内的 position) Response: { watched_count, total_items }Admin API 需要新增的:
GET /admin/v1/collections → 合集列表POST /admin/v1/collections → 创建合集PUT /admin/v1/collections/:id → 编辑合集POST /admin/v1/collections/:id/items → 添加内容到合集DELETE /admin/v1/collections/:id/items/:news_id → 移除内容PUT /admin/v1/collections/:id/items/reorder → 调整合集内排序(接受 [{news_id, sort_position}] 数组)PATCH /admin/v1/collections/:id/visibility → 切换显示/隐藏GET /admin/v1/collection-suggestions → 合集归档建议列表PATCH /admin/v1/collection-suggestions/:id → 处理建议(accepted/rejected/deferred)卡点 C-2:GET /v1/featured 的改造方式(不破坏现有 iOS)
Section titled “卡点 C-2:GET /v1/featured 的改造方式(不破坏现有 iOS)”问题:现有首页用 GET /v1/featured 返回频道列表。新首页需要合集、每日资讯 Tab、JLPT 专区等多个模块,不可能都塞进 featured 这一个接口。iOS 需要知道调用哪些接口来组装首页。
方案:新增 GET /v2/featured,旧接口保持不变,iOS 新首页调新接口。
GET /v2/featuredResponse:{ "banner": [{ image_url, link_type, link_id, expire_at }], // 3条 "daily_featured": { news }, // 今日推荐(1条) "collections": [{ collection基础信息 }], // 合集列表(is_visible=1的全部,前端取前8个展示) "jlpt_counts": { "N5": 312, "N4": 421, "N3": 834, "N2": 512, "N1": 298 }, // 各级别内容数(用于 JLPT 专区按钮) "quick_start": [{ news }] // 轻松入门4条(N5,固定)}为什么一次返回多个模块:首页冷启动需要多个模块的数据,多次请求会导致首屏白屏时间过长。一次请求返回所有首页必要数据,iOS 本地并行渲染各模块,体验最优。
每日资讯 Tab 数据:不放在 featured 接口里(数据量大,且用户只看一个 Tab),单独调用 GET /v2/news?tab=社会&limit=6&offset=0,Tab 切换时按需请求。
卡点 C-3:今日学习时长的客户端上报机制
Section titled “卡点 C-3:今日学习时长的客户端上报机制”问题:15 分钟打卡目标需要客户端统计并上报时长,但上报接口和触发时机未定义。
方案:
客户端统计:
- 进入视频/音频播放时,开始计时(local timer)
- 退到后台或离开内容页时,停止计时,累计本次时长
- 文章页:viewDidAppear 开始,viewWillDisappear 停止,最多计 5 分钟/篇
上报接口:
POST /v2/learning/report (需要登录)Body: { date: "2026-03-28", duration_sec: 180 } → 服务端写入 user_daily_learning 表(UPSERT,累加 duration_sec) → 当累计达到 900 秒(15分钟)时,自动触发 Streak +1 逻辑Response: { today_duration_sec, streak_days, is_checked_in }上报时机:
- App 进后台时(
scenePhase == .background)上报本次累计 - 退出内容页时上报
- App 每 5 分钟静默上报一次(防止 App 闪退丢失进度)
为什么不在内容播完时才上报:用户可能看一半就退出,进度不应丢失。每 5 分钟 + 退出时上报,确保进度实时准确。
卡点 C-4:Streak 服务端逻辑完整定义
Section titled “卡点 C-4:Streak 服务端逻辑完整定义”问题:Streak 涉及用户每日学习的核心激励机制,服务端逻辑只有零散描述,没有完整定义。
完整逻辑:
-- user_streaks 表(新增)CREATE TABLE user_streaks ( user_id BIGINT UNSIGNED PRIMARY KEY, current_streak INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '当前连续天数', longest_streak INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '历史最长连续天数', last_checkin_date DATE NULL COMMENT '上次打卡的 JST 日期', revival_used_at DATE NULL COMMENT '最近一次使用复活的日期(每30天限1次)', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Streak 计算逻辑(在 POST /v2/learning/report 返回时触发):
today = CURRENT_JST_DATElast = user_streaks.last_checkin_date
IF last == today: → 今天已打卡,不重复计算,直接返回 current_streak
IF last == today - 1 day: → 昨天打过卡,current_streak += 1 → 更新 last_checkin_date = today → 如果 current_streak > longest_streak,更新 longest_streak
IF last < today - 1 day(或 last IS NULL): → 断连了,检查是否可以复活: IF 付费用户 AND DATE_DIFF(today, last) <= 2 AND revival_used_at IS NULL OR DATE_DIFF(today, revival_used_at) > 30: → 可以触发复活(用户主动点击,不自动复活) ELSE: → current_streak = 1(从今天重新开始) → 更新 last_checkin_date = todayAPI 接口:
POST /v2/learning/streak/revive (需要付费会员) → 使用一次复活机会,补全昨天的打卡 → 检查条件:距上次打卡 ≤ 2 天 + 本月复活次数 < 1 → 写入 revival_used_at = todayResponse: { current_streak, revival_remaining_this_month }
GET /v2/learning/streak → 返回当前 Streak 信息Response: { current_streak, longest_streak, is_checked_in_today, revival_available }卡点 C-5:内容流水线新增 Step 14 的实现位置
Section titled “卡点 C-5:内容流水线新增 Step 14 的实现位置”问题:规格 C 附录修复 3 中定义了 rolling_feed 合集的 Step 14 自动追加逻辑,但在现有 MQ 消费者架构中,这个 Step 需要在哪里加?
确认位置:查阅 content_process_consumer.go,现有流水线中 messages.ContentProcessTypeAddScene 是最后一步。Step 14 应作为新的 MQ 消息类型加在 AddScene 之后:
// 在 messages 包中新增消息类型const ContentProcessTypeAppendToCollections = "append_to_collections"
// content_process_consumer.go 中新增 casecase messages.ContentProcessTypeAppendToCollections: err := c.collectionService.AppendNewsToMatchingCollections(ctx, e.UniqueID) if err != nil { log.Error()... }触发时机:AddScene 完成(打完场景标签)后,发送 ContentProcessTypeAppendToCollections 消息到同一队列。这样 Step 14 能在打完 scene 标签后运行,scene_id 已写入 news_scenes 表,合集规则中如果有 scene 过滤条件也能正确评估。
卡点 C-6:GET /v2/news 按 Tab 查询的服务端实现
Section titled “卡点 C-6:GET /v2/news 按 Tab 查询的服务端实现”问题:每日资讯按 Tab 显示,需要服务端支持 ?tab=动漫 这种查询,但现有 GET /v1/news 只支持按 channel 过滤。
现有接口:GET /v1/news?channel=NHK&offset=0&limit=20
新接口设计:
GET /v2/news?tab=动漫&offset=0&limit=6&language=zh-Hans
Tab 参数到 scene_id 范围的映射在服务端处理: "社会" → WHERE news_scenes.scene_id IN (49,50,51,52,57,58,59) "娱乐" → WHERE news_scenes.scene_id IN (31,32,33,34,35,36,37,38) "文化" → WHERE news_scenes.scene_id IN (11..26, 39..48) "动漫" → WHERE news_scenes.scene_id IN (27,28,29,30) "科技" → WHERE news_scenes.scene_id IN (53,54,55,56) "旅行" → WHERE news_scenes.scene_id IN (15,16,17,18)
并且只返回 visibility IN (VISIBLE, MEMBERSHIP_VISIBLE) 且 scene_primary = 'primary' 的内容排序:published_at DESC注意:Tab 查询需要 JOIN news_scenes 表,比现有按 channel 查询多一个 JOIN,需要确保 news_scenes(scene_id, news_id) 有复合索引。
-- 确认这个索引是否存在(若不存在则新增)ALTER TABLE news_scenes ADD INDEX idx_scene_primary (scene_id, scene_primary);卡点 C-7:轻松入门区内容的选取查询
Section titled “卡点 C-7:轻松入门区内容的选取查询”问题:轻松入门区展示 4 条 N5 内容(sentences 行数 ≤ 5),但”sentences 行数”需要子查询,现有 API 没有这个过滤参数。
方案:轻松入门区是运营手动维护的 4 条固定内容,不做动态查询。
实现方式:
- 在
daily_featured表中新增quick_start字段(JSON 数组存 4 个 news_id):
ALTER TABLE daily_featured ADD COLUMN quick_start_news_ids JSON NULL COMMENT '轻松入门区4条内容的 news_id 数组,运营每月人工更新一次';- 运营每月在 Admin 后台(或直接更新数据库)设置 4 个 N5 内容的 ID
- 服务端从这个字段读取并返回,不做动态筛选
为什么不动态查询:轻松入门区 30 天才更新一次,动态查询浪费资源。运营选内容本来就是一种质量保证(确保这 4 条真的适合入门用户),不应该完全自动化。
卡点 C-8:LLM 打标的 Prompt 在哪里存储、如何更新
Section titled “卡点 C-8:LLM 打标的 Prompt 在哪里存储、如何更新”问题:LLM 打标 Prompt 规格已写清楚,但在工程里这个 Prompt 存在哪里?如何更新?不能硬编码,因为后续需要调整。
工程确认:已有 prompts 表和完整的 Admin 管理接口(GET/POST/PUT/PATCH /admin/v1/prompts)。Prompt 动态存储在数据库,可通过 Admin API 更新,无需改代码重新部署。
新增 Prompt 类型:
INSERT INTO prompts (type, name, content, status, created_at, updated_at)VALUES ( 'content_intake_analysis', '内容入库打标 Prompt v1', '你是一个日语内容分类专家。根据以下内容信息,输出 JSON 格式的分类标签...[完整 Prompt 见规格第四章]', 'active', NOW(), NOW());调用方式:在新的打标服务中,通过 PromptsRepository.GetByType("content_intake_analysis") 动态读取 Prompt 内容,而不是硬编码。后续优化 Prompt 时只需通过 Admin 后台更新,立即生效,零停机。
卡点 C-9:JLPT 专区各级别的内容数量 API
Section titled “卡点 C-9:JLPT 专区各级别的内容数量 API”问题:首页 JLPT 专区展示 5 个级别按钮,按钮上可能需要显示各级别内容数量(“N3 · 834条”),服务端需要提供这个统计数据。
方案:在 GET /v2/featured 的响应中,新增 jlpt_counts 字段:
"jlpt_counts": { "N5": 312, "N4": 421, "N3": 834, "N2": 512, "N1": 298}服务端查询:
SELECT level, COUNT(*) as cntFROM newsWHERE visibility IN ('VISIBLE', 'MEMBERSHIP_VISIBLE') AND deleted_at IS NULL AND level BETWEEN 1 AND 5GROUP BY level;缓存策略:这个统计数据 1 小时内不会有明显变化,缓存到 Redis(TTL = 3600 秒),避免每次请求都执行 COUNT 查询。
卡点 C-10:collection_items 表的唯一约束
Section titled “卡点 C-10:collection_items 表的唯一约束”问题:如果同一条 news 被两次添加到同一个合集(重复操作),会产生重复条目,影响合集显示。collection_items 表缺少唯一约束。
修复:
ALTER TABLE collection_itemsADD UNIQUE KEY uk_collection_news (collection_id, news_id);应用层:Admin API 添加内容到合集时,使用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE sort_position=VALUES(sort_position),重复添加时更新排序位而不报错。
卡点 C-11:合集的 item_count 字段如何保持同步
Section titled “卡点 C-11:合集的 item_count 字段如何保持同步”问题:collections.item_count 是冗余字段(规格定义了),但没有说明它在哪里更新、如何保证与 collection_items 表同步。
方案:
- 新增
collection_item时:UPDATE collections SET item_count = item_count + 1 WHERE id = ? - 删除
collection_item时:UPDATE collections SET item_count = item_count - 1 WHERE id = ? - 每日 02:00 JST 运行一次全量校正(防止异常导致计数偏移):
UPDATE collections cSET c.item_count = ( SELECT COUNT(*) FROM collection_items ci WHERE ci.collection_id = c.id);卡点 C-12:视频/音频内容的 duration_sec 字段确认
Section titled “卡点 C-12:视频/音频内容的 duration_sec 字段确认”问题:多处规格引用了 news.duration_sec 字段(如”时长 ≤ 1800 秒”,“时长 ≤ 300 秒”),但未确认此字段是否存在于 news 表。
确认结果:查阅 internal/domain/entity/news.go,duration_sec 字段不存在于当前 news 表。图文内容无时长,但视频/音频内容需要。
修复:
ALTER TABLE news ADD COLUMN duration_sec INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '内容时长(秒),图文填0,视频/音频由导入时写入';在 YouTubeImporter.buildContentInfo() 中,将 videoInfo.Duration 写入 news.duration_sec(该字段在 VideoInfo 中已有)。
卡点 C-13:内容无法正常播放时的降级方案
Section titled “卡点 C-13:内容无法正常播放时的降级方案”问题:YouTube 视频可能因为各种原因无法播放(地区限制、版权下架、账号封禁),但规格没有定义这种情况下的用户体验。
方案:
- iOS 播放器层:YouTube WebView 播放失败时(error callback),展示”该内容暂时无法播放”提示,不崩溃
- 服务端定期健康检查(每月一次,不影响冷启动期):检测视频链接是否仍可访问,不可访问的设置
visibility = ARCHIVED - 冷启动期不做,等视频内容 ≥ 200 条后再考虑
卡点 C-14:新版首页的 A/B 测试或灰度发布策略
Section titled “卡点 C-14:新版首页的 A/B 测试或灰度发布策略”问题:新首页(合集区、Tab 区等)是大改版,直接全量上线风险较高。是否需要灰度?
结论(定死):冷启动期不做 A/B 测试,新首页通过 App 版本号控制:
- 旧版 App(< v0.6.0):调用
GET /v1/featured,显示旧首页 - 新版 App(≥ v0.6.0):调用
GET /v2/featured,显示新首页 - 这和现有
X-App-Versionheader 的处理模式完全一致(已有minVersionForPaidContent的实现参考)
无需额外工程,直接沿用版本门控机制。
卡点 C-15:飞书通知的消息格式与接收账号配置
Section titled “卡点 C-15:飞书通知的消息格式与接收账号配置”问题:规格多次提到”飞书通知 cc”,但没有说明通知发往哪个群/账号,使用哪个飞书 Bot,消息格式是什么。
工程确认:已有 feishu_notification_consumer.go,飞书通知基础设施完整。
配置项(补充到环境变量/config):
notification: ops_feishu_chat_id: "oc_13ebca873dd4888b08ff34cb31f71f08" # 当前运营群 ops_alert_threshold: 3 # 异常队列超过3条时发告警消息格式:复用现有飞书卡片消息格式(已有 feishu.SendCard 实现),不重新设计。
卡点 C-16:内容打标失败时的补打机制
Section titled “卡点 C-16:内容打标失败时的补打机制”问题:LLM 打标偶尔会因 API 超时或输出不稳定而失败,失败的内容 primary_scene 为空,无法被 Tab 分类查询到(因为 JOIN news_scenes 时找不到记录)。这类内容会被永久埋没。
方案:新增补打机制:
-- 每日 01:00 JST 扫描打标缺失的内容(入库超过24小时但无 primary scene)SELECT n.idFROM news nLEFT JOIN news_scenes ns ON n.id = ns.news_id AND ns.scene_primary = 'primary'WHERE ns.news_id IS NULL AND n.created_at < DATE_SUB(NOW(), INTERVAL 1 DAY) AND n.deleted_at IS NULLLIMIT 50;-- 对每条记录,重新发送 ContentProcessTypeAddScene MQ 消息这个定时任务已有天然的运行框架(cmd/scheduler),只需新增一个定时任务注册即可。
附录C 创建:2026-03-28 | 覆盖 API 设计、客户端上报、服务端 Streak、MQ 扩展、缓存策略等16个实施卡点
附录D:产品运营定义层排查——消灭所有运营执行模糊点
Section titled “附录D:产品运营定义层排查——消灭所有运营执行模糊点”本附录专注于产品体验和内容运营层面的未定义问题。这些不是工程问题,而是”运营人员不知道该怎么做”或”用户体验会出现空洞”的问题。
D-1:新用户首次打开 App 的完整 Onboarding 流程
Section titled “D-1:新用户首次打开 App 的完整 Onboarding 流程”问题:规格只定义了”注册时做一次兴趣选择”,但完整的新用户引导流程完全是空白。用户不知道这个 App 是干什么的、该怎么用。
完整 Onboarding 流程定义(共 4 步,不超过 60 秒):
Step 1:价值主张页(可跳过) 标题:「每天 15 分钟,沉浸式日语输入」 副标题:「不背单词,不做练习题,看视频和资讯就能学日语」 CTA:「开始使用」 跳过按钮:右上角「跳过」
Step 2:兴趣选择(必须完成,不可跳过) 标题:「你最想用日语做什么?」 选项(单选): · 🎌 看懂动漫/日剧 · ✈️ 去日本旅行 · 📚 通过 JLPT 考试 · 🏯 了解日本文化 · 🎵 随便看看/保持语感
Step 3:难度选择(必须完成) 标题:「你的日语大概是什么水平?」 选项(单选): · 完全零基础(对应 N5) · 会五十音,学过一点(对应 N4) · 能看懂简单新闻(对应 N3) · 日常会话没问题(对应 N2) · 想挑战专业内容(对应 N1)
Step 4:个人化首屏预览(可跳过) 根据选择展示「为你定制的内容预览」 CTA:「进入我的首页」选择存储:Step 2 存 users.interest_category,Step 3 存 users.preferred_level(新增字段,N1-N5,冷启动期仅存储不消费)。
跳过逻辑:Step 2 和 3 若用户跳过,默认 interest_category = casual,preferred_level = N3。
D-2:免费用户与付费用户的内容权益对比未定义
Section titled “D-2:免费用户与付费用户的内容权益对比未定义”问题:文档里散落着”前 4 条免费,后 4 条会员专属”,但没有一个完整的权益对比表。用户不清楚”买会员能多获得什么”,运营也不知道如何设置付费墙。
权益对比表(定死):
| 内容类型 | 免费用户 | 会员用户 |
|---|---|---|
| 每日资讯(Tab 区) | ✅ 全部 | ✅ 全部 |
| NHK 今日要闻合集 | ✅ 全部 | ✅ 全部 |
| 日本娱乐速报合集 | ✅ 全部 | ✅ 全部 |
| 轻松入门区 4 条 | ✅ 全部 | ✅ 全部 |
| 日本文化冷知识合集 | ✅ 前 3 条 🔒 其余 | ✅ 全部 |
| 去日本旅行必备合集 | ✅ 前 4 条 🔒 后 4 条 | ✅ 全部 |
| 动漫日语精听合集 | ✅ 前 3 条 🔒 其余 | ✅ 全部 |
| JLPT N3 冲刺合集 | ✅ 图文(10条)🔒 视频 | ✅ 全部 |
| 日语播客系列 | ✅ 最新 2 期 🔒 历史 | ✅ 全部 |
| AI 解析功能 | ❌ 不可用 | ✅ 可用 |
| Streak 复活 | ❌ 不可用 | ✅ 每月 1 次 |
| 无广告 | ❌ | ✅ |
原则:免费用户可以体验到所有合集的前几条,感受到内容质量,但精华内容锁定付费。每日资讯永远免费——这是 DAU 发动机,绝不设付费墙。
D-3:内容运营日历——每周/每月的固定运营工作
Section titled “D-3:内容运营日历——每周/每月的固定运营工作”问题:规格定义了”每两周更换 Banner""每 30 天更换轻松入门区”,但没有完整的运营日历,运营人员不知道每周/每月要做什么。
运营日历(定死):
每日(自动,无需人工):
- NHK Web Easy 日更内容自动入库
- Yahoo 娱乐资讯自动入库
- 09:00 JST:视频内容扩充追踪(仅异常时通知)
每周一(需人工处理):
- 查收合集归档建议通知,决策是否创建新合集
- 查收视频内容分布日报,判断哪个类别需要重点补充
每周五(需人工处理):
- 查收热点内容采集建议,决定本周补充哪些主题
- 发链接给 Captain 触发视频入库(本周采集计划执行)
每两周(需人工执行):
- 更新首页 Banner(准备新的 3 张图片和跳转配置)
- 评估当前热门专题效果,准备下一个专题的内容筛选
每月(需人工执行):
- 更换轻松入门区 4 条内容(从 N5 内容中选 4 条最近的好内容)
- 查收合集健康度报告,决定是否下线低效合集
- 审视会员权益设置是否需要调整
JLPT 节点(特殊):
- 考前 2 个月(5月/10月):上线「JLPT X月冲刺」专题,补充对应级别的题库类内容
- 成绩公布后(8月/2月):更新专题为「下次备考计划」
D-4:内容质量标准——什么样的内容不该上首页
Section titled “D-4:内容质量标准——什么样的内容不该上首页”问题:目前规格只定义了技术层面的”不上架条件”(时长过短/违规/重复),但没有定义运营层面的内容质量标准。运营人员不知道哪些内容该推哪些不该推。
内容质量评判标准(运营维度):
适合上合集/热门推荐的内容:
- 内容与日语学习有直接关联(不是纯娱乐新闻)
- 视频清晰度 ≥ 480p
- 字幕完整(或有完整文字稿)
- 时长在 3–20 分钟之间(太短没学习价值,太长用户不完成)
- 内容为 2024 年以后的(太旧的文化/新闻资讯失去时效性)
不推首页但保留在库的内容:
- 纯娱乐内容(无日语学习价值)
- 仅英语配音/字幕(无日语学习属性)
- 时长 < 3 分钟(放入每日资讯 Tab 而非合集)
直接下架的内容(自动过滤 + 运营确认):
- 含政治敏感内容
- 含明显广告/促销内容为主(内容 >50% 是广告)
D-5:合集的”完结”与”下线”如何运营
Section titled “D-5:合集的”完结”与”下线”如何运营”问题:collections.is_finished 字段代表合集完结,但没有定义什么情况下合集应该完结,完结后展示给用户是什么体验。
完结定义:
| 场景 | 是否完结 | 展示文案 |
|---|---|---|
| 播客系列断更超过 4 周 | ✅ 设为完结 | 「已完结 · 共 X 期」 |
| 动漫某季度 PV 集合(季度结束) | ✅ 设为完结 | 「2026春番精选 · 已完结」 |
| JLPT 冲刺专题(考试过后) | ✅ 设为完结 | 「7月场已结束」 |
| 日更合集(NHK/Yahoo) | ❌ 永不完结 | 「持续更新中」 |
完结合集的首页展示规则:
- 已完结合集不出现在首页热门推荐中
- 用户个人的”已看合集”历史中仍可见
- 合集详情页顶部标注”已完结”灰色徽章
下线(is_visible = 0)规则:
- 合集健康度报告显示 30 天打开率 < 3% → 运营决策下线
- 内容版权出现问题(收到下架要求)→ 立即下线
- 下线 ≠ 删除,数据保留,随时可重新上线
D-6:用户分享功能的完整定义
Section titled “D-6:用户分享功能的完整定义”问题:策划文档多次提到”分享率”是 KPI,但整个规格没有任何地方定义什么可以分享、分享出去是什么样子。
可分享的内容(定义):
| 分享类型 | 分享载体 | 分享内容 |
|---|---|---|
| 分享单条内容 | 图片卡片 | 内容封面 + 标题 + 难度标签 + App Logo + 二维码 |
| 分享今日学习成果 | 图片卡片 | Streak 天数 + 今日学习时长 + 今日内容标题 + App Logo |
| 分享合集 | 文字 + 链接 | 「我在学《去日本旅行必备》 |
分享卡片设计原则:
- 必须包含「Yomiya 每日日语」品牌
- 包含二维码(指向 App Store 下载页)
- 图片比例:9:16(适合微信/小红书故事发布)
什么时候触发分享入口:
- 内容详情页底部固定”分享”按钮
- Streak 达到整数里程碑(7天/30天/100天)时弹出分享引导
- 合集全部看完时展示分享引导
D-7:推送通知策略完整定义
Section titled “D-7:推送通知策略完整定义”问题:没有任何地方定义 App 会发什么推送、什么时候发、发给谁。推送是最强的召回工具,不定义等于放弃。
推送策略(分类定义):
每日内容推送(所有用户,默认开启):
- 时间:09:00 JST(用户可自定义时间,暂不做,默认09:00)
- 内容:「今日推荐:[今日内容标题]」
- 频率:每日1条,不可增加
Streak 相关推送:
- Streak 将断提醒:当日 21:00 JST,如果用户今日未达 15 分钟 → 发送「你今天还有 X 分钟就完成打卡了!」
- Streak 里程碑祝贺:达到 7天/30天/100天 时 → 「🎉 连续学习 X 天了!」(一次性,不重复)
- Streak 断连后召回:断连第 2 天 → 「X 天的连续记录别丢了,今天回来继续!」
内容更新推送(用户订阅的合集/系列):
- 播客系列新一期上线 → 「[系列名] 更新了:[新一期标题]」
- 仅对”已看过该系列 ≥ 2 期”的用户推送(降低骚扰)
推送频率上限:每日最多接收 2 条推送,超出则按优先级取前 2 条(优先级:Streak 断连 > 每日内容 > 系列更新)。
推送可关闭:用户可在设置中分类关闭推送(每日内容 / Streak 提醒 / 系列更新 分开控制)。
D-8:用户回归体验(超过 7 天未打开 App)
Section titled “D-8:用户回归体验(超过 7 天未打开 App)”问题:Streak 断了的用户回来会看到什么?规格完全没有定义回归用户的体验,这批用户极其宝贵,需要特别设计。
回归用户场景与体验:
离开 2–7 天(轻度流失):
- 首屏正常显示,不做特殊处理
- 今日推荐显示正常
- Streak 显示已断(数字变灰),显示”继续新的 Streak”引导
离开 7–30 天(中度流失):
- 首次进入时展示「欢迎回来」全屏弹窗
- 内容:“上次你在学 [上次看的内容],这几天又有 [X] 条新内容等你”
- CTA 按钮:「继续看」(直接跳到上次内容)和「看看新内容」(跳首页)
离开 30 天以上(重度流失):
- 强制引导重新选择兴趣(提示”你的偏好设置需要更新”)
- 走 Onboarding Step 2 和 Step 3 流程(跳过 Step 1)
- 目的:重新激活用户兴趣,刷新个性化推荐(冷启动期虽然不启用个性化,但数据要记录)
D-9:内容版权风险运营规范
Section titled “D-9:内容版权风险运营规范”问题:规格只说”AI 给出版权风险提示,人工最终确认”,但没有定义具体的版权操作 SOP。运营人员遇到版权问题不知道怎么处理。
版权风险分级与操作规范:
| 风险等级 | 判断依据 | 操作 |
|---|---|---|
| 安全 | 官方认证频道(NHK/JNTO/东宝官方等白名单) | 直接入库,无需审核 |
| 低风险 | 来自已知媒体机构的公开视频,标注来源 | 入库,标注版权来源,定期核查 |
| 中风险 | 来源不明的视频、混剪类内容 | 入库但标注 is_risk = true,不上首页,不放入合集,仅存档 |
| 高风险 | AI 判断含明显未授权内容、第三方转载 | 拒绝入库 |
收到下架要求时的 SOP:
- 立即将该内容
visibility = INVISIBLE(5分钟内执行) - 从所有合集中移除该内容
- 记录下架原因和时间
- 不删除数据库记录(保留审计追踪)
YouTube 白名单维护:在服务端配置中维护可信频道 Handle 列表,白名单内的频道无需每次人工确认版权。白名单更新由 cc 决策,操作通过飞书发指令给 Captain 写入配置。
D-10:内容难度分级的准确性验证
Section titled “D-10:内容难度分级的准确性验证”问题:LLM 自动判断难度(N5-N1),但这个判断准不准?没有验证机制,难度标签错误会严重影响用户体验(N1 内容标了 N3,初学者点进去完全看不懂)。
难度分级质量控制方案:
冷启动期(内容 < 500 条时):
- 每批新采集内容,人工抽检 10%(约 5 条/50 条新内容)验证难度是否正确
- 发现系统性偏差(如 NHK World 视频普遍被标为 N2,实际是 N3)→ 修正 Prompt
冷启动期结束后:
- 用用户行为数据反向验证:某级别内容的完成率与该难度下的用户占比对比
- 完成率远低于期望 → 内容可能被低估难度(标 N3 但实际是 N1)
快速人工修正通道:Admin 后台”内容难度修正”功能,运营可一键修改某条内容的 level,修改后自动重新触发 LLM 打标(确保其他标签与新难度一致)。
D-11:空状态设计(各模块内容不足时展示什么)
Section titled “D-11:空状态设计(各模块内容不足时展示什么)”问题:多个首页模块在初期内容不足时不展示(如动漫 Tab 未达 30 条),但完全没有定义用户在”即将上线”阶段看到什么。
各模块空状态/未上线状态处理:
| 模块 | 内容不足时的展示 |
|---|---|
| 每日资讯 Tab(未达到条件) | 该 Tab 完全不显示(不展示空 Tab) |
| 合集区(不足 8 个合集) | 展示已有合集(如 2 个),剩余位置展示”即将上线”占位卡片(灰色,写”更多合集即将到来”) |
| 今日推荐(无当日内容) | 展示昨日最高完成率的1条内容,标注”今日内容加载中” |
| 播客系列(未上线) | 不显示该模块,不展示占位 |
| 轻松入门(无 N5 内容) | 不显示该模块 |
关键原则:宁愿不展示,也不展示空壳。用户看到空壳比看不到更差。
D-12:内容消费的激励体系是否需要积分/勋章
Section titled “D-12:内容消费的激励体系是否需要积分/勋章”问题:规格只有 Streak 一种留存激励,但市场上主流语言学习 App(Duolingo/Babbel)都有更丰富的激励体系。每日日语是否需要勋章/成就系统?
结论(定死,不在冷启动期做):
勋章/成就系统延后到 v1.0 以后,原因:
- 冷启动期内容不足,过早引入成就系统会因内容单一而失效
- 勋章系统工程量大,影响首页 MVP 上线时间
- 当前阶段只需 Streak + 会员权益两个激励杠杆
但以下数据必须在冷启动期开始统计(为未来勋章系统准备):
- 用户累计消费内容总数
- 用户消费过的 scene 中类数量(“涉猎广度”)
- 用户在某个 scene 中类的累计消费量(“深度专精”)
D-13:搜索功能的边界定义
Section titled “D-13:搜索功能的边界定义”问题:C-12 将搜索延后,但搜索入口放在哪里?什么时候上线?搜索结果页是什么样?这些都是 iOS 开发需要预留位置的。
搜索功能分阶段定义:
Phase 1(预留入口,不实现):
- 首页顶部预留搜索图标入口(灰色,点击展示”即将上线”Toast)
- 目的:让用户知道将来会有搜索,不造成功能缺失的负面印象
Phase 2(延后,内容 ≥ 500 条后):
- 支持按标题关键词搜索(日文/中文均可)
- 搜索结果按相关度排序,显示内容类型标签
- 搜索结果支持按 level/type 筛选
Phase 3(更晚):
- 按场景标签浏览(“旅行日语”频道页)
- 语音搜索
D-14:会员订阅后的服务承诺是否清晰
Section titled “D-14:会员订阅后的服务承诺是否清晰”问题:付费墙存在,但用户在订阅前看到的”会员权益说明”是什么?如果权益说明不够清晰,转化率会很低。
会员权益展示定义(付费页必须包含以下内容):
权益清单(用图标+文字展示):
- 🎬 解锁全部视频合集(含动漫/旅行/文化精选)
- 🎧 收听完整播客档案(历史所有期数)
- 🤖 AI 解析:搞懂每句话的文化背景
- 🔥 Streak 复活:每月 1 次,守护连续记录
- 📚 JLPT 冲刺视频:N3/N2/N1 高频词汇讲解
不做隐藏条款:权益页必须标注”免费用户可永久使用的内容”(今日资讯/每日推荐/基础合集预览),避免用户感觉”被骗订阅”。
D-15:内容归属版权标注规范
Section titled “D-15:内容归属版权标注规范”问题:使用 NHK、Yahoo 等外部内容,App 内是否需要显示版权归属?不显示可能有法律风险。
版权标注规范:
| 内容来源 | 标注方式 | 标注位置 |
|---|---|---|
| NHK Web Easy | 「© NHK」 | 内容详情页底部 |
| NHK World YouTube | 「© NHK World」 | 内容详情页底部 |
| Yahoo! 娱乐新闻 | 「© Yahoo! JAPAN」 | 内容详情页底部 |
| 动漫官方 PV | 「© [制作公司]」(从 YouTube 频道信息中提取) | 内容详情页底部 |
| 平台原创内容 | 「© Yomiya」 | 不显示(默认) |
内容详情页底部信息区:
[内容标题][日期] · [来源名称] · [难度标签]---[正文]---© NHK · 内容来源于 NHK Web Easy,仅用于日语学习目的附录D 创建:2026-03-28 | 产品运营定义层,15个卡点,覆盖 Onboarding、权益对比、运营日历、版权规范、通知策略等
文档拆分后索引(2026-03-28 新增)
Section titled “文档拆分后索引(2026-03-28 新增)”为防止当前执行继续依赖这份持续膨胀的长文档,现补充拆分后索引如下:
当前执行优先阅读
Section titled “当前执行优先阅读”yomiya-phase1-execution-spec.mdyomiya-canonical-naming.mdyomiya-phase1-scope-boundary.mdyomiya-implementation-spec-core.md
yomiya-implementation-review-log.mdyomiya-implementation-spec-split-plan.md
开放问题与后续阶段
Section titled “开放问题与后续阶段”yomiya-implementation-open-questions.mdyomiya-implementation-future-phases.md
说明:
- 本文档继续保留原始实施规格与历史扩展内容
- 当前新增文档用于把“当前有效规格、历史问题、开放问题、未来阶段”分层
- 后续如果继续拆分或迁移,以新增文档为主,本文件继续作为历史留档入口