Browse Source

feat(articles): update stroke risk factors section and screenshot plan

Reframe stroke risk factors to focus on root causes of atherosclerosis (inflammation, TMAO, insulin resistance) rather than hypertension as standalone factor. Update SCREENSHOT-TODO.md with comprehensive audit of all 32 articles. Add reading time tracking plan and spec docs.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Xiaogang Liao 1 month ago
parent
commit
b00f279f69

+ 456 - 0
docs/superpowers/plans/2026-07-28-article-reading-time-tracking-plan.md

@@ -0,0 +1,456 @@
+# 文章阅读时长自动跟踪 实施计划
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** 去除文章详情页的"阅读完成"按钮,改为自动跟踪阅读时长,用户离开页面时上报
+
+**Architecture:** 后端新增累计式API(按article_id+child_id+DATE(read_at) upsert),前端利用onShow/onHide生命周期+App级前后台切换实现可见性感知计时,每30秒自动同步,离开时最终上报
+
+**Tech Stack:** Spring Boot 2.7 + MyBatis-Plus / uni-app Vue 2 小程序
+
+**设计文档:** `docs/superpowers/specs/2026-07-28-article-reading-time-tracking-design.md`
+
+## Global Constraints
+
+- 后端接口统一使用 `@PostMapping`
+- 小程序禁止可选链 `?.`(用 `&&` 替代)、禁止 CSS Grid(用 flexbox)
+- Vue 2 Options API,禁止 Composition API
+- 所有迁移必须幂等(try-catch 忽略已存在的列/表)
+
+---
+### Task 1: 数据库迁移 + 实体更新
+
+**Files:**
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/entity/ArticleReadingRecord.java`
+- Modify: `cfc-backend/src/main/resources/schema.sql`
+
+**Interfaces:**
+- Consumes: 现有 `article_reading_records` 表定义(DatabaseInitializer 第 3946 行)
+- Produces: `ArticleReadingRecord.articleId` 字段, `article_reading_records.article_id` 列, `article_reading_records.updated_at` 列
+
+- [ ] **Step 1: DatabaseInitializer 添加迁移**
+
+在 DatabaseInitializer 末尾(搜索 `// 迁移` 找最新编号),添加:
+
+```java
+// 迁移N: article_reading_records表添加article_id和updated_at列
+try {
+    jdbcTemplate.execute("ALTER TABLE article_reading_records ADD COLUMN article_id BIGINT DEFAULT NULL COMMENT '文章ID' AFTER id");
+    log.info("已添加article_id列到article_reading_records表");
+} catch (Exception e) {
+    // 列已存在,忽略
+}
+try {
+    jdbcTemplate.execute("ALTER TABLE article_reading_records ADD COLUMN updated_at DATETIME DEFAULT NULL COMMENT '更新时间' AFTER read_at");
+    log.info("已添加updated_at列到article_reading_records表");
+} catch (Exception e) {
+    // 列已存在,忽略
+}
+try {
+    jdbcTemplate.execute("ALTER TABLE article_reading_records ADD INDEX idx_article_child_date (article_id, child_id, read_at)");
+    log.info("已添加idx_article_child_date索引");
+} catch (Exception e) {
+    // 索引已存在,忽略
+}
+```
+
+- [ ] **Step 2: ArticleReadingRecord 实体添加字段**
+
+```java
+// 在 content 字段后添加
+private Long articleId;
+// 在 readAt 字段后添加
+private Date updatedAt;
+```
+
+- [ ] **Step 3: 同步 schema.sql**
+
+在 schema.sql 的 `article_reading_records` CREATE TABLE 语句中添加 `article_id` 和 `updated_at` 列:
+
+```sql
+CREATE TABLE IF NOT EXISTS article_reading_records (
+    id BIGINT AUTO_INCREMENT PRIMARY KEY,
+    article_id BIGINT DEFAULT NULL COMMENT '文章ID',
+    user_id BIGINT COMMENT '用户ID',
+    child_id BIGINT NOT NULL COMMENT '孩子ID',
+    content VARCHAR(500) COMMENT '阅读内容摘要',
+    duration_seconds INT DEFAULT 0 COMMENT '阅读时长(秒)',
+    read_at DATETIME COMMENT '阅读时间',
+    updated_at DATETIME DEFAULT NULL COMMENT '更新时间',
+    created_at DATETIME,
+    INDEX idx_child_id (child_id),
+    INDEX idx_read_at (read_at),
+    INDEX idx_article_child_date (article_id, child_id, read_at)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章阅读记录表';
+```
+
+- [ ] **Step 4: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile
+```
+
+---
+### Task 2: 后端累计式阅读时长上报 API
+
+**Files:**
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/controller/content/ArticleController.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/service/ArticleService.java`
+
+**Interfaces:**
+- Consumes: `ArticleReadingRecord` entity, `ArticleReadingRecordMapper`
+- Produces: `POST /api/articles/report-reading-time` 端点
+
+- [ ] **Step 1: ArticleService 添加 reportReadingTime 方法**
+
+```java
+public void reportReadingTime(Long articleId, Long userId, Long childId, int durationSeconds) {
+    if (childId == null) return;
+    
+    // 查找当日已有记录(同文章+同孩子)
+    LambdaQueryWrapper<ArticleReadingRecord> wrapper = new LambdaQueryWrapper<ArticleReadingRecord>()
+            .eq(ArticleReadingRecord::getArticleId, articleId)
+            .eq(ArticleReadingRecord::getChildId, childId)
+            .apply("DATE(read_at) = CURDATE()")
+            .last("LIMIT 1");
+    ArticleReadingRecord existing = articleReadingRecordMapper.selectOne(wrapper);
+    
+    if (existing != null) {
+        // 累加时长
+        existing.setDurationSeconds(existing.getDurationSeconds() + durationSeconds);
+        existing.setUpdatedAt(new Date());
+        articleReadingRecordMapper.updateById(existing);
+    } else {
+        // 新建记录
+        ArticleReadingRecord record = new ArticleReadingRecord();
+        record.setArticleId(articleId);
+        record.setUserId(userId);
+        record.setChildId(childId);
+        record.setDurationSeconds(durationSeconds);
+        record.setReadAt(new Date());
+        record.setCreatedAt(new Date());
+        articleReadingRecordMapper.insert(record);
+    }
+}
+```
+
+- [ ] **Step 2: ArticleController 添加端点**
+
+```java
+@PostMapping("/report-reading-time")
+public Result<Void> reportReadingTime(@RequestBody Map<String, Object> body,
+                                       @RequestAttribute(value = "userId", required = false) Long userId) {
+    Long articleId = Long.valueOf(body.get("articleId").toString());
+    int durationSeconds = body.get("durationSeconds") != null
+            ? Integer.parseInt(body.get("durationSeconds").toString()) : 0;
+    Long childId = body.get("childId") != null
+            ? Long.valueOf(body.get("childId").toString()) : null;
+    
+    articleService.reportReadingTime(articleId, userId, childId, durationSeconds);
+    return Result.success(null);
+}
+```
+
+- [ ] **Step 3: 编译验证**
+
+```bash
+cd cfc-backend && mvn clean compile
+```
+
+---
+### Task 3: 前端 — 删除按钮 + 答题 + 旧记录逻辑
+
+**Files:**
+- Modify: `cfc-frontend/pages/article-center/article-detail.vue`
+
+**Interfaces:**
+- Consumes: 现有 article-detail.vue 完整组件
+- Produces: 干净的文章详情页(无"阅读完成"按钮、无答题界面、无 quiz/result 状态)
+
+- [ ] **Step 1: 删除模板中的按钮和答题界面**
+
+从 template 中移除:
+- `read-btn` 按钮(第 58-61 行)
+- 整个 `quiz-container` 区块(第 65-98 行)
+- 整个 `result-container` 区块(第 100-109 行)
+- 保留 `share-btn` 和底部栏
+
+底部栏变为(只保留分享):
+```html
+<!-- 底部 -->
+<view v-if="!showQuiz && !showResult" class="detail-footer">
+  <button class="share-btn" open-type="share" @click="onShareTap">
+    <text class="share-btn-icon">📤</text>
+    <text class="share-btn-text">分享</text>
+  </button>
+</view>
+```
+
+注意同时删除外层 `v-if="!showQuiz && !showResult"` 条件(因为不再有 quiz/result 状态),改为直接显示。
+
+- [ ] **Step 2: 删除 script 中的相关数据和方法**
+
+从 data 中删除:
+- `readSeconds`, `readTimer`(将在 Task 4 重新以不同方式添加)
+- `showQuiz`, `quizLoading`, `quizError`, `quizErrorMsg`, `quizQuestions`, `quizRecordId`
+- `selectedAnswers`, `showResult`, `resultScore`, `resultTotal`, `resultPoints`
+
+从 computed 中删除:`canSubmit`
+
+从 methods 中删除:
+- `startTimer()`, `stopTimer()`(将在 Task 4 重新实现)
+- `onReadComplete()`
+- `loadQuiz()`, `onSubmitQuiz()`
+- `selectAnswer()`, `getOptionLetter()`, `getOptionText()`
+- `goBack()`
+
+从 `onLoad` 中删除 `this.startTimer()` 调用(Task 4 重新实现)
+从 `onUnload` 中删除 `this.stopTimer()` 调用(Task 4 重新实现)
+
+- [ ] **Step 3: 删除 import**
+
+删除 `getAiQuestions`, `submitAnswers`, `recordArticleRead` 的导入(只保留 `getArticleDetail`)
+
+删除 `shareMixin` 导入和 mixins 注册(如果分享还在的话保留,确认 share-mixin 是否在其他地方使用... 保留分享功能)
+
+实际上分享按钮还在,所以保留 `shareMixin` 和 `onShareTap`。
+
+---
+### Task 4: 前端 — 添加自动阅读时长跟踪
+
+**Files:**
+- Modify: `cfc-frontend/pages/article-center/article-detail.vue`
+
+**Interfaces:**
+- Consumes: `POST /api/articles/report-reading-time`
+- Produces: 右侧阅读时长显示 + 可见性感知计时 + 自动上报
+
+- [ ] **Step 1: 在 data 中添加计时相关状态**
+
+```javascript
+data() {
+  return {
+    // ... 保留 articleId, article, loading, error, errorMsg
+    readingSeconds: 0,
+    lastReportedSeconds: 0,
+    isTimerRunning: false,
+    timerHandle: null,
+    syncHandle: null
+  }
+}
+```
+
+- [ ] **Step 2: 添加计时器方法**
+
+```javascript
+methods: {
+  // ... 保留现有方法
+
+  startReadingTimer: function() {
+    if (this.isTimerRunning) return
+    this.isTimerRunning = true
+    var self = this
+    // 每秒累加
+    this.timerHandle = setInterval(function() {
+      self.readingSeconds++
+    }, 1000)
+    // 每30秒自动上报
+    this.syncHandle = setInterval(function() {
+      self.reportReadingTime()
+    }, 30000)
+  },
+
+  pauseReadingTimer: function() {
+    if (!this.isTimerRunning) return
+    this.isTimerRunning = false
+    if (this.timerHandle) {
+      clearInterval(this.timerHandle)
+      this.timerHandle = null
+    }
+    if (this.syncHandle) {
+      clearInterval(this.syncHandle)
+      this.syncHandle = null
+    }
+    this.reportReadingTime()
+  },
+
+  reportReadingTime: function() {
+    var delta = this.readingSeconds - this.lastReportedSeconds
+    if (delta <= 0) return
+    this.lastReportedSeconds = this.readingSeconds
+
+    var childId = uni.getStorageSync('currentChildId')
+    if (!childId || !this.article) return
+
+    // 调用新 API
+    try {
+      uni.request({
+        url: getApp().globalData.baseUrl + '/api/articles/report-reading-time',
+        method: 'POST',
+        data: {
+          articleId: this.article.id,
+          childId: parseInt(childId),
+          durationSeconds: delta
+        },
+        header: {
+          'Authorization': 'Bearer ' + (uni.getStorageSync('token') || ''),
+          'Content-Type': 'application/json'
+        },
+        fail: function() {
+          // silent fail
+        }
+      })
+    } catch (e) {
+      // silent fail
+    }
+  },
+
+  formatReadingTime: function(seconds) {
+    if (seconds < 60) {
+      return '已读 ' + seconds + '秒'
+    }
+    var min = Math.floor(seconds / 60)
+    var sec = seconds % 60
+    return '已读 ' + min + '分' + (sec > 0 ? sec + '秒' : '')
+  }
+}
+```
+
+关于 API 调用方式——需要检查项目中统一请求封装。看一下 `utils/api.js` 中的 `request` 函数签名,用统一方式调用更好。
+
+```javascript
+import { reportReadingTime } from '@/utils/api.js'
+```
+
+在 `utils/api.js` 添加:
+```javascript
+export const reportReadingTime = (data) => request('/api/articles/report-reading-time', 'POST', data)
+```
+
+然后在组件中:
+```javascript
+reportReadingTime: function() {
+  var delta = this.readingSeconds - this.lastReportedSeconds
+  if (delta <= 0) return
+  this.lastReportedSeconds = this.readingSeconds
+  var childId = uni.getStorageSync('currentChildId')
+  if (!childId || !this.article) return
+  reportReadingTime({
+    articleId: this.article.id,
+    childId: parseInt(childId),
+    durationSeconds: delta
+  })
+}
+```
+
+- [ ] **Step 3: 生命周期钩子**
+
+```javascript
+onLoad(options) {
+  if (options && options.id) {
+    this.articleId = options.id
+    this.loadDetail(options.id)
+    // 不再在 onLoad 启动计时器,改为 onShow
+  } else {
+    this.error = true
+    this.errorMsg = '参数错误'
+    this.loading = false
+  }
+},
+
+onShow: function() {
+  // 页面显示时开始/恢复计时
+  if (this.article && !this.error) {
+    this.startReadingTimer()
+  }
+},
+
+onHide: function() {
+  // 页面隐藏时暂停+上报
+  this.pauseReadingTimer()
+},
+
+onUnload: function() {
+  // 页面卸载时最终上报
+  this.pauseReadingTimer()
+}
+```
+
+- [ ] **Step 4: 模板中添加时长显示**
+
+在 meta 信息区旁边或正文区右上角添加阅读时长显示:
+
+```html
+<!-- 在元信息行旁边 -->
+<view class="detail-meta">
+  <text class="meta-author">{{ article.author || '浠艾福' }}</text>
+  <text class="meta-sep">|</text>
+  <text class="meta-date">{{ formatDate(article.publishedAt) }}</text>
+  <text class="meta-sep">|</text>
+  <text class="meta-readtime">{{ article.readTime || 3 }}分钟阅读</text>
+  <!-- 阅读时长追踪显示 -->
+  <text class="reading-time-badge">{{ formatReadingTime(readingSeconds) }}</text>
+</view>
+```
+
+CSS:
+```css
+.reading-time-badge {
+  margin-left: auto;
+  font-size: 20rpx;
+  color: #5B9BD5;
+  background: rgba(91,155,213,0.08);
+  padding: 4rpx 12rpx;
+  border-radius: 20rpx;
+}
+```
+
+- [ ] **Step 5: 添加 App 级前后台切换监听**
+
+在 `App.vue` 中,当前页面通过 `onShow`/`onHide` 已能处理大部分场景。对于小程序切后台,`App.vue` 的 `onHide`/`onShow` 会触发全局事件,所有页面的 `onShow`/`onHide` 也会被触发,所以不需要额外处理。
+
+但为了保险,在 `article-detail.vue` 中:
+
+在 `onShow` 中需要加入:当 App 从后台切回时,恢复计时。
+在 `onHide` 中:当 App 切后台时,暂停计时+上报。
+
+这些已经通过 `onShow`/`onHide` 实现。不需要额外代码。
+
+- [ ] **Step 6: 验证模板和逻辑一致性**
+
+确保:
+- 模板中没有引用已删除的 `showQuiz`、`showResult`、`canSubmit` 等变量
+- 没有引用已删除的方法
+- `share-btn` 的 `open-type="share"` 正常工作
+- 阅读时长显示正确绑定 `readingSeconds`
+
+---
+### Task 5: 编译验证 + 最终检查
+
+**Files:**
+- Run: 后端 `mvn clean compile`
+
+- [ ] **Step 1: 后端编译**
+
+```bash
+cd cfc-backend && mvn clean compile
+```
+
+- [ ] **Step 2: 前端编译**
+
+```bash
+cd cfc-frontend && npm run build:mp-weixin
+```
+
+- [ ] **Step 3: 最终检查列表**
+
+- [ ] `article_reading_records` 表有 `article_id` 和 `updated_at` 列
+- [ ] `ArticleReadingRecord` 实体有 `articleId` 和 `updatedAt` 字段
+- [ ] `POST /api/articles/report-reading-time` 端点存在且可调用
+- [ ] `recordArticleRead` 旧 API 保留(其他页面可能使用)
+- [ ] article-detail.vue 无"阅读完成"按钮
+- [ ] article-detail.vue 无答题/结果界面
+- [ ] article-detail.vue 右上角显示阅读时长
+- [ ] 切后台时暂停计时,返回时恢复
+- [ ] 离开页面时上报累计时长

