feat: switch to MySQL, add Chinese comments, health check, and technical docs

1. 数据源切换至 MySQL:
   - app.py 从 SQLite 切换到 PyMySQL,所有 API 读取 MySQL weread_dashboard 数据库
   - 新增 db_required 装饰器统一管理数据库连接和异常处理
   - 新增 /api/health 健康检查接口,检测 MySQL 连接状态
   - MySQL 连接失败时返回 HTTP 503 和告警信息
   - 修复 fetched_at 字段 Unix 时间戳到 MySQL TIMESTAMP 的兼容问题

2. 前端数据库连接告警:
   - 页面加载时优先调用 /api/health 检查 MySQL 连接
   - 连接失败时显示固定告警横幅,阻止后续数据加载
   - CSS 告警横幅样式(黄色背景、下滑动画)

3. 代码中文注释:
   - app.py:所有函数、API 接口、工具函数添加完整中文文档注释
   - fetch_data.py:所有 SQLite 建表语句添加列级中文注释
   - migrate_to_mysql.py:保留已有中文注释

4. 技术文档与元数据文档:
   - docs/TECHNICAL_DOC.md:项目概述、系统架构、API 设计、部署指南
   - docs/METADATA_DOC.md:18 张表完整字段说明、索引、表关系、数据类型规范
This commit is contained in:
admin committed 2026-07-17 10:26:07 +08:00
1 parent 72e9971a69
commit 507b9c5704
8 files changed
+1805 -436

No files matched your search

