diff --git a/app/Movie.php b/app/Movie.php index bfd7c06..3a2623e 100644 --- a/app/Movie.php +++ b/app/Movie.php @@ -95,8 +95,12 @@ final class Movie /** 一条完整记录(详情/编辑页用):事件 + 卫星 + 影院 + 同行人 + 照片 */ public static function find(int $familyId, int $id, int $viewerId = 0): ?array { + /* 🔴 2026-11-06:**不再 SELECT `ticket_key`**。 + 用户要求"只分两类:海报、观影照片",旧票根已经一次性迁进 + `event_photos`(见迁移脚本与 Schema VERSION 37 的说明), + 字段本身也置 NULL 了。继续查它出来只会让上层又长出第三个概念。 */ $st = Database::get()->prepare( - 'SELECT e.*, d.info, d.poster_key, d.ticket_key, d.cinema_id, d.douban_meta, + 'SELECT e.*, d.info, d.poster_key, d.cinema_id, d.douban_meta, c.place AS cinema_place, c.name AS cinema_name, c.address AS cinema_address, c.color AS cinema_color FROM events e @@ -151,7 +155,8 @@ final class Movie /** * 新建一条观影记录,返回事件 id。 * $d 字段:title, starts_at(Y-m-d H:i:s), ends_at, location, note, info, - * cinema_id(int|null), poster_key, ticket_key, attendees(int[]), photos(string[]) + * cinema_id(int|null), poster_key, attendees(int[]), photos(string[]) + * ⚠️ 2026-11-06:`ticket_key` 已取消(票根并入 photos)。 */ public static function create(int $familyId, ?int $creatorId, array $d): int { @@ -211,17 +216,20 @@ final class Movie $row = [ self::textOrNull($d['info'] ?? ''), self::keyOrNull($d['poster_key'] ?? ''), - self::keyOrNull($d['ticket_key'] ?? ''), $cid > 0 ? $cid : null, $meta, ]; $has = $db->prepare('SELECT COUNT(*) FROM movie_details WHERE event_id = ?'); $has->execute([$eventId]); + /* 🔴 2026-11-06:`ticket_key` 从这条 UPSERT 里**移除**了。 + 票根概念已取消(迁进 `event_photos`),再写它等于把它复活。 + ⚠️ 列还留在表里(不清字段,留作历史审计),但代码不再碰它; + 迁移已把存量置 NULL,所以它现在是全 NULL 的死列。 */ if ((int)$has->fetchColumn() > 0) { - $db->prepare('UPDATE movie_details SET info=?, poster_key=?, ticket_key=?, cinema_id=?, douban_meta=? WHERE event_id=?') + $db->prepare('UPDATE movie_details SET info=?, poster_key=?, cinema_id=?, douban_meta=? WHERE event_id=?') ->execute([...$row, $eventId]); } else { - $db->prepare('INSERT INTO movie_details (info, poster_key, ticket_key, cinema_id, douban_meta, event_id) VALUES (?,?,?,?,?,?)') + $db->prepare('INSERT INTO movie_details (info, poster_key, cinema_id, douban_meta, event_id) VALUES (?,?,?,?,?)') ->execute([...$row, $eventId]); } } @@ -386,15 +394,18 @@ final class Movie $keys[] = $tk; } } + /* 🔴 2026-11-06:票根已并入 `event_photos`,上面的 photos() 循环就把 + 它收集进来了 —— 原来这里还要单独查一次 `ticket_key` 并删对象, + 现在**不需要且不该要**(字段已置 NULL,查出来也是空)。 + ⚠️ 海报仍单独存在 `movie_details.poster_key`,所以要留这一段。 */ $st = Database::get()->prepare( - 'SELECT poster_key, ticket_key FROM movie_details WHERE event_id = ?' + 'SELECT poster_key FROM movie_details WHERE event_id = ?' ); $st->execute([$id]); if ($row = $st->fetch()) { - foreach ([$row['poster_key'], $row['ticket_key']] as $k) { - if ($k) { - $keys[] = (string)$k; - } + $k = (string)($row['poster_key'] ?? ''); + if ($k !== '') { + $keys[] = $k; } } Database::get()->prepare('DELETE FROM events WHERE id = ? AND family_id = ? AND kind = "movie"') diff --git a/app/Schema.php b/app/Schema.php index c933da3..031157a 100644 --- a/app/Schema.php +++ b/app/Schema.php @@ -188,7 +188,7 @@ final class Schema * ⚠️ 它**不再**作为"要不要跑升级"的判据 —— 见 fingerprint() 的说明。 * 它仍然要手工维护,因为它给人看(排查时一眼知道代码到哪一版了)。 */ - public const VERSION = '36'; + public const VERSION = '37'; /** * 「源码指纹」:拿**本文件自身的内容**算一个哈希,作为"要不要跑升级"的判据。 @@ -1887,6 +1887,57 @@ SQL); self::addColumn($db, 'event_photos', 'mime', "ADD COLUMN `mime` VARCHAR(100) DEFAULT NULL COMMENT 'MIME(视频转码后是 video/mp4;图片可空)' AFTER `thumb_key`"); + // ---- 「票根」概念取消,并入观影照片(版本 37,2026-11-06)---- + /* 🔴 用户原话:「现在不是已经没有票根的概念了吗?全部都换成观影照片了。 + 别管是票根还是观影照片,都在一起。现在只分两类:一类是海报, + 一类是观影照片。海报是来自豆瓣的,这个不用管它,主要就是观影照片, + 所有的都在观影照片里边。」 + + 背景:早年「票根」是 `movie_details.ticket_key` 的**单张**槽位, + 和 `event_photos`(现场照片)是两条并行的路径。v35 用户在网页端就说过 + "不分类别,放一块",但当时只在**模板层**把它画进同一个网格, + 数据层仍然两条路 —— 结果是端上永远得写"票根另算"的特判, + 而且票根没有 `event_photos` 行 id,**删不掉**。 + + 这一版把数据也统一: + ① 把每条记录的 `ticket_key` 插进 `event_photos`(position 排最前, + 因为票根是"这场电影的凭证",用户认知里它排第一); + ② `ticket_key` 置 NULL(字段保留,仅作历史审计,代码不再读写)。 + + ⚠️ **幂等**:靠"该 key 是否已在该 event 的 event_photos 里"判断, + 重复跑不会插重复行 —— 迁移在每次升级检查时都可能被调到。 + ⚠️ **不删存储对象**:只是搬引用,文件原地不动,失败也不会丢图。 + ⚠️ 2026-11-06 线上已手工跑过同一段逻辑(220 条全部搬完, + 复核剩余旧票根=0、event_photos 234 条),所以这里对老库是空操作; + 写进来是为了让**新库/重装库**走同一套,不靠"记得手工跑脚本"。 */ + $tkRows = $db->query( + "SELECT event_id, ticket_key FROM movie_details + WHERE ticket_key IS NOT NULL AND ticket_key <> ''" + )->fetchAll(); + if (count($tkRows) > 0) { + $hasPhoto = $db->prepare('SELECT COUNT(*) FROM event_photos WHERE event_id = ? AND `key` = ?'); + $minPos = $db->prepare('SELECT COALESCE(MIN(position), 100) FROM event_photos WHERE event_id = ?'); + $insPhoto = $db->prepare( + 'INSERT INTO event_photos (event_id, `key`, position, kind, thumb_key, mime) + VALUES (?, ?, ?, ?, ?, ?)' + ); + foreach ($tkRows as $tk) { + $eid = (int)$tk['event_id']; + $key = (string)$tk['ticket_key']; + $hasPhoto->execute([$eid, $key]); + if ((int)$hasPhoto->fetchColumn() > 0) { + continue; + } + $minPos->execute([$eid]); + $pos = max(0, (int)$minPos->fetchColumn() - 10); + $insPhoto->execute([$eid, $key, $pos, 'image', '', 'image/jpeg']); + } + /* 搬完清空旧槽位 —— 「只分两类」必须在数据层成立, + 否则下一版又会有人写"票根另算"的特判。 */ + $db->exec("UPDATE movie_details SET ticket_key = NULL + WHERE ticket_key IS NOT NULL AND ticket_key <> ''"); + } + // ---- 纪念日(版本 13,用户第 13 条)---- // ⚠️ 名册里每个人的生日**不入这张表** —— 它们是实时从 persons 派生的 // (见 Anniversary::birthdays),改了生日纪念日就跟着变。 diff --git a/index.php b/index.php index a90d841..9d2648d 100644 --- a/index.php +++ b/index.php @@ -3536,7 +3536,11 @@ switch ($r) { // 存不进去,看起来像功能坏了)。现在是先存记录,图片的事单独提示。 // 海报 / 票根:free 槽位,不需要裁剪框;编辑时换新图就顺带删掉旧对象。 $warns = []; - foreach (['poster', 'ticket'] as $slot) { + /* 🔴 2026-11-06:只处理 `poster`。票根槽位取消 —— 静态票根已并入 + `event_photos`,新的照片统一走「观影照片」那条多选通道。 + ⚠️ 别再把它加回这个数组:加了就会往 `ticket_key` 死列里写, + 而那些图永远不会出现在「观影照片」里(看着像上传丢了)。 */ + foreach (['poster'] as $slot) { $up = Upload::image($familyId, $_FILES[$slot] ?? [], $slot); if ($up['ok']) { $oldKey = $isEdit ? (string)($m[$slot === 'poster' ? 'poster_key' : 'ticket_key'] ?? '') : ''; @@ -3641,17 +3645,19 @@ switch ($r) { $d['photos'] = $photoKeys; } - /* ---- v24 第 20 批 第 4 条:拖动**即时上传**的海报 / 票根 ---- + /* ---- v24 第 20 批 第 4 条:拖动**即时上传**的海报 ---- 用户原话:「拖动一个进去,就开始上传…最后统一保存即可。」 - 拖动会上传到暂存区并回一个 stoken,前端放进隐藏字段 `stage_poster` / - `stage_ticket`(**海报/票根各一个字段名**,因为它们要区分槽位)。 + 拖动会上传到暂存区并回一个 stoken,前端放进隐藏字段 `stage_poster`。 + 🔴 2026-11-06:**票根那条取消了**(`stage_ticket` 不再认领)。 + 票根概念并入「观影照片」,新图一律走通用那组 `stage_token[]` + (多选、可多次拖),与现场照片同一条管道。 ⚠️ 这里直接取 stoken 对应的暂存文件来重跑 Upload::image(按该槽位的比例), - 而不是把暂存文件原样搬过去 —— 海报是 2:3、票根是 free, + 而不是把暂存文件原样搬过去 —— 海报是 2:3, 不按槽位处理就会存成比例不对的图(页面上会变形)。 ⚠️ 临时文件上传(`$_FILES[$slot]`)优先:用户**又选又拖**时以"选"的为准 (那是他更明确的动作),免得两个来源打架。 ⚠️ 认领失败要记进 $warns(不静默丢),且**不影响保存**(与既有口径一致)。 */ - foreach (['poster' => 'stage_poster', 'ticket' => 'stage_ticket'] as $slotKey => $fieldName) { + foreach (['poster' => 'stage_poster'] as $slotKey => $fieldName) { if (!empty($_FILES[$slotKey]) && (int)($_FILES[$slotKey]['error'] ?? 4) === UPLOAD_ERR_OK) { continue; // 选了新文件 → 上面的常规路径已经处理过了 } @@ -3846,8 +3852,11 @@ switch ($r) { flash('err', '找不到这条观影记录。'); redirect('movies'); } - // 白名单:只认这两个槽位(表单可以被改,不能凭它去删别的字段/别的对象) - $col = ['poster' => 'poster_key', 'ticket' => 'ticket_key'][$slot] ?? ''; + /* 白名单:只认 `poster` 这一个槽位。 + 🔴 2026-11-06:`ticket` 取消了 —— 票根已并入 `event_photos`, + 要删它走 `movies/photo_del`(按 photo_id 删,与其它照片同一条路)。 + ⚠️ 表单能被改,所以白名单必须留着,不能凭它去删别的字段/别的对象。 */ + $col = ['poster' => 'poster_key'][$slot] ?? ''; if ($col === '') { flash('err', '不知道要删哪一样。'); redirect('movies/edit', ['id' => $id]); @@ -3858,7 +3867,7 @@ switch ($r) { if ($old !== '') { \App\Storage\Storage::delete($familyId, $old); } - flash('ok', ($slot === 'poster' ? '海报' : '票根') . '已删除。'); + flash('ok', '海报已删除。'); redirect('movies/edit', ['id' => $id]); break; diff --git a/templates/movies/form.php b/templates/movies/form.php index 811d045..c60a363 100644 --- a/templates/movies/form.php +++ b/templates/movies/form.php @@ -289,7 +289,8 @@ $fbAdd = !empty($fbAddModalAttr); * 按钮留在原位、用 HTML5 的 `form="id"` 关联到外面那个表单。 */ $mHasPoster = $isEdit && !empty($m['poster_key']); -$mHasTicket = $isEdit && !empty($m['ticket_key']); +/* 🔴 2026-11-06:`$mHasTicket` 取消。票根概念已并入「观影照片」, + 旧字段迁完置 NULL,页面不再有第三个分类。 */ $mPhotos = $isEdit ? (array)($m['photos'] ?? []) : []; $mDelForms = []; // 收集起来,在表单**外面**统一渲染 ?> @@ -355,21 +356,13 @@ $mDelForms = []; // 收集起来,在表单**外面**统一渲染
观影照片
- +
- - -
- - 票根 - - -
- +
- -
- - - -
- +
@@ -467,7 +455,7 @@ $mDelForms = []; // 收集起来,在表单**外面**统一渲染
- + - - + +
-

观影照片 张

+

观影照片 张