观影:豆瓣简介补上导演/主演/上映年份等主要信息

用户原话:「观影模块,添加电影的时候,从豆瓣获取的信息不够,
          应该把导演、主要演员,上映年份等主要信息,也都放在简介中。」

**改了什么**
- 新增 `Douban::summaryText($meta, $title, $withPlot)`:把主要信息拼成一段可读文本。
  格式(只有有值的行才出现,绝不写"导演:"这种空壳行):
      《片名》(年份)
      导演:X     主演:A、B、C     类型:…     片长:…     制片国家/地区:…     又名:…     豆瓣评分:…
      <空行>
      <剧情简介原文>
  标签词与详情页 `db-meta` 用的同一套(年份/导演/主演…),同一字段在两处叫同一个名字。
- `movies/douban` 接口新增 `summary` 字段,**在服务端拼**。
  为什么不在前端拼:这段文本会**写进 movie_details.info**(落库、与豆瓣脱钩),
  拼法必须可测、只有一处;拼在 JS 里既测不到,也没法在"重新抓取"之外复用。
- 前端 `apply()` 改为填 `data.summary`(原来只填 `meta.plot`),
  并补了两处**原来缺失的反馈**:
  · 简介里已有用户写的内容 → 明确告知"没有覆盖它"(并提示清空后重抓);
  · 豆瓣确实没有可用简介文本 → 也明确说一声。
  之前这两种情况都是**静默什么都不做**,用户会以为"抓取坏了"。
  简介框 rows 3 → 6(内容变多了),placeholder 也说明了会自动带上主要信息。

**验证**
- 新增 `_fb_douban_summary.py` 22 条,覆盖:字段齐全 / 缺字段不留空壳行 /
  全空返回空串 / 只有剧情时不带多余空行 / 接口确实吐 summary /
  前端写入的值只能是 summary(按 `infoEl.value = …` 的实际赋值断言,
  不是"文件里有没有某个词")/ 仍然只填空不覆盖。
- **反向验证**:把拼装改回"简介=plot"→ ① 组 8 条立刻红(片名/年份/导演/主演/
  类型/片长/国家/评分全部缺失),还原后复绿。
- **线上真连豆瓣**实测(`哪吒之魔童降世` / `流浪地球`):year/director/actors/
  genre/runtime/country/rating/plot 全部有值,拼出的简介如
  《哪吒之魔童降世》(2019)/导演:饺子/主演:吕艳婷 / 囧森瑟夫 / 瀚墨 …/
  类型:剧情 / 喜剧 / 动画/片长:110分钟/豆瓣评分:8.4 + 剧情原文。
- 部署脚本加 4 条静态断言(summaryText 在服务端、接口吐 summary、
  前端不许再只拿 meta.plot、摘不到要提示)。
- 回归:e2e 775/0 · 豆瓣简介 22/0 · 第 5 轮专项 91/0 · 导入 21/0。
This commit is contained in:
2026-10-03 20:23:22 +08:00
parent b5725b449a
commit 4377fa995f
3 changed files with 110 additions and 4 deletions
+79
View File
@@ -173,6 +173,85 @@ final class Douban
return isset($d[$k]) && is_scalar($d[$k]) ? (string)$d[$k] : '';
}
/**
* 拼一份**带主要信息**的简介(v24 用户 2026-10-03 第 2 条)。
*
* 用户原话:「添加电影的时候,从豆瓣获取的信息不够,应该把导演、主要演员,
* 上映年份等主要信息,也都放在简介中。」
*
* 为什么要有这个函数(而不是在路上顺手拼字符串):
* · 简介是**写进 movie_details.info 的正文**,一旦存下来就与豆瓣脱钩了
* (以后豆瓣改了也不会变)。所以拼法必须是一处定义、可测:
* `_fb_douban_summary.php` 直接断言各种组合下的输出。
* · 用户要求"主要信息**也都放在简介中**",即这些字段要**落进正文**,
* 而不是只躺在隐藏的 douban_meta JSON 里(那个详情页另有展示,
* 但编辑表单里看不到、复制出去也没有)。
*
* 格式(只有有值的行才出现,绝不写"导演:"这种空壳行):
* 《片名》(年份)
* 导演:X
* 主演:A、B、C
* 类型:剧情 / 家庭
* 片长:125分钟
* 制片国家/地区:中国大陆
* 豆瓣评分:8.6
*
* <剧情简介原文>
*
* ⚠️ 用与详情页 `db-meta` 相同的标签词(年份/类型/片长/导演/主演/制片国家/地区),
* 这样同一个字段在表单与详情页叫同一个名字,用户不会以为是两回事。
*
* @param array $meta fetchDetail() 产出的 meta
* @param string $title 片名(可能来自候选/用户输入)
* @param bool $withPlot 是否附带剧情简介原文(默认带)
* @return string 可直接写进 info 的文本;什么信息都没有时返回 ''
*/
public static function summaryText(array $meta, string $title = '', bool $withPlot = true): string
{
$title = trim($title);
$year = trim((string)($meta['year'] ?? ''));
$lines = [];
// 第一行:片名(年份)
$head = '';
if ($title !== '') {
$head = '《' . $title . '》';
if ($year !== '') {
$head .= '(' . $year . ')';
}
} elseif ($year !== '') {
$head = $year . ' 年';
}
if ($head !== '') {
$lines[] = $head;
}
// 主要信息:顺序按"用户最关心 → 次要",空的不占行
$fields = [
'导演' => (string)($meta['director'] ?? ''),
'主演' => (string)($meta['actors'] ?? ''),
'类型' => (string)($meta['genre'] ?? ''),
'片长' => (string)($meta['runtime'] ?? ''),
'制片国家/地区' => (string)($meta['country'] ?? ''),
'又名' => (string)($meta['alias'] ?? ''),
'豆瓣评分' => (string)($meta['rating'] ?? ''),
];
foreach ($fields as $label => $val) {
$val = trim($val);
if ($val !== '') {
$lines[] = $label . ':' . $val;
}
}
$plot = $withPlot ? trim((string)($meta['plot'] ?? '')) : '';
if ($plot !== '') {
$lines[] = ''; // 空行:把"事实"与"剧情"分开,读起来清爽
$lines[] = $plot;
}
return trim(implode("\n", $lines));
}
// ---------------- 搜索候选整理 ----------------
/** 规范化片名用于匹配:去空白、去常见标点、转小写 */