+165 -158
View File
@@ -35,268 +35,275 @@ def api_call(api_name, **kwargs):
def init_db(db_path):
"""初始化数据库表结构"""
"""
初始化 SQLite 数据库表结构。
创建所有数据表(如果不存在),包括:
- 基础数据表:书架、阅读统计、笔记本等(采集时写入)
- 按需数据表:书籍详情、划线、想法等(用户点击时写入)
"""
conn = sqlite3.connect(db_path)
c = conn.cursor()
# 书架书籍表
# ===== 基础数据表(采集脚本写入) =====
# 书架书籍表 - 存储用户书架中的所有书籍信息
c.execute("""
CREATE TABLE IF NOT EXISTS books (
book_id TEXT PRIMARY KEY,
title TEXT,
author TEXT,
cover TEXT,
category TEXT,
finish_reading INTEGER DEFAULT 0,
read_update_time INTEGER,
secret INTEGER DEFAULT 0,
is_top INTEGER DEFAULT 0,
update_time INTEGER,
deep_link TEXT
book_id TEXT PRIMARY KEY, -- 书籍唯一标识
title TEXT, -- 书名
author TEXT, -- 作者
cover TEXT, -- 封面图 URL
category TEXT, -- 分类(如 "文学-古代诗词")
finish_reading INTEGER DEFAULT 0, -- 是否读完 (1=读完, 0=未读完)
read_update_time INTEGER, -- 最近阅读时间 (Unix 时间戳)
secret INTEGER DEFAULT 0, -- 是否私密书籍 (1=私密)
is_top INTEGER DEFAULT 0, -- 是否置顶 (1=置顶)
update_time INTEGER, -- 书籍信息更新时间 (Unix 时间戳)
deep_link TEXT -- 微信读书 App 深度链接
)
""")
# 阅读统计概要表
# 阅读统计概要表 - 存储总体/年度阅读统计数据
c.execute("""
CREATE TABLE IF NOT EXISTS reading_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT,
period TEXT,
total_read_time INTEGER,
read_days INTEGER,
day_average_read_time REAL,
regist_time INTEGER,
raw_json TEXT
mode TEXT, -- 统计维度: overall(总体)/annually(年度)
period TEXT, -- 周期标识: "overall" 或 "2026"
total_read_time INTEGER, -- 总阅读时长 (秒)
read_days INTEGER, -- 有效阅读天数
day_average_read_time REAL, -- 日均阅读时长 (秒)
regist_time INTEGER, -- 用户注册时间 (Unix 时间戳)
raw_json TEXT -- 原始 API 返回 JSON (完整备份)
)
""")
# 阅读时长时间序列(按年/月/日分桶)
# 阅读时长时间序列表 - 按年/月/日分桶的阅读时长数据
c.execute("""
CREATE TABLE IF NOT EXISTS read_times (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT,
period TEXT,
bucket_timestamp INTEGER,
duration INTEGER
mode TEXT, -- 统计维度 (含 _daily 后缀表示每日数据)
period TEXT, -- 周期标识
bucket_timestamp INTEGER, -- 分桶起始时间戳 (Unix 时间戳)
duration INTEGER -- 该时段阅读时长 (秒)
)
""")
# 阅读时长最长的书籍排行
# 阅读时长排行表 - 阅读时间最久的书籍排名
c.execute("""
CREATE TABLE IF NOT EXISTS read_longest (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT,
period TEXT,
book_id TEXT,
title TEXT,
author TEXT,
cover TEXT,
read_time INTEGER,
tags TEXT
mode TEXT, -- 统计维度
period TEXT, -- 周期标识
book_id TEXT, -- 书籍 ID
title TEXT, -- 书名
author TEXT, -- 作者
cover TEXT, -- 封面图 URL
read_time INTEGER, -- 阅读时长 (秒)
tags TEXT -- 标签 (JSON 数组格式)
)
""")
# 偏好分类
# 偏好分类表 - 用户阅读的书籍分类统计
c.execute("""
CREATE TABLE IF NOT EXISTS prefer_categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT,
period TEXT,
category_id INTEGER,
category_title TEXT,
parent_category_title TEXT,
val REAL,
reading_time INTEGER,
reading_count INTEGER
mode TEXT, -- 统计维度
period TEXT, -- 周期标识
category_id INTEGER, -- 分类 ID
category_title TEXT, -- 分类名称 (如 "文学")
parent_category_title TEXT, -- 父分类名称 (如 "精品小说")
val REAL, -- 偏好权重值
reading_time INTEGER, -- 该分类阅读时长 (秒)
reading_count INTEGER -- 该分类阅读本数
)
""")
# 偏好作者
# 偏好作者表 - 用户阅读最多的作者统计
c.execute("""
CREATE TABLE IF NOT EXISTS prefer_authors (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT,
period TEXT,
author_id INTEGER,
name TEXT,
count INTEGER,
read_time TEXT
mode TEXT, -- 统计维度
period TEXT, -- 周期标识
author_id INTEGER, -- 作者 ID
name TEXT, -- 作者名称
count INTEGER, -- 阅读该作者的书本数
read_time TEXT -- 阅读时长 (格式化字符串)
)
""")
# 24小时阅读时段分布
# 24小时阅读时段分布表 - 一天中每个小时的阅读时长
c.execute("""
CREATE TABLE IF NOT EXISTS prefer_time (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT,
period TEXT,
hour_index INTEGER,
duration INTEGER
mode TEXT, -- 统计维度
period TEXT, -- 周期标识
hour_index INTEGER, -- 小时 (0-23)
duration INTEGER -- 该小时阅读时长 (秒)
)
""")
# 笔记本概览
# 笔记本概览表 - 有笔记的书籍及其笔记统计
c.execute("""
CREATE TABLE IF NOT EXISTS notebooks (
book_id TEXT PRIMARY KEY,
title TEXT,
author TEXT,
cover TEXT,
review_count INTEGER DEFAULT 0,
note_count INTEGER DEFAULT 0,
bookmark_count INTEGER DEFAULT 0,
reading_progress INTEGER DEFAULT 0,
marked_status INTEGER DEFAULT 0,
sort INTEGER
book_id TEXT PRIMARY KEY, -- 书籍 ID
title TEXT, -- 书名
author TEXT, -- 作者
cover TEXT, -- 封面图 URL
review_count INTEGER DEFAULT 0, -- 想法/点评数
note_count INTEGER DEFAULT 0, -- 划线数
bookmark_count INTEGER DEFAULT 0, -- 书签数
reading_progress INTEGER DEFAULT 0, -- 阅读进度 (0-100)
marked_status INTEGER DEFAULT 0, -- 标记状态 (1=读完)
sort INTEGER -- 排序值 (微信读书返回的排序字段)
)
""")
# 阅读统计摘要项
# 阅读统计摘要项表 - App 首页展示的统计摘要
c.execute("""
CREATE TABLE IF NOT EXISTS read_stat_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mode TEXT,
period TEXT,
stat_name TEXT,
counts TEXT,
scheme TEXT
mode TEXT, -- 统计维度
period TEXT, -- 周期标识
stat_name TEXT, -- 统计项名称 (读过/读完/阅读/笔记)
counts TEXT, -- 统计值文案 (如 "404本")
scheme TEXT -- App 跳转链接
)
""")
# 书单/归档
# 书单/归档表 - 用户创建的书单
c.execute("""
CREATE TABLE IF NOT EXISTS archives (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
book_ids TEXT
name TEXT, -- 书单名称
book_ids TEXT -- 包含的书籍 ID 列表 (JSON 数组)
)
""")
# 数据采集元信息
# 数据采集元信息表 - 记录采集时间等元数据
c.execute("""
CREATE TABLE IF NOT EXISTS fetch_meta (
key TEXT PRIMARY KEY,
value TEXT
key TEXT PRIMARY KEY, -- 元信息键名 (如 "last_fetch_time")
value TEXT -- 元信息值
)
""")
# ===== 按需获取的详细数据表 =====
# ===== 按需数据表(用户点击时实时获取并写入) =====
# 书籍详细信息(来自 /book/info)
# 书籍详细信息表 - 来自微信读书 /book/info 接口
c.execute("""
CREATE TABLE IF NOT EXISTS book_details (
book_id TEXT PRIMARY KEY,
title TEXT,
author TEXT,
translator TEXT,
cover TEXT,
intro TEXT,
category TEXT,
publisher TEXT,
publish_time TEXT,
isbn TEXT,
word_count INTEGER,
new_rating REAL,
new_rating_count INTEGER,
fetched_at INTEGER
book_id TEXT PRIMARY KEY, -- 书籍 ID
title TEXT, -- 书名
author TEXT, -- 作者
translator TEXT, -- 译者
cover TEXT, -- 封面图 URL
intro TEXT, -- 书籍简介
category TEXT, -- 分类
publisher TEXT, -- 出版社
publish_time TEXT, -- 出版时间
isbn TEXT, -- ISBN 号
word_count INTEGER, -- 总字数
new_rating REAL, -- 评分 (百分制)
new_rating_count INTEGER, -- 评分人数
fetched_at INTEGER -- 数据获取时间 (Unix 时间戳)
)
""")
# 阅读进度(来自 /book/getprogress)
# 阅读进度表 - 来自微信读书 /book/getprogress 接口
c.execute("""
CREATE TABLE IF NOT EXISTS book_progress (
book_id TEXT PRIMARY KEY,
progress INTEGER,
chapter_uid INTEGER,
chapter_offset INTEGER,
update_time INTEGER,
record_reading_time INTEGER,
finish_time INTEGER,
is_start_reading INTEGER,
fetched_at INTEGER
book_id TEXT PRIMARY KEY, -- 书籍 ID
progress INTEGER, -- 阅读进度百分比 (0-100)
chapter_uid INTEGER, -- 当前阅读章节 UID
chapter_offset INTEGER, -- 当前章节内偏移量
update_time INTEGER, -- 最后阅读时间 (Unix 时间戳)
record_reading_time INTEGER, -- 累计阅读时长 (秒)
finish_time INTEGER, -- 读完时间 (Unix 时间戳)
is_start_reading INTEGER, -- 是否已开始阅读 (1=是)
fetched_at INTEGER -- 数据获取时间 (Unix 时间戳)
)
""")
# 划线内容(来自 /book/bookmarklist)
# 划线内容表 - 来自微信读书 /book/bookmarklist 接口
c.execute("""
CREATE TABLE IF NOT EXISTS book_highlights (
id INTEGER PRIMARY KEY AUTOINCREMENT,
book_id TEXT,
bookmark_id TEXT,
chapter_uid INTEGER,
mark_text TEXT,
create_time INTEGER,
type INTEGER,
range_text TEXT,
color_style INTEGER,
fetched_at INTEGER
book_id TEXT, -- 书籍 ID
bookmark_id TEXT, -- 划线唯一 ID
chapter_uid INTEGER, -- 所在章节 UID
mark_text TEXT, -- 划线原文内容
create_time INTEGER, -- 创建时间 (Unix 时间戳)
type INTEGER, -- 划线类型
range_text TEXT, -- 位置范围
color_style INTEGER, -- 划线颜色样式
fetched_at INTEGER -- 数据获取时间 (Unix 时间戳)
)
""")
# 章节信息(来自 /book/bookmarklist 的 chapters)
# 章节信息表 - 来自微信读书 /book/bookmarklist 接口的 chapters 字段
c.execute("""
CREATE TABLE IF NOT EXISTS book_chapters (
id INTEGER PRIMARY KEY AUTOINCREMENT,
book_id TEXT,
chapter_uid INTEGER,
chapter_idx INTEGER,
title TEXT,
UNIQUE(book_id, chapter_uid)
book_id TEXT, -- 书籍 ID
chapter_uid INTEGER, -- 章节 UID
chapter_idx INTEGER, -- 章节序号
title TEXT, -- 章节标题
UNIQUE(book_id, chapter_uid) -- 联合唯一约束
)
""")
# 个人想法/点评(来自 /review/list/mine)
# 个人想法/点评表 - 来自微信读书 /review/list/mine 接口
c.execute("""
CREATE TABLE IF NOT EXISTS book_reviews (
id INTEGER PRIMARY KEY AUTOINCREMENT,
book_id TEXT,
review_id TEXT,
content TEXT,
create_time INTEGER,
star INTEGER,
chapter_name TEXT,
is_finish INTEGER,
fetched_at INTEGER
book_id TEXT, -- 书籍 ID
review_id TEXT, -- 想法唯一 ID
content TEXT, -- 想法内容文本
create_time INTEGER, -- 创建时间 (Unix 时间戳)
star INTEGER, -- 评分 (0-5, -1=无评分)
chapter_name TEXT, -- 所在章节名称
is_finish INTEGER, -- 是否书评 (1=书评)
fetched_at INTEGER -- 数据获取时间 (Unix 时间戳)
)
""")
# 推荐书籍(来自 /book/recommend)
# 推荐书籍表 - 来自微信读书 /book/recommend 接口
c.execute("""
CREATE TABLE IF NOT EXISTS recommended_books (
id INTEGER PRIMARY KEY AUTOINCREMENT,
book_id TEXT,
title TEXT,
author TEXT,
cover TEXT,
intro TEXT,
category TEXT,
reason TEXT,
reading_count INTEGER,
new_rating REAL,
new_rating_count INTEGER,
rating_title TEXT,
fetched_at INTEGER
book_id TEXT, -- 书籍 ID
title TEXT, -- 书名
author TEXT, -- 作者
cover TEXT, -- 封面图 URL
intro TEXT, -- 书籍简介
category TEXT, -- 分类
reason TEXT, -- 推荐理由
reading_count INTEGER, -- 在读人数
new_rating REAL, -- 评分 (百分制)
new_rating_count INTEGER, -- 评分人数
rating_title TEXT, -- 评分标签 (神作/力荐等)
fetched_at INTEGER -- 数据获取时间 (Unix 时间戳)
)
""")
# 搜索结果(来自 /store/search)
# 搜索结果表 - 来自微信读书 /store/search 接口
c.execute("""
CREATE TABLE IF NOT EXISTS search_results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT,
book_id TEXT,
title TEXT,
author TEXT,
cover TEXT,
intro TEXT,
publisher TEXT,
category TEXT,
reading_count INTEGER,
new_rating REAL,
new_rating_count INTEGER,
rating_title TEXT,
search_idx INTEGER,
fetched_at INTEGER
keyword TEXT, -- 搜索关键词
book_id TEXT, -- 书籍 ID
title TEXT, -- 书名
author TEXT, -- 作者
cover TEXT, -- 封面图 URL
intro TEXT, -- 书籍简介
publisher TEXT, -- 出版社
category TEXT, -- 分类
reading_count INTEGER, -- 在读人数
new_rating REAL, -- 评分 (百分制)
new_rating_count INTEGER, -- 评分人数
rating_title TEXT, -- 评分标签
search_idx INTEGER, -- 搜索序号
fetched_at INTEGER -- 数据获取时间 (Unix 时间戳)
)
""")