+ 82 - 0
docs/superpowers/specs/2026-07-28-article-reading-time-tracking-design.md

@@ -0,0 +1,82 @@
+# 文章阅读时长自动跟踪设计
+
+## 概述
+
+去除文章详情页的"阅读完成"按钮,改为自动跟踪阅读时长——用户进入页面即开始计时,离开/切后台时自动上报。纯统计用途,不涉及积分/任务/答题。
+
+## 变更清单
+
+### 数据库
+
+**表**: `article_reading_records`
+- 新增 `article_id` BIGINT COMMENT '文章ID'
+- 新增 `updated_at` DATETIME
+- 新增索引 `INDEX idx_article_child_date (article_id, child_id, read_at)`
+
+### 后端
+
+**新 API**: `POST /api/articles/report-reading-time`
+
+请求体:
+```json
+{
+  "articleId": 123,
+  "childId": 456,
+  "durationSeconds": 185
+}
+```
+
+逻辑:
+1. 按 `article_id + child_id + DATE(read_at)` 查找当日已有记录
+2. 找到 → `UPDATE SET duration_seconds = duration_seconds + :duration, updated_at = NOW()`
+3. 未找到 → INSERT 新记录
+4. 返回 `{ success: true }`,无积分/任务逻辑
+
+**实体变更**: `ArticleReadingRecord.java` 新增 `articleId` 字段
+
+### 前端
+
+**页面**: `pages/article-center/article-detail.vue`
+
+删除:
+- "阅读完成"按钮(`read-btn`)
+- `onReadComplete` 方法
+- `loadQuiz`、`onSubmitQuiz`、`selectAnswer` 等答题相关方法
+- `showQuiz`、`showResult`、`quizQuestions` 等答题相关状态
+- `recordArticleRead` API 调用(旧版一次性记录)
+
+新增:
+- 右上角/边侧阅读时长显示: `"已读 3分12秒"`
+- 计时器按可见性控制:
+  - `onShow` → 开始计时(或恢复)
+  - `onHide` → 暂停计时,上报累计时长
+  - `onUnload` → 最终上报
+  - App 级 `onShow`/`onHide` 同样处理(小程序切后台)
+- 每 30 秒自动同步一次
+- 调用新 API `/api/articles/report-reading-time`
+
+保留:
+- 分享按钮
+- 文章内容展示
+- 底部占位区(只保留分享按钮)
+
+## 时序
+
+```
+用户进入页面 → onLoad → 加载文章 → 开始计时 → 显示"已读 0秒"
+                                                         ↓
+                                              每30秒 → 上报当前累计时长
+                                                         ↓
+用户切后台/隐藏 → onHide → 暂停计时 → 上报累计时长
+                    ↓
+用户返回 → onShow → 恢复计时
+                    ↓
+用户离开页面 → onUnload → 最终上报
+```
+
+## 边界情况
+
+- **未登录用户**: 不上报(`childId` 不存在)
+- **无 `currentChildId`**: 不上报
+- **网络失败**: silent fail,不阻塞用户
+- **累计时长": 同一用户同一天阅读同一篇文章,时长累加

