OncoLit: a multi-tenant oncology literature search, feed, and collaboration platform. Built with FastAPI + Vue 3 + PostgreSQL. Includes PubMed pipeline, drug approvals, AI summaries, and systematic review tools.
60 KiB
数据库设计
日期时间类型规范
全部使用 TIMESTAMPTZ。固定日期字段(出生日期、发表日期、批准日期、排期日期)用 DATE。
-- ✅ 正确
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
pub_date DATE -- 文献发表日期不精确到时间
-- ❌ 禁止
created_at TIMESTAMP -- 不知道时区
created_at INTEGER -- Unix时间戳,人不可读,SQL 中日期运算困难
ER 关系总图
tenants ──1:N── user_tenants ──N:1── users
│ │
├── teams ──N:M── team_members ├── user_subscriptions ── global_tags
├── invitations ├── user_journal_subscriptions ── global_journals
├── export_jobs ├── user_feed ── global_literature
├── shared_folders ├── user_literature ── global_literature
├── journal_club_queue ├── user_notes ── global_literature
├── approval_workflows ├── user_pdf_highlights
├── api_keys ├── user_folders
├── tenant_subscriptions ├── user_feedback
├── subscription_events └── systematic_reviews ── review_literatures
├── payment_orders
└── ai_provider_config
global_literature ──N:M── global_literature_tags ──N:M── global_tags
│ │
├── drug_approvals(pmid) ├── drug_approvals(cancer_type_tag)
├── guideline_evidence ├── guideline_versions(cancer_type_tag)
├── review_literatures └── user_subscriptions(tag)
└── global_journals(issn)
user_journal_subscriptions ──N:1── global_journals
mdt_cases ──1:N── mdt_decisions ──1:N── mdt_evidence
└── global_literature
一、基础架构表
1.1 tenants — 租户
CREATE TABLE tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 租户ID
name VARCHAR(200) NOT NULL, -- 租户名称
slug VARCHAR(100) UNIQUE NOT NULL, -- 唯一标识符(用于URL)
hospital_name VARCHAR(200), -- 医院名称
department_name VARCHAR(200), -- 科室名称
plan_type VARCHAR(30) NOT NULL DEFAULT 'free', -- 套餐类型:free/pro/team/enterprise
is_personal BOOLEAN NOT NULL DEFAULT TRUE, -- 是否个人租户
status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active/inactive/suspended
logo_url VARCHAR(500), -- Logo 图片 URL
primary_color VARCHAR(7) DEFAULT '#1a5276', -- 主题色(十六进制)
settings JSONB NOT NULL DEFAULT '{}', -- 租户级 JSON 配置
max_members INTEGER NOT NULL DEFAULT 1, -- 最大成员数
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
1.2 users — 用户
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 用户ID
email VARCHAR(320) NOT NULL UNIQUE, -- 邮箱(登录账号)
hashed_password VARCHAR(128), -- 密码哈希(SSO用户可为NULL)
display_name VARCHAR(150) NOT NULL, -- 显示名称
title VARCHAR(100), -- 职称/头衔
avatar_url VARCHAR(500), -- 头像 URL
phone VARCHAR(30), -- 手机号
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- 是否激活
is_superuser BOOLEAN NOT NULL DEFAULT FALSE, -- 是否超级管理员
sso_provider VARCHAR(20), -- SSO 提供商:wechat/oauth
sso_subject_id VARCHAR(255), -- SSO 主体 ID
email_verified BOOLEAN NOT NULL DEFAULT FALSE, -- 邮箱是否已验证
phone_verified BOOLEAN NOT NULL DEFAULT FALSE, -- 手机是否已验证
notify_email BOOLEAN NOT NULL DEFAULT TRUE, -- 是否接收邮件通知
digest_frequency VARCHAR(10) NOT NULL DEFAULT 'daily', -- 摘要频率:daily/weekly/never
last_login_at TIMESTAMPTZ, -- 最后登录时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_users_email ON users(email);
用户通知偏好(notify_email, digest_frequency)和验证状态(email_verified, phone_verified)存储在 users 表内,不单独建通知表。
1.3 user_tenants — 用户-租户关联
CREATE TABLE user_tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 关联ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
role VARCHAR(20) NOT NULL DEFAULT 'viewer', -- 角色:viewer/member/admin/owner
is_default BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为该用户的默认租户
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 加入时间
UNIQUE (user_id, tenant_id)
);
CREATE INDEX idx_ut_user ON user_tenants(user_id);
CREATE INDEX idx_ut_user_tenant ON user_tenants(user_id, tenant_id);
1.4 login_logs — 登录日志
CREATE TABLE login_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 日志ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
tenant_id UUID REFERENCES tenants(id) ON DELETE SET NULL, -- 租户ID(可能为空)
ip_address VARCHAR(45), -- 登录IP(支持IPv6)
user_agent VARCHAR(500), -- 用户代理字符串
success BOOLEAN NOT NULL DEFAULT TRUE, -- 是否登录成功
fail_reason VARCHAR(200), -- 失败原因
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 登录时间
);
CREATE INDEX idx_ll_user_created ON login_logs(user_id, created_at);
二、文献核心表(全局库,所有租户共享)
2.1 global_literature — 全局文献库
CREATE TABLE global_literature (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 文献ID(内部)
pmid INTEGER UNIQUE NOT NULL, -- PubMed ID 【PubMed: MedlineCitation/PMID】
title TEXT NOT NULL, -- 标题 【PubMed: Article/ArticleTitle】
abstract TEXT, -- 摘要 【PubMed: Article/Abstract/AbstractText】
authors JSONB NOT NULL DEFAULT '[]', -- 作者列表 [{family,given,affiliations}] 【PubMed: Article/AuthorList/Author】
doi VARCHAR(500), -- DOI 标识符 【PubMed: Article/ELocationID[@EIdType="doi"] 或 ArticleId[@IdType="doi"]】
journal VARCHAR(500), -- 期刊全名 【PubMed: Article/Journal/Title】
journal_issn VARCHAR(20), -- 期刊 ISSN 【PubMed: Article/Journal/ISSN】
journal_iso VARCHAR(100), -- NLM ISO 缩写(如 "N Engl J Med")【PubMed: Article/Journal/ISOAbbreviation】
volume VARCHAR(50), -- 卷号 【PubMed: Article/Journal/JournalIssue/Volume】
issue VARCHAR(50), -- 期号 【PubMed: Article/Journal/JournalIssue/Issue】
pages VARCHAR(100), -- 页码 【PubMed: Article/Pagination/MedlinePgn】
pub_date DATE, -- 纸质出版日期(期刊卷期日期,若纯电子刊则为电子日期)【PubMed: Article/Journal/JournalIssue/PubDate 组合 Year+Month+Day】
pub_year INTEGER, -- 出版年份 【PubMed: 从 PubDate/Year 提取】
pub_types JSONB NOT NULL DEFAULT '[]', -- 出版类型列表 【PubMed: Article/PublicationTypeList/PublicationType】
mesh_headings JSONB NOT NULL DEFAULT '[]', -- MeSH 标引词列表(含 DescriptorName+QualifierName)【PubMed: MeshHeadingList/MeshHeading】
language VARCHAR(10) DEFAULT 'en', -- 语言 【PubMed: Article/Language】
raw_xml_hash VARCHAR(64), -- 原始 XML SHA-256 哈希(去重与增量更新用,非 PubMed 字段)
pmc_id VARCHAR(20), -- PMC ID(PMC1234567)【PubMed: ArticleId[@IdType="pmc"]】
is_oa BOOLEAN NOT NULL DEFAULT FALSE, -- 是否开放获取【根据 pmc_id 或 PMC OA API 判定,非 PubMed 原始字段】
keywords JSONB NOT NULL DEFAULT '[]', -- 作者关键词 【PubMed: Article/KeywordList/Keyword】
pubmed_revised TIMESTAMPTZ, -- PubMed 数据库修订日期(NLM 说明不可靠)【PubMed: MedlineCitation/DateRevised】
grants JSONB NOT NULL DEFAULT '[]', -- 基金资助信息 【PubMed: Article/GrantList/Grant】
cited_by_count INTEGER NOT NULL DEFAULT 0, -- 被引次数 【来自 NCBI elink 接口 pubmed_pubmed_citedin,非 PMID 固定字段】
full_text_sections JSONB, -- PMC OA 全文结构化段落【来自 PMC OA Service API JATS XML,非 PubMed EFetch】
full_text_path VARCHAR(255), -- COS 对象存储键(内部)
trial_reg JSONB, -- 试验注册号 {nct, eudract, chictr} 【PubMed: Article/DataBankList/DataBank,注册号在 ArticleId[@IdType="ClinicalTrials.gov"] 中也有】
study_design JSONB, -- 研究设计分类 {primary: "RCT", sub: "Phase 3", design: "parallel"}(AI 推断,非 PubMed 原始字段)
pico JSONB, -- PICO 结构化抽取(AI 推断,非 PubMed 原始字段)
citation_status VARCHAR(20), -- MedlineCitation 状态 【PubMed: MedlineCitation/@Status 属性,如 "MEDLINE"/"PubMed-not-MEDLINE"/"In-Data-Review"】
date_completed TIMESTAMPTZ, -- MeSH 标引完成日期 【PubMed: MedlineCitation/DateCompleted】
retracted BOOLEAN NOT NULL DEFAULT FALSE, -- 是否已撤稿【依据 PubMed: Article/PublicationTypeList/PublicationType[.="Retracted Publication"]】
retraction_details JSONB, -- 撤稿详情(关联撤稿文献等)【PubMed: CommentsCorrectionsList/CommentsCorrections[@RefType="retractionof"/"retractionin"]】
license_info JSONB, -- 许可证信息(来自 PMC OA XML 或出版商元数据,非 PubMed 原始字段)
pre_extracted_data JSONB, -- Table 1 预提取数据(AI 抽取,非 PubMed 原始字段)
is_negative_result BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为阴性结果(AI 判定,非 PubMed 原始字段)
rct_detection JSONB, -- RCT 自动检测结果 {is_rct, confidence, source}(AI,非 PubMed 原始字段)
search_tsv TSVECTOR, -- 全文检索向量(内部 PG tsvector,非 PubMed 字段)
negative_result_details JSONB, -- 阴性结果详情(AI 抽取,非 PubMed 原始字段)
ai_summary JSONB, -- AI 摘要(内部,非 PubMed 字段)
chemical_list JSONB NOT NULL DEFAULT '[]', -- 化学物质列表 【PubMed: ChemicalList/Chemical】
gene_symbols JSONB NOT NULL DEFAULT '[]', -- 基因符号列表 【PubMed: SupplMeshList/SupplMeshName;部分来自 Gene Symbol 或 ChemicalList】
num_refs INTEGER, -- 参考文献数量(来自 PMC 全文或解析,非 PubMed EFetch 原始字段)
publication_status VARCHAR(30), -- 出版状态 【PubMed: PubmedData/PublicationStatus,取值 epublish/ppublish/aheadofprint】
article_date DATE, -- 电子出版日期(先于印刷版)【PubMed: Article/ArticleDate】
databank_list JSONB NOT NULL DEFAULT '[]', -- 数据库引用列表(如 ClinicalTrials.gov)【PubMed: Article/DataBankList/DataBank】
suppl_mesh_list JSONB NOT NULL DEFAULT '[]', -- 补充 MeSH 词列表(含物质名)【PubMed: SupplMeshList/SupplMeshName】
source VARCHAR(30) NOT NULL DEFAULT 'pubmed_ftp', -- 数据来源(内部追踪用:pubmed_api/pubmed_ftp,非 PubMed 字段)
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_gl_pub_date ON global_literature(pub_date);
CREATE INDEX idx_gl_pub_year ON global_literature(pub_year);
CREATE INDEX idx_gl_journal_issn ON global_literature(journal_issn);
CREATE INDEX idx_gl_journal_iso ON global_literature(journal_iso);
CREATE INDEX idx_gl_created ON global_literature(created_at);
CREATE INDEX idx_gl_pmc_id ON global_literature(pmc_id);
CREATE INDEX idx_gl_search_tsv ON global_literature USING gin(search_tsv);
预估值:约 600 万条(PubMed 中肿瘤相关的历史累积),单表不需要分区
全文搜索:
search_tsv(TSVECTOR类型,GIN 索引ix_gl_search_tsv)由 PG 触发器自动维护,包含 title + abstract + authors.family + authors.affiliation。所有搜索(普通搜索field="all"和高级搜索字段选择中的作者/机构)均走 tsvector@@plainto_tsquery(),ILIKE 仅作为兜底(NULL tsvector 记录)。触发器trg_global_literature_tsv在 INSERT 或 UPDATE title/abstract 时触发重建。千万级无压力。GIN 索引重建说明: 迁移
f80dca5baa02(add_pico_column_to_review_literatures)的upgrade()误将ix_gl_search_tsv删除后未重建,导致所有 tsvector 搜索走全表扫描。迁移0314f4d28728(rebuild_search_tsv_gin_index)已修复,紧跟在e341edea85e2之后。
日期字段说明: pub_date(印刷/电子出版日期)+ article_date(电子出版日期,实际上线时间)+ date_completed(MeSH 标引完成日,检索质量控制用)+ pubmed_revised(NLM 定义为不可靠)+ publication_status(epublish / ppublish / aheadofprint 出版阶段标识)。
PubMed 对应 XML 结构速查:
| 表字段 | PubMed XML Path | 说明 |
|---|---|---|
pmid |
MedlineCitation/PMID |
Pubmed ID |
title |
Article/ArticleTitle |
文章标题 |
abstract |
Article/Abstract/AbstractText |
多个 AbstractText 拼接,按 Label 区分 |
authors |
Article/AuthorList/Author |
LastName + ForeName + AffiliationInfo |
doi |
ArticleId[@IdType="doi"] |
优先取 PubmedData 下的 ArticleIdList |
journal |
Article/Journal/Title |
期刊全名 |
journal_issn |
Article/Journal/ISSN |
注意区分 Print / Electronic |
journal_iso |
Article/Journal/ISOAbbreviation |
NLM 标准缩写 |
volume |
Article/Journal/JournalIssue/Volume |
卷 |
issue |
Article/Journal/JournalIssue/Issue |
期 |
pages |
Article/Pagination/MedlinePgn |
页码范围 |
pub_date |
Article/Journal/JournalIssue/PubDate |
仅取 Year/Month/Day 组合 |
pub_year |
PubDate/Year |
单独提取年份 |
pub_types |
Article/PublicationTypeList/PublicationType |
可多个,含 "Retracted Publication" |
mesh_headings |
MeshHeadingList/MeshHeading |
DescriptorName(UI) + QualifierName(UI) |
language |
Article/Language |
2-3 字母 ISO 语言码 |
pmc_id |
ArticleId[@IdType="pmc"] |
带 "PMC" 前缀 |
keywords |
Article/KeywordList/Keyword |
作者关键词(非 MeSH) |
pubmed_revised |
MedlineCitation/DateRevised |
不可靠,不应视为修订依据 |
grants |
Article/GrantList/Grant |
GrantID + Agency + Country |
chemical_list |
ChemicalList/Chemical |
RegistryNumber + NameOfSubstance(UI) |
suppl_mesh_list |
SupplMeshList/SupplMeshName |
补充概念词 |
databank_list |
Article/DataBankList/DataBank |
DataBankName + AccessionNumberList |
trial_reg |
Article/DataBankList/DataBank |
从中提取 NCT/EudraCT/ChiCTR |
citation_status |
MedlineCitation/@Status |
"MEDLINE","PubMed-not-MEDLINE","In-Data-Review","PubMed" |
date_completed |
MedlineCitation/DateCompleted |
MeSH 标引完成日 |
retraction_details |
CommentsCorrectionsList/CommentsCorrections[@RefType="retractionof" or "retractionin"] |
撤稿关联关系 |
publication_status |
PubmedData/PublicationStatus |
"epublish" / "ppublish" / "aheadofprint" |
article_date |
Article/ArticleDate |
电子出版日期(印刷版之前的日期) |
2.2 global_tags — 肿瘤科标签树
CREATE TABLE global_tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 标签ID
mesh_ui VARCHAR(20), -- MeSH 唯一标识符(UI)
name_zh VARCHAR(200) NOT NULL, -- 中文名称
name_en VARCHAR(200), -- 英文名称
path VARCHAR(500) NOT NULL, -- 标签路径(如 /癌种/肺癌/非小细胞肺癌)
level INTEGER NOT NULL DEFAULT 1, -- 层级深度
parent_id UUID REFERENCES global_tags(id) ON DELETE CASCADE, -- 父标签ID
tree_number VARCHAR(100), -- MeSH 树编号
tag_category VARCHAR(30) NOT NULL DEFAULT 'cancer', -- 标签分类
sort_order INTEGER DEFAULT 0, -- 排序序号
icon VARCHAR(50), -- 图标名
is_selectable BOOLEAN NOT NULL DEFAULT TRUE, -- 是否可选(叶子节点)
article_count INTEGER DEFAULT 0, -- 关联文献数
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_tags_path ON global_tags(path);
CREATE INDEX idx_tags_parent ON global_tags(parent_id);
CREATE INDEX idx_tags_category ON global_tags(tag_category);
CREATE INDEX idx_tags_mesh_ui ON global_tags(mesh_ui) WHERE mesh_ui IS NOT NULL;
tag_category 枚举:cancer(癌种) | histology(组织分型) | gene(驱动基因) | treatment(治疗方式) | study_type(研究类型) | endpoint(临床终点) | scenario(临床场景) | journal_tier(期刊等级)
2.3 global_literature_tags — 文献标签关联
CREATE TABLE global_literature_tags (
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE CASCADE, -- 标签ID
is_major BOOLEAN NOT NULL DEFAULT FALSE, -- 是否为主要主题(MeSH Major Topic)
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
PRIMARY KEY (literature_id, tag_id)
);
CREATE INDEX idx_glt_tag ON global_literature_tags(tag_id);
预估值:约 6000 万行(600万×10标签),B-tree 索引足够,不需要分区。两种查询都命中索引。
2.4 global_journals — 期刊库
CREATE TABLE global_journals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 期刊ID
name VARCHAR(500) NOT NULL, -- 期刊名称
issn VARCHAR(20), -- 印刷版 ISSN
eissn VARCHAR(20), -- 电子版 ISSN
tier VARCHAR(5) NOT NULL DEFAULT '4', -- 期刊等级:1/2/3/4
impact_factor DOUBLE PRECISION, -- 影响因子
publisher VARCHAR(300), -- 出版商
article_count INTEGER DEFAULT 0, -- 收录文献数
is_active BOOLEAN DEFAULT TRUE, -- 是否活跃
priority_score REAL, -- 综合优先级评分 0-100
priority_source VARCHAR(20) DEFAULT 'auto', -- 优先级来源:auto/manual
specialty VARCHAR(50), -- 专科领域:oncology
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 更新时间
UNIQUE (issn),
UNIQUE (name)
);
CREATE INDEX idx_gj_tier ON global_journals(tier);
CREATE INDEX idx_gj_priority ON global_journals(priority_score DESC);
权重评分规则(priority_score):
| 维度 | 规则 | 最高加分 |
|---|---|---|
| Tier 基础分 | 1→80, 2→65, 3→50, 4→20 | 80 |
| IF 加成分 | ≥20→+15, ≥10→+10, ≥5→+5, ≥2→+2 | 15 |
| 收录量加成分 | ≥5000→+10, ≥1000→+5, ≥100→+2 | 10 |
| 专科加成分 | specialty='oncology'→+10 | 10 |
封顶 100 分。priority_source='manual' 时自动任务跳过该行。用户关注的期刊查询时 +20。
specialty 自动判定: Tier 1-3 → oncology;name 包含 oncol/cancer/tumor/neoplas → oncology;其余 NULL。
三、用户交互表
3.1 user_subscriptions — 关注领域
CREATE TABLE user_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订阅ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE CASCADE, -- 标签ID
match_mode VARCHAR(10) NOT NULL DEFAULT 'standard', -- 匹配模式:loose/standard/strict
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- 是否激活
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 更新时间
UNIQUE (user_id, tag_id)
);
CREATE INDEX idx_us_user_active ON user_subscriptions(user_id, is_active);
CREATE INDEX idx_us_tag_active ON user_subscriptions(tag_id, is_active);
match_mode:loose(任一命中) | standard(主标签命中,默认) | strict(全部命中)
3.2 user_feed — 每日推送 ⚠️ 需要分区
CREATE TABLE user_feed (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- FeedID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
matched_tags JSONB NOT NULL DEFAULT '[]', -- 命中的标签列表
priority VARCHAR(20) NOT NULL DEFAULT 'related', -- 优先级:must_read/recommended/related
is_read BOOLEAN NOT NULL DEFAULT FALSE, -- 是否已读
read_at TIMESTAMPTZ, -- 阅读时间
is_dismissed BOOLEAN NOT NULL DEFAULT FALSE, -- 是否已忽略
pushed_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 推送时间
);
CREATE INDEX idx_uf_user_push ON user_feed(user_id, pushed_at);
CREATE INDEX idx_uf_user_priority ON user_feed(user_id, is_dismissed, priority);
CREATE INDEX idx_uf_user_lit ON user_feed(user_id, literature_id);
预估值:年增数千万行。当前模型未分区,生产需改为按月分区 + 自动删除旧分区(保留最近3个月)。查询永远只走最新分区。
3.3 user_literature — 个人收藏
CREATE TABLE user_literature (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 收藏ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
folder_id UUID REFERENCES user_folders(id) ON DELETE SET NULL, -- 所属收藏夹
reading_status VARCHAR(20) NOT NULL DEFAULT 'unread', -- 阅读状态:unread/reading/read
rating INTEGER CHECK (rating >= 1 AND rating <= 5), -- 评分 1-5
personal_tags JSONB DEFAULT '[]', -- 个人标签
saved_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 收藏时间
read_started_at TIMESTAMPTZ, -- 开始阅读时间
read_finished_at TIMESTAMPTZ, -- 读完时间
UNIQUE (user_id, literature_id)
);
CREATE INDEX idx_ul_user_saved ON user_literature(user_id, saved_at);
CREATE INDEX idx_ul_literature ON user_literature(literature_id);
3.4 user_folders — 收藏夹
CREATE TABLE user_folders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 收藏夹ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
name VARCHAR(200) NOT NULL, -- 收藏夹名称
description TEXT, -- 描述
color VARCHAR(7) DEFAULT '#808080', -- 颜色(十六进制)
is_shared BOOLEAN NOT NULL DEFAULT FALSE, -- 是否共享
sort_order INTEGER DEFAULT 0, -- 排序序号
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_folders_user ON user_folders(user_id);
CREATE INDEX idx_folders_shared ON user_folders(tenant_id, is_shared) WHERE is_shared = TRUE;
3.5 user_notes — 阅读笔记
CREATE TABLE user_notes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 笔记ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
content TEXT NOT NULL, -- 笔记内容
quote_text TEXT, -- 引用的原文
is_private BOOLEAN NOT NULL DEFAULT TRUE, -- 是否私密
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_un_user ON user_notes(user_id, created_at);
CREATE INDEX idx_un_literature ON user_notes(literature_id);
3.6 user_pdf_highlights — PDF批注
CREATE TABLE user_pdf_highlights (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 批注ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
highlight_type VARCHAR(20) NOT NULL, -- 批注类型:highlight/underline/strikeout
page_number INTEGER NOT NULL, -- 页码
position JSONB NOT NULL, -- 位置坐标
text TEXT, -- 批注文本
note TEXT, -- 批注笔记
color VARCHAR(7) DEFAULT '#ffff00', -- 高亮颜色
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_hl_user_lit ON user_pdf_highlights(user_id, literature_id);
3.7 user_feedback — 文献反馈
CREATE TABLE user_feedback (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 反馈ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
literature_id UUID REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID(可为NULL)
feedback_type VARCHAR(30) NOT NULL, -- 反馈类型
detail TEXT, -- 反馈详情
is_resolved BOOLEAN DEFAULT FALSE, -- 是否已处理
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
UNIQUE (user_id, feedback_type)
);
literature_id可为 NULL(反馈不一定针对具体文献,也可以是针对搜索结果或系统功能)。UNIQUE (user_id, feedback_type)确保同一用户不会重复提交同类型反馈。
3.8 user_journal_subscriptions — 期刊关注
CREATE TABLE user_journal_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订阅ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
journal_id UUID NOT NULL REFERENCES global_journals(id) ON DELETE CASCADE, -- 期刊ID
is_active BOOLEAN DEFAULT TRUE, -- 是否激活
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX ix_ujs_user_active ON user_journal_subscriptions(user_id, is_active);
CREATE INDEX ix_ujs_journal ON user_journal_subscriptions(journal_id);
关注/取消关注采用软删除(
is_active),与user_subscriptions模式一致。关注后,该期刊的新文献会自动推送到用户 Feed。
3.9 user_activity_logs — 用户活动日志
CREATE TABLE user_activity_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 日志ID
user_id UUID REFERENCES users(id) ON DELETE SET NULL, -- 用户ID
tenant_id UUID REFERENCES tenants(id) ON DELETE SET NULL, -- 租户ID
activity_type VARCHAR(50) NOT NULL, -- 活动类型
target_type VARCHAR(30), -- 目标类型(literature/search等)
target_id VARCHAR(200), -- 目标ID
detail JSONB, -- 详情 JSON
ip_address VARCHAR(45), -- 客户端IP
user_agent VARCHAR(500), -- 用户代理
session_id VARCHAR(100), -- 会话ID
duration_seconds INTEGER, -- 停留时长(秒)
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE INDEX ix_ual_user_created ON user_activity_logs(user_id, created_at);
CREATE INDEX ix_ual_type_created ON user_activity_logs(activity_type, created_at);
CREATE INDEX ix_ual_target ON user_activity_logs(target_type, target_id);
activity_type 枚举: page_view(页面访问) | search(搜索) | view_literature(查看文献) | save_literature(收藏) | export(导出) | browse_journals(浏览期刊) | click_tag(点击标签)
前端通过
router.afterEach自动追踪page_view;关键交互(搜索、收藏、导出等)通过trackAction()手动埋点。未登录用户不记录。配合 login_logs(登录安全)和 api_usage_logs(API 调用)构成完整的四维分析体系。
3.10 user_dismissed_tags — 用户负偏好标签(不感兴趣学习)
CREATE TABLE user_dismissed_tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 记录ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE CASCADE, -- 标签ID
dismiss_count INTEGER NOT NULL DEFAULT 1, -- 不感兴趣次数
last_dismissed_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 最后不感兴趣时间
UNIQUE(user_id, tag_id)
);
CREATE INDEX ix_udt_user_count ON user_dismissed_tags(user_id, dismiss_count);
说明: 用户点"不感兴趣"后,系统提取该文章的标签累计到 dismiss_count。当某标签的 dismiss_count >= 3 时,Feed 引擎不再为用户推送该标签下的文章。可在管理后台或用户设置中重置。user_id + tag_id 唯一约束确保每个用户对每个标签只有一条累计记录。
四、团队协作表
4.1 teams + team_members — 团队与成员
CREATE TABLE teams (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 团队ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
name VARCHAR(200) NOT NULL, -- 团队名称
description TEXT, -- 描述
created_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 创建者
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_teams_tenant ON teams(tenant_id);
CREATE TABLE team_members (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 成员ID
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE, -- 团队ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
role VARCHAR(20) NOT NULL DEFAULT 'member', -- 角色:member/lead
joined_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 加入时间
UNIQUE (user_id, team_id)
);
CREATE INDEX idx_tm_user_team ON team_members(user_id, team_id);
CREATE INDEX idx_tm_team ON team_members(team_id);
4.2 invitations + shared_folders — 邀请与共享
CREATE TABLE invitations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 邀请ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
inviter_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 邀请人
email VARCHAR(320) NOT NULL, -- 被邀请人邮箱
role VARCHAR(20) NOT NULL DEFAULT 'viewer', -- 被邀请角色
token VARCHAR(200) UNIQUE NOT NULL, -- 邀请令牌
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/accepted/expired
expires_at TIMESTAMPTZ NOT NULL, -- 过期时间
accepted_at TIMESTAMPTZ, -- 接受时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE TABLE shared_folders (
folder_id UUID NOT NULL REFERENCES user_folders(id) ON DELETE CASCADE, -- 收藏夹ID
target_type VARCHAR(20) NOT NULL, -- 共享目标类型:team/user
target_id UUID NOT NULL, -- 共享目标ID
permission VARCHAR(20) NOT NULL DEFAULT 'view', -- 权限:view/edit
created_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 共享发起人
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
PRIMARY KEY (folder_id, target_type, target_id)
);
4.3 journal_club_queue — Journal Club 文献排队
CREATE TABLE journal_club_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 排队ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
suggested_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 建议人
title VARCHAR(500), -- 讨论标题
scheduled_date DATE, -- 排期日期
presenter_id UUID REFERENCES users(id) ON DELETE SET NULL, -- 主讲人
status VARCHAR(20) NOT NULL DEFAULT 'queued', -- 状态:queued/completed/cancelled
discussion_notes TEXT, -- 讨论纪要
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 更新时间
UNIQUE (tenant_id, literature_id, scheduled_date)
);
CREATE INDEX idx_jc_tenant ON journal_club_queue(tenant_id, status);
五、业务扩展表
5.1 drug_approvals — FDA/NMPA/EMA 药品审批
CREATE TABLE drug_approvals (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 审批ID
drug_name VARCHAR(500) NOT NULL, -- 药品商品名
generic_name VARCHAR(500) NOT NULL, -- 通用名/活性成分
target VARCHAR(200), -- 靶点/作用机制
indication TEXT NOT NULL, -- 获批适应症
cancer_type_tag_id UUID REFERENCES global_tags(id) ON DELETE SET NULL, -- 关联癌种标签
approval_agency VARCHAR(10) NOT NULL, -- 审批机构:FDA/NMPA/EMA
approval_type VARCHAR(30), -- 审批类型:regular/accelerated/breakthrough
approval_date DATE NOT NULL, -- 批准日期
pivotal_trial_nct VARCHAR(20), -- 关键试验 NCT 号
pivotal_trial_pmid INTEGER, -- 关键试验 PMID(非外键)
source_url VARCHAR(1000), -- 数据来源 URL
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE INDEX idx_da_agency_date ON drug_approvals(approval_agency, approval_date);
pivotal_trial_pmid是普通 INTEGER 而非外键:因为试验可能在文献入库前就已记录,删除文献也不应级联删除审批记录。
5.2 guideline_versions + guideline_evidence — 指南版本与证据
CREATE TABLE guideline_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 指南版本ID
guideline_source VARCHAR(10) NOT NULL, -- 指南来源:NCCN/ASCO/CSCO
cancer_type_tag_id UUID NOT NULL REFERENCES global_tags(id) ON DELETE RESTRICT, -- 关联癌种标签
version VARCHAR(30) NOT NULL, -- 版本号
publish_date DATE NOT NULL, -- 发布日期
change_summary_zh TEXT, -- 中文变更摘要
change_summary_en TEXT, -- 英文变更摘要
pdf_url VARCHAR(1000), -- PDF 下载 URL
source_url VARCHAR(1000), -- 来源 URL
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
UNIQUE (guideline_source, cancer_type_tag_id, version)
);
CREATE TABLE guideline_evidence (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 证据ID
guideline_version_id UUID NOT NULL REFERENCES guideline_versions(id) ON DELETE CASCADE, -- 指南版本ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
recommendation TEXT, -- 推荐意见
recommendation_level VARCHAR(10), -- 推荐级别:1A/1B/2A/2B/3
treatment_line VARCHAR(20), -- 治疗线数:first/second/third
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
UNIQUE (guideline_version_id, literature_id)
);
guideline_versions.cancer_type_tag_id使用ON DELETE RESTRICT:指南指向的癌种标签禁止被删除。
六、审批表(企业版)
6.1 approval_workflows — 审批工作流
CREATE TABLE approval_workflows (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 工作流ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
submitted_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 提交人
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/approved/rejected
current_step INTEGER NOT NULL DEFAULT 1, -- 当前步骤
total_steps INTEGER NOT NULL DEFAULT 1, -- 总步骤数
comment TEXT, -- 备注
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE TABLE approval_steps (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 步骤ID
workflow_id UUID NOT NULL REFERENCES approval_workflows(id) ON DELETE CASCADE, -- 工作流ID
step_order INTEGER NOT NULL, -- 步骤序号
approver_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 审批人
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/approved/rejected
comment TEXT, -- 审批意见
decided_at TIMESTAMPTZ, -- 审批时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
6.2 ai_provider_config — AI 供应商配置
CREATE TABLE ai_provider_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 配置ID
provider VARCHAR(50) NOT NULL, -- AI 供应商:deepseek/openai/claude
api_key VARCHAR(500) NOT NULL, -- API Key(加密存储)
base_url VARCHAR(500) NOT NULL, -- API 基础 URL
model_name VARCHAR(100) NOT NULL, -- 模型名称
temperature INTEGER NOT NULL DEFAULT 3, -- 温度参数(0-10,展示为0.0-1.0)
is_active BOOLEAN NOT NULL DEFAULT TRUE, -- 是否激活
updated_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 最后修改人
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
七、运营与基础设施表
7.1 pipeline_runs — 数据管道日志
CREATE TABLE pipeline_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 运行ID
run_type VARCHAR(20) NOT NULL, -- 运行类型:daily/weekly/manual
status VARCHAR(20) NOT NULL DEFAULT 'running', -- 状态:running/completed/failed
files_processed INTEGER DEFAULT 0, -- 处理文件数
articles_total INTEGER DEFAULT 0, -- 总文献数
articles_new INTEGER DEFAULT 0, -- 新增文献数
articles_updated INTEGER DEFAULT 0, -- 更新文献数
articles_deleted INTEGER DEFAULT 0, -- 删除文献数
articles_filtered INTEGER DEFAULT 0, -- 过滤文献数
users_matched INTEGER DEFAULT 0, -- 匹配用户数
feeds_generated INTEGER DEFAULT 0, -- 生成 Feed 数
error_log TEXT, -- 错误日志
started_at TIMESTAMPTZ, -- 开始时间
completed_at TIMESTAMPTZ, -- 完成时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE INDEX idx_pr_type ON pipeline_runs(run_type, created_at);
7.2 tenant_subscriptions — Stripe 订阅
CREATE TABLE tenant_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订阅ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE UNIQUE, -- 租户ID
stripe_customer_id VARCHAR(100), -- Stripe 客户ID
stripe_subscription_id VARCHAR(100), -- Stripe 订阅ID
plan_id VARCHAR(50), -- 套餐ID
status VARCHAR(30) NOT NULL DEFAULT 'active', -- 状态:active/past_due/canceled
current_period_start TIMESTAMPTZ NOT NULL, -- 当前计费周期开始
current_period_end TIMESTAMPTZ NOT NULL, -- 当前计费周期结束
cancel_at_period_end BOOLEAN DEFAULT FALSE, -- 是否到期取消
trial_end TIMESTAMPTZ, -- 试用结束时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_ts_stripe_sub ON tenant_subscriptions(stripe_subscription_id);
CREATE TABLE subscription_events ( -- 订阅事件日志
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 事件ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
event_type VARCHAR(30) NOT NULL, -- 事件类型
from_plan VARCHAR(30), -- 原套餐
to_plan VARCHAR(30), -- 目标套餐
stripe_event_id VARCHAR(100), -- Stripe 事件ID
metadata JSONB, -- 附加元数据
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE INDEX idx_se_tenant ON subscription_events(tenant_id);
7.3 payment_orders — 支付订单
CREATE TABLE payment_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 订单ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
plan VARCHAR(30) NOT NULL, -- 套餐:pro/team/enterprise
amount INTEGER NOT NULL, -- 金额(分)
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/paid/expired/cancelled
out_trade_no VARCHAR(64) UNIQUE NOT NULL, -- 商户订单号
prepay_id VARCHAR(128), -- 预支付ID(微信)
code_url VARCHAR(512), -- 支付二维码 URL
pay_time TIMESTAMPTZ, -- 支付时间
transaction_id VARCHAR(64), -- 支付平台交易ID
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
CREATE INDEX idx_po_tenant ON payment_orders(tenant_id);
7.4 api_keys + api_usage_logs — API密钥与使用日志
CREATE TABLE api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 密钥ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 创建者
name VARCHAR(200) NOT NULL, -- 密钥名称
key_prefix VARCHAR(20) NOT NULL, -- 密钥前缀(便于识别)
key_hash VARCHAR(128) NOT NULL, -- 密钥哈希
scopes JSONB DEFAULT '[]', -- 权限范围
last_used_at TIMESTAMPTZ, -- 最后使用时间
expires_at TIMESTAMPTZ, -- 过期时间
is_active BOOLEAN DEFAULT TRUE, -- 是否激活
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE INDEX idx_ak_tenant_active ON api_keys(tenant_id, is_active);
CREATE TABLE api_usage_logs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 日志ID
api_key_id UUID REFERENCES api_keys(id) ON DELETE SET NULL, -- API密钥ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
endpoint VARCHAR(300) NOT NULL, -- 请求端点
method VARCHAR(10) NOT NULL, -- HTTP 方法
status_code INTEGER, -- HTTP 状态码
response_ms INTEGER, -- 响应时间(毫秒)
ip_address VARCHAR(45), -- 请求IP
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE INDEX idx_aul_tenant ON api_usage_logs(tenant_id);
CREATE INDEX idx_aul_tenant_created ON api_usage_logs(tenant_id, created_at);
7.5 export_jobs — 导出任务
CREATE TABLE export_jobs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 任务ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 用户ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
export_format VARCHAR(10) NOT NULL, -- 导出格式:ris/bibtex/csv/pdf
citation_style VARCHAR(50), -- 引文格式
filter_params JSONB, -- 筛选参数
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- 状态:pending/processing/done/failed
file_url VARCHAR(1000), -- 文件下载 URL
file_size_bytes INTEGER, -- 文件大小(字节)
error_message TEXT, -- 错误信息
total_items INTEGER, -- 导出条目数
completed_at TIMESTAMPTZ, -- 完成时间
expires_at TIMESTAMPTZ, -- 文件过期时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
CREATE INDEX idx_ej_user_status ON export_jobs(user_id, status);
7.6 system_notifications — 系统通知
CREATE TABLE system_notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 通知ID
title VARCHAR(300) NOT NULL, -- 通知标题
content TEXT, -- 通知内容
target_type VARCHAR(20) NOT NULL DEFAULT 'all', -- 目标类型:all/tenant/user
target_id UUID, -- 目标ID(对应租户或用户)
is_read BOOLEAN DEFAULT FALSE, -- 是否已读
read_at TIMESTAMPTZ, -- 阅读时间
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
八、系统评价 / Meta 分析
8.1 systematic_reviews — 系统评价项目
CREATE TABLE systematic_reviews (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 评价项目ID
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, -- 创建者
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
title VARCHAR(500) NOT NULL, -- 评价标题
objective TEXT, -- 研究目的
protocol TEXT, -- 研究方案
inclusion_criteria JSONB DEFAULT '[]', -- 纳入标准 [str]
exclusion_criteria JSONB DEFAULT '[]', -- 排除标准 [str]
databases_searched JSONB DEFAULT '[]', -- 检索数据库 [str]
search_strategy JSONB DEFAULT '{}', -- 检索策略 {pubmed: "..."}
prisma_stage VARCHAR(20) DEFAULT 'identification', -- PRISMA 阶段
status VARCHAR(20) DEFAULT 'draft', -- 状态:draft/active/completed/archived
notes TEXT, -- 备注
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
prisma_stage 流转:identification → screening → eligibility → included
status:draft / active / completed / archived
8.2 review_literatures — 系统评价文献
CREATE TABLE review_literatures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 记录ID
review_id UUID NOT NULL REFERENCES systematic_reviews(id) ON DELETE CASCADE, -- 系统评价ID
literature_id UUID NOT NULL REFERENCES global_literature(id) ON DELETE CASCADE, -- 文献ID
stage VARCHAR(20) NOT NULL DEFAULT 'screening', -- 当前阶段
exclusion_reason TEXT, -- 排除原因
notes TEXT, -- 备注
quality_score VARCHAR(10), -- 质量评分:high/moderate/low/unclear
quality_detail JSONB DEFAULT '{}', -- 质量评估细节 {domain: "low", ...}
pico JSONB, -- 评价级 PICO(与文献库隔离)
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
pico字段与global_literature.pico隔离:同一篇文献被多个系统评价引用时,各评价可独立编辑 PICO。
九、MDT 多学科协作
9.1 mdt_cases — MDT 病例
CREATE TABLE mdt_cases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 病例ID
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, -- 租户ID
title VARCHAR(300) NOT NULL, -- 病例标题
patient_summary TEXT NOT NULL, -- 患者摘要
cancer_type VARCHAR(100), -- 癌种(冗余字段,用于快速筛选)
status VARCHAR(20) DEFAULT 'open', -- 状态:open/closed
created_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 创建者
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), -- 创建时间
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 更新时间
);
9.2 mdt_decisions — MDT 决策
CREATE TABLE mdt_decisions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 决策ID
case_id UUID NOT NULL REFERENCES mdt_cases(id) ON DELETE CASCADE, -- 关联病例
conclusion TEXT NOT NULL, -- 决策结论
meeting_date TIMESTAMPTZ, -- 会议时间
participants JSONB DEFAULT '[]', -- 参会人员 [{user_id, display_name, role}]
decided_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 决策者
decided_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 决策时间
);
9.3 mdt_evidence — MDT 引用证据
CREATE TABLE mdt_evidence (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- 证据ID
case_id UUID NOT NULL REFERENCES mdt_cases(id) ON DELETE CASCADE, -- 关联病例
decision_id UUID REFERENCES mdt_decisions(id) ON DELETE SET NULL, -- 关联决策
literature_id UUID REFERENCES global_literature(id) ON DELETE SET NULL, -- 关联文献(可为NULL)
pmid INTEGER, -- PubMed ID(备用)
citation TEXT, -- 引文文本
relevance_note TEXT, -- 相关性说明
added_by UUID REFERENCES users(id) ON DELETE SET NULL, -- 添加人
created_at TIMESTAMPTZ NOT NULL DEFAULT now() -- 创建时间
);
literature_id和pmid双字段:文献可能尚未入库(只有 PMID),此时先存 PMID,待 pipeline 灌入后关联。
十、全表统计数据
| # | 表名 | 预估数据量 | 说明 |
|---|---|---|---|
| 1 | tenants | 百~千 | |
| 2 | users | 千~万 | |
| 3 | user_tenants | 千~万 | |
| 4 | login_logs | 万~百万 | |
| 5 | global_literature | ~600万 | B-tree 索引足够 |
| 6 | global_tags | ~800 | |
| 7 | global_literature_tags | ~6000万 | B-tree 索引足够 |
| 8 | global_journals | ~9000 | priority_score DESC 索引 |
| 9 | user_subscriptions | 千~万 | |
| 10 | user_journal_subscriptions | 千~万 | |
| 11 | user_feed | 年增数千万 | ⚠️ 生产需按月分区,保留3个月 |
| 12 | user_literature | 万~百万 | |
| 13 | user_folders | 千~万 | |
| 14 | user_notes | 万~十万 | |
| 15 | user_pdf_highlights | 万~十万 | |
| 16 | user_feedback | 千 | |
| 17 | teams | 千 | |
| 18 | team_members | 千~万 | |
| 19 | invitations | 千 | |
| 20 | shared_folders | 千 | |
| 21 | journal_club_queue | 千 | |
| 22 | drug_approvals | ~500/年 | |
| 23 | guideline_versions | ~200/年 | |
| 24 | guideline_evidence | 万 | |
| 25 | approval_workflows | 千 | |
| 26 | approval_steps | 千 | |
| 27 | ai_provider_config | ~5 | |
| 28 | pipeline_runs | ~365/年 | |
| 29 | tenant_subscriptions | 百~千 | |
| 29 | subscription_events | 千 | |
| 30 | payment_orders | 千 | |
| 31 | api_keys | 百 | |
| 32 | api_usage_logs | 百万 | 按月分区(可选) |
| 33 | system_notifications | 千 | |
| 34 | systematic_reviews | 百~千 | |
| 35 | review_literatures | 千~万 | |
| 36 | mdt_cases | 百~千 | |
| 37 | mdt_decisions | 百~千 | |
| 38 | mdt_evidence | 千 | |
| 39 | export_jobs | 千 |