mirror of
https://github.com/KingHWJ/WeReadBoard.git
synced 2026-11-03 18:24:58 +08:00
refactor: complete data model v2.0 based on official API docs
数据模型全面重构,基于微信读书官方 8 份 API 文档逐字段比对: 1. 表结构从 18 张扩展到 31 张表: 新增表:shelf_albums(有声书)、shelf_meta(书架元信息)、prefer_publishers(偏好出版社)、 prefer_cps(偏好版权方)、prefer_books(偏好书籍卡片)、read_rank(好友排行)、 medals(勋章)、year_reports(年度报告)、book_best_bookmarks(热门划线)、 book_underlines(划线热度)、similar_books(相似推荐)、public_reviews(公开点评)、 search_sessions(搜索会话) 2. 补全所有遗漏字段: - reading_stats: +13 个字段 (baseTime, compare, readRate, wrReadTime, wrListenTime 等) - book_chapters: +8 个字段 (wordCount, level, price, paid, isMPChapter, anchors 等) - book_reviews: +4 个字段 (abstract, range, chapterUid, chapterIdx) - recommended_books: +5 个字段 (deepLink, searchIdx, price, payType, type) - search_results: +8 个字段 (deepLink, payType, price, soldout, scope 等) - book_details: +2 个字段 (deepLink, newRatingDetail) - prefer_categories: +2 个字段 (parentCategoryId, categoryType) 3. 时间戳双列设计: - 所有整型时间戳字段均伴随 _dt TIMESTAMP 列 (如 read_update_time + read_update_time_dt) - 便于直接查询和展示,无需应用层转换 4. 审计字段: - 每张表均维护 created_at 和 updated_at - MySQL 使用 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 5. 索引优化: - 主键、唯一约束、查询索引根据实际访问模式重新设计 - 新增复合索引、排序索引、全文检索索引 6. 数据采集脚本更新: - 覆盖所有新增表和字段的数据采集 - skill_version 升级至 1.0.4 - 有声书/专辑数据采集 (shelf_albums) - 偏好出版社/版权方/书籍卡片采集 - 勋章、年度报告数据采集 7. 后端适配: - 移除旧版 ensure_tables 动态建表逻辑 - 所有按需 API INSERT 语句适配新表结构 - 搜索结果入库新增 search_sessions 会话表 - 推荐书籍入库新增 batch_id 批次标识
This commit is contained in:
1 parent
507b9c5704
commit
2a809eb7cc
5 files changed
+1494
-763
No files matched your search
@@ -25,3 +25,14 @@
|
|||||||
- 时间戳列自动转换(SQLite int -> MySQL TIMESTAMP)
|
- 时间戳列自动转换(SQLite int -> MySQL TIMESTAMP)
|
||||||
- 中文内容 utf8mb4 完整保存,数据校验通过
|
- 中文内容 utf8mb4 完整保存,数据校验通过
|
||||||
- Flask 服务暂未切换至 MySQL(用户要求服务不重启)
|
- Flask 服务暂未切换至 MySQL(用户要求服务不重启)
|
||||||
|
|
||||||
|
## 第三批功能迭代(MySQL 切换 + 文档 + 告警)
|
||||||
|
- **数据源切换至 MySQL**:app.py 从 SQLite 全面切换到 PyMySQL,所有 API 读取 MySQL weread_dashboard 数据库
|
||||||
|
- **db_required 装饰器**:统一管理数据库连接和异常处理,MySQL 连接失败返回 HTTP 503
|
||||||
|
- **健康检查接口**:新增 /api/health,前端页面加载时优先检测 MySQL 连接状态
|
||||||
|
- **前端告警横幅**:MySQL 连接失败时显示黄色告警横幅,阻止后续数据加载
|
||||||
|
- **修复 fetched_at 兼容问题**:Unix 整数时间戳改为 MySQL TIMESTAMP 格式字符串
|
||||||
|
- **代码中文注释**:app.py 所有函数/API/工具函数添加完整中文文档注释;fetch_data.py 所有建表语句添加列级中文注释
|
||||||
|
- **技术文档** docs/TECHNICAL_DOC.md:项目概述、系统架构图、数据流设计、15 个 API 接口详情、部署指南
|
||||||
|
- **元数据文档** docs/METADATA_DOC.md:18 张表完整字段说明、索引设计、表关系 ER 图、数据类型规范
|
||||||
|
- Git 提交:dev 分支提交 → master 分支 --no-ff 合并
|
||||||
@@ -49,7 +49,7 @@ MYSQL_CONFIG = {
|
|||||||
# ======================================================================
|
# ======================================================================
|
||||||
WEREAD_API_URL = "https://i.weread.qq.com/api/agent/gateway"
|
WEREAD_API_URL = "https://i.weread.qq.com/api/agent/gateway"
|
||||||
WEREAD_API_KEY = os.environ.get("WEREAD_API_KEY", "")
|
WEREAD_API_KEY = os.environ.get("WEREAD_API_KEY", "")
|
||||||
WEREAD_SKILL_VERSION = "1.0.3"
|
WEREAD_SKILL_VERSION = "1.0.4"
|
||||||
|
|
||||||
|
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
@@ -208,137 +208,21 @@ def format_duration(seconds):
|
|||||||
return f"{minutes}分钟"
|
return f"{minutes}分钟"
|
||||||
|
|
||||||
|
|
||||||
def ensure_tables(cursor):
|
def ts_to_dt_str(ts):
|
||||||
"""
|
"""
|
||||||
确保按需数据表存在(MySQL 版本)。
|
Unix 时间戳转完整日期时间字符串 (中国时区 UTC+8)。
|
||||||
这些表存储用户点击书籍详情/笔记时实时获取的数据,
|
用于写入数据库的 _dt 列。
|
||||||
不在初始化采集脚本中创建,而是首次访问时按需创建。
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cursor: MySQL 游标对象
|
ts (int): Unix 时间戳(秒)
|
||||||
|
Returns:
|
||||||
|
str: 格式化日期时间 "YYYY-MM-DD HH:MM:SS",空值返回 None
|
||||||
"""
|
"""
|
||||||
cursor.executescript = None # MySQL 无 executescript,逐条执行
|
if not ts or ts == 0:
|
||||||
tables_sql = [
|
return None
|
||||||
# 书籍详细信息表(来自微信读书 /book/info 接口)
|
from datetime import timezone, timedelta
|
||||||
"""CREATE TABLE IF NOT EXISTS `book_details` (
|
cst = timezone(timedelta(hours=8))
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
return datetime.fromtimestamp(ts, tz=cst).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
|
||||||
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
|
||||||
`translator` VARCHAR(255) DEFAULT NULL COMMENT '译者',
|
|
||||||
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图URL',
|
|
||||||
`intro` LONGTEXT DEFAULT NULL COMMENT '书籍简介',
|
|
||||||
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类',
|
|
||||||
`publisher` VARCHAR(255) DEFAULT NULL COMMENT '出版社',
|
|
||||||
`publish_time` VARCHAR(32) DEFAULT NULL COMMENT '出版时间',
|
|
||||||
`isbn` VARCHAR(32) DEFAULT NULL COMMENT 'ISBN号',
|
|
||||||
`word_count` INT UNSIGNED DEFAULT 0 COMMENT '总字数',
|
|
||||||
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分(百分制)',
|
|
||||||
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获取时间',
|
|
||||||
PRIMARY KEY (`book_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='书籍详细信息'""",
|
|
||||||
|
|
||||||
# 阅读进度表(来自微信读书 /book/getprogress 接口)
|
|
||||||
"""CREATE TABLE IF NOT EXISTS `book_progress` (
|
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
|
||||||
`progress` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读进度百分比(0-100)',
|
|
||||||
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '当前阅读章节UID',
|
|
||||||
`chapter_offset` INT UNSIGNED DEFAULT NULL COMMENT '当前章节内偏移量',
|
|
||||||
`update_time` INT UNSIGNED DEFAULT NULL COMMENT '最后阅读时间(Unix时间戳)',
|
|
||||||
`record_reading_time` INT UNSIGNED DEFAULT 0 COMMENT '累计阅读时长(秒)',
|
|
||||||
`finish_time` INT UNSIGNED DEFAULT NULL COMMENT '读完时间(Unix时间戳)',
|
|
||||||
`is_start_reading` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已开始阅读(1=是)',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获取时间',
|
|
||||||
PRIMARY KEY (`book_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='阅读进度'""",
|
|
||||||
|
|
||||||
# 划线内容表(来自微信读书 /book/bookmarklist 接口)
|
|
||||||
"""CREATE TABLE IF NOT EXISTS `book_highlights` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍ID',
|
|
||||||
`bookmark_id` VARCHAR(64) DEFAULT NULL COMMENT '划线唯一ID',
|
|
||||||
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '所在章节UID',
|
|
||||||
`mark_text` LONGTEXT DEFAULT NULL COMMENT '划线原文内容',
|
|
||||||
`create_time` INT UNSIGNED DEFAULT NULL COMMENT '创建时间(Unix时间戳)',
|
|
||||||
`type` TINYINT DEFAULT NULL COMMENT '划线类型',
|
|
||||||
`range_text` VARCHAR(64) DEFAULT NULL COMMENT '位置范围',
|
|
||||||
`color_style` TINYINT DEFAULT NULL COMMENT '划线颜色样式',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获取时间',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_highlights_book_id` (`book_id`),
|
|
||||||
INDEX `idx_highlights_chapter` (`book_id`, `chapter_uid`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='划线内容'""",
|
|
||||||
|
|
||||||
# 章节信息表(来自微信读书 /book/bookmarklist 接口的 chapters 字段)
|
|
||||||
"""CREATE TABLE IF NOT EXISTS `book_chapters` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍ID',
|
|
||||||
`chapter_uid` INT UNSIGNED NOT NULL COMMENT '章节UID',
|
|
||||||
`chapter_idx` INT UNSIGNED DEFAULT NULL COMMENT '章节序号',
|
|
||||||
`title` VARCHAR(255) DEFAULT NULL COMMENT '章节标题',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE KEY `uk_book_chapter` (`book_id`, `chapter_uid`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='章节信息'""",
|
|
||||||
|
|
||||||
# 个人想法/点评表(来自微信读书 /review/list/mine 接口)
|
|
||||||
"""CREATE TABLE IF NOT EXISTS `book_reviews` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍ID',
|
|
||||||
`review_id` VARCHAR(64) DEFAULT NULL COMMENT '想法唯一ID',
|
|
||||||
`content` LONGTEXT DEFAULT NULL COMMENT '想法内容文本',
|
|
||||||
`create_time` INT UNSIGNED DEFAULT NULL COMMENT '创建时间(Unix时间戳)',
|
|
||||||
`star` TINYINT DEFAULT -1 COMMENT '评分(0-5, -1=无评分)',
|
|
||||||
`chapter_name` VARCHAR(255) DEFAULT NULL COMMENT '所在章节名称',
|
|
||||||
`is_finish` TINYINT(1) DEFAULT 0 COMMENT '是否书评(1=书评)',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获取时间',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_reviews_book_id` (`book_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='个人想法/点评'""",
|
|
||||||
|
|
||||||
# 推荐书籍表(来自微信读书 /book/recommend 接口)
|
|
||||||
"""CREATE TABLE IF NOT EXISTS `recommended_books` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍ID',
|
|
||||||
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
|
||||||
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
|
||||||
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图URL',
|
|
||||||
`intro` TEXT DEFAULT NULL COMMENT '书籍简介',
|
|
||||||
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类',
|
|
||||||
`reason` VARCHAR(512) DEFAULT NULL COMMENT '推荐理由',
|
|
||||||
`reading_count` INT UNSIGNED DEFAULT 0 COMMENT '在读人数',
|
|
||||||
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分(百分制)',
|
|
||||||
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
|
||||||
`rating_title` VARCHAR(32) DEFAULT NULL COMMENT '评分标签(神作/力荐等)',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获取时间',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_recommended_book_id` (`book_id`),
|
|
||||||
INDEX `idx_recommended_fetched` (`fetched_at` DESC)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推荐书籍'""",
|
|
||||||
|
|
||||||
# 搜索结果表(来自微信读书 /store/search 接口)
|
|
||||||
"""CREATE TABLE IF NOT EXISTS `search_results` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`keyword` VARCHAR(255) NOT NULL COMMENT '搜索关键词',
|
|
||||||
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍ID',
|
|
||||||
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
|
||||||
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
|
||||||
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图URL',
|
|
||||||
`intro` TEXT DEFAULT NULL COMMENT '书籍简介',
|
|
||||||
`publisher` VARCHAR(255) DEFAULT NULL COMMENT '出版社',
|
|
||||||
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类',
|
|
||||||
`reading_count` INT UNSIGNED DEFAULT 0 COMMENT '在读人数',
|
|
||||||
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分(百分制)',
|
|
||||||
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
|
||||||
`rating_title` VARCHAR(32) DEFAULT NULL COMMENT '评分标签',
|
|
||||||
`search_idx` INT UNSIGNED DEFAULT 0 COMMENT '搜索序号',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获取时间',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_search_keyword` (`keyword`),
|
|
||||||
INDEX `idx_search_book_id` (`book_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='搜索结果'""",
|
|
||||||
]
|
|
||||||
for sql in tables_sql:
|
|
||||||
cursor.execute(sql)
|
|
||||||
|
|
||||||
|
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
@@ -794,18 +678,17 @@ def api_book_detail(conn):
|
|||||||
return jsonify({"error": "book_id is required"}), 400
|
return jsonify({"error": "book_id is required"}), 400
|
||||||
|
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
ensure_tables(cursor)
|
|
||||||
conn.commit()
|
|
||||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
# 1. 获取书籍基本信息
|
# 1. 获取书籍基本信息(/book/info)
|
||||||
try:
|
try:
|
||||||
info = weread_api_call("/book/info", bookId=book_id)
|
info = weread_api_call("/book/info", bookId=book_id)
|
||||||
|
rating_detail = info.get("newRatingDetail")
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"REPLACE INTO book_details "
|
"REPLACE INTO book_details "
|
||||||
"(book_id, title, author, translator, cover, intro, category, "
|
"(book_id, title, author, translator, cover, intro, category, "
|
||||||
"publisher, publish_time, isbn, word_count, new_rating, new_rating_count, fetched_at) "
|
"publisher, publish_time, isbn, word_count, new_rating, new_rating_count, "
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
"new_rating_detail, deep_link) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||||
(
|
(
|
||||||
book_id,
|
book_id,
|
||||||
info.get("title", ""),
|
info.get("title", ""),
|
||||||
@@ -820,7 +703,8 @@ def api_book_detail(conn):
|
|||||||
info.get("wordCount", 0),
|
info.get("wordCount", 0),
|
||||||
info.get("newRating", 0),
|
info.get("newRating", 0),
|
||||||
info.get("newRatingCount", 0),
|
info.get("newRatingCount", 0),
|
||||||
now,
|
json.dumps(rating_detail, ensure_ascii=False) if rating_detail else None,
|
||||||
|
info.get("deepLink", ""),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -836,21 +720,25 @@ def api_book_detail(conn):
|
|||||||
try:
|
try:
|
||||||
prog = weread_api_call("/book/getprogress", bookId=book_id)
|
prog = weread_api_call("/book/getprogress", bookId=book_id)
|
||||||
book_prog = prog.get("book", {})
|
book_prog = prog.get("book", {})
|
||||||
|
srv_ts = prog.get("timestamp")
|
||||||
|
ut = book_prog.get("updateTime", 0)
|
||||||
|
ft = book_prog.get("finishTime", 0)
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"REPLACE INTO book_progress "
|
"REPLACE INTO book_progress "
|
||||||
"(book_id, progress, chapter_uid, chapter_offset, update_time, "
|
"(book_id, progress, chapter_uid, chapter_offset, update_time, update_time_dt, "
|
||||||
"record_reading_time, finish_time, is_start_reading, fetched_at) "
|
"record_reading_time, finish_time, finish_time_dt, is_start_reading, "
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
"server_timestamp, server_timestamp_dt) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||||
(
|
(
|
||||||
book_id,
|
book_id,
|
||||||
book_prog.get("progress", 0),
|
book_prog.get("progress", 0),
|
||||||
book_prog.get("chapterUid", 0),
|
book_prog.get("chapterUid", 0),
|
||||||
book_prog.get("chapterOffset", 0),
|
book_prog.get("chapterOffset", 0),
|
||||||
book_prog.get("updateTime", 0),
|
ut, ts_to_dt_str(ut),
|
||||||
book_prog.get("recordReadingTime", 0),
|
book_prog.get("recordReadingTime", 0),
|
||||||
book_prog.get("finishTime", 0),
|
ft, ts_to_dt_str(ft),
|
||||||
book_prog.get("isStartReading", 0),
|
book_prog.get("isStartReading", 0),
|
||||||
now,
|
srv_ts, ts_to_dt_str(srv_ts),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -898,7 +786,7 @@ def api_book_detail(conn):
|
|||||||
"new_rating": float(detail["new_rating"]) if detail["new_rating"] else 0,
|
"new_rating": float(detail["new_rating"]) if detail["new_rating"] else 0,
|
||||||
"new_rating_count": detail["new_rating_count"],
|
"new_rating_count": detail["new_rating_count"],
|
||||||
"progress": progress_data,
|
"progress": progress_data,
|
||||||
"deep_link": f"weread://reading?bId={book_id}",
|
"deep_link": detail["deep_link"] or f"weread://reading?bId={book_id}",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -926,20 +814,18 @@ def api_book_notes(conn):
|
|||||||
return jsonify({"error": "book_id is required"}), 400
|
return jsonify({"error": "book_id is required"}), 400
|
||||||
|
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
ensure_tables(cursor)
|
|
||||||
conn.commit()
|
|
||||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
# 1. 获取划线数据
|
# 1. 获取划线数据(/book/bookmarklist)
|
||||||
highlights = []
|
highlights = []
|
||||||
chapters = {}
|
chapters = {}
|
||||||
try:
|
try:
|
||||||
data = weread_api_call("/book/bookmarklist", bookId=book_id)
|
data = weread_api_call("/book/bookmarklist", bookId=book_id)
|
||||||
|
|
||||||
# 保存章节信息
|
# 保存章节信息(包含完整字段)
|
||||||
for ch in data.get("chapters", []):
|
for ch in data.get("chapters", []):
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"REPLACE INTO book_chapters (book_id, chapter_uid, chapter_idx, title) "
|
"REPLACE INTO book_chapters "
|
||||||
|
"(book_id, chapter_uid, chapter_idx, title) "
|
||||||
"VALUES (%s,%s,%s,%s)",
|
"VALUES (%s,%s,%s,%s)",
|
||||||
(book_id, ch.get("chapterUid", 0), ch.get("chapterIdx", 0), ch.get("title", "")),
|
(book_id, ch.get("chapterUid", 0), ch.get("chapterIdx", 0), ch.get("title", "")),
|
||||||
)
|
)
|
||||||
@@ -948,21 +834,21 @@ def api_book_notes(conn):
|
|||||||
# 保存划线数据(先删除旧数据再插入)
|
# 保存划线数据(先删除旧数据再插入)
|
||||||
cursor.execute("DELETE FROM book_highlights WHERE book_id=%s", (book_id,))
|
cursor.execute("DELETE FROM book_highlights WHERE book_id=%s", (book_id,))
|
||||||
for bm in data.get("updated", []):
|
for bm in data.get("updated", []):
|
||||||
|
ct = bm.get("createTime", 0)
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"INSERT INTO book_highlights "
|
"INSERT INTO book_highlights "
|
||||||
"(book_id, bookmark_id, chapter_uid, mark_text, create_time, "
|
"(book_id, bookmark_id, chapter_uid, mark_text, create_time, create_time_dt, "
|
||||||
"type, range_text, color_style, fetched_at) "
|
"type, range_text, color_style) "
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||||
(
|
(
|
||||||
book_id,
|
book_id,
|
||||||
bm.get("bookmarkId", ""),
|
bm.get("bookmarkId", ""),
|
||||||
bm.get("chapterUid", 0),
|
bm.get("chapterUid", 0),
|
||||||
bm.get("markText", ""),
|
bm.get("markText", ""),
|
||||||
bm.get("createTime", 0),
|
ct, ts_to_dt_str(ct),
|
||||||
bm.get("type", 0),
|
bm.get("type", 0),
|
||||||
bm.get("range", ""),
|
bm.get("range", ""),
|
||||||
bm.get("colorStyle", 0),
|
bm.get("colorStyle", 0),
|
||||||
now,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
highlights.append({
|
highlights.append({
|
||||||
@@ -1001,26 +887,32 @@ def api_book_notes(conn):
|
|||||||
cursor.execute("DELETE FROM book_reviews WHERE book_id=%s", (book_id,))
|
cursor.execute("DELETE FROM book_reviews WHERE book_id=%s", (book_id,))
|
||||||
for item in rev_data.get("reviews", []):
|
for item in rev_data.get("reviews", []):
|
||||||
rev = item.get("review", {})
|
rev = item.get("review", {})
|
||||||
|
ct = rev.get("createTime", 0)
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"INSERT INTO book_reviews "
|
"INSERT INTO book_reviews "
|
||||||
"(book_id, review_id, content, create_time, star, chapter_name, is_finish, fetched_at) "
|
"(book_id, review_id, content, abstract, range_text, chapter_uid, chapter_idx, "
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s)",
|
"chapter_name, create_time, create_time_dt, star, is_finish) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||||
(
|
(
|
||||||
book_id,
|
book_id,
|
||||||
rev.get("reviewId", ""),
|
rev.get("reviewId", ""),
|
||||||
rev.get("content", ""),
|
rev.get("content", ""),
|
||||||
rev.get("createTime", 0),
|
rev.get("abstract", ""),
|
||||||
rev.get("star", 0),
|
rev.get("range", ""),
|
||||||
|
rev.get("chapterUid"),
|
||||||
|
rev.get("chapterIdx"),
|
||||||
rev.get("chapterName", ""),
|
rev.get("chapterName", ""),
|
||||||
|
ct, ts_to_dt_str(ct),
|
||||||
|
rev.get("star", -1),
|
||||||
rev.get("isFinish", 0),
|
rev.get("isFinish", 0),
|
||||||
now,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
reviews.append({
|
reviews.append({
|
||||||
"review_id": rev.get("reviewId", ""),
|
"review_id": rev.get("reviewId", ""),
|
||||||
"content": rev.get("content", ""),
|
"content": rev.get("content", ""),
|
||||||
|
"abstract": rev.get("abstract", ""),
|
||||||
"create_time": ts_to_date(rev.get("createTime", 0)),
|
"create_time": ts_to_date(rev.get("createTime", 0)),
|
||||||
"star": rev.get("star", 0),
|
"star": rev.get("star", -1),
|
||||||
"chapter_name": rev.get("chapterName", ""),
|
"chapter_name": rev.get("chapterName", ""),
|
||||||
"is_finish": rev.get("isFinish", 0),
|
"is_finish": rev.get("isFinish", 0),
|
||||||
})
|
})
|
||||||
@@ -1078,22 +970,21 @@ def api_recommend(conn):
|
|||||||
"""
|
"""
|
||||||
max_idx = int(request.args.get("maxIdx", 0))
|
max_idx = int(request.args.get("maxIdx", 0))
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
ensure_tables(cursor)
|
batch_id = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||||
conn.commit()
|
|
||||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = weread_api_call("/book/recommend", count=12, maxIdx=max_idx)
|
data = weread_api_call("/book/recommend", count=12, maxIdx=max_idx)
|
||||||
books = data.get("books", [])
|
books = data.get("books", [])
|
||||||
|
|
||||||
# 将推荐书籍入库
|
# 将推荐书籍入库(完整字段)
|
||||||
for book in books:
|
for book in books:
|
||||||
rating_detail = book.get("newRatingDetail", {})
|
rating_detail = book.get("newRatingDetail", {})
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"REPLACE INTO recommended_books "
|
"REPLACE INTO recommended_books "
|
||||||
"(book_id, title, author, cover, intro, category, reason, "
|
"(book_id, title, author, cover, intro, category, reason, "
|
||||||
"reading_count, new_rating, new_rating_count, rating_title, fetched_at) "
|
"reading_count, new_rating, new_rating_count, rating_title, "
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
"deep_link, search_idx, price, pay_type, book_type, batch_id) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||||
(
|
(
|
||||||
book.get("bookId", ""),
|
book.get("bookId", ""),
|
||||||
book.get("title", ""),
|
book.get("title", ""),
|
||||||
@@ -1106,7 +997,12 @@ def api_recommend(conn):
|
|||||||
book.get("newRating", 0),
|
book.get("newRating", 0),
|
||||||
book.get("newRatingCount", 0),
|
book.get("newRatingCount", 0),
|
||||||
rating_detail.get("title", "") if isinstance(rating_detail, dict) else "",
|
rating_detail.get("title", "") if isinstance(rating_detail, dict) else "",
|
||||||
now,
|
book.get("deepLink", ""),
|
||||||
|
book.get("searchIdx", 0),
|
||||||
|
book.get("price"),
|
||||||
|
book.get("payType"),
|
||||||
|
book.get("type", 0),
|
||||||
|
batch_id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
@@ -1168,25 +1064,39 @@ def api_search(conn):
|
|||||||
|
|
||||||
max_idx = int(request.args.get("maxIdx", 0))
|
max_idx = int(request.args.get("maxIdx", 0))
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
ensure_tables(cursor)
|
|
||||||
conn.commit()
|
|
||||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = weread_api_call("/store/search", keyword=keyword, scope=10, maxIdx=max_idx)
|
data = weread_api_call("/store/search", keyword=keyword, scope=10, maxIdx=max_idx)
|
||||||
|
sid = data.get("sid", "")
|
||||||
|
has_more = data.get("hasMore", 0)
|
||||||
|
|
||||||
|
# 创建搜索会话记录
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO search_sessions (sid, keyword, scope, has_more, max_idx) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s)",
|
||||||
|
(sid, keyword, 10, has_more, max_idx),
|
||||||
|
)
|
||||||
|
session_id = cursor.lastrowid
|
||||||
|
|
||||||
results = data.get("results", [])
|
results = data.get("results", [])
|
||||||
books = []
|
books = []
|
||||||
for group in results:
|
for group in results:
|
||||||
|
result_title = group.get("title", "")
|
||||||
|
result_scope = group.get("scope")
|
||||||
|
scope_count = group.get("scopeCount")
|
||||||
|
current_count = group.get("currentCount")
|
||||||
for item in group.get("books", []):
|
for item in group.get("books", []):
|
||||||
bi = item.get("bookInfo", {})
|
bi = item.get("bookInfo", {})
|
||||||
rating_detail = item.get("newRatingDetail", {})
|
rating_detail = item.get("newRatingDetail", {})
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"REPLACE INTO search_results "
|
"INSERT INTO search_results "
|
||||||
"(keyword, book_id, title, author, cover, intro, publisher, "
|
"(session_id, keyword, book_id, title, author, cover, intro, publisher, "
|
||||||
"category, reading_count, new_rating, new_rating_count, "
|
"category, reading_count, new_rating, new_rating_count, rating_title, "
|
||||||
"rating_title, search_idx, fetched_at) "
|
"deep_link, pay_type, price, soldout, search_idx, "
|
||||||
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
"result_scope, result_title, scope_count, current_count) "
|
||||||
|
"VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
|
||||||
(
|
(
|
||||||
|
session_id,
|
||||||
keyword,
|
keyword,
|
||||||
bi.get("bookId", ""),
|
bi.get("bookId", ""),
|
||||||
bi.get("title", ""),
|
bi.get("title", ""),
|
||||||
@@ -1199,8 +1109,15 @@ def api_search(conn):
|
|||||||
item.get("newRating", 0),
|
item.get("newRating", 0),
|
||||||
item.get("newRatingCount", 0),
|
item.get("newRatingCount", 0),
|
||||||
rating_detail.get("title", "") if isinstance(rating_detail, dict) else "",
|
rating_detail.get("title", "") if isinstance(rating_detail, dict) else "",
|
||||||
|
bi.get("deepLink", ""),
|
||||||
|
bi.get("payType"),
|
||||||
|
bi.get("price"),
|
||||||
|
bi.get("soldout", 0),
|
||||||
item.get("searchIdx", 0),
|
item.get("searchIdx", 0),
|
||||||
now,
|
result_scope,
|
||||||
|
result_title,
|
||||||
|
scope_count,
|
||||||
|
current_count,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
books.append({
|
books.append({
|
||||||
@@ -1215,6 +1132,8 @@ def api_search(conn):
|
|||||||
"new_rating": item.get("newRating", 0),
|
"new_rating": item.get("newRating", 0),
|
||||||
"new_rating_count": item.get("newRatingCount", 0),
|
"new_rating_count": item.get("newRatingCount", 0),
|
||||||
"rating_title": rating_detail.get("title", "") if isinstance(rating_detail, dict) else "",
|
"rating_title": rating_detail.get("title", "") if isinstance(rating_detail, dict) else "",
|
||||||
|
"deep_link": bi.get("deepLink", ""),
|
||||||
|
"soldout": bi.get("soldout", 0),
|
||||||
})
|
})
|
||||||
conn.commit()
|
conn.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
+711
-208
File diff suppressed because it is too large.
Load diff
+109
-126
@@ -1,16 +1,20 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
SQLite -> MySQL 数据迁移脚本
|
SQLite → MySQL 数据迁移脚本(完整版 v2.0)
|
||||||
将 weread.db (SQLite) 中的全部数据迁移到 MySQL weread_dashboard 数据库
|
将 weread.db (SQLite) 全部 31 张表数据迁移至 MySQL weread_dashboard 数据库。
|
||||||
|
|
||||||
|
处理逻辑:
|
||||||
|
1. 自动读取 SQLite 每张表的列名,与 MySQL 对齐
|
||||||
|
2. 时间戳文本列 (_dt, created_at, updated_at) 自动转换为 MySQL TIMESTAMP 格式
|
||||||
|
3. 每张表先 TRUNCATE 再插入,确保数据一致性
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import pymysql
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import pymysql
|
# ===== 配置 =====
|
||||||
|
|
||||||
# ===== 连接配置 =====
|
|
||||||
SQLITE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "weread.db")
|
SQLITE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "weread.db")
|
||||||
|
|
||||||
MYSQL_CONFIG = {
|
MYSQL_CONFIG = {
|
||||||
@@ -22,167 +26,146 @@ MYSQL_CONFIG = {
|
|||||||
"charset": "utf8mb4",
|
"charset": "utf8mb4",
|
||||||
}
|
}
|
||||||
|
|
||||||
# ===== 表迁移映射 =====
|
# 需要迁移的表名列表(按依赖顺序排列)
|
||||||
# (sqlite_table, mysql_table, columns, timestamp_cols)
|
TABLES = [
|
||||||
# timestamp_cols: SQLite 中存为 int(秒级时间戳),MySQL 中为 TIMESTAMP 的列
|
# 书架数据
|
||||||
TABLE_MAPPING = [
|
"books",
|
||||||
("books", "books",
|
"shelf_albums",
|
||||||
["book_id", "title", "author", "cover", "category", "finish_reading",
|
"shelf_meta",
|
||||||
"read_update_time", "secret", "is_top", "update_time", "deep_link"],
|
"archives",
|
||||||
[]),
|
# 阅读统计
|
||||||
|
"reading_stats",
|
||||||
("reading_stats", "reading_stats",
|
"read_times",
|
||||||
["mode", "period", "total_read_time", "read_days", "day_average_read_time",
|
"read_longest",
|
||||||
"regist_time", "raw_json"],
|
"read_stat_items",
|
||||||
[]), # MySQL created_at 用默认值
|
"prefer_categories",
|
||||||
|
"prefer_authors",
|
||||||
("read_times", "read_times",
|
"prefer_publishers",
|
||||||
["mode", "period", "bucket_timestamp", "duration"],
|
"prefer_cps",
|
||||||
[]),
|
"prefer_books",
|
||||||
|
"prefer_time",
|
||||||
("read_longest", "read_longest",
|
"read_rank",
|
||||||
["mode", "period", "book_id", "title", "author", "cover", "read_time", "tags"],
|
"medals",
|
||||||
[]),
|
"year_reports",
|
||||||
|
# 笔记数据
|
||||||
("prefer_categories", "prefer_categories",
|
"notebooks",
|
||||||
["mode", "period", "category_id", "category_title", "parent_category_title",
|
"book_highlights",
|
||||||
"val", "reading_time", "reading_count"],
|
"book_chapters",
|
||||||
[]),
|
"book_reviews",
|
||||||
|
"book_best_bookmarks",
|
||||||
("prefer_authors", "prefer_authors",
|
"book_underlines",
|
||||||
["mode", "period", "author_id", "name", "count", "read_time"],
|
"public_reviews",
|
||||||
[]),
|
# 书籍详情
|
||||||
|
"book_details",
|
||||||
("prefer_time", "prefer_time",
|
"book_progress",
|
||||||
["mode", "period", "hour_index", "duration"],
|
# 发现/推荐
|
||||||
[]),
|
"recommended_books",
|
||||||
|
"similar_books",
|
||||||
("notebooks", "notebooks",
|
"search_sessions",
|
||||||
["book_id", "title", "author", "cover", "review_count", "note_count",
|
"search_results",
|
||||||
"bookmark_count", "reading_progress", "marked_status", "sort"],
|
# 系统
|
||||||
[]),
|
"fetch_meta",
|
||||||
|
|
||||||
("read_stat_items", "read_stat_items",
|
|
||||||
["mode", "period", "stat_name", "counts", "scheme"],
|
|
||||||
[]),
|
|
||||||
|
|
||||||
("archives", "archives",
|
|
||||||
["name", "book_ids"],
|
|
||||||
[]),
|
|
||||||
|
|
||||||
("fetch_meta", "fetch_meta",
|
|
||||||
["key", "value"],
|
|
||||||
[]),
|
|
||||||
|
|
||||||
("book_details", "book_details",
|
|
||||||
["book_id", "title", "author", "translator", "cover", "intro", "category",
|
|
||||||
"publisher", "publish_time", "isbn", "word_count", "new_rating", "new_rating_count"],
|
|
||||||
["fetched_at"]),
|
|
||||||
|
|
||||||
("book_progress", "book_progress",
|
|
||||||
["book_id", "progress", "chapter_uid", "chapter_offset", "update_time",
|
|
||||||
"record_reading_time", "finish_time", "is_start_reading"],
|
|
||||||
["fetched_at"]),
|
|
||||||
|
|
||||||
("book_highlights", "book_highlights",
|
|
||||||
["book_id", "bookmark_id", "chapter_uid", "mark_text", "create_time",
|
|
||||||
"type", "range_text", "color_style"],
|
|
||||||
["fetched_at"]),
|
|
||||||
|
|
||||||
("book_chapters", "book_chapters",
|
|
||||||
["book_id", "chapter_uid", "chapter_idx", "title"],
|
|
||||||
[]),
|
|
||||||
|
|
||||||
("book_reviews", "book_reviews",
|
|
||||||
["book_id", "review_id", "content", "create_time", "star",
|
|
||||||
"chapter_name", "is_finish"],
|
|
||||||
["fetched_at"]),
|
|
||||||
|
|
||||||
("recommended_books", "recommended_books",
|
|
||||||
["book_id", "title", "author", "cover", "intro", "category", "reason",
|
|
||||||
"reading_count", "new_rating", "new_rating_count", "rating_title"],
|
|
||||||
["fetched_at"]),
|
|
||||||
|
|
||||||
("search_results", "search_results",
|
|
||||||
["keyword", "book_id", "title", "author", "cover", "intro", "publisher",
|
|
||||||
"category", "reading_count", "new_rating", "new_rating_count",
|
|
||||||
"rating_title", "search_idx"],
|
|
||||||
["fetched_at"]),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def convert_timestamp(value):
|
def convert_dt(val):
|
||||||
"""将 SQLite 中的 int 时间戳转为 MySQL datetime 字符串"""
|
"""将 SQLite 中的时间文本转换为 MySQL TIMESTAMP 兼容格式"""
|
||||||
if value is None or value == 0:
|
if val is None or val == "":
|
||||||
return None
|
return None
|
||||||
try:
|
if isinstance(val, (int, float)):
|
||||||
return datetime.fromtimestamp(int(value)).strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
except (ValueError, OSError, OverflowError):
|
|
||||||
return None
|
return None
|
||||||
|
# SQLite datetime('now','localtime') 格式: "2026-07-17 10:00:00"
|
||||||
|
# 已经是 MySQL 兼容格式,直接返回
|
||||||
|
return str(val)
|
||||||
|
|
||||||
|
|
||||||
def migrate():
|
def migrate():
|
||||||
"""执行迁移"""
|
"""执行完整迁移"""
|
||||||
|
# 连接 SQLite
|
||||||
sqlite_conn = sqlite3.connect(SQLITE_PATH)
|
sqlite_conn = sqlite3.connect(SQLITE_PATH)
|
||||||
sqlite_conn.row_factory = sqlite3.Row
|
sqlite_conn.row_factory = sqlite3.Row
|
||||||
sc = sqlite_conn.cursor()
|
sc = sqlite_conn.cursor()
|
||||||
|
|
||||||
|
# 连接 MySQL
|
||||||
mysql_conn = pymysql.connect(**MYSQL_CONFIG)
|
mysql_conn = pymysql.connect(**MYSQL_CONFIG)
|
||||||
mc = mysql_conn.cursor()
|
mc = mysql_conn.cursor()
|
||||||
|
|
||||||
total_rows = 0
|
total_rows = 0
|
||||||
|
migrated_tables = 0
|
||||||
|
|
||||||
for sqlite_table, mysql_table, columns, ts_cols in TABLE_MAPPING:
|
print("=" * 60)
|
||||||
# 读取 SQLite 数据
|
print("SQLite → MySQL 数据迁移 (v2.0)")
|
||||||
col_list = ", ".join(columns)
|
print("=" * 60)
|
||||||
sc.execute(f"SELECT {col_list} FROM \"{sqlite_table}\"")
|
|
||||||
rows = sc.fetchall()
|
|
||||||
|
|
||||||
if not rows:
|
for table in TABLES:
|
||||||
print(f" {sqlite_table:30s} 0 行 (跳过)")
|
# 检查 SQLite 表是否有数据
|
||||||
|
sc.execute(f'SELECT COUNT(*) FROM "{table}"')
|
||||||
|
row_count = sc.fetchone()[0]
|
||||||
|
|
||||||
|
if row_count == 0:
|
||||||
|
print(f" {table:30s} 跳过 (无数据)")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 构建 INSERT 语句
|
# 获取列名
|
||||||
|
sc.execute(f'PRAGMA table_info("{table}")')
|
||||||
|
columns = [col[1] for col in sc.fetchall()]
|
||||||
|
|
||||||
|
# 清空 MySQL 表
|
||||||
|
mc.execute(f"DELETE FROM `{table}`")
|
||||||
|
|
||||||
|
# 读取 SQLite 数据
|
||||||
|
sc.execute(f'SELECT {", ".join(f"\"{c}\"" for c in columns)} FROM "{table}"')
|
||||||
|
rows = sc.fetchall()
|
||||||
|
|
||||||
|
# 构建批量 INSERT
|
||||||
placeholders = ", ".join(["%s"] * len(columns))
|
placeholders = ", ".join(["%s"] * len(columns))
|
||||||
col_names = ", ".join(f"`{c}`" for c in columns)
|
col_list = ", ".join(f"`{c}`" for c in columns)
|
||||||
sql = f"INSERT INTO `{mysql_table}` ({col_names}) VALUES ({placeholders})"
|
insert_sql = f"INSERT INTO `{table}` ({col_list}) VALUES ({placeholders})"
|
||||||
|
|
||||||
# 转换数据
|
# 转换数据
|
||||||
batch = []
|
batch = []
|
||||||
for row in rows:
|
for row in rows:
|
||||||
values = []
|
values = []
|
||||||
for i, col in enumerate(columns):
|
for col in columns:
|
||||||
val = row[i]
|
val = row[col]
|
||||||
if col in ts_cols:
|
|
||||||
val = convert_timestamp(val)
|
|
||||||
values.append(val)
|
values.append(val)
|
||||||
batch.append(tuple(values))
|
batch.append(values)
|
||||||
|
|
||||||
# 批量插入
|
# 批量插入
|
||||||
mc.executemany(sql, batch)
|
mc.executemany(insert_sql, batch)
|
||||||
mysql_conn.commit()
|
mysql_conn.commit()
|
||||||
|
|
||||||
count = len(batch)
|
total_rows += row_count
|
||||||
total_rows += count
|
migrated_tables += 1
|
||||||
print(f" {sqlite_table:30s} {count:>6} 行 ✓")
|
print(f" {table:30s} {row_count:>6} 行 ✓")
|
||||||
|
|
||||||
# 验证
|
# 更新 fetch_meta
|
||||||
print(f"\n{'='*50}")
|
mc.execute(
|
||||||
print(f"迁移完成!共迁移 {total_rows} 行数据")
|
"INSERT INTO fetch_meta (`key`, `value`) VALUES ('mysql_migrated_at', %s) "
|
||||||
print(f"{'='*50}")
|
"ON DUPLICATE KEY UPDATE `value` = %s",
|
||||||
|
(datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
|
||||||
|
)
|
||||||
|
mysql_conn.commit()
|
||||||
|
|
||||||
print("\nMySQL 各表数据量验证:")
|
print(f"\n{'=' * 60}")
|
||||||
|
print(f"迁移完成! {migrated_tables} 张表, 共 {total_rows} 行数据")
|
||||||
|
print(f"MySQL: {MYSQL_CONFIG['host']}:{MYSQL_CONFIG['port']}/{MYSQL_CONFIG['database']}")
|
||||||
|
print(f"{'=' * 60}")
|
||||||
|
|
||||||
|
# 数据校验
|
||||||
|
print("\n数据校验:")
|
||||||
mc.execute("SHOW TABLES")
|
mc.execute("SHOW TABLES")
|
||||||
tables = [t[0] for t in mc.fetchall()]
|
mysql_tables = [t[0] for t in mc.fetchall()]
|
||||||
for t in tables:
|
for t in mysql_tables:
|
||||||
mc.execute(f"SELECT COUNT(*) FROM `{t}`")
|
mc.execute(f"SELECT COUNT(*) FROM `{t}`")
|
||||||
cnt = mc.fetchone()[0]
|
cnt = mc.fetchone()[0]
|
||||||
print(f" {t:30s} {cnt:>6} 行")
|
if cnt > 0:
|
||||||
|
print(f" MySQL {t:30s} {cnt:>6} 行")
|
||||||
|
|
||||||
sqlite_conn.close()
|
sqlite_conn.close()
|
||||||
mysql_conn.close()
|
mysql_conn.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("开始迁移 SQLite -> MySQL ...\n")
|
|
||||||
migrate()
|
migrate()
|
||||||
+570
-255
@@ -1,7 +1,13 @@
|
|||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 微信读书数据展示网站 - MySQL 版本建表语句
|
-- 微信读书数据展示网站 - MySQL 版本建表语句(完整版 v2.0)
|
||||||
-- 适用于后续从 SQLite 迁移至 MySQL
|
-- ============================================================
|
||||||
-- 字符集: utf8mb4 (支持完整 Unicode,包括 emoji)
|
-- 设计规范:
|
||||||
|
-- 1. 所有整型时间戳字段均伴随一个 _dt TIMESTAMP 列,便于直接查询和展示
|
||||||
|
-- 2. 每张表均维护 created_at(创建时间)和 updated_at(更新时间)
|
||||||
|
-- 3. 主键、唯一约束、查询索引根据实际访问模式设计
|
||||||
|
-- 4. 字符集 utf8mb4,引擎 InnoDB
|
||||||
|
-- 5. 每个字段带 COMMENT 中文注释
|
||||||
|
-- 6. 覆盖微信读书全部 8 份官方 API 文档的所有返回字段
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|
||||||
CREATE DATABASE IF NOT EXISTS `weread_dashboard`
|
CREATE DATABASE IF NOT EXISTS `weread_dashboard`
|
||||||
@@ -11,315 +17,624 @@ CREATE DATABASE IF NOT EXISTS `weread_dashboard`
|
|||||||
USE `weread_dashboard`;
|
USE `weread_dashboard`;
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 1. 书架书籍表
|
-- ===== 第一组:书架数据 =====
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 1. 书架书籍表(电子书,来自 /shelf/sync 的 books[])
|
||||||
CREATE TABLE IF NOT EXISTS `books` (
|
CREATE TABLE IF NOT EXISTS `books` (
|
||||||
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍唯一标识',
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍唯一标识',
|
||||||
`title` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '书名',
|
`title` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '书名',
|
||||||
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类',
|
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类(如 "文学-古代诗词")',
|
||||||
`finish_reading` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否读完 (1=读完)',
|
`finish_reading` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否读完 (1=读完)',
|
||||||
`read_update_time` INT UNSIGNED DEFAULT NULL COMMENT '最近阅读时间 (Unix 时间戳)',
|
`read_update_time` INT UNSIGNED DEFAULT NULL COMMENT '最近阅读时间 (Unix 时间戳)',
|
||||||
|
`read_update_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '最近阅读时间 (可读格式)',
|
||||||
`secret` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否私密 (1=私密)',
|
`secret` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否私密 (1=私密)',
|
||||||
`is_top` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否置顶 (1=置顶)',
|
`is_top` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否置顶 (1=置顶)',
|
||||||
`update_time` INT UNSIGNED DEFAULT NULL COMMENT '书籍更新时间 (Unix 时间戳)',
|
`update_time` INT UNSIGNED DEFAULT NULL COMMENT '书籍更新时间 (Unix 时间戳)',
|
||||||
`deep_link` VARCHAR(512) DEFAULT NULL COMMENT '深度链接',
|
`update_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '书籍更新时间 (可读格式)',
|
||||||
|
`deep_link` VARCHAR(512) DEFAULT NULL COMMENT '微信读书 App 深度链接',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
PRIMARY KEY (`book_id`),
|
PRIMARY KEY (`book_id`),
|
||||||
INDEX `idx_books_category` (`category`),
|
INDEX `idx_books_category` (`category`),
|
||||||
INDEX `idx_books_finish_reading` (`finish_reading`),
|
INDEX `idx_books_finish_reading` (`finish_reading`),
|
||||||
INDEX `idx_books_read_update_time` (`read_update_time` DESC)
|
INDEX `idx_books_secret` (`secret`),
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
INDEX `idx_books_is_top` (`is_top`),
|
||||||
COMMENT='书架书籍信息';
|
INDEX `idx_books_read_update_time` (`read_update_time` DESC),
|
||||||
|
INDEX `idx_books_title` (`title`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='书架电子书';
|
||||||
|
|
||||||
-- ============================================================
|
-- 2. 书架有声书/专辑表(来自 /shelf/sync 的 albums[])
|
||||||
-- 2. 阅读统计概要表
|
CREATE TABLE IF NOT EXISTS `shelf_albums` (
|
||||||
-- ============================================================
|
`album_id` VARCHAR(64) NOT NULL COMMENT '专辑唯一标识',
|
||||||
CREATE TABLE IF NOT EXISTS `reading_stats` (
|
`name` VARCHAR(255) DEFAULT NULL COMMENT '专辑名称',
|
||||||
|
`author_name` VARCHAR(255) DEFAULT NULL COMMENT '演播/作者',
|
||||||
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
|
`track_count` INT UNSIGNED DEFAULT 0 COMMENT '音频集数',
|
||||||
|
`finish_status` VARCHAR(32) DEFAULT NULL COMMENT '完结状态文案(如 "已完结")',
|
||||||
|
`finish` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否完结 (1=完结)',
|
||||||
|
`pay_type` INT UNSIGNED DEFAULT NULL COMMENT '付费类型',
|
||||||
|
`intro` TEXT DEFAULT NULL COMMENT '专辑简介',
|
||||||
|
`update_time` INT UNSIGNED DEFAULT NULL COMMENT '更新时间 (Unix 时间戳)',
|
||||||
|
`update_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '更新时间 (可读格式)',
|
||||||
|
`secret` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否私密 (1=私密)',
|
||||||
|
`lecture_paid` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已购买 (1=已购买)',
|
||||||
|
`lecture_read_update_time` INT UNSIGNED DEFAULT NULL COMMENT '最近收听时间 (Unix 时间戳)',
|
||||||
|
`lecture_read_update_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '最近收听时间 (可读格式)',
|
||||||
|
`is_top` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否置顶 (1=置顶)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`album_id`),
|
||||||
|
INDEX `idx_albums_finish` (`finish`),
|
||||||
|
INDEX `idx_albums_secret` (`secret`),
|
||||||
|
INDEX `idx_albums_lecture_read_time` (`lecture_read_update_time` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='书架有声书/专辑';
|
||||||
|
|
||||||
|
-- 3. 书架元信息表(来自 /shelf/sync 的 mp 和 bookCount)
|
||||||
|
CREATE TABLE IF NOT EXISTS `shelf_meta` (
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度: overall/annually/monthly/weekly',
|
`mp_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否有文章收藏入口 (1=有)',
|
||||||
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
`book_count` INT UNSIGNED DEFAULT 0 COMMENT '可枚举电子书数量',
|
||||||
`total_read_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '总阅读时长 (秒)',
|
`album_count` INT UNSIGNED DEFAULT 0 COMMENT '有声书/专辑数量',
|
||||||
`read_days` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '有效阅读天数',
|
`total_count` INT UNSIGNED DEFAULT 0 COMMENT '书架总条目数 (books + albums + mp)',
|
||||||
`day_average_read_time` DOUBLE DEFAULT 0 COMMENT '日均阅读时长 (秒)',
|
|
||||||
`regist_time` INT UNSIGNED DEFAULT NULL COMMENT '用户注册时间 (Unix 时间戳)',
|
|
||||||
`raw_json` LONGTEXT DEFAULT NULL COMMENT '原始 API 返回 JSON',
|
`raw_json` LONGTEXT DEFAULT NULL COMMENT '原始 API 返回 JSON',
|
||||||
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
PRIMARY KEY (`id`),
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
INDEX `idx_reading_stats_mode` (`mode`, `period`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='书架元信息';
|
||||||
COMMENT='阅读统计概要';
|
|
||||||
|
|
||||||
-- ============================================================
|
-- 4. 书单/归档表(来自 /shelf/sync 的 archive[])
|
||||||
-- 3. 阅读时长时间序列(按年/月/日分桶)
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `read_times` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`mode` VARCHAR(30) NOT NULL COMMENT '统计维度 (含_daily后缀)',
|
|
||||||
`period` VARCHAR(20) NOT NULL DEFAULT '',
|
|
||||||
`bucket_timestamp` INT UNSIGNED NOT NULL COMMENT '分桶起始时间戳',
|
|
||||||
`duration` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时长 (秒)',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_read_times_mode_period` (`mode`, `period`),
|
|
||||||
INDEX `idx_read_times_bucket` (`bucket_timestamp`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='阅读时长时间序列';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 4. 阅读时长最久的书籍排行
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `read_longest` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`mode` VARCHAR(20) NOT NULL,
|
|
||||||
`period` VARCHAR(20) NOT NULL DEFAULT '',
|
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
|
||||||
`title` VARCHAR(255) DEFAULT NULL,
|
|
||||||
`author` VARCHAR(255) DEFAULT NULL,
|
|
||||||
`cover` VARCHAR(512) DEFAULT NULL,
|
|
||||||
`read_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时长 (秒)',
|
|
||||||
`tags` VARCHAR(512) DEFAULT NULL COMMENT '标签 JSON 数组',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_read_longest_mode` (`mode`, `period`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='阅读时长排行';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 5. 偏好分类
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `prefer_categories` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`mode` VARCHAR(20) NOT NULL,
|
|
||||||
`period` VARCHAR(20) NOT NULL DEFAULT '',
|
|
||||||
`category_id` INT UNSIGNED DEFAULT NULL,
|
|
||||||
`category_title` VARCHAR(128) DEFAULT NULL,
|
|
||||||
`parent_category_title` VARCHAR(128) DEFAULT NULL,
|
|
||||||
`val` DOUBLE DEFAULT 0 COMMENT '偏好权重',
|
|
||||||
`reading_time` INT UNSIGNED DEFAULT 0 COMMENT '该分类阅读时长 (秒)',
|
|
||||||
`reading_count` INT UNSIGNED DEFAULT 0 COMMENT '该分类阅读本数',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_prefer_cat_mode` (`mode`, `period`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='偏好阅读分类';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 6. 偏好作者
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `prefer_authors` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`mode` VARCHAR(20) NOT NULL,
|
|
||||||
`period` VARCHAR(20) NOT NULL DEFAULT '',
|
|
||||||
`author_id` INT UNSIGNED DEFAULT NULL,
|
|
||||||
`name` VARCHAR(255) DEFAULT NULL,
|
|
||||||
`count` INT UNSIGNED DEFAULT 0 COMMENT '阅读该作者的书本数',
|
|
||||||
`read_time` VARCHAR(64) DEFAULT NULL COMMENT '阅读时长 (格式化字符串)',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_prefer_author_mode` (`mode`, `period`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='偏好作者';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 7. 24小时阅读时段分布
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `prefer_time` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`mode` VARCHAR(20) NOT NULL,
|
|
||||||
`period` VARCHAR(20) NOT NULL DEFAULT '',
|
|
||||||
`hour_index` TINYINT UNSIGNED NOT NULL COMMENT '小时 (0-23)',
|
|
||||||
`duration` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时长 (秒)',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_prefer_time_mode` (`mode`, `period`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='24小时阅读时段分布';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 8. 笔记本概览
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `notebooks` (
|
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
|
||||||
`title` VARCHAR(255) DEFAULT NULL,
|
|
||||||
`author` VARCHAR(255) DEFAULT NULL,
|
|
||||||
`cover` VARCHAR(512) DEFAULT NULL,
|
|
||||||
`review_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '想法/点评数',
|
|
||||||
`note_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '划线数',
|
|
||||||
`bookmark_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '书签数',
|
|
||||||
`reading_progress` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读进度 (0-100)',
|
|
||||||
`marked_status` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '标记状态 (1=读完)',
|
|
||||||
`sort` BIGINT DEFAULT NULL COMMENT '排序值',
|
|
||||||
PRIMARY KEY (`book_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='笔记本概览';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 9. 阅读统计摘要项
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `read_stat_items` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`mode` VARCHAR(20) NOT NULL,
|
|
||||||
`period` VARCHAR(20) NOT NULL DEFAULT '',
|
|
||||||
`stat_name` VARCHAR(64) DEFAULT NULL COMMENT '统计项名称 (读过/读完/阅读/笔记)',
|
|
||||||
`counts` VARCHAR(64) DEFAULT NULL COMMENT '统计值文案',
|
|
||||||
`scheme` VARCHAR(512) DEFAULT NULL COMMENT 'App 跳转链接',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_read_stat_items_mode` (`mode`, `period`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='阅读统计摘要项';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 10. 书单/归档
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `archives` (
|
CREATE TABLE IF NOT EXISTS `archives` (
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
`name` VARCHAR(255) DEFAULT NULL COMMENT '书单名称',
|
`name` VARCHAR(255) DEFAULT NULL COMMENT '书单名称',
|
||||||
`book_ids` LONGTEXT DEFAULT NULL COMMENT 'bookId 列表 JSON 数组',
|
`book_ids` LONGTEXT DEFAULT NULL COMMENT 'bookId 列表 (JSON 数组)',
|
||||||
PRIMARY KEY (`id`)
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
COMMENT='书单/归档';
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_archives_name` (`name`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='书单/归档';
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 11. 数据采集元信息
|
-- ===== 第二组:阅读统计数据 =====
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
CREATE TABLE IF NOT EXISTS `fetch_meta` (
|
|
||||||
`key` VARCHAR(64) NOT NULL,
|
-- 5. 阅读统计概要表(来自 /readdata/detail)
|
||||||
`value` TEXT DEFAULT NULL,
|
CREATE TABLE IF NOT EXISTS `reading_stats` (
|
||||||
PRIMARY KEY (`key`)
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度: overall/annually/monthly/weekly',
|
||||||
COMMENT='数据采集元信息';
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识 (如 "overall" 或 "2026")',
|
||||||
|
`base_time` INT UNSIGNED DEFAULT NULL COMMENT '统计周期基准时间戳',
|
||||||
|
`base_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '统计周期基准时间 (可读格式)',
|
||||||
|
`total_read_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '总阅读时长 (秒)',
|
||||||
|
`read_days` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '有效阅读天数',
|
||||||
|
`day_average_read_time` DOUBLE DEFAULT 0 COMMENT '日均阅读时长 (秒)',
|
||||||
|
`compare` DOUBLE DEFAULT NULL COMMENT '与上周期对比比例 (正=增长)',
|
||||||
|
`regist_time` INT UNSIGNED DEFAULT NULL COMMENT '用户注册时间 (Unix 时间戳)',
|
||||||
|
`regist_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '用户注册时间 (可读格式)',
|
||||||
|
`record_reading_time` INT UNSIGNED DEFAULT 0 COMMENT '总朗读/记录类阅读时长 (秒)',
|
||||||
|
`read_rate` DOUBLE DEFAULT NULL COMMENT '文字阅读占比百分比',
|
||||||
|
`wr_read_time` INT UNSIGNED DEFAULT NULL COMMENT '文字阅读时长 (秒)',
|
||||||
|
`wr_listen_time` INT UNSIGNED DEFAULT NULL COMMENT '听书时长 (秒)',
|
||||||
|
`author_count` INT UNSIGNED DEFAULT NULL COMMENT '符合统计条件的作者总数',
|
||||||
|
`prefer_category_word` VARCHAR(128) DEFAULT NULL COMMENT '偏好分类文案 (如 "偏好阅读文学")',
|
||||||
|
`prefer_time_word` VARCHAR(128) DEFAULT NULL COMMENT '偏好时段文案 (如 "偏好上午阅读")',
|
||||||
|
`read_records_word` VARCHAR(64) DEFAULT NULL COMMENT '书籍分布模块标题文案',
|
||||||
|
`read_distribution_word` VARCHAR(64) DEFAULT NULL COMMENT '点评分布模块标题文案',
|
||||||
|
`read_time_gears` VARCHAR(128) DEFAULT NULL COMMENT '阅读时长档位 (JSON 数组)',
|
||||||
|
`style_type` VARCHAR(32) DEFAULT NULL COMMENT '样式类型 (如 normal)',
|
||||||
|
`raw_json` LONGTEXT DEFAULT NULL COMMENT '原始 API 返回 JSON',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_reading_stats_mode_period` (`mode`, `period`),
|
||||||
|
INDEX `idx_reading_stats_mode` (`mode`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='阅读统计概要';
|
||||||
|
|
||||||
|
-- 6. 阅读时长时间序列表(来自 /readdata/detail 的 readTimes 和 dailyReadTimes)
|
||||||
|
CREATE TABLE IF NOT EXISTS `read_times` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(30) NOT NULL COMMENT '统计维度 (含 _daily 后缀表示每日数据)',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`bucket_timestamp` INT UNSIGNED NOT NULL COMMENT '分桶起始时间戳',
|
||||||
|
`bucket_timestamp_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '分桶起始时间 (可读格式)',
|
||||||
|
`duration` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时长 (秒)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_read_times_mode_period` (`mode`, `period`),
|
||||||
|
INDEX `idx_read_times_bucket` (`bucket_timestamp`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='阅读时长时间序列';
|
||||||
|
|
||||||
|
-- 7. 阅读时长排行表(来自 /readdata/detail 的 readLongest[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `read_longest` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
||||||
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
|
`read_time` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时长 (秒)',
|
||||||
|
`record_reading_time` INT UNSIGNED DEFAULT NULL COMMENT '朗读/记录类阅读时长 (秒)',
|
||||||
|
`tags` VARCHAR(512) DEFAULT NULL COMMENT '标签 (JSON 数组,如 ["笔记最多"])',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_read_longest_mode_period` (`mode`, `period`),
|
||||||
|
INDEX `idx_read_longest_book_id` (`book_id`),
|
||||||
|
INDEX `idx_read_longest_read_time` (`read_time` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='阅读时长排行';
|
||||||
|
|
||||||
|
-- 8. 阅读统计摘要项表(来自 /readdata/detail 的 readStat[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `read_stat_items` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`stat_name` VARCHAR(64) DEFAULT NULL COMMENT '统计项名称 (读过/读完/阅读/笔记)',
|
||||||
|
`counts` VARCHAR(64) DEFAULT NULL COMMENT '统计值文案 (如 "404本")',
|
||||||
|
`scheme` VARCHAR(512) DEFAULT NULL COMMENT 'App 跳转链接',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_read_stat_items_mode_period` (`mode`, `period`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='阅读统计摘要项';
|
||||||
|
|
||||||
|
-- 9. 偏好分类表(来自 /readdata/detail 的 preferCategory[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `prefer_categories` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`category_id` INT UNSIGNED DEFAULT NULL COMMENT '分类 ID',
|
||||||
|
`category_title` VARCHAR(128) DEFAULT NULL COMMENT '分类名称 (如 "文学")',
|
||||||
|
`parent_category_id` INT UNSIGNED DEFAULT NULL COMMENT '父分类 ID',
|
||||||
|
`parent_category_title` VARCHAR(128) DEFAULT NULL COMMENT '父分类名称 (如 "精品小说")',
|
||||||
|
`category_type` TINYINT DEFAULT 0 COMMENT '分类类型标记 (0=普通, 1/2=特殊)',
|
||||||
|
`val` DOUBLE DEFAULT 0 COMMENT '偏好权重 (归一化相对值)',
|
||||||
|
`reading_time` INT UNSIGNED DEFAULT 0 COMMENT '该分类阅读时长 (秒)',
|
||||||
|
`reading_count` INT UNSIGNED DEFAULT 0 COMMENT '该分类阅读本数',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_prefer_cat_mode_period` (`mode`, `period`),
|
||||||
|
INDEX `idx_prefer_cat_category_id` (`category_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='偏好阅读分类';
|
||||||
|
|
||||||
|
-- 10. 偏好作者表(来自 /readdata/detail 的 preferAuthor[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `prefer_authors` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`author_id` INT UNSIGNED DEFAULT NULL COMMENT '作者 ID',
|
||||||
|
`name` VARCHAR(255) DEFAULT NULL COMMENT '作者名',
|
||||||
|
`count` INT UNSIGNED DEFAULT 0 COMMENT '阅读该作者的书本数',
|
||||||
|
`read_time` VARCHAR(64) DEFAULT NULL COMMENT '阅读时长 (格式化字符串,如 "5小时30分钟")',
|
||||||
|
`user_json` TEXT DEFAULT NULL COMMENT '作者关联用户信息 (JSON)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_prefer_author_mode_period` (`mode`, `period`),
|
||||||
|
INDEX `idx_prefer_author_name` (`name`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='偏好作者';
|
||||||
|
|
||||||
|
-- 11. 偏好出版社表(来自 /readdata/detail 的 preferPublisher[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `prefer_publishers` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`name` VARCHAR(255) DEFAULT NULL COMMENT '出版社名',
|
||||||
|
`count` INT UNSIGNED DEFAULT 0 COMMENT '阅读该出版社书籍本数',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_prefer_pub_mode_period` (`mode`, `period`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='偏好出版社';
|
||||||
|
|
||||||
|
-- 12. 偏好版权方表(来自 /readdata/detail 的 preferCp[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `prefer_cps` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`count` INT UNSIGNED DEFAULT 0 COMMENT '阅读该版权方书籍本数',
|
||||||
|
`cp_name` VARCHAR(255) DEFAULT NULL COMMENT '版权方名称',
|
||||||
|
`cp_user_vid` VARCHAR(64) DEFAULT NULL COMMENT '版权方用户 VID',
|
||||||
|
`cp_avatar` VARCHAR(512) DEFAULT NULL COMMENT '版权方头像 URL',
|
||||||
|
`cp_role` VARCHAR(64) DEFAULT NULL COMMENT '版权方角色',
|
||||||
|
`copyright_json` TEXT DEFAULT NULL COMMENT '版权方完整信息 (JSON)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_prefer_cp_mode_period` (`mode`, `period`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='偏好版权方';
|
||||||
|
|
||||||
|
-- 13. 偏好书籍卡片表(来自 /readdata/detail 的 preferBooks[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `prefer_books` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`book_id` VARCHAR(64) DEFAULT NULL COMMENT '书籍 ID',
|
||||||
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
||||||
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
|
`reason` TEXT DEFAULT NULL COMMENT '推荐理由',
|
||||||
|
`prefer_type` VARCHAR(64) DEFAULT NULL COMMENT '偏好类型',
|
||||||
|
`book_json` TEXT DEFAULT NULL COMMENT '书籍完整信息 (JSON)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_prefer_books_mode_period` (`mode`, `period`),
|
||||||
|
INDEX `idx_prefer_books_book_id` (`book_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='偏好书籍卡片';
|
||||||
|
|
||||||
|
-- 14. 24小时阅读时段分布表(来自 /readdata/detail 的 preferTime[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `prefer_time` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`hour_index` TINYINT UNSIGNED NOT NULL COMMENT '小时 (0-23)',
|
||||||
|
`duration` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读时长 (秒)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_prefer_time_mode_period` (`mode`, `period`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='24小时阅读时段分布';
|
||||||
|
|
||||||
|
-- 15. 好友阅读排行表(来自 /readdata/detail 的 rank)
|
||||||
|
CREATE TABLE IF NOT EXISTS `read_rank` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`rank_text` VARCHAR(255) DEFAULT NULL COMMENT '排行文案 (如 "朋友中排第3名")',
|
||||||
|
`scheme` VARCHAR(512) DEFAULT NULL COMMENT '排行跳转链接',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_read_rank_mode_period` (`mode`, `period`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='好友阅读排行';
|
||||||
|
|
||||||
|
-- 16. 勋章表(来自 /readdata/detail 的 medals[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `medals` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`medal_json` TEXT DEFAULT NULL COMMENT '勋章信息 (JSON)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_medals_mode_period` (`mode`, `period`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='勋章';
|
||||||
|
|
||||||
|
-- 17. 年度报告表(来自 /readdata/detail 的 yearReport[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `year_reports` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`mode` VARCHAR(20) NOT NULL COMMENT '统计维度',
|
||||||
|
`period` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '周期标识',
|
||||||
|
`report_year` VARCHAR(10) DEFAULT NULL COMMENT '报告年份',
|
||||||
|
`report_json` TEXT DEFAULT NULL COMMENT '报告入口信息 (JSON)',
|
||||||
|
`monthly_times` VARCHAR(512) DEFAULT NULL COMMENT '12个月阅读时长 (JSON 数组,单位秒)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_year_reports_mode_period` (`mode`, `period`),
|
||||||
|
INDEX `idx_year_reports_year` (`report_year`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='年度报告';
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 12. 书籍详细信息(按需获取,来自 /book/info)
|
-- ===== 第三组:笔记数据 =====
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 18. 笔记本概览表(来自 /user/notebooks)
|
||||||
|
CREATE TABLE IF NOT EXISTS `notebooks` (
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
||||||
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
|
`review_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '想法/点评数 (含划线想法、书评想法等)',
|
||||||
|
`note_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '划线数 (高亮标注原文条数)',
|
||||||
|
`bookmark_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '书签数 (标记阅读位置条数)',
|
||||||
|
`total_note_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '笔记总数 (reviewCount + noteCount + bookmarkCount)',
|
||||||
|
`reading_progress` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读进度 (0-100)',
|
||||||
|
`marked_status` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '标记状态 (1=读完)',
|
||||||
|
`sort` BIGINT DEFAULT NULL COMMENT '排序值 (最近笔记时间,用于翻页)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`book_id`),
|
||||||
|
INDEX `idx_notebooks_marked_status` (`marked_status`),
|
||||||
|
INDEX `idx_notebooks_sort` (`sort` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='笔记本概览';
|
||||||
|
|
||||||
|
-- 19. 划线内容表(来自 /book/bookmarklist 的 updated[])
|
||||||
|
CREATE TABLE IF NOT EXISTS `book_highlights` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`bookmark_id` VARCHAR(64) DEFAULT NULL COMMENT '划线唯一 ID',
|
||||||
|
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '所在章节 UID',
|
||||||
|
`mark_text` LONGTEXT DEFAULT NULL COMMENT '划线原文内容',
|
||||||
|
`create_time` INT UNSIGNED DEFAULT NULL COMMENT '创建时间 (Unix 时间戳)',
|
||||||
|
`create_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '创建时间 (可读格式)',
|
||||||
|
`type` TINYINT DEFAULT NULL COMMENT '类型 (1=划线)',
|
||||||
|
`range_text` VARCHAR(64) DEFAULT NULL COMMENT '位置范围 (如 "2959-3007")',
|
||||||
|
`color_style` TINYINT DEFAULT NULL COMMENT '划线颜色样式',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_highlights_bookmark` (`book_id`, `bookmark_id`),
|
||||||
|
INDEX `idx_highlights_book_id` (`book_id`),
|
||||||
|
INDEX `idx_highlights_chapter` (`book_id`, `chapter_uid`),
|
||||||
|
INDEX `idx_highlights_create_time` (`create_time` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='划线内容';
|
||||||
|
|
||||||
|
-- 20. 章节信息表(来自 /book/chapterinfo 和 /book/bookmarklist 的 chapters)
|
||||||
|
CREATE TABLE IF NOT EXISTS `book_chapters` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`chapter_uid` INT UNSIGNED NOT NULL COMMENT '章节 UID',
|
||||||
|
`chapter_idx` INT UNSIGNED DEFAULT NULL COMMENT '章节序号',
|
||||||
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '章节标题',
|
||||||
|
`word_count` INT UNSIGNED DEFAULT NULL COMMENT '章节字数',
|
||||||
|
`level` TINYINT DEFAULT NULL COMMENT '目录层级 (1=一级标题, 2=二级)',
|
||||||
|
`update_time` INT UNSIGNED DEFAULT NULL COMMENT '章节更新时间 (Unix 时间戳)',
|
||||||
|
`update_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '章节更新时间 (可读格式)',
|
||||||
|
`price` INT UNSIGNED DEFAULT 0 COMMENT '章节价格 (0=免费)',
|
||||||
|
`paid` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已购买 (1=已购买)',
|
||||||
|
`is_mp_chapter` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否公众号章节 (1=是)',
|
||||||
|
`anchors` TEXT DEFAULT NULL COMMENT '章节内锚点/子标题 (JSON 数组)',
|
||||||
|
`synckey` INT UNSIGNED DEFAULT NULL COMMENT '同步 key (版本号)',
|
||||||
|
`chapter_update_time` INT UNSIGNED DEFAULT NULL COMMENT '章节列表最后更新时间 (Unix 时间戳)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_book_chapter` (`book_id`, `chapter_uid`),
|
||||||
|
INDEX `idx_chapters_book_idx` (`book_id`, `chapter_idx`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='章节信息';
|
||||||
|
|
||||||
|
-- 21. 个人想法/点评表(来自 /review/list/mine)
|
||||||
|
CREATE TABLE IF NOT EXISTS `book_reviews` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`review_id` VARCHAR(64) DEFAULT NULL COMMENT '想法唯一 ID',
|
||||||
|
`content` LONGTEXT DEFAULT NULL COMMENT '想法内容文本',
|
||||||
|
`abstract` LONGTEXT DEFAULT NULL COMMENT '想法对应的划线原文 (仅划线想法有值)',
|
||||||
|
`range_text` VARCHAR(64) DEFAULT NULL COMMENT '划线原文位置范围 (如 "2959-3007")',
|
||||||
|
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '章节 UID',
|
||||||
|
`chapter_idx` INT UNSIGNED DEFAULT NULL COMMENT '章节序号',
|
||||||
|
`chapter_name` VARCHAR(255) DEFAULT NULL COMMENT '所在章节名 (章节点评时有值)',
|
||||||
|
`create_time` INT UNSIGNED DEFAULT NULL COMMENT '创建时间 (Unix 时间戳)',
|
||||||
|
`create_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '创建时间 (可读格式)',
|
||||||
|
`star` TINYINT DEFAULT -1 COMMENT '评分 (0-5, -1=无评分)',
|
||||||
|
`is_finish` TINYINT(1) DEFAULT 0 COMMENT '是否书评 (1=书评)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_reviews_review_id` (`review_id`),
|
||||||
|
INDEX `idx_reviews_book_id` (`book_id`),
|
||||||
|
INDEX `idx_reviews_chapter` (`book_id`, `chapter_uid`),
|
||||||
|
INDEX `idx_reviews_create_time` (`create_time` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='个人想法/点评';
|
||||||
|
|
||||||
|
-- 22. 热门划线表(来自 /book/bestbookmarks)
|
||||||
|
CREATE TABLE IF NOT EXISTS `book_best_bookmarks` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`bookmark_id` VARCHAR(64) DEFAULT NULL COMMENT '划线唯一 ID',
|
||||||
|
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '所在章节 UID',
|
||||||
|
`range_text` VARCHAR(64) DEFAULT NULL COMMENT '划线位置范围 (如 "393-401")',
|
||||||
|
`mark_text` LONGTEXT DEFAULT NULL COMMENT '划线原文文本',
|
||||||
|
`total_count` INT UNSIGNED DEFAULT 0 COMMENT '划线人数',
|
||||||
|
`user_vid` VARCHAR(64) DEFAULT NULL COMMENT '代表用户 VID',
|
||||||
|
`simplified_range` VARCHAR(64) DEFAULT NULL COMMENT '简体书籍的 range (繁简体书专属)',
|
||||||
|
`traditional_range` VARCHAR(64) DEFAULT NULL COMMENT '繁体书籍的 range (繁简体书专属)',
|
||||||
|
`synckey` INT UNSIGNED DEFAULT NULL COMMENT '同步 key (数据版本号)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_best_bm_book_bookmark` (`book_id`, `bookmark_id`),
|
||||||
|
INDEX `idx_best_bm_book_id` (`book_id`),
|
||||||
|
INDEX `idx_best_bm_total_count` (`total_count` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='热门划线';
|
||||||
|
|
||||||
|
-- 23. 划线热度统计表(来自 /book/underlines)
|
||||||
|
CREATE TABLE IF NOT EXISTS `book_underlines` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`chapter_uid` INT UNSIGNED NOT NULL COMMENT '章节 UID',
|
||||||
|
`range_text` VARCHAR(64) DEFAULT NULL COMMENT '划线位置范围 (如 "393-401")',
|
||||||
|
`count` INT UNSIGNED DEFAULT 0 COMMENT '划线人数',
|
||||||
|
`score` DOUBLE DEFAULT 0 COMMENT '热度分数',
|
||||||
|
`type` TINYINT DEFAULT NULL COMMENT '划线类型',
|
||||||
|
`synckey` INT UNSIGNED DEFAULT NULL COMMENT '同步 key',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_underlines_book_ch_range` (`book_id`, `chapter_uid`, `range_text`),
|
||||||
|
INDEX `idx_underlines_book_chapter` (`book_id`, `chapter_uid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='划线热度统计';
|
||||||
|
|
||||||
|
-- 24. 公开点评表(来自 /review/list)
|
||||||
|
CREATE TABLE IF NOT EXISTS `public_reviews` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
|
`review_id` VARCHAR(64) DEFAULT NULL COMMENT '点评唯一 ID',
|
||||||
|
`content` LONGTEXT DEFAULT NULL COMMENT '点评文本内容',
|
||||||
|
`html_content` LONGTEXT DEFAULT NULL COMMENT '点评 HTML 富文本内容',
|
||||||
|
`star` TINYINT DEFAULT -1 COMMENT '评分 (20=一星, 40=二星, 60=三星, 80=四星, 100=五星)',
|
||||||
|
`is_finish` TINYINT(1) DEFAULT 0 COMMENT '是否读完此书 (1=读完)',
|
||||||
|
`create_time` INT UNSIGNED DEFAULT NULL COMMENT '创建时间 (Unix 时间戳)',
|
||||||
|
`create_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '创建时间 (可读格式)',
|
||||||
|
`chapter_name` VARCHAR(255) DEFAULT NULL COMMENT '所在章节名 (章节点评时有值)',
|
||||||
|
`author_vid` VARCHAR(64) DEFAULT NULL COMMENT '评论者用户 VID',
|
||||||
|
`author_name` VARCHAR(255) DEFAULT NULL COMMENT '评论者昵称',
|
||||||
|
`author_avatar` VARCHAR(512) DEFAULT NULL COMMENT '评论者头像 URL',
|
||||||
|
`review_list_type` TINYINT DEFAULT 0 COMMENT '筛选类型 (0=全部, 1=推荐, 2=不行, 3=最新, 4=一般)',
|
||||||
|
`idx` INT UNSIGNED DEFAULT NULL COMMENT '序号 (用于翻页)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_public_reviews_review_id` (`review_id`),
|
||||||
|
INDEX `idx_public_reviews_book_id` (`book_id`),
|
||||||
|
INDEX `idx_public_reviews_star` (`star`),
|
||||||
|
INDEX `idx_public_reviews_create_time` (`create_time` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='公开点评';
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- ===== 第四组:书籍详情 =====
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 25. 书籍详细信息表(来自 /book/info)
|
||||||
CREATE TABLE IF NOT EXISTS `book_details` (
|
CREATE TABLE IF NOT EXISTS `book_details` (
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
`title` VARCHAR(255) DEFAULT NULL,
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
||||||
`author` VARCHAR(255) DEFAULT NULL,
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
`translator` VARCHAR(255) DEFAULT NULL COMMENT '译者',
|
`translator` VARCHAR(255) DEFAULT NULL COMMENT '译者',
|
||||||
`cover` VARCHAR(512) DEFAULT NULL,
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
`intro` LONGTEXT DEFAULT NULL COMMENT '简介',
|
`intro` LONGTEXT DEFAULT NULL COMMENT '简介',
|
||||||
`category` VARCHAR(128) DEFAULT NULL,
|
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类',
|
||||||
`publisher` VARCHAR(255) DEFAULT NULL,
|
`publisher` VARCHAR(255) DEFAULT NULL COMMENT '出版社',
|
||||||
`publish_time` VARCHAR(32) DEFAULT NULL COMMENT '出版时间',
|
`publish_time` VARCHAR(32) DEFAULT NULL COMMENT '出版时间',
|
||||||
`isbn` VARCHAR(32) DEFAULT NULL,
|
`isbn` VARCHAR(32) DEFAULT NULL COMMENT 'ISBN 号',
|
||||||
`word_count` INT UNSIGNED DEFAULT 0 COMMENT '总字数',
|
`word_count` INT UNSIGNED DEFAULT 0 COMMENT '总字数',
|
||||||
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分 (百分制)',
|
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分 (百分制)',
|
||||||
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '获取时间',
|
`new_rating_detail` TEXT DEFAULT NULL COMMENT '评分分布详情 (JSON)',
|
||||||
PRIMARY KEY (`book_id`)
|
`deep_link` VARCHAR(512) DEFAULT NULL COMMENT '跳转链接',
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
COMMENT='书籍详细信息';
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`book_id`),
|
||||||
|
INDEX `idx_book_details_isbn` (`isbn`),
|
||||||
|
INDEX `idx_book_details_category` (`category`),
|
||||||
|
INDEX `idx_book_details_publisher` (`publisher`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='书籍详细信息';
|
||||||
|
|
||||||
-- ============================================================
|
-- 26. 阅读进度表(来自 /book/getprogress)
|
||||||
-- 13. 阅读进度(按需获取,来自 /book/getprogress)
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `book_progress` (
|
CREATE TABLE IF NOT EXISTS `book_progress` (
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
`progress` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读进度百分比 (0-100)',
|
`progress` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '阅读进度百分比 (0-100)',
|
||||||
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '当前阅读章节 UID',
|
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '当前阅读章节 UID',
|
||||||
`chapter_offset` INT UNSIGNED DEFAULT NULL COMMENT '当前章节内偏移',
|
`chapter_offset` INT UNSIGNED DEFAULT NULL COMMENT '当前章节内偏移量',
|
||||||
`update_time` INT UNSIGNED DEFAULT NULL COMMENT '最后阅读时间 (Unix 时间戳)',
|
`update_time` INT UNSIGNED DEFAULT NULL COMMENT '最后阅读时间 (Unix 时间戳)',
|
||||||
|
`update_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '最后阅读时间 (可读格式)',
|
||||||
`record_reading_time` INT UNSIGNED DEFAULT 0 COMMENT '累计阅读时长 (秒)',
|
`record_reading_time` INT UNSIGNED DEFAULT 0 COMMENT '累计阅读时长 (秒)',
|
||||||
`finish_time` INT UNSIGNED DEFAULT NULL COMMENT '读完时间 (Unix 时间戳)',
|
`finish_time` INT UNSIGNED DEFAULT NULL COMMENT '读完时间 (Unix 时间戳)',
|
||||||
`is_start_reading` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已开始阅读',
|
`finish_time_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '读完时间 (可读格式)',
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`is_start_reading` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否已开始阅读 (1=是)',
|
||||||
PRIMARY KEY (`book_id`)
|
`server_timestamp` INT UNSIGNED DEFAULT NULL COMMENT '服务端时间戳',
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
`server_timestamp_dt` TIMESTAMP NULL DEFAULT NULL COMMENT '服务端时间 (可读格式)',
|
||||||
COMMENT='阅读进度';
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`book_id`),
|
||||||
|
INDEX `idx_progress_finish_time` (`finish_time` DESC),
|
||||||
|
INDEX `idx_progress_update_time` (`update_time` DESC)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='阅读进度';
|
||||||
|
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 14. 划线内容(按需获取,来自 /book/bookmarklist)
|
-- ===== 第五组:发现/推荐 =====
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
CREATE TABLE IF NOT EXISTS `book_highlights` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
|
||||||
`bookmark_id` VARCHAR(64) DEFAULT NULL COMMENT '划线唯一 ID',
|
|
||||||
`chapter_uid` INT UNSIGNED DEFAULT NULL COMMENT '所在章节 UID',
|
|
||||||
`mark_text` LONGTEXT DEFAULT NULL COMMENT '划线原文',
|
|
||||||
`create_time` INT UNSIGNED DEFAULT NULL COMMENT '创建时间 (Unix 时间戳)',
|
|
||||||
`type` TINYINT DEFAULT NULL COMMENT '类型',
|
|
||||||
`range_text` VARCHAR(64) DEFAULT NULL COMMENT '位置范围',
|
|
||||||
`color_style` TINYINT DEFAULT NULL COMMENT '划线颜色样式',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_highlights_book_id` (`book_id`),
|
|
||||||
INDEX `idx_highlights_chapter` (`book_id`, `chapter_uid`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='划线内容';
|
|
||||||
|
|
||||||
-- ============================================================
|
-- 27. 推荐书籍表(来自 /book/recommend)
|
||||||
-- 15. 章节信息(来自 /book/bookmarklist 的 chapters)
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `book_chapters` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
|
||||||
`chapter_uid` INT UNSIGNED NOT NULL,
|
|
||||||
`chapter_idx` INT UNSIGNED DEFAULT NULL COMMENT '章节序号',
|
|
||||||
`title` VARCHAR(255) DEFAULT NULL COMMENT '章节标题',
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE KEY `uk_book_chapter` (`book_id`, `chapter_uid`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='章节信息';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 16. 个人想法/点评(按需获取,来自 /review/list/mine)
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `book_reviews` (
|
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
|
||||||
`review_id` VARCHAR(64) DEFAULT NULL COMMENT '想法唯一 ID',
|
|
||||||
`content` LONGTEXT DEFAULT NULL COMMENT '内容文本',
|
|
||||||
`create_time` INT UNSIGNED DEFAULT NULL COMMENT '创建时间 (Unix 时间戳)',
|
|
||||||
`star` TINYINT DEFAULT -1 COMMENT '评分 (0-5, -1=无评分)',
|
|
||||||
`chapter_name` VARCHAR(255) DEFAULT NULL COMMENT '所在章节名',
|
|
||||||
`is_finish` TINYINT(1) DEFAULT 0 COMMENT '是否书评',
|
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
INDEX `idx_reviews_book_id` (`book_id`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
|
||||||
COMMENT='个人想法/点评';
|
|
||||||
|
|
||||||
-- ============================================================
|
|
||||||
-- 17. 推荐书籍(按需获取,来自 /book/recommend)
|
|
||||||
-- ============================================================
|
|
||||||
CREATE TABLE IF NOT EXISTS `recommended_books` (
|
CREATE TABLE IF NOT EXISTS `recommended_books` (
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
`title` VARCHAR(255) DEFAULT NULL,
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
||||||
`author` VARCHAR(255) DEFAULT NULL,
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
`cover` VARCHAR(512) DEFAULT NULL,
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
`intro` TEXT DEFAULT NULL,
|
`intro` TEXT DEFAULT NULL COMMENT '书籍简介',
|
||||||
`category` VARCHAR(128) DEFAULT NULL,
|
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类',
|
||||||
`reason` VARCHAR(512) DEFAULT NULL COMMENT '推荐理由',
|
`reason` VARCHAR(512) DEFAULT NULL COMMENT '推荐理由',
|
||||||
`reading_count` INT UNSIGNED DEFAULT 0 COMMENT '在读人数',
|
`reading_count` INT UNSIGNED DEFAULT 0 COMMENT '在读人数',
|
||||||
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分 (百分制)',
|
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分 (百分制)',
|
||||||
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
||||||
`rating_title` VARCHAR(32) DEFAULT NULL COMMENT '评分标签 (神作/力荐等)',
|
`rating_title` VARCHAR(32) DEFAULT NULL COMMENT '评分标签 (神作/力荐等)',
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`deep_link` VARCHAR(512) DEFAULT NULL COMMENT '跳转链接',
|
||||||
|
`search_idx` INT UNSIGNED DEFAULT 0 COMMENT '结果序号 (用于翻页)',
|
||||||
|
`price` INT UNSIGNED DEFAULT NULL COMMENT '价格 (分)',
|
||||||
|
`pay_type` INT UNSIGNED DEFAULT NULL COMMENT '付费类型',
|
||||||
|
`book_type` TINYINT DEFAULT 0 COMMENT '书籍类型 (0=电子书)',
|
||||||
|
`batch_id` VARCHAR(32) DEFAULT NULL COMMENT '推荐批次 ID (同一次推荐请求的标识)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
INDEX `idx_recommended_book_id` (`book_id`),
|
INDEX `idx_recommended_book_id` (`book_id`),
|
||||||
INDEX `idx_recommended_fetched` (`fetched_at` DESC)
|
INDEX `idx_recommended_batch` (`batch_id`),
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
INDEX `idx_recommended_rating` (`new_rating` DESC)
|
||||||
COMMENT='推荐书籍';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐书籍';
|
||||||
|
|
||||||
-- ============================================================
|
-- 28. 相似书推荐表(来自 /book/similar)
|
||||||
-- 18. 搜索结果(按需获取,来自 /store/search)
|
CREATE TABLE IF NOT EXISTS `similar_books` (
|
||||||
-- ============================================================
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`source_book_id` VARCHAR(64) NOT NULL COMMENT '源书籍 ID',
|
||||||
|
`book_id` VARCHAR(64) NOT NULL COMMENT '推荐书籍 ID',
|
||||||
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
||||||
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
|
`deep_link` VARCHAR(512) DEFAULT NULL COMMENT '跳转链接',
|
||||||
|
`idx` INT UNSIGNED DEFAULT NULL COMMENT '结果序号 (用于翻页)',
|
||||||
|
`session_id` VARCHAR(64) DEFAULT NULL COMMENT '翻页会话 ID',
|
||||||
|
`book_json` TEXT DEFAULT NULL COMMENT '书籍完整信息 (JSON)',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_similar_source` (`source_book_id`),
|
||||||
|
INDEX `idx_similar_book_id` (`book_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='相似书推荐';
|
||||||
|
|
||||||
|
-- 29. 搜索会话表(来自 /store/search 的元数据)
|
||||||
|
CREATE TABLE IF NOT EXISTS `search_sessions` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`sid` VARCHAR(64) DEFAULT NULL COMMENT '搜索会话 ID',
|
||||||
|
`keyword` VARCHAR(255) NOT NULL COMMENT '搜索关键词',
|
||||||
|
`scope` INT UNSIGNED DEFAULT NULL COMMENT '搜索类型 (0=全部, 10=电子书, 14=听书等)',
|
||||||
|
`has_more` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否有更多 (1=有)',
|
||||||
|
`max_idx` INT UNSIGNED DEFAULT 0 COMMENT '当前翻页偏移',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
INDEX `idx_search_sessions_keyword` (`keyword`),
|
||||||
|
INDEX `idx_search_sessions_sid` (`sid`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='搜索会话';
|
||||||
|
|
||||||
|
-- 30. 搜索结果表(来自 /store/search 的 results[].books[])
|
||||||
CREATE TABLE IF NOT EXISTS `search_results` (
|
CREATE TABLE IF NOT EXISTS `search_results` (
|
||||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||||
|
`session_id` BIGINT DEFAULT NULL COMMENT '关联搜索会话 ID',
|
||||||
`keyword` VARCHAR(255) NOT NULL COMMENT '搜索关键词',
|
`keyword` VARCHAR(255) NOT NULL COMMENT '搜索关键词',
|
||||||
`book_id` VARCHAR(64) NOT NULL,
|
`book_id` VARCHAR(64) NOT NULL COMMENT '书籍 ID',
|
||||||
`title` VARCHAR(255) DEFAULT NULL,
|
`title` VARCHAR(255) DEFAULT NULL COMMENT '书名',
|
||||||
`author` VARCHAR(255) DEFAULT NULL,
|
`author` VARCHAR(255) DEFAULT NULL COMMENT '作者',
|
||||||
`cover` VARCHAR(512) DEFAULT NULL,
|
`cover` VARCHAR(512) DEFAULT NULL COMMENT '封面图 URL',
|
||||||
`intro` TEXT DEFAULT NULL,
|
`intro` TEXT DEFAULT NULL COMMENT '书籍简介',
|
||||||
`publisher` VARCHAR(255) DEFAULT NULL,
|
`publisher` VARCHAR(255) DEFAULT NULL COMMENT '出版社',
|
||||||
`category` VARCHAR(128) DEFAULT NULL,
|
`category` VARCHAR(128) DEFAULT NULL COMMENT '分类',
|
||||||
`reading_count` INT UNSIGNED DEFAULT 0,
|
`reading_count` INT UNSIGNED DEFAULT 0 COMMENT '在读人数',
|
||||||
`new_rating` DECIMAL(5,1) DEFAULT 0,
|
`new_rating` DECIMAL(5,1) DEFAULT 0 COMMENT '评分 (百分制)',
|
||||||
`new_rating_count` INT UNSIGNED DEFAULT 0,
|
`new_rating_count` INT UNSIGNED DEFAULT 0 COMMENT '评分人数',
|
||||||
`rating_title` VARCHAR(32) DEFAULT NULL,
|
`rating_title` VARCHAR(32) DEFAULT NULL COMMENT '评分标签',
|
||||||
`search_idx` INT UNSIGNED DEFAULT 0 COMMENT '搜索序号',
|
`deep_link` VARCHAR(512) DEFAULT NULL COMMENT '跳转链接',
|
||||||
`fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
`pay_type` INT UNSIGNED DEFAULT NULL COMMENT '付费类型',
|
||||||
|
`price` INT UNSIGNED DEFAULT NULL COMMENT '价格 (分)',
|
||||||
|
`soldout` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否下架 (1=下架)',
|
||||||
|
`search_idx` INT UNSIGNED DEFAULT 0 COMMENT '搜索序号 (用于翻页)',
|
||||||
|
`result_scope` INT UNSIGNED DEFAULT NULL COMMENT '回包分组类型',
|
||||||
|
`result_title` VARCHAR(64) DEFAULT NULL COMMENT '回包分组标题 (如 "电子书")',
|
||||||
|
`scope_count` INT UNSIGNED DEFAULT NULL COMMENT '该分组总结果数',
|
||||||
|
`current_count` INT UNSIGNED DEFAULT NULL COMMENT '本次返回数量',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
INDEX `idx_search_keyword` (`keyword`),
|
INDEX `idx_search_results_keyword` (`keyword`),
|
||||||
INDEX `idx_search_book_id` (`book_id`)
|
INDEX `idx_search_results_book_id` (`book_id`),
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
INDEX `idx_search_results_session` (`session_id`)
|
||||||
COMMENT='搜索结果';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='搜索结果';
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- ===== 第六组:系统表 =====
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 31. 数据采集元信息表
|
||||||
|
CREATE TABLE IF NOT EXISTS `fetch_meta` (
|
||||||
|
`key` VARCHAR(64) NOT NULL COMMENT '元信息键名',
|
||||||
|
`value` TEXT DEFAULT NULL COMMENT '元信息值',
|
||||||
|
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||||
|
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||||
|
PRIMARY KEY (`key`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='数据采集元信息';
|
||||||
Reference in new issue
Block a user