+ 84 - 100
web/articles/SCREENSHOT-TODO.md

@@ -1,123 +1,107 @@
-# 待处理截图清单
+# 待处理截图清单(更新版)
 
-> 目标:为以下14篇文章补充学术文献截图,插入位置已标注
+> 目标:为所有文章补充学术文献截图,增强文章可信度和视觉说服力
 > 截图保存至 `../img/ref-screenshots/`,命名规则见下方。
 > 插入HTML时使用:`<img src="../img/ref-screenshots/文件名" alt="描述" style="max-width:100%;border-radius:8px;display:block;margin:1rem 0;">`
 
 ---
 
-## 一、indicator 文章(8篇)— 肠道菌群检测报告指标解读
-
-### 1. `indicator-microbiome-diversity.html` — 菌群平衡与多样性
-- **截图内容:** Shannon指数、菌群多样性与健康的关联研究
-- **推荐来源:** PubMed 搜索 "Shannon index gut microbiome diversity health"
-- **插入位置:** `<h3>这是衡量菌群多样性的"金标准"</h3>` 段落之后
-- **文件名:** `indicator-diversity-shannon.png`
-
-### 2. `indicator-gut-barrier.html` — 肠道屏障与代谢产物 ✅ 已有 gut-microbiome-damage.png
-- **已完成,无需操作**
-
-### 3. `indicator-neurotransmitters.html` — 神经递质
-- **截图内容:** 肠道菌群产生神经递质(5-HT、GABA、多巴胺)的研究证据
-- **推荐来源:** 搜索 "gut microbiome neurotransmitter production serotonin GABA"
-- **插入位置:** 菌群产生神经递质的段落之后
-- **文件名:** `indicator-neurotransmitter.png`
-
-### 4. `indicator-nutrition.html` — 营养代谢
-- **截图内容:** 菌群参与维生素合成、矿物质吸收的研究
-- **推荐来源:** 搜索 "gut microbiota vitamin synthesis nutrient absorption"
-- **插入位置:** 菌群与营养代谢相关的段落之后
-- **文件名:** `indicator-nutrition.png`
-
-### 5. `indicator-pathogens.html` — 有害菌与致病菌
-- **截图内容:** 肠道致病菌(如具核梭杆菌、艰难梭菌)与疾病的关联研究
-- **推荐来源:** 搜索 "Fusobacterium nucleatum colorectal cancer" 或 "Clostridioides difficile infection"
-- **插入位置:** 致病菌致病机制段落之后
-- **文件名:** `indicator-pathogen.png`
-
-### 6. `indicator-scfas.html` — 短链脂肪酸
-- **截图内容:** 短链脂肪酸(丁酸、丙酸、乙酸)的抗炎机制研究
-- **推荐来源:** 搜索 "short chain fatty acids butyrate anti-inflammatory gut"
-- **插入位置:** SCFA功能说明段落之后
-- **文件名:** `indicator-scfas.png`
-
-### 7. `indicator-vitamins.html` — 维生素
-- **截图内容:** 肠道菌群合成维生素K、B族维生素的研究
-- **推荐来源:** 搜索 "gut microbiome vitamin K B vitamins synthesis"
-- **插入位置:** 维生素与菌群关系段落之后
-- **文件名:** `indicator-vitamins.png`
-
-### 8. `indicator-disease-risk.html` — 疾病风险预测
-- **截图内容:** 菌群与疾病风险预测模型的研究(如糖尿病、心血管疾病风险)
-- **推荐来源:** 搜索 "gut microbiome disease risk prediction model"
-- **插入位置:** 疾病风险预测段落之后
-- **文件名:** `indicator-disease-risk.png`
+## 截图规范
+
+1. **尺寸:** 宽度不超过 1200px,高度适中(能看清文字即可)
+2. **格式:** PNG 或 WebP 优先(保留文字清晰度),JPG 也可
+3. **来源:** PubMed、Google Scholar、学术期刊官网(Nature、NEJM、Lancet、Cell、Science、Gut 等)
+4. **标注:** 截图后无需翻译,但 alt 属性中写清中文含义
+5. **版权:** 学术论文截图属于合理引用(fair use),标注出处即可
+6. **命名:** 按下方建议的文件名命名,保存到 `web/img/ref-screenshots/` 目录
 
 ---
 
