v24 第 15 批:活动 / 旅程两级语义(术语 · 派生时间城市 · 表单三项 · 题图可点)
用户原话四条,逐条落地:
1. 「旅程模块,按钮是记一次活动,而不是记一趟旅程。旅程是活动里面的概念。」
→ 顶层一律改称**活动**:按钮「记一次活动」、页标题「活动」、`N 次` / `N 段旅程`;
活动详情三组改叫「计划的旅程 / 走过的旅程 / 没成行的」;
节点表单「这段旅程叫什么」「阶段」;flash 文案 17 处同步。
(导航条仍叫「旅程」——用户就是这么称呼这个模块,列表页加副标题说清两级关系。)
2. 「活动不需要开始和结束时间,是根据活动中的旅程,最早的旅程开始时间,和最晚的
旅程结束时间,计算出来的。目的地也不需要…去的城市,也是根据旅程,得到的。」
→ 活动**不再存**这三样,全部**读时算出来**:
· Trip::derived() 最早开始 / 最晚结束 / 城市去重
· Trip::cities() 按最早出现时间排(先后顺序本身是信息,不是字母序)
· Trip::attachDerived() 列表页批量补(避免 N+1)
· Trip::activitySpan() 没有旅程时说「还没有旅程」,不用「日期待补」
create/update 不再写 events 的起止与地点(老值保留不动,页面不读)。
列表排序/搜索、首页「最近去了」改走子查询。
3. 「题图那里,没有办法上传图片,点击哪里都没反应。」——**真 bug**
「点图 = 开文件框」那句 JS 只写在 people/form.php 的内页脚本里(头像专用);
第 14 批加的旅程题图与图书封面照抄了同一套 DOM 却没有对应的 JS,
于是图块有 cursor:pointer 与 hover 高亮(看着能点),点下去毫无反应。
→ 做成声明式通用绑定:图上写 data-pick-input="#fileInputId",
app.js 统一接管(一个页面多个也各绑各的),并删掉旧的那份专用实现。
三处(头像 / 题图 / 图书封面)全挂上。
4. 「添加活动的页面,顺序是这样的:添加标题,上传并裁剪题图,然后一段简介,就可以了。」
→ 主区就这三项,顺序照原话;时间/城市不做输入项(只留一句说明它们从哪来,
不说清用户会满页找"怎么没地方填时间")。
备注 / 公开 / 同行的人收进「更多设置」折叠区——功能一个没丢。
顺带修掉两处会炸的实现问题:
· ORDER BY 引用聚合别名(first_at)在 MariaDB/MySQL 报 1247,且只在运行时炸、
php -l 看不出 → 改成 ORDER BY MIN(starts_at),否则整个旅程模块 Fatal。
· e2e 里"详情页显示了派生时间"原本写成整页查找,下面每段旅程自己也渲染日期,
于是把派生值塞成 null 也照样绿(借光假绿)→ 判据收进页头那一段。
验证:e2e 845/0(+15 条本批断言)· 升级 134/0 · 桌面几何 428/0 · 移动几何 688/0 ·
交互 92/0 · 题图真机点击 4/0 · 读书窄屏 15/0 · 家谱 34/0 + 10/0 + 反向 3/3 ·
观影按钮 60/0 + 反向 3/3 · 本批反向验证 4/4(题图绑定抽掉→红、派生值不挂→红、
城市退回读 location→红、时间字段加回表单→红)· 部署静态断言新增两组(含"活动不许
再写起止时间/目的地"与"ORDER BY 不许引用聚合别名"的反退化防线)。
部署后服务器自检 19/19,五个关键文件 md5 与线上逐一核对一致。
This commit is contained in:
+85
-1
@@ -80,7 +80,7 @@ final class Schema
|
||||
* ⚠️ 它**不再**作为"要不要跑升级"的判据 —— 见 fingerprint() 的说明。
|
||||
* 它仍然要手工维护,因为它给人看(排查时一眼知道代码到哪一版了)。
|
||||
*/
|
||||
public const VERSION = '19';
|
||||
public const VERSION = '20';
|
||||
|
||||
/**
|
||||
* 「源码指纹」:拿**本文件自身的内容**算一个哈希,作为"要不要跑升级"的判据。
|
||||
@@ -646,6 +646,90 @@ CREATE TABLE IF NOT EXISTS `regions` (
|
||||
PRIMARY KEY (`code`),
|
||||
KEY `idx_region_parent` (`parent_code`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='全国行政区划(省/市/区县三级):地址结构化与导入时对齐用'
|
||||
SQL,
|
||||
|
||||
// ---- 读书(版本 20,用户 2026-10-05 第 14 批第 5 条)----
|
||||
// 像 calibre 那样管自己的书:书架 + 上传/导入 + 在线读 + 读完 + 笔记 + 统计。
|
||||
//
|
||||
// 三张表,各管一件事,**不要**合并:
|
||||
// · books —— 书本身(元数据 + 文件 + 读到哪 + 读完没读完)
|
||||
// · book_chapters—— 解析出来的正文(一本书几十上百段,单独一张表,
|
||||
// 否则列表页 SELECT * 会把整本书一起捞出来)
|
||||
// · book_notes —— 笔记 / 划线 / 感想,**按人**记(同一个家里两个人读
|
||||
// 同一本书,各自的笔记不能混)
|
||||
<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS `books` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`family_id` INT UNSIGNED NOT NULL,
|
||||
`owner_id` INT UNSIGNED DEFAULT NULL COMMENT '谁在读(NULL = 全家共用一本)',
|
||||
`creator_id` INT UNSIGNED DEFAULT NULL COMMENT '谁把它加进书架的',
|
||||
`title` VARCHAR(300) NOT NULL,
|
||||
`subtitle` VARCHAR(300) DEFAULT NULL,
|
||||
`author` VARCHAR(200) DEFAULT NULL,
|
||||
`translator` VARCHAR(200) DEFAULT NULL,
|
||||
`publisher` VARCHAR(120) DEFAULT NULL,
|
||||
`published` VARCHAR(40) DEFAULT NULL COMMENT '出版时间(自由文本:2012 / 2012-05 / 2012年5月)',
|
||||
`isbn` VARCHAR(40) DEFAULT NULL,
|
||||
`language` VARCHAR(40) DEFAULT NULL,
|
||||
`category` VARCHAR(80) DEFAULT NULL COMMENT '分类(自由文本,家庭自己约定叫法)',
|
||||
`tags` VARCHAR(300) DEFAULT NULL COMMENT '标签,逗号分隔',
|
||||
`summary` TEXT DEFAULT NULL COMMENT '内容简介',
|
||||
`cover_key` VARCHAR(255) DEFAULT NULL COMMENT '封面(走 media.php 代理,与头像同一套)',
|
||||
`file_key` VARCHAR(255) DEFAULT NULL COMMENT '原书文件(books 目录下的相对键,如 books/xxx.epub)',
|
||||
`file_name` VARCHAR(255) DEFAULT NULL COMMENT '上传时的文件名(下载时用)',
|
||||
`file_format` VARCHAR(20) DEFAULT NULL COMMENT 'epub / txt / pdf / mobi / azw3 …',
|
||||
`file_size` INT UNSIGNED DEFAULT NULL,
|
||||
`pages` INT UNSIGNED DEFAULT NULL COMMENT '页数(有就填,用于统计)',
|
||||
`words` INT UNSIGNED DEFAULT NULL COMMENT '字数(解析时估算)',
|
||||
`chapter_count` INT NOT NULL DEFAULT 0 COMMENT '解析出多少章(0 = 没解析过)',
|
||||
`status` ENUM('wish','reading','done','paused') NOT NULL DEFAULT 'wish'
|
||||
COMMENT '想读 / 在读 / 读完 / 搁置',
|
||||
`rating` TINYINT DEFAULT NULL COMMENT '评分 0-10(NULL = 还没打分)',
|
||||
`progress` INT NOT NULL DEFAULT 0 COMMENT '读到第几章(0 = 还没开始)',
|
||||
`started_at` DATE DEFAULT NULL,
|
||||
`finished_at` DATE DEFAULT NULL,
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_book_family` (`family_id`, `status`, `updated_at`),
|
||||
KEY `idx_book_owner` (`family_id`, `owner_id`),
|
||||
CONSTRAINT `fk_book_family` FOREIGN KEY (`family_id`) REFERENCES `families` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_book_owner` FOREIGN KEY (`owner_id`) REFERENCES `persons` (`id`) ON DELETE SET NULL,
|
||||
CONSTRAINT `fk_book_creator` FOREIGN KEY (`creator_id`) REFERENCES `persons` (`id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='书架:一本书一行(元数据 + 文件 + 读到哪 + 读完没读完)'
|
||||
SQL,
|
||||
<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS `book_chapters` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`book_id` INT UNSIGNED NOT NULL,
|
||||
`seq` INT NOT NULL DEFAULT 1 COMMENT '第几章(从 1 开始)',
|
||||
`title` VARCHAR(300) DEFAULT NULL,
|
||||
`content` MEDIUMTEXT DEFAULT NULL COMMENT '正文(已去标签的纯文本,段落之间换行)',
|
||||
`words` INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_bc_book` (`book_id`, `seq`),
|
||||
CONSTRAINT `fk_bc_book` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='书的正文(解析出来的章节)'
|
||||
SQL,
|
||||
<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS `book_notes` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`book_id` INT UNSIGNED NOT NULL,
|
||||
`person_id` INT UNSIGNED DEFAULT NULL COMMENT '谁的笔记(导入时可能不知道是谁 → NULL)',
|
||||
`kind` ENUM('quote','note','review') NOT NULL DEFAULT 'note'
|
||||
COMMENT 'quote=划线/原文摘录,note=想法/笔记,review=书评',
|
||||
`chapter` VARCHAR(200) DEFAULT NULL COMMENT '在哪一章(导入时带得上的话)',
|
||||
`loc` VARCHAR(80) DEFAULT NULL COMMENT '位置(页数 / 百分比 / 微信读书的定位串)',
|
||||
`content` TEXT NOT NULL,
|
||||
`source` VARCHAR(40) NOT NULL DEFAULT 'manual' COMMENT 'manual / weread / douban / …(哪来的)',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_bn_book` (`book_id`, `created_at`),
|
||||
KEY `idx_bn_person` (`person_id`),
|
||||
CONSTRAINT `fk_bn_book` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_bn_person` FOREIGN KEY (`person_id`) REFERENCES `persons` (`id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='读书笔记 / 划线 / 书评(按人记)'
|
||||
SQL,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user