数据库结构文档

Rin 使用 SQLite 数据库(通过 Drizzle ORM),共包含 12 张表。

表概览

表名说明
users用户表
feeds文章/动态表
moments朋友圈/动态表
comments评论表
visits访问记录表
visit_stats访问统计表
friends友链表
hashtags标签表
feed_hashtags文章-标签关联表
info系统配置表
cache缓存表
linked_accounts第三方登录绑定表

users(用户表)

存储用户信息,支持账号密码、OAuth、QQ、邮箱等多种登录方式。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
usernametextNOT NULL, UNIQUE用户名,全局唯一
openidtextNOT NULLOAuth OpenID,格式为 provider:openid(如 github:12345、qq:abcdef)
emailtextDEFAULT ""邮箱地址(用于邮箱验证码登录)
avatartextNULLABLE头像 URL
passwordtextNULLABLE密码哈希(账号密码登录使用),SHA-256 无盐哈希,64 位十六进制小写字符串
permissionintegerDEFAULT 0权限级别,0 为普通用户
created_atintegerDEFAULT (unixepoch())创建时间戳(Unix 时间戳)
updated_atintegerDEFAULT (unixepoch())更新时间戳(Unix 时间戳)

索引:

  • 唯一索引 users_username_unique ON username

注意:

  • 同一用户可通过多种方式登录(GitHub + QQ + 邮箱),openid 用于区分不同登录方式
  • email 字段用于邮箱验证码登录,登录后 username 为完整邮箱地址

密码哈希规则:

  • users.password 保存的是 SHA-256(无盐) 哈希值,格式为 64 位十六进制小写字符串,服务端登录时对输入的密码做同样的哈希计算后比对
  • 需要手动在 D1 数据库中为用户设置密码时,必须写入加密后的哈希值,直接填写明文密码会导致该账号无法登录
  • 计算哈希值(Linux):
printf '%s' '你的明文密码' | sha256sum | cut -d' ' -f1
  • 计算哈希值(macOS):
printf '%s' '你的明文密码' | shasum -a 256 | cut -d' ' -f1
  • 更新 D1 中的密码:
UPDATE users
SET password = '上一步得到的哈希值'
WHERE username = '目标用户名';

feeds(文章/动态表)

存储博客文章内容。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
aliastextNULLABLEURL 别名(自定义slug)
titletextNULLABLE文章标题
summarytextDEFAULT ''文章摘要
ai_summarytextDEFAULT ''AI 生成的摘要
ai_summary_statustextDEFAULT 'idle'AI 摘要状态:idle/processing/done/error
ai_summary_errortextDEFAULT ''AI 摘要生成错误信息
contenttextNOT NULL文章内容(Markdown)
listedintegerDEFAULT 1是否在列表页显示,1=显示,0=隐藏
draftintegerDEFAULT 1是否为草稿,1=草稿,0=已发布
topintegerDEFAULT 0是否置顶,1=置顶,0=普通
uidintegerNOT NULL作者用户 ID,关联 users.id
created_atintegerDEFAULT (unixepoch())创建时间戳
updated_atintegerDEFAULT (unixepoch())更新时间戳

关系:

  • uid → users.id(文章属于一个用户)

moments(动态/朋友圈表)

存储用户发布的短动态。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
contenttextNOT NULL动态内容
uidintegerNOT NULL发布用户 ID,关联 users.id
created_atintegerDEFAULT (unixepoch())创建时间戳
updated_atintegerDEFAULT (unixepoch())更新时间戳

关系:

  • uid → users.id(动态属于一个用户)

comments(评论表)

存储文章评论,支持游客评论。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
feed_idintegerNOT NULL关联文章 ID,关联 feeds.id,删除时级联删除
user_idintegerNULLABLE评论用户 ID,关联 users.id,删除时级联删除,游客评论为空
contenttextNOT NULL评论内容
guest_nametextDEFAULT ''游客名称(游客评论时使用)
guest_emailtextDEFAULT ''游客邮箱(游客评论时使用)
guest_websitetextDEFAULT ''游客网站(游客评论时使用)
approvedintegerDEFAULT 1是否审核通过,1=已通过,0=待审核
created_atintegerDEFAULT (unixepoch())创建时间戳
updated_atintegerDEFAULT (unixepoch())更新时间戳

关系:

  • feed_id → feeds.id ON DELETE CASCADE
  • user_id → users.id ON DELETE CASCADE(可为空)

注意:

  • 游客评论时 user_id 为空,使用 guest_name/guest_email/guest_website 记录游客信息

visits(访问记录表)

记录每篇文章的每次访问。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
feed_idintegerNOT NULL关联文章 ID,关联 feeds.id,删除时级联删除
iptextNOT NULL访问者 IP 地址
created_atintegerDEFAULT (unixepoch())访问时间戳

关系:

  • feed_id → feeds.id ON DELETE CASCADE

注意:

  • 此表记录原始访问数据,统计时通过 visit_stats 表聚合

visit_stats(访问统计表)

存储文章的访问统计信息,使用 HyperLogLog 去重。

字段名类型约束说明
feed_idintegerPRIMARY KEY关联文章 ID(同时为主键),关联 feeds.id,删除时级联删除
pvintegerDEFAULT 0页面浏览量(PV)
hll_datatextDEFAULT ''HyperLogLog 数据,用于高效统计独立访客(UV)
updated_atintegerDEFAULT (unixepoch())更新时间戳

关系:

  • feed_id → feeds.id ON DELETE CASCADE

friends(友链表)