-## 二、益生菌/益生元文章(4篇)
+## 一、新增文章(3篇)
+
+### 1. `stroke.html` — 脑卒中(5张截图)
+
+| # | 截图内容 | 推荐来源 | 插入位置 | 文件名 |
+|---|---------|---------|---------|-------|
+| 1 | CANTOS研究——IL-1β单抗降低心血管事件15%(NEJM 2017) | NEJM 搜索 "CANTOS canakinumab" | 第五节 慢性炎症段落 | `stroke-cantos.png` |
+| 2 | INTERSTROKE研究——10大因素占90%脑卒中风险(Lancet 2016) | Lancet 搜索 "INTERSTROKE 2016" | 第四节 风险因素 | `stroke-interstroke.png` |
+| 3 | Bogalusa/PDAY研究——动脉粥样硬化从儿童期开始(NEJM 1998) | NEJM 搜索 "Bogalusa Heart Study atherosclerosis" | 第二节 动脉粥样硬化 | `stroke-bogalusa.png` |
+| 4 | TMAO→动脉粥样硬化(Nature Medicine 2013) | PubMed 搜索 "TMAO Koeth Nature Medicine 2013" | 第五节 TMAO段落 | `stroke-tmao.png` |
+| 5 | 微塑料与颈动脉斑块(NEJM 2024) | NEJM 搜索 "Microplastics and Nanoplastics in Atheromas" | 第二节 微塑料 | `stroke-microplastic.png` |
 
