My Computer · 2026/01/26 0

苹果cms 豆瓣数据自动更新与核查

挖一个巨坑,缓慢推进中。。。

豆瓣数据自动更新与核查系统方案(面向 17 万视频库)

0. 总体目标与边界

0.1 目标

  1. 在苹果CMS后台“视频”菜单新增页面:豆瓣数据
  2. 系统以可控频率、持续地对视频进行豆瓣数据更新(调用 douban.php),并满足一致性约束:
  • 年份差异较大、标题差异过大、地区冲突、导演冲突的记录不自动更新,进入待核查
  1. 对简介执行 AI 优化(例如 OpenRouter DeepSeek v3.2),并提供简介锁定,避免重复改写。
  2. 提供数据治理功能:无豆瓣ID、豆瓣ID重复、已锁定豆瓣ID、待核查豆瓣ID等列表与批量处理能力。
  3. 在 17 万数据规模下,保证:可持续运行、可观测、可回滚、避免全表扫描。

0.2 核心原则

  • 写库必须可解释、可回滚:任何自动写入都记录来源、评分、差异点、前后值。
  • 自动化只做“高置信”:低置信或冲突进入待核查,避免误伤。
  • 锁定优先:豆瓣ID锁定、简介锁定后,自动任务不再覆盖。
  • 任务队列化:更新、匹配、AI简介均通过队列执行,严格限速与并发控制。

1. 数据库设计与变更(字段、状态、索引)

1.1 在视频表(mac_vod)新增/确认字段

建议新增(或确认已有同义字段):

1) 豆瓣ID与锁定

  • vod_douban_id:varchar(20) 或 bigint(存 subject_id)
  • douban_id_locked:tinyint(1) 默认 0(是否锁定豆瓣ID)
  • douban_id_lock_time:datetime 可空(锁定时间)
  • douban_id_source:varchar(16)(manual/auto/ai_search/import)
  • douban_id_confidence:tinyint/smallint(0–100,记录最终评分)
  • douban_review_status:varchar(16)(NOT_FOUND/REVIEW/CONFIRMED/IGNORED)
  • douban_review_reason:varchar(255) 或 text(冲突原因摘要)
  • douban_ignore_until:datetime 可空(忽略到期,期间不再入队)

2) 豆瓣数据更新节奏

  • douban_last_sync_at:datetime 可空(上次成功同步豆瓣数据时间)
  • douban_next_sync_at:datetime 可空(下次计划同步时间,关键字段)
  • douban_sync_fail_count:int 默认 0(连续失败次数)
  • douban_last_fail_at:datetime 可空
  • douban_last_fail_reason:varchar(255) 可空

3) 简介AI优化与锁定

  • intro_locked:tinyint(1) 默认 0(简介锁定)
  • intro_lock_time:datetime 可空
  • intro_ai_source:varchar(32) 可空(deepseek/openrouter/…)
  • intro_ai_last_at:datetime 可空(上次AI改写时间)
  • intro_ai_fail_count:int 默认 0
  • intro_ai_last_fail_at:datetime 可空
  • intro_ai_last_fail_reason:varchar(255) 可空

说明:锁定后不允许自动采集更新豆瓣ID和简介,上述字段直接支持该约束。


1.2 新增业务表(建议)

1) 豆瓣任务队列表 mac_douban_task

用于调度“抓取/回填”与“匹配/复核”任务,避免频繁扫主表。

字段建议:

  • task_id 主键
  • vod_id
  • task_type(SYNC_Douban / MATCH_DoubanID / AI_Intro / RECHECK)
  • status(PENDING/RUNNING/SUCCESS/FAIL/SKIP)
  • priority(int,最近更新/新片更高)
  • run_after(datetime,最早运行时间)
  • attempts(int)
  • last_error(varchar(255))
  • payload(text/json:候选ID列表、打分明细摘要等)
  • created_at / updated_at

2) 豆瓣操作日志表 mac_douban_log(必须)

用于审计与回滚:

  • log_id
  • vod_id
  • action(AUTO_CONFIRM / AUTO_SYNC / MANUAL_SET_ID / MANUAL_REPLACE_ID / LOCK_ID / UNLOCK_ID / AI_INTRO_UPDATE 等)
  • old_values(json:旧字段快照)
  • new_values(json:新字段快照)
  • reason(文本/摘要)
  • score(0–100)
  • operator(admin账号或系统)
  • created_at

3) 待核查候选表(可选但推荐)mac_douban_review_candidate

用于保存 TopN 候选及评分,避免每次打开页面重算:

  • id
  • vod_id
  • douban_id
  • score_total
  • score_detail(json)
  • conflicts(json/文本标签)
  • rank(1..N)
  • created_at