存储友情链接。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
nametextNOT NULL友链名称
desctextNULLABLE友链描述
avatartextNOT NULL友链头像 URL
urltextNOT NULL友链地址
uidintegerNOT NULL提交用户 ID,关联 users.id,删除时级联删除
acceptedintegerDEFAULT 0是否已接受,1=已接受,0=待审核
healthtextDEFAULT ''健康状态(用于链接检测)
sort_orderintegerDEFAULT 0排序顺序,数字越小越靠前
grouptextDEFAULT '',NOT NULL友链分组,空字符串表示默认分组
created_atintegerDEFAULT (unixepoch())创建时间戳
updated_atintegerDEFAULT (unixepoch())更新时间戳

关系:

  • uid → users.id ON DELETE CASCADE

hashtags(标签表)

存储文章标签。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
nametextNOT NULL标签名称
created_atintegerDEFAULT (unixepoch())创建时间戳
updated_atintegerDEFAULT (unixepoch())更新时间戳

feed_hashtags(文章-标签关联表)

关联文章和标签的多对多关系。

字段名类型约束说明
feed_idintegerNOT NULL文章 ID,关联 feeds.id,删除时级联删除
hashtag_idintegerNOT NULL标签 ID,关联 hashtags.id,删除时级联删除
created_atintegerDEFAULT (unixepoch())创建时间戳
updated_atintegerDEFAULT (unixepoch())更新时间戳

关系:

  • feed_id → feeds.id ON DELETE CASCADE
  • hashtag_id → hashtags.id ON DELETE CASCADE

注意:

  • 复合主键为 (feed_id, hashtag_id),确保同一文章不会重复添加同一标签

info(系统配置表)

存储系统键值对配置。

字段名类型约束说明
keytextNOT NULL, UNIQUE配置键名
valuetextNOT NULL配置值

索引:

  • 唯一索引 info_key_unique ON key

常见 key 示例:

  • site.name - 网站名称
  • site.description - 网站描述
  • site.avatar - 网站头像
  • site.page_size - 分页大小
  • rss - RSS 启用状态
  • 其他运行时配置

cache(缓存表)

存储应用缓存数据。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
keytextNOT NULL缓存键
valuetextNOT NULL缓存值(JSON 字符串)
typetextDEFAULT 'cache'缓存类型,用于分类(如 cache、server_config、client_config)
created_atintegerDEFAULT (unixepoch())创建时间戳
updated_atintegerDEFAULT (unixepoch())更新时间戳
expires_atintegerNULL过期时间戳(Unix 时间),NULL 表示永不过期

约束:

  • 复合唯一约束 cache_key_type_unique ON (key, type)

索引:

  • idx_cache_type ON type
  • idx_cache_key ON key

邮件自动友链申请使用的 type 值:

  • friend.email:以申请人邮箱为 key,存储其在途申请的状态(阶段、操作类型、JSON 载荷、验证码、截止时间等)。
  • friend.email.lock:固定 key 为 global,用于实现「同一时间仅允许一个申请」的并发锁。

友链分组使用的 type='client.config' 键:

  • friend_groups:管理员自定义的分组名 JSON 数组(不含默认分组)。
  • friend_group_order:控制友链展示顺序的 JSON 数组,空字符串元素代表默认分组(「朋友们」),新建分组默认插入到它之后。

linked_accounts(第三方登录绑定表)

存储用户绑定的第三方登录账号(GitHub、Gitee、QQ、WeChat 等)。

字段名类型约束说明
idintegerPRIMARY KEY, NOT NULL自增主键
user_idintegerNOT NULL用户 ID,关联 users.id
providertextNOT NULL登录提供商,如 github、gitee、qq、wechat
provider_idtextNOT NULL提供商侧的用户标识(OpenID 等)
linked_atintegerDEFAULT (unixepoch())绑定时间戳

关系:

  • user_id → users.id ON DELETE CASCADE

注意:

  • 同一用户可绑定多个第三方账号
  • 同一第三方账号不可绑定到多个用户

表关系图

users (1) ──< (N) feeds
                 ├──< (N) comments
                 ├──< (N) friends
                 └──< (N) linked_accounts

feeds (1) ──< (N) comments
feeds (1) ──< (N) visits
feeds (1) ──< (1) visit_stats
feeds (N) >──< (N) hashtags (via feed_hashtags)

moments (N) >── (1) users

直接修改数据库注意事项

  1. 使用 Drizzle ORM 迁移:修改表结构时,应生成 Drizzle 迁移文件而非直接执行 SQL
  2. 禁止 DROP/RENAME:Cloudflare D1 不支持 DROP TABLE 和 RENAME TABLE,迁移需使用 ALTER TABLE 添加列
  3. 时间戳格式:所有时间戳使用 Unix 时间戳(整数,单位秒)
  4. 外键约束:SQLite 默认不强制外键约束,应用层负责维护数据一致性
  5. 游客评论:comments.user_id 可为空,删除对应用户时级联删除该用户的所有评论

sqlite_sequence 表

sqlite_sequence 是 SQLite 内部系统表,不需要手动创建或修改。

作用:

  • 记录使用 AUTOINCREMENT 的表的当前最大 ROWID
  • 确保 INTEGER PRIMARY KEY AUTOINCREMENT 列的值单调递增,避免 ROWID 复用
  • 每次插入新行时,SQLite 会自动从该表读取并更新序列值

注意:

  • 这是 SQLite 自动维护的内部表,不应直接修改
  • 如果某张表的序列值出现异常,可使用 UPDATE sqlite_sequence SET seq = N WHERE name = 'table_name' 修复,但通常不需要