-### 9. `prebiotics-science.html` — 益生元科学
-- **截图内容:** 益生元促进双歧杆菌等有益菌增殖的RCT研究
-- **推荐来源:** 搜索 "prebiotic bifidobacterium RCT inulin fructooligosaccharide"
-- **插入位置:** 益生元功效说明段落之后
-- **文件名:** `prebiotics-science.png`
+### 2. `exercise-mood-cognition.html` — 运动(4张截图)
 
-### 10. `probiotics-science.html` — 益生菌科学
-- **截图内容:** 益生菌调节肠道菌群、改善免疫的临床研究
-- **推荐来源:** 搜索 "probiotic Lactobacillus Bifidobacterium clinical trial gut microbiome"
-- **插入位置:** 益生菌功效说明段落之后
-- **文件名:** `probiotics-science.png`
+| # | 截图内容 | 推荐来源 | 插入位置 | 文件名 |
+|---|---------|---------|---------|-------|
+| 1 | BMJ 2024荟萃分析——运动vs抗抑郁药效果相当 | BMJ 搜索 "exercise depression network meta-analysis 2024" | 第一节 情绪 | `exercise-bmj-meta.png` |
+| 2 | Erickson 2011 PNAS——运动增加海马体体积 | PNAS 搜索 "Erickson exercise hippocampus PNAS 2011" | 第二节 认知 | `exercise-hippocampus.png` |
+| 3 | Clarke 2014 Gut——运动员肠道菌群多样性更高 | Gut 搜索 "Clarke exercise gut microbial diversity 2014" | 第四节 菌群 | `exercise-gut-diversity.png` |
+| 4 | Allen 2018——6周运动改变菌群,停练后恢复 | PubMed 搜索 "Allen exercise gut microbiota 2018 Med Sci Sports Exerc" | 第四节 菌群RCT | `exercise-gut-rct.png` |
 