1.3 索引建议(17 万规模避免全表扫描)

主表(mac_vod)关键索引:

  1. idx_douban_next_sync_at:(douban_next_sync_at, type_id)
  2. idx_douban_review_status:(douban_review_status, douban_next_sync_at)
  3. idx_vod_douban_id:(vod_douban_id)
  4. idx_intro_locked:(intro_locked, intro_ai_last_at)
  5. idx_update_time:(update_time)(若你按更新时间优先处理)

任务表(mac_douban_task)索引:

  1. idx_task_poll:(status, run_after, priority)
  2. idx_task_vod_type:(vod_id, task_type, status)

2. 后台页面与配置项(豆瓣数据菜单)

2.1 新增菜单:视频 → 豆瓣数据

页面建议分 Tab:

  1. 自动更新设置
  2. 待核查豆瓣ID
  3. 无豆瓣ID
  4. 豆瓣ID重复
  5. 已锁定豆瓣ID
  6. 简介锁定/AI处理
  7. 任务监控与日志

2.2 自动更新设置页(可配置项)

  • 默认更新间隔:2分钟(随机抖动 0–120 秒)
  • 排除分类ID:逗号分隔(type_id)
  • 更新频率分层(按 update_time 优先、vod_year 兜底):
  • 最近1个月:每 3 天
  • 最近半年:每 7 天
  • 最近1年:每 30 天
  • 1年以上:每 60 天
  • 2年以上:每 90 天
  • 候选数量 TopN:默认 5
  • 自动确认阈值、差值阈值
  • AI 简介:启用/禁用、模型、RPM/TPM、每日上限、失败退避
  • douban.php 调用:每分钟最大请求数、并发数、失败退避

3. 任务调度与运行机制(先后顺序与队列)

3.1 初始化(一次性)

对现有 17 万视频,批量初始化:

  1. vod_douban_id 为空:
  • douban_review_status=NOT_FOUND
  • douban_next_sync_at 按更新时间分层写入未来时间(均匀散列,避免集中)
  1. vod_douban_id 不为空:
  • douban_review_status=CONFIRMED(不锁定,除非你已有锁定策略)
  • douban_next_sync_at 按分层策略写入
  1. 近期 update_time 的记录提高优先级(更早 next_sync 或更高 priority)

3.2 周期调度器(Scheduler)

每隔 N 秒(例如 30 秒)执行一次:

  1. mac_voddouban_next_sync_at<=now() 拉取一批 vod_id(例如 50–200 条/批),要求:
  • 不在排除分类
  • douban_ignore_until 为空或已过期
  1. 对每条生成任务:
  • vod_douban_id:入队 SYNC_Douban
  • vod_douban_id:入队 MATCH_DoubanID
  1. 去重:同 vod_id + task_type 若已 PENDING/RUNNING,不重复入队。

3.3 Worker(执行器)

  • Douban Worker:处理 MATCH_DoubanIDSYNC_Douban
  • AI Worker:处理 AI_Intro

每类 Worker 支持:

  • 并发上限(Douban 2–5;AI 1–3,可配)
  • 全局速率限制(token bucket)
  • 失败退避(指数 backoff + 抖动)
  • 熔断(失败率过高自动暂停)

4. 自动匹配豆瓣ID:候选获取与打分决策(关键)

4.1 候选ID来源(按顺序)

MATCH_DoubanID 任务:

  1. 规范化输入(标题去发行后缀、提取年份/季/集等)
  2. 生成 Query(至少三条):
  • "{片名} 豆瓣"
  • "{片名} {年份} site:movie.douban.com/subject"
  • "{片名} {导演/主演} 豆瓣 subject"(可选)
  1. 调用“联网搜索 API”获取结果,抽取 subject_id TopN
  2. 对每个候选调用 douban.php?id=xxxx 获取摘要字段
  3. 进入否决项与打分规则,输出决策

4.2 否决项(Hard Reject)

命中即不自动写入,进入待核查(并写明原因标签):

  1. 年份强冲突:两边年份都有且相差 ≥3 年 → 否决;相差 2 年 → 降级待核查
  2. 标题相似度过低:综合标题相似度 <0.55 → 否决
  3. 地区强冲突:两边地区都有、归一化后交集为空且双方都不是“未知/*/多地区” → 否决
  4. 导演强冲突:两边导演集合均非空且交集为空 → 否决
  5. 剧/影强冲突(建议启用):剧/影类型相反且标题相似度 <0.75 → 否决

4.3 打分项(0–100)与默认权重

对每个候选计算总分 S,并保存分项明细(用于后台解释)。

  1. 标题匹配(45分)
  • 取 max(本地名vs豆瓣名、本地别名vs豆瓣名、本地名vs豆瓣别名)
  • 相似度用“编辑距离 + 分词Jaccard + 包含关系”混合
  1. 年份匹配(15分)
  • 同年 15;差1年 10;差2年 5;缺失 8;差≥3年 0
  1. 地区匹配(10分)
  • 有交集 10;缺失 5;弱冲突 4;强冲突 0
  1. 导演匹配(12分)
  • 有交集 12;缺失 6;交集0 0
  1. 主演匹配(8分)
  • 交集≥2:8;交集=1:6;缺失:4;交集=0:0(不否决)
  1. 类型匹配(5分)
  • 有交集 5;缺失 2;明显不一致 0
  1. 时长/集数(5分,可选)
  • 电影:差<=10分钟 5;<=20 3;>20 0
  • 剧集:集数接近加 3–5
  1. 评分存在性弱加分(2分)
  • vod_score_num>0:+2(仅用于同分打破平局)

4.4 阈值决策(CONFIRMED / REVIEW / NOT_FOUND)

候选按 S 降序,取 Top1/Top2:

  • CONFIRMED(自动写入)
  • S_top1 >= 85
  • 且(无 Top2 或 S_top1 - S_top2 >= 8
  • 且未触发否决项
  • douban_id_locked=0
    → 写入豆瓣ID,记录来源与分数
  • REVIEW(待核查)
  • 70 <= S_top1 < 85
  • S_top1>=85 但差值不足(歧义)
  • 或触发否决项
  • 或被锁定阻止写入
    → 进入待核查,保存 TopN 候选与原因
  • NOT_FOUND(未找到)
  • 无候选 或 S_top1 < 70
    → 标记未找到,后续可人工补充/重跑

5. 调用 douban.php 更新并回填(SYNC_Douban)

5.1 执行顺序

SYNC_Douban

  1. 检查是否允许更新(排除分类、忽略期等)
  2. douban.php?id=vod_douban_id 获取 data
  3. 回填策略(取不到不覆盖)
  • 只写入 douban.php 返回的字段
  • douban_id_locked=1 时不改 vod_douban_id
  • intro_locked=1 时不改简介
  1. 成功:
  • douban_last_sync_at=now()
  • douban_sync_fail_count=0
  • 写入 douban_next_sync_at(按第6节)
  1. 失败:
  • douban_sync_fail_count++
  • 写失败原因与时间
  • douban_next_sync_at 按退避策略延后(第7节)

6. 更新频率策略(按时间分层 + 随机抖动)

6.1 分层频率(你给的规则)

  • 最近 1 个月:+3天
  • 最近 6 个月:+7天
  • 最近 1 年:+30天
  • 1 年以上:+60天
  • 2 年以上:+90天

6.2 运行时抖动(避免固定节奏)

  • 调度器按批次入队
  • Worker 在执行前为任务设置 run_after 随机抖动(0–120秒)或采用令牌桶限速
  • 不建议大量 sleep,更建议 run_after + token bucket 的组合

7. 并发、限速、退避与熔断(稳定性关键)

7.1 Douban 请求限速

默认建议:

  • 并发:2(可配到 5)
  • 速率:每分钟 20–60 次(逐步调大)
  • 退避:指数退避 + 抖动
  • 1次失败:+10分钟
  • 2次失败:+30分钟
  • 3次失败:+2小时
  • 4次失败:+6小时
  • ≥5次:+24小时,并提示人工关注/或转待核查
  • 熔断:5分钟失败率>80% 或出现大量“登录/风控”特征 → 暂停 30–60分钟

7.2 搜索 API 限速(MATCH)

  • 并发:1–3
  • 速率:按主机限制配置
  • 缓存:同一 (normalized_title, year) 结果缓存 7–30 天

7.3 AI 简介接口限速(OpenRouter)

  • AI Worker 独立队列,严格限速(RPM/TPM/RPD)
  • 429:延后 10–30 分钟;5xx:延后 5–10 分钟;其它:延后 30–120 分钟
  • 成功写入后:intro_locked=1,后续自动任务不再改写(除非手动触发)

8. 待核查工作流(后台呈现、批量处理、一键确认/替换/锁定)

8.1 待核查列表页(REVIEW)

列表字段:

  • 本地:vod_id、名称、年份、地区、导演(摘要)
  • 当前豆瓣ID(如有)+ 锁定状态
  • Top1:豆瓣标题/年份/地区/导演/评分/人数
  • S_top1、标题相似度、Top2简要、差值
  • 冲突标签(年份/地区/导演/歧义/相似度低/锁定阻止)
  • 更新时间
  • 操作

展开面板:

  • Top1–Top5 候选卡片:标题/别名/年份/地区/类型/导演/主演/时长/集数
  • 分项得分明细与否决项命中原因

8.2 单条操作

  • 确认 Top1 并更新(可勾选“同时锁定豆瓣ID”)
  • 选择其它候选(Top2–Top5)
  • 手动输入豆瓣ID(强校验,冲突需二次确认)
  • 替换并锁定(旧ID vs 新ID 差异对比弹窗)
  • 标记忽略(30天/180天/永久)
  • 锁定/解锁豆瓣ID、锁定/解锁简介

8.3 批量操作

  • 批量“确认Top1并锁定”(仅对高置信且无否决项生效)
  • 批量确认不锁定
  • 批量忽略
  • 批量解锁(高权限)
  • 批量重跑匹配(规则/搜索源更新后)

全部批量操作要求:

  • 异步队列执行
  • 展示进度与失败原因
  • 写入日志,支持回滚

9. 其它治理页面

9.1 无豆瓣ID(NOT_FOUND)

  • 按近期更新/人气排序
  • 一键生成候选(入队 MATCH)
  • 手动搜索/手动输入豆瓣ID(同样走强校验)

9.2 豆瓣ID重复

  • vod_douban_id 分组展示
  • 一键保留最匹配/最早绑定/最高人气,其它清空并入待核查或无豆瓣队列
  • 支持批量处理

9.3 已锁定豆瓣ID

  • 展示锁定来源/时间/原因
  • 支持筛选与批量解锁(高权限)
  • 锁定条目默认不参与自动“改ID”,可配置是否参与“同步其他字段”

10. 简介 AI 优化:触发、写入与锁定

10.1 触发条件

  • intro_locked=0
  • 本次豆瓣同步成功(至少标题/类型等可用于约束)
  • 距离上次 AI 处理超过最小间隔(例如 30 天)
  • 未触发 AI 限速/熔断

10.2 输入输出规范

输入包含:标题(含年份)、豆瓣ID/URL、豆瓣简介(若有)、类型/地区/导演/主演。
输出要求:纯文本简介、长度范围可配置(如 120–300字)、不带外链、不杜撰关键事实。

写入规则:

  • 成功写入后:intro_locked=1,记录来源与时间
  • 手动触发“重新AI简介”:允许覆盖,但覆盖后仍锁定

11. 可观测性与回滚

  1. 任务监控页:按 task_type/status 展示队列长度、成功率、失败率、平均耗时
  2. 日志查询:按 vod_id 查询操作链路(谁改了什么、依据、评分)
  3. 回滚:单条支持回退到上一版本(用 mac_douban_log.old_values
  4. 告警(可选):连续失败率过高、触发风控/登录墙时提示管理员

12. 实施步骤(按先后顺序)

  1. 数据库变更:新增字段、建索引、创建任务表与日志表
  2. 后台菜单与页面骨架:豆瓣数据入口 + 7个Tab(列表、筛选、操作占位)
  3. Scheduler:按 douban_next_sync_at 拉取应处理记录并入队(去重)
  4. Douban Worker:实现 SYNC_Douban(回填、日志、next_sync、退避)
  5. 候选生成:接入搜索 API 抽取候选豆瓣ID(缓存与限速)
  6. 打分与决策:实现否决项、分项得分、CONFIRMED/REVIEW/NOT_FOUND 流转
  7. 待核查联动:候选卡片、分项解释、一键确认/替换/锁定/忽略
  8. 批量处理:批量确认/忽略/解锁/重跑匹配全部队列化
  9. AI Worker:接入 OpenRouter/DeepSeek,限速、退避、成功即锁定简介
  10. 监控与回滚:任务监控、日志审计、单条回滚
  11. 灰度上线:先小范围分类/时间段启用,观察命中率与待核查比例,再逐步扩大

进度截图

在后台“视频”菜单里新增一个入口 「豆瓣数据」

第 3 步(小步落地):仅“配置保存 + 页面展示配置”

第 4 步(只读统计面板):在“豆瓣数据”页面顶部增加一块统计区,展示关键数量(无豆瓣ID、重复豆瓣ID、待核查、锁定等)。

第 5 步(只读列表页 + 分页 + 基础筛选)

第 6 步(单条操作:核查/确认/忽略/锁定)。

第 7 步:批量操作(最小可用版)。目标是:在列表页勾选多条记录后,一次性执行 核查 / 确认 / 忽略 / 锁定ID / 锁定简介 等操作。

第 8 步:后台“手动触发同步(单条/批量)”——真正去调用 douban.php 并回填字段,但依旧不做定时任务