-### 11. `prebiotics-guide.html` — 益生元指南
-- **截图内容:** 富含益生元的食物(洋葱、大蒜、香蕉等)的营养数据或膳食纤维含量研究
-- **推荐来源:** 搜索 "prebiotic foods dietary fiber content inulin"
-- **插入位置:** 食物推荐段落之后
-- **文件名:** `prebiotics-guide.png`
+### 3. `puerarin.html` — 葛根素(3张截图)
 
-### 12. `probiotics-guide.html` — 益生菌指南
-- **截图内容:** 发酵食品(酸奶、泡菜、纳豆等)的菌株含量研究
-- **推荐来源:** 搜索 "fermented foods probiotic content yogurt kimchi"
-- **插入位置:** 发酵食品推荐段落之后
-- **文件名:** `probiotics-guide.png`
+| # | 截图内容 | 推荐来源 | 插入位置 | 文件名 |
+|---|---------|---------|---------|-------|
+| 1 | 葛根素解酒RCT(Shen 2012) | PubMed 搜索 "Puerarin alcohol hangover RCT" | 循证功效→解酒护肝 | `puerarin-hangover.png` |
+| 2 | 葛根素降血糖Meta分析(Guo 2018) | PubMed 搜索 "Puerarin type 2 diabetes meta-analysis" | 循证功效→血糖 | `puerarin-diabetes.png` |
+| 3 | 葛根素脑缺血神经保护(动物研究) | PubMed 搜索 "Puerarin cerebral ischemia neuroprotection" | 循证功效→脑保护 | `puerarin-brain.png` |
 
 ---
 
-## 三、其他文章(2篇)
+## 二、已标记但缺少实际图片的文章(2篇)
 
-### 13. `water-purifier-guide.html` — 净水器选购指南
-- **截图内容:** 电解还原水(富氢水)的抗氧化/抗炎临床研究证据
-- **推荐来源:** 搜索 "hydrogen rich water clinical trial antioxidant" 或复用已有截图 `nature-tapwater-page*.png`
-- **插入位置:** 还原水功效说明段落之后
-- **文件名:** `water-purifier-hydrogen.png`
+### 4. `gut-microbiota-testing.html` — 肠道菌群检测(2张截图)
 
-### 14. `geo-faq-2-attention.html` — 注意力/学习力FAQ
-- **截图内容:** 肠道菌群与大脑认知/注意力的关联研究(肠-脑轴)
-- **推荐来源:** 搜索 "gut brain axis attention cognitive function microbiome"
-- **插入位置:** 菌群影响大脑功能的段落之后
-- **文件名:** `geo-faq-brain-gut.png`
+现有占位div(无img标签),需插入实际截图:
 
-### 15. `geo-faq-3-immunity.html` — 免疫力FAQ
-- **截图内容:** 肠道菌群调控免疫系统的里程碑研究
-- **推荐来源:** 搜索 "gut microbiome immune system regulation review"
-- **插入位置:** 免疫与菌群关系的段落之后
-- **文件名:** `geo-faq-immunity.png`
+| # | 截图内容 | 推荐来源 | 插入位置 | 文件名 |
+|---|---------|---------|---------|-------|
+| 1 | NIH Human Microbiome Project 2012/2019 | Nature 搜索 "Human Microbiome Project 2012" | 第825行screenshot div | `gut-testing-hmp.png` |
+| 2 | Cell 2022——菌群失调→免疫疾病分子机制 | Cell 搜索 "gut microbiota dysbiosis immune disease 2022" | 第832行screenshot div | `gut-testing-dysbiosis.png` |
 
 ---
 
-## 截图规范
+## 三、已有截图(无需操作)
+
+| 文章 | 截图文件 | 状态 |
+|------|---------|:----:|
+| alzheimer | gut-microbiome-damage, ref3_nature_stt, ref12_pmc_hydrogen_water | ✅ |
+| cancer | cancer_ref1-4, cancer_ref_bicarb, cancer_ref_molcancer | ✅ |
+| cardio-cerebro | cardio_ref_cantos, cardio_ref_gut_leak, misdiagnosis_* | ✅ |
+| diabetes | diabetes_ref1-4 | ✅ |
+| enagic-turmeric | enagic-comparison | ✅ |
+| geo-faq-1-allergy | gut-microbiome-damage | ✅ |
+| geo-faq-2-attention | (已有截图) | ✅ |
+| geo-faq-3-immunity | geo-faq-immunity | ✅ |
+| gut-microbiome-immune | gut-microbiome-damage | ✅ |
+| indicator-disease-risk | indicator-disease-risk | ✅ |
+| indicator-gut-barrier | gut-microbiome-damage | ✅ |
+| indicator-microbiome-diversity | indicator-diversity-shannon | ✅ |
+| indicator-neurotransmitters | indicator-neurotransmitter | ✅ |
+| indicator-nutrition | indicator-nutrition | ✅ |
+| indicator-pathogens | indicator-pathogen | ✅ |
+| indicator-scfas | indicator-scfas | ✅ |
+| indicator-vitamins | indicator-vitamins | ✅ |
+| metabolic-syndrome | cell-inflammation-* | ✅ |
+| osteoporosis | osteoporosis_ref1-3 | ✅ |
+| plastic-health | ref-pet-sources, ref-nejm 等5张 | ✅ |
+| prebiotics-guide | (已有截图) | ✅ |
+| prebiotics-science | prebiotics-science | ✅ |
+| probiotics-guide | (已有截图) | ✅ |
+| probiotics-science | probiotics-science | ✅ |
+| tap-water-pollution | 23张 nature-tapwater 系列 + 多张 | ✅ |
+| turmeric | pubmed17-21, taobao, iherb 等 | ✅ |
+| uric-acid | ref1_annual_reviews, ref7_jds_zhang 等 | ✅ |
+| water-purifier-guide | water-purifier-hydrogen | ✅ |
 
-1. **尺寸:** 宽度不超过 1200px,高度适中(能看清文字即可)
-2. **格式:** PNG 优先(保留文字清晰度),JPG 也可
-3. **标注:** 如原文为英文,截图后无需翻译,但建议在 alt 属性中写清中文含义
-4. **版权:** 学术论文截图属于合理引用(fair use),标注出处即可
-5. **命名:** 按上方建议的文件名命名,保存到 `web/img/ref-screenshots/` 目录
-
-## 已有可复用截图(无需重复制作)
-
-| 文件名 | 用途 |
-|---|---|
-| `gut-microbiome-damage.png` | 肠漏机制(已在3篇文章中使用) |
-| `cardio_ref_gut_leak.png` | 肠漏与心血管 |
-| `enagic-comparison.svg` | Enagic产品对比 |
-| `nature-tapwater-page*.png` | 自来水安全研究(23张) |
+---
+
+## 汇总
+
+| 优先级 | 文章 | 截图数 | 说明 |
+|:-----:|------|:------:|------|
+| 🔴 P0 | stroke.html | 5 | 新增,零截图 |
+| 🔴 P0 | exercise-mood-cognition.html | 4 | 新增,零截图 |
+| 🔴 P0 | puerarin.html | 3 | 新增,零截图 |
+| 🟡 P1 | gut-microbiota-testing.html | 2 | 有占位div,无实际图片 |
+| **合计** | **4篇** | **14张** | |

+ 20 - 14
web/articles/stroke.html

@@ -409,26 +409,32 @@
 <!-- ===== SECTION 04: RISK FACTORS ===== -->
 <section id="risk">
   <div class="container">
-    <h2><span class="emoji">📊</span> 四、脑卒中的危险因素——90%可归因于十大因素</h2>
+    <h2><span class="emoji">📊</span> 四、脑卒中的根本驱动因素——高血压不是"原因",而是"信号"</h2>
 
-    <p>INTERSTROKE研究(2016, <em>Lancet</em>)——全球最大规模的脑卒中病例对照研究,纳入32个国家、26,919名参与者——发现:<strong>90%的脑卒中风险可归因于10个可干预的危险因素</strong><sup><a href="#ref-10">[10]</a></sup>。</p>
+    <p>INTERSTROKE研究(2016, <em>Lancet</em>)——全球最大规模的脑卒中病例对照研究,纳入32个国家、26,919名参与者——发现90%的脑卒中可归因于10个可干预因素。但需要正确理解这些因素的层次关系:<strong>高血压不是脑卒中的"根因",而是动脉粥样硬化已经存在的"信号"</strong><sup><a href="#ref-10">[10]</a></sup>。</p>
 
     <div class="info-card blue">
-      <h3>十大危险因素及其贡献比例</h3>
-      <p><strong>1. 高血压(OR 2.64)</strong>——贡献最大,占人群归因风险的47.9%。收缩压每升高10mmHg,脑卒中风险增加约20%</p>
-      <p><strong>2. 缺乏运动(OR 1.35)</strong>——占人群归因风险的35.8%。规律运动可使脑卒中风险降低约25-30%</p>
-      <p><strong>3. 血脂异常(ApoB/A1比值高,OR 1.84)</strong>——贡献26.5%</p>
-      <p><strong>4. 不健康饮食(OR 1.35)</strong>——贡献23.2%。高盐(>5g/天)是东亚人群最突出的饮食风险因素</p>
-      <p><strong>5. 腹型肥胖(腰臀比高,OR 1.44)</strong>——贡献18.6%</p>
-      <p><strong>6. 心理因素(OR 1.53)</strong>——压力、抑郁,贡献17.4%</p>
-      <p><strong>7. 吸烟(OR 1.67)</strong>——贡献12.4%。吸烟使缺血性卒中风险增加约2倍</p>
-      <p><strong>8. 心脏病(OR 2.17)</strong>——房颤、冠心病,贡献9.1%。房颤使卒中风险增加约5倍</p>
-      <p><strong>9. 糖尿病(OR 1.36)</strong>——贡献5.0%。糖尿病使脑卒中风险增加1.5-3倍</p>
-      <p><strong>10. 酒精摄入(OR 1.51)</strong>——贡献5.8%。大量饮酒(>60g/天)使风险显著升高</p>
+      <h3>🔄 重新理解高血压——因与果的颠倒</h3>
+      <p>传统观念将高血压列为脑卒中的"头号危险因素",但这一认知存在根本性的逻辑颠倒:</p>
+      <p><strong>高血压本身就是动脉粥样硬化的表现之一。</strong>动脉粥样硬化使血管壁失去弹性(僵硬),血管无法有效缓冲心脏泵血产生的压力波动,从而导致收缩压升高和脉压差增大。换句话说,<strong>血压升高不是"血管突然出了问题",而是血管已经"硬化"多年的结果</strong>。</p>
+      <p><strong>大规模流行病学证据:</strong>Framingham心脏研究等长期队列研究证实,<strong>动脉僵硬度的升高早于高血压的发生</strong>——血管弹性下降出现数年甚至数十年后,血压数值才开始爬升(Mitchell GF, <em>Hypertension</em> 2014)。这意味着,当血压计读数开始升高时,血管壁的损伤已经存在了相当长的时间。</p>
+      <p>理解这一点极为重要:单纯把血压数字"压下去"(吃降压药),而不处理导致血管硬化的根本原因,就像把火灾报警器关掉但不灭火——数字好看了,但底层风险依然存在。这就是为什么有些患者血压"达标"了仍然会发生脑卒中。</p>
+    </div>
+
+    <div class="info-card">
+      <h3>🔬 真正驱动动脉粥样硬化的根本因素</h3>
+      <p>理解了高血压是"果"而非"因",就应该聚焦于那些真正启动和加速动脉粥样硬化的根本因素。这些因素通过损伤血管内皮、引发慢性炎症、促进斑块形成,最终导致脑卒中<sup><a href="#ref-5">[5]</a></sup>:</p>
+      <p><strong>① 慢性炎症</strong>——驱动动脉粥样硬化的核心引擎。CANTOS研究(2017, <em>NEJM</em>)证实:单纯抗炎即可降低心血管事件风险15%,不依赖降脂<sup><a href="#ref-12">[12]</a></sup>。炎症来源包括:肠漏(LPS入血)、吸烟、肥胖、压力、慢性感染、不良饮食。<strong>hsCRP预测脑卒中风险的能力强于LDL胆固醇</strong>。</p>
+      <p><strong>② 肠道菌群失调/TMAO</strong>——红肉/蛋黄的胆碱和肉碱被肠道菌群代谢为TMAO,直接促进动脉粥样硬化和血小板高反应性。血液TMAO最高四分位数人群的3年心梗/卒中风险比最低者高出约2.5倍<sup><a href="#ref-14">[14]</a></sup>。</p>
+      <p><strong>③ 胰岛素抵抗/代谢综合征</strong>——高血糖和高胰岛素水平直接损伤血管内皮,促进LDL氧化和斑块形成。糖尿病使脑卒中风险增加1.5-3倍。</p>
+      <p><strong>④ 氧化应激(LDL氧化)</strong>——LDL胆固醇本身不致病,但被氧化修饰后的oxLDL才是血管壁炎症和泡沫细胞形成的直接诱因。抗氧化能力下降(如蔬菜摄入不足、吸烟)加速这一过程。</p>
+      <p><strong>⑤ 久坐不动</strong>——缺乏运动导致血管内皮剪切应力降低,削弱内皮功能,降低内皮型一氧化氮合酶(eNOS)活性,使血管失去舒张能力。规律运动可使脑卒中风险降低约25-30%<sup><a href="#ref-10">[10]</a></sup>。</p>
+      <p><strong>⑥ 吸烟</strong>——烟草中的数千种化学物质直接损伤血管内皮,引发氧化应激和炎症反应,加速动脉粥样硬化。吸烟使缺血性卒中风险增加约2倍。</p>
+      <p><strong>⑦ 不健康饮食</strong>——高盐(>5g/天)、高糖、高加工食品、低膳食纤维的饮食模式,通过多条通路促进炎症、氧化应激和内皮损伤。</p>
     </div>
 
     <div class="highlight-box">
-      <p><strong>关键启示:</strong>90%的脑卒中是可预防的。控制血压是最重要的单一措施——收缩压降至130mmHg以下,可使脑卒中风险降低约30%。结合规律运动、健康饮食、戒烟限酒,可以将风险降低80%以上。</p>
+      <p><strong>关键启示:</strong>90%的脑卒中是可预防的。但预防策略不应只盯着血压数字——<strong>需要从根源上降低血管炎症和氧化应激</strong>。修复肠屏障、增加膳食纤维、规律运动、戒烟、控制代谢异常——这些干预措施降低的是"驱动血管硬化的根本原因",而不是仅仅处理一个数字。当根本原因被控制后,血压自然回落,才是真正的"达标"。</p>
     </div>
   </div>
 </section>