Преглед изворни кода

fix(membership): add membership_levels seed data and period-specific pricing keys

- Migration 120: insert FREE/FAMILY/PROVIDER seed data into membership_levels
- Add member_fee_family_yearly/monthly/quarterly sys_config keys
- Fix getPricingPlans() hardcoded zero prices for FAMILY monthly/quarterly
- feat(report): report summary management (backend/frontend/admin)
Xiaogang Liao пре 3 месеци
родитељ
комит
c3cee79508
36 измењених фајлова са 4369 додато и 1224 уклоњено
  1. 51 1
      cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java
  2. 6 3
      cfc-backend/src/main/java/com/etotem/cfc/controller/MembershipController.java
  3. 62 0
      cfc-backend/src/main/java/com/etotem/cfc/controller/ReportSummaryController.java
  4. 12 0
      cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminProductController.java
  5. 40 0
      cfc-backend/src/main/java/com/etotem/cfc/controller/admin/ReportSummaryAdminController.java
  6. 14 0
      cfc-backend/src/main/java/com/etotem/cfc/dto/ReportListQueryDTO.java
  7. 37 0
      cfc-backend/src/main/java/com/etotem/cfc/dto/ReportSummaryVO.java
  8. 11 0
      cfc-backend/src/main/java/com/etotem/cfc/dto/ReportSyncResultDTO.java
  9. 34 0
      cfc-backend/src/main/java/com/etotem/cfc/entity/ReportSummary.java
  10. 7 0
      cfc-backend/src/main/java/com/etotem/cfc/mapper/ReportSummaryMapper.java
  11. 10 0
      cfc-backend/src/main/java/com/etotem/cfc/service/DanReportUploadService.java
  12. 9 0
      cfc-backend/src/main/java/com/etotem/cfc/service/HealthReportService.java
  13. 18 0
      cfc-backend/src/main/java/com/etotem/cfc/service/InsurancePolicyService.java
  14. 51 0
      cfc-backend/src/main/java/com/etotem/cfc/service/ProductService.java
  15. 517 0
      cfc-backend/src/main/java/com/etotem/cfc/service/ReportSummaryService.java
  16. 32 0
      cfc-backend/src/main/java/com/etotem/cfc/task/ReportSummarySyncTask.java
  17. 26 0
      cfc-backend/src/main/resources/mapper/ReportSummaryMapper.xml
  18. 47 13
      cfc-backend/src/main/resources/schema.sql
  19. 68 139
      cfc-frontend/components/DimensionActivities.vue
  20. 12 0
      cfc-frontend/pages.json
  21. 7 0
      cfc-frontend/pages/profile/components/ProfileMenu.vue
  22. 232 0
      cfc-frontend/pages/profile/report-detail.vue
  23. 302 0
      cfc-frontend/pages/profile/report-management.vue
  24. 6 1
      cfc-frontend/utils/api.js
  25. 1 1
      cfc-web/.last_build_commit
  26. 1 1
      cfc-web/package.json
  27. 6 0
      cfc-web/public/CHANGELOG-v1.0.md
  28. 7 1
      cfc-web/public/CHANGELOG.md
  29. 8 0
      cfc-web/src/api/admin.js
  30. 6 0
      cfc-web/src/router/index.js
  31. 1 2
      cfc-web/src/views/Layout.vue
  32. 1 1
      cfc-web/src/views/admin/ProductEdit.vue
  33. 13 1
      cfc-web/src/views/admin/ProductManage.vue
  34. 176 0
      cfc-web/src/views/admin/ReportManagement.vue
  35. 1931 0
      docs/superpowers/plans/2026-07-28-report-management-plan.md
  36. 607 1060
      web/ppt/index.html

+ 51 - 1
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -1022,6 +1022,9 @@ log.info("已添加template_id列到tasks表");
             insertSysConfigSeed("member_discount_standard", "0.9", "标准会员折扣率");
             insertSysConfigSeed("member_discount_premium", "8000", "高级家庭会员商品折扣(bps)");
             insertSysConfigSeed("member_fee_family", "131400", "家庭会员年费(分)");
+            insertSysConfigSeed("member_fee_family_yearly", "131400", "家庭会员年费(分)");
+            insertSysConfigSeed("member_fee_family_monthly", "13140", "家庭会员月费(分)");
+            insertSysConfigSeed("member_fee_family_quarterly", "32850", "家庭会员季费(分)");
             insertSysConfigSeed("member_upgrade_threshold", "131400", "自动升级家庭会员累计消费门槛(分)");
             insertSysConfigSeed("member_discount_family", "9000", "家庭会员商品折扣(bps)");
             insertSysConfigSeed("member_fee_premium_monthly", "131400", "高级家庭会员月费(分)");
@@ -5860,7 +5863,18 @@ try {
         log.warn("创建membership_levels表可能已存在: {}", e.getMessage());
     }
 
-    // 迁移120: 创建 family_memberships 表(家庭会员订阅记录)
+    // 迁移120: 插入 membership_levels 种子数据
+    try {
+        jdbcTemplate.execute("INSERT IGNORE INTO membership_levels (level_code, level_name, level_desc, price_monthly, price_quarterly, price_yearly, max_children, max_tasks_per_day, ai_review_enabled, priority_support, teacher_consultation) VALUES " +
+            "('FREE', '免费会员', '基础免费功能', 0, 0, 0, 2, 5, 0, 0, 0), " +
+            "('FAMILY', '家庭会员', '家庭会员全部功能', 13140, 32850, 131400, 6, 20, 1, 1, 1), " +
+            "('PROVIDER', '标准会员', '标准会员服务', 13140, 32850, 131400, 10, 50, 1, 1, 1)");
+        log.info("已插入membership_levels种子数据");
+    } catch (Exception e) {
+        log.warn("插入membership_levels种子数据可能已存在: {}", e.getMessage());
+    }
+
+    // 迁移121: 创建 family_memberships 表(家庭会员订阅记录)
     try {
         jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS family_memberships (" +
             "id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
@@ -6909,5 +6923,41 @@ private void runMigration100() {
 		} catch (Exception e) {
 			// 索引已存在,忽略
 		}
+
+		// 迁移106: 创建 report_summary 统一报告汇总表
+		try {
+			jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS report_summary (" +
+					"id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', " +
+					"report_type VARCHAR(32) NOT NULL COMMENT '报告类型', " +
+					"source_table VARCHAR(32) NOT NULL COMMENT '来源表', " +
+					"source_id BIGINT NOT NULL COMMENT '来源表记录ID', " +
+					"user_id BIGINT NOT NULL COMMENT '创建者用户ID', " +
+					"family_id BIGINT COMMENT '家庭ID', " +
+					"subject_id BIGINT COMMENT '报告主体ID', " +
+					"subject_name VARCHAR(50) COMMENT '报告主体名称', " +
+					"title VARCHAR(200) COMMENT '报告标题', " +
+					"summary TEXT COMMENT '摘要', " +
+					"file_url VARCHAR(500) COMMENT '文件URL', " +
+					"file_size BIGINT COMMENT '文件大小', " +
+					"file_name VARCHAR(200) COMMENT '文件名', " +
+					"has_file TINYINT DEFAULT 0 COMMENT '是否有文件', " +
+					"overall_score INT COMMENT '综合评分', " +
+					"report_date DATE COMMENT '报告日期', " +
+					"status VARCHAR(20) COMMENT '状态', " +
+					"detail_route VARCHAR(100) COMMENT '详情页路由', " +
+					"created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+					"updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
+					"INDEX idx_user_id (user_id), " +
+					"INDEX idx_family_id (family_id), " +
+					"INDEX idx_subject_id (subject_id), " +
+					"INDEX idx_report_type (report_type), " +
+					"INDEX idx_report_date (report_date), " +
+					"INDEX idx_status (status), " +
+					"UNIQUE KEY uk_source (source_table, source_id)" +
+					") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一报告汇总表'");
+			log.info("已创建 report_summary 表");
+		} catch (Exception e) {
+			// 表已存在,忽略
+		}
 	}
 }

+ 6 - 3
cfc-backend/src/main/java/com/etotem/cfc/controller/MembershipController.java

@@ -230,10 +230,13 @@ public class MembershipController {
             plan.put("recommended", "FAMILY".equals(level.getLevelCode()));
             // 付费等级价格从 sys_config 读取
             if ("FAMILY".equals(level.getLevelCode())) {
-                String yearlyStr = sysConfigService.getValue("member_fee_family");
+                String yearlyStr = sysConfigService.getValue("member_fee_family_yearly");
+                if (yearlyStr == null) yearlyStr = sysConfigService.getValue("member_fee_family");
+                String monthlyStr = sysConfigService.getValue("member_fee_family_monthly");
+                String quarterlyStr = sysConfigService.getValue("member_fee_family_quarterly");
                 plan.put("yearly", yearlyStr != null ? Integer.parseInt(yearlyStr) : 131400);
-                plan.put("monthly", 0);
-                plan.put("quarterly", 0);
+                plan.put("monthly", monthlyStr != null ? Integer.parseInt(monthlyStr) : 13140);
+                plan.put("quarterly", quarterlyStr != null ? Integer.parseInt(quarterlyStr) : 32850);
             } else if ("PREMIUM".equals(level.getLevelCode())) {
                 String yearlyStr = sysConfigService.getValue("member_fee_premium");
                 String monthlyStr = sysConfigService.getValue("member_fee_premium_monthly");

+ 62 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/ReportSummaryController.java

@@ -0,0 +1,62 @@
+package com.etotem.cfc.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.dto.ReportListQueryDTO;
+import com.etotem.cfc.dto.ReportSummaryVO;
+import com.etotem.cfc.service.ReportSummaryService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/report-summary")
+public class ReportSummaryController {
+
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
+    @PostMapping("/list")
+    public Result<Object> list(@RequestBody ReportListQueryDTO query, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        if (userId == null) return Result.error("用户未登录");
+        query.setUserId(userId);
+
+        Page<ReportSummaryVO> page = reportSummaryService.pageList(query);
+        return Result.success(page);
+    }
+
+    @PostMapping("/detail/{id}")
+    public Result<ReportSummaryVO> detail(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        if (userId == null) return Result.error("用户未登录");
+
+        ReportSummaryVO vo = reportSummaryService.getDetail(id);
+        if (vo == null) return Result.error("报告不存在");
+        return Result.success(vo);
+    }
+
+    @PostMapping("/download/{id}")
+    public Result<Object> download(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        if (userId == null) return Result.error("用户未登录");
+
+        ReportSummaryVO vo = reportSummaryService.getDetail(id);
+        if (vo == null) return Result.error("报告不存在");
+        if (!vo.getHasFile()) return Result.error("该报告无文件可下载");
+
+        Object data = new Object() {
+            public String getFileUrl() { return vo.getFileUrl(); }
+            public String getFileName() { return vo.getFileName(); }
+            public Long getFileSize() { return vo.getFileSize(); }
+        };
+        return Result.success(data);
+    }
+}

+ 12 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminProductController.java

@@ -155,6 +155,18 @@ if (params.get("distributionSystemId") != null) {
         return productService.adminUpdate(product, request.getAssessmentExt());
     }
 
+    @Operation(summary = "复制商品")
+    @PostMapping("/copy")
+    public Result<ProductDTO> copy(@RequestBody Map<String, Object> params) {
+        Long productId = params.get("productId") != null
+                ? Long.valueOf(params.get("productId").toString())
+                : null;
+        if (productId == null) {
+            return Result.error("productId不能为空");
+        }
+        return productService.adminCopy(productId);
+    }
+
     @Operation(summary = "删除商品")
     @PostMapping("/delete")
     public Result<String> delete(@RequestBody Map<String, Object> params) {

+ 40 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/admin/ReportSummaryAdminController.java

@@ -0,0 +1,40 @@
+package com.etotem.cfc.controller.admin;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.dto.ReportListQueryDTO;
+import com.etotem.cfc.dto.ReportSummaryVO;
+import com.etotem.cfc.dto.ReportSyncResultDTO;
+import com.etotem.cfc.service.ReportSummaryService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/admin/report-summary")
+public class ReportSummaryAdminController {
+
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
+    @PostMapping("/list")
+    public Result<Object> adminList(@RequestBody ReportListQueryDTO query) {
+        Page<ReportSummaryVO> page = reportSummaryService.adminPageList(query);
+        return Result.success(page);
+    }
+
+    @PostMapping("/sync")
+    public Result<ReportSyncResultDTO> sync() {
+        ReportSyncResultDTO result = reportSummaryService.fullSync();
+        String msg = "同步完成: 新增" + result.getNewCount()
+                + "条, 更新" + result.getUpdateCount()
+                + "条, 废弃" + result.getDiscardedCount()
+                + "条, 错误" + result.getErrorCount() + "条";
+        return Result.success(msg, result);
+    }
+}

+ 14 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ReportListQueryDTO.java

@@ -0,0 +1,14 @@
+package com.etotem.cfc.dto;
+
+import lombok.Data;
+
+@Data
+public class ReportListQueryDTO {
+  private Integer page = 1;
+  private Integer size = 20;
+  private String type;
+  private String keyword;
+  private Long userId;
+  private String startDate;
+  private String endDate;
+}

+ 37 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ReportSummaryVO.java

@@ -0,0 +1,37 @@
+package com.etotem.cfc.dto;
+
+import lombok.Data;
+import java.util.Date;
+
+@Data
+public class ReportSummaryVO {
+  private Long id;
+  private String reportType;
+  private String reportTypeName;
+  private String sourceTable;
+  private Long sourceId;
+  private String title;
+  private String subjectName;
+  private String summary;
+  private String fileUrl;
+  private Long fileSize;
+  private String fileName;
+  private Boolean hasFile;
+  private Integer overallScore;
+  private String reportDate;
+  private String status;
+  private String detailRoute;
+  private Date createdAt;
+
+  public static String getReportTypeName(String reportType) {
+    if (reportType == null) return "未知";
+    switch (reportType) {
+      case "gut_flora": return "菌群检测";
+      case "physical_exam": return "体检报告";
+      case "dan": return "DAN测评";
+      case "insurance": return "保单";
+      case "fortune": return "家庭周报";
+      default: return reportType;
+    }
+  }
+}

+ 11 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/ReportSyncResultDTO.java

@@ -0,0 +1,11 @@
+package com.etotem.cfc.dto;
+
+import lombok.Data;
+
+@Data
+public class ReportSyncResultDTO {
+  private int newCount;
+  private int updateCount;
+  private int discardedCount;
+  private int errorCount;
+}

+ 34 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/ReportSummary.java

@@ -0,0 +1,34 @@
+package com.etotem.cfc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("report_summary")
+public class ReportSummary implements Serializable {
+  @TableId(type = IdType.AUTO)
+  private Long id;
+  private String reportType;
+  private String sourceTable;
+  private Long sourceId;
+  private Long userId;
+  private Long familyId;
+  private Long subjectId;
+  private String subjectName;
+  private String title;
+  private String summary;
+  private String fileUrl;
+  private Long fileSize;
+  private String fileName;
+  private Integer hasFile;
+  private Integer overallScore;
+  private Date reportDate;
+  private String status;
+  private String detailRoute;
+  private Date createdAt;
+  private Date updatedAt;
+}

+ 7 - 0
cfc-backend/src/main/java/com/etotem/cfc/mapper/ReportSummaryMapper.java

@@ -0,0 +1,7 @@
+package com.etotem.cfc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.etotem.cfc.entity.ReportSummary;
+
+public interface ReportSummaryMapper extends BaseMapper<ReportSummary> {
+}

+ 10 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/DanReportUploadService.java

@@ -36,6 +36,9 @@ public class DanReportUploadService {
     @Resource
     private DanReportUploadMapper uploadMapper;
 
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
     @Resource
     private DanReportParseService danReportParseService;
 
@@ -194,6 +197,13 @@ if (!DanReportUpload.DIM_MIND.equals(dimension) && !DanReportUpload.DIM_WISDOM.e
         upload.setUpdatedAt(new Date());
         uploadMapper.updateById(upload);
 
+        try {
+            upload.setLinkedResultId(result.getId());
+            reportSummaryService.syncFromDanReport(upload);
+        } catch (Exception e) {
+            log.warn("同步DAN报告汇总失败 uploadId={}", uploadId, e);
+        }
+
         // 刷新五维能量
         try {
             dimensionScoreService.refreshFromDanAssessment(

+ 9 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/HealthReportService.java

@@ -61,6 +61,9 @@ public class HealthReportService {
     @Resource
     private HealthReportDraftMapper draftMapper;
 
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
     @Resource
     private ObjectMapper objectMapper;
 
@@ -79,6 +82,12 @@ public class HealthReportService {
         }
         healthReportMapper.insert(report);
 
+        try {
+            reportSummaryService.syncFromHealthReport(report);
+        } catch (Exception e) {
+            log.warn("同步健康报告汇总失败 id={}", report.getId(), e);
+        }
+
         if (indicators != null && !indicators.isEmpty()) {
             // 指标去重: 同一批中相同 indicatorName 的只保留最后一条
             java.util.Map<String, HealthIndicator> dedupMap = new java.util.LinkedHashMap<>();

+ 18 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/InsurancePolicyService.java

@@ -18,6 +18,9 @@ public class InsurancePolicyService {
     @Resource
     private InsurancePolicyMapper insurancePolicyMapper;
 
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
     public List<InsurancePolicy> getPolicies(Long userId, String status) {
         LambdaQueryWrapper<InsurancePolicy> wrapper = new LambdaQueryWrapper<InsurancePolicy>()
                 .eq(InsurancePolicy::getUserId, userId);
@@ -37,6 +40,11 @@ public class InsurancePolicyService {
         policy.setCreatedAt(new Date());
         policy.setUpdatedAt(new Date());
         insurancePolicyMapper.insert(policy);
+        try {
+            reportSummaryService.syncFromInsurancePolicy(policy);
+        } catch (Exception e) {
+            log.warn("同步保单汇总失败 policyId={}", policy.getId(), e);
+        }
         return policy;
     }
 
@@ -44,10 +52,20 @@ public class InsurancePolicyService {
     public void updatePolicy(InsurancePolicy policy) {
         policy.setUpdatedAt(new Date());
         insurancePolicyMapper.updateById(policy);
+        try {
+            reportSummaryService.syncFromInsurancePolicy(policy);
+        } catch (Exception e) {
+            log.warn("同步保单汇总失败 policyId={}", policy.getId(), e);
+        }
     }
 
     @Transactional
     public void deletePolicy(Long id) {
         insurancePolicyMapper.deleteById(id);
+        try {
+            reportSummaryService.markDiscarded("insurance_policies", id);
+        } catch (Exception e) {
+            log.warn("标记保单废弃失败 policyId={}", id, e);
+        }
     }
 }

+ 51 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/ProductService.java

@@ -13,6 +13,7 @@ import com.etotem.cfc.entity.User;
 import com.etotem.cfc.mapper.ProductMapper;
 import com.etotem.cfc.mapper.UserMapper;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.util.Date;
@@ -337,6 +338,56 @@ public class ProductService {
         return result;
     }
 
+    /**
+     * 管理员复制商品
+     */
+    @Transactional
+    public Result<ProductDTO> adminCopy(Long productId) {
+        Product original = productMapper.selectById(productId);
+        if (original == null) {
+            return Result.error("商品不存在");
+        }
+        Product copy = new Product();
+        copy.setName(original.getName() + " (复制)");
+        copy.setDescription(original.getDescription());
+        copy.setIntro(original.getIntro());
+        copy.setCoverImage(original.getCoverImage());
+        copy.setImages(original.getImages());
+        copy.setPrice(original.getPrice());
+        copy.setStock(original.getStock());
+        copy.setSalesCount(0);
+        copy.setProductType(original.getProductType());
+        copy.setVendorId(original.getVendorId());
+        copy.setVendorName(original.getVendorName());
+        copy.setStatus("on_shelf");
+        copy.setMemberPrice(original.getMemberPrice());
+        copy.setMemberEligible(original.getMemberEligible());
+        copy.setDomain(original.getDomain());
+        copy.setDimensionWeights(original.getDimensionWeights());
+        copy.setProfitRate(original.getProfitRate());
+        copy.setCategoryId(original.getCategoryId());
+        copy.setDistributionSystemId(original.getDistributionSystemId());
+        copy.setDeliveryMethod(original.getDeliveryMethod());
+        copy.setMinQuantity(original.getMinQuantity());
+        copy.setCreatedAt(new Date());
+        copy.setUpdatedAt(new Date());
+        productMapper.insert(copy);
+
+        // 复制测评扩展信息
+        if (Product.TYPE_ASSESSMENT.equals(original.getProductType())) {
+            AssessmentProduct ext = assessmentProductService.getByProductId(productId);
+            if (ext != null) {
+                AssessmentProduct extCopy = new AssessmentProduct();
+                extCopy.setAssessmentType(ext.getAssessmentType());
+                extCopy.setTotalSessions(ext.getTotalSessions());
+                extCopy.setValidityDays(ext.getValidityDays());
+                extCopy.setGuideScope(ext.getGuideScope());
+                assessmentProductService.saveOrUpdate(copy.getId(), extCopy);
+            }
+        }
+        return Result.success(ProductDTO.from(copy));
+    }
+
     /**
      * 管理员删除商品
      */

+ 517 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/ReportSummaryService.java

@@ -0,0 +1,517 @@
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.etotem.cfc.dto.ReportListQueryDTO;
+import com.etotem.cfc.dto.ReportSummaryVO;
+import com.etotem.cfc.dto.ReportSyncResultDTO;
+import com.etotem.cfc.entity.DanAssessmentResult;
+import com.etotem.cfc.entity.DanReportUpload;
+import com.etotem.cfc.entity.FamilyFortuneReport;
+import com.etotem.cfc.entity.FamilyMember;
+import com.etotem.cfc.entity.HealthReport;
+import com.etotem.cfc.entity.InsurancePolicy;
+import com.etotem.cfc.entity.ReportSummary;
+import com.etotem.cfc.entity.User;
+import com.etotem.cfc.mapper.DanAssessmentResultMapper;
+import com.etotem.cfc.mapper.DanReportUploadMapper;
+import com.etotem.cfc.mapper.FamilyFortuneReportMapper;
+import com.etotem.cfc.mapper.FamilyMemberMapper;
+import com.etotem.cfc.mapper.HealthReportMapper;
+import com.etotem.cfc.mapper.InsurancePolicyMapper;
+import com.etotem.cfc.mapper.ReportSummaryMapper;
+import com.etotem.cfc.mapper.UserMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+@Service
+@Slf4j
+public class ReportSummaryService {
+
+    @Resource
+    private ReportSummaryMapper reportSummaryMapper;
+
+    @Resource
+    private HealthReportMapper healthReportMapper;
+
+    @Resource
+    private DanReportUploadMapper danReportUploadMapper;
+
+    @Resource
+    private InsurancePolicyMapper insurancePolicyMapper;
+
+    @Resource
+    private UserMapper userMapper;
+
+    @Resource
+    private FamilyMemberMapper familyMemberMapper;
+
+    @Resource
+    private DanAssessmentResultMapper danAssessmentResultMapper;
+
+    @Resource
+    private FamilyFortuneReportMapper familyFortuneReportMapper;
+
+    @Resource
+    private JdbcTemplate jdbcTemplate;
+
+    public void syncFromHealthReport(HealthReport report) {
+        ReportSummary existing = reportSummaryMapper.selectOne(
+                new LambdaQueryWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, "health_reports")
+                        .eq(ReportSummary::getSourceId, report.getId())
+        );
+
+        ReportSummary summary = new ReportSummary();
+        summary.setReportType(report.getReportType());
+        summary.setSourceTable("health_reports");
+        summary.setSourceId(report.getId());
+        summary.setUserId(report.getUserId());
+        summary.setFamilyId(report.getFamilyId());
+        summary.setSubjectId(report.getSubjectId());
+        if (report.getSubjectId() != null) {
+            User subject = userMapper.selectById(report.getSubjectId());
+            summary.setSubjectName(subject != null ? subject.getNickname() : "");
+        }
+        String typeLabel = "gut_flora".equals(report.getReportType()) ? "菌群检测报告" : "体检报告";
+        String name = summary.getSubjectName() != null && !summary.getSubjectName().isEmpty() ? summary.getSubjectName() : "用户";
+        summary.setTitle(name + "的" + typeLabel);
+        summary.setFileUrl(report.getFileUrl());
+        summary.setHasFile(report.getFileUrl() != null && !report.getFileUrl().isEmpty() ? 1 : 0);
+        summary.setOverallScore(report.getOverallScore());
+        summary.setReportDate(report.getReportDate());
+        summary.setStatus(report.getStatus());
+        summary.setDetailRoute("gut_flora".equals(report.getReportType())
+                ? "/pages/health/gut-flora-detail"
+                : "/pages/health/physical-exam-detail");
+
+        if (existing != null) {
+            summary.setId(existing.getId());
+            reportSummaryMapper.updateById(summary);
+        } else {
+            reportSummaryMapper.insert(summary);
+        }
+    }
+
+    public void syncFromDanReport(DanReportUpload upload) {
+        ReportSummary existing = reportSummaryMapper.selectOne(
+                new LambdaQueryWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, "dan_report_uploads")
+                        .eq(ReportSummary::getSourceId, upload.getId())
+        );
+
+        ReportSummary summary = new ReportSummary();
+        summary.setReportType("dan");
+        summary.setSourceTable("dan_report_uploads");
+        summary.setSourceId(upload.getId());
+        summary.setUserId(upload.getUploaderUserId());
+        summary.setFamilyId(upload.getFamilyId());
+        summary.setSubjectId(upload.getFamilyMemberId());
+
+        FamilyMember member = familyMemberMapper.selectById(upload.getFamilyMemberId());
+        summary.setSubjectName(member != null ? member.getNickname() : "");
+
+        String dimLabel = "mind".equals(upload.getDimension()) ? "心智" : "智慧";
+        String name = summary.getSubjectName() != null && !summary.getSubjectName().isEmpty() ? summary.getSubjectName() : "成员";
+        summary.setTitle(name + "的" + dimLabel + "测评");
+
+        summary.setFileUrl(upload.getFileUrl());
+        summary.setHasFile(upload.getFileUrl() != null && !upload.getFileUrl().isEmpty() ? 1 : 0);
+
+        if (upload.getLinkedResultId() != null) {
+            DanAssessmentResult result = danAssessmentResultMapper.selectById(upload.getLinkedResultId());
+            if (result != null) summary.setOverallScore(result.getOverallScore());
+        }
+
+        summary.setReportDate(upload.getUploadedAt());
+        String status = upload.getDraftStatus();
+        if ("confirmed".equals(status)) {
+            status = "confirmed";
+        } else if ("pending".equals(status)) {
+            status = "active";
+        } else if ("discarded".equals(status)) {
+            status = "discarded";
+        } else {
+            status = "active";
+        }
+        summary.setStatus(status);
+        summary.setDetailRoute("/pages/dan-assessment/report-upload");
+
+        if (existing != null) {
+            summary.setId(existing.getId());
+            reportSummaryMapper.updateById(summary);
+        } else {
+            reportSummaryMapper.insert(summary);
+        }
+    }
+
+    public void syncFromInsurancePolicy(InsurancePolicy policy) {
+        ReportSummary existing = reportSummaryMapper.selectOne(
+                new LambdaQueryWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, "insurance_policies")
+                        .eq(ReportSummary::getSourceId, policy.getId())
+        );
+
+        ReportSummary summary = new ReportSummary();
+        summary.setReportType("insurance");
+        summary.setSourceTable("insurance_policies");
+        summary.setSourceId(policy.getId());
+        summary.setUserId(policy.getUserId());
+        summary.setSubjectId(policy.getChildId());
+        summary.setSubjectName(policy.getInsuredPerson());
+        summary.setTitle(policy.getPolicyName());
+        String company = policy.getInsuranceCompany() != null ? policy.getInsuranceCompany() : "未知公司";
+        String type = policy.getPolicyType() != null ? policy.getPolicyType() : "保险";
+        Integer sum = policy.getSumInsured() != null ? policy.getSumInsured() : 0;
+        summary.setSummary(company + " · " + type + " · 保额 ¥" + sum / 100);
+        summary.setHasFile(0);
+        summary.setReportDate(policy.getStartDate());
+        summary.setStatus(policy.getStatus());
+        summary.setDetailRoute("/pages/wealth-sub/insurance-list");
+
+        if (existing != null) {
+            summary.setId(existing.getId());
+            reportSummaryMapper.updateById(summary);
+        } else {
+            reportSummaryMapper.insert(summary);
+        }
+    }
+
+    public void syncFromFamilyFortuneReport(FamilyFortuneReport report) {
+        ReportSummary existing = reportSummaryMapper.selectOne(
+                new LambdaQueryWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, "family_fortune_report")
+                        .eq(ReportSummary::getSourceId, report.getId())
+        );
+
+        ReportSummary summary = new ReportSummary();
+        summary.setReportType("fortune");
+        summary.setSourceTable("family_fortune_report");
+        summary.setSourceId(report.getId());
+        summary.setFamilyId(report.getFamilyId() != null ? report.getFamilyId().longValue() : null);
+
+        FamilyMember member = familyMemberMapper.selectOne(
+                new LambdaQueryWrapper<FamilyMember>()
+                        .eq(FamilyMember::getFamilyId, report.getFamilyId())
+                        .last("LIMIT 1")
+        );
+        summary.setUserId(member != null ? member.getUserId() : null);
+
+        String element = report.getElement() != null ? report.getElement() : "天盘";
+        summary.setTitle(element + "天盘周报");
+        summary.setSummary(report.getLuckyDirection() != null ? report.getLuckyDirection() : "");
+        summary.setFileUrl(report.getPdfPath());
+        summary.setFileSize(report.getFileSize());
+        summary.setHasFile(report.getPdfPath() != null && !report.getPdfPath().isEmpty() ? 1 : 0);
+        summary.setReportDate(report.getCreatedAt());
+        summary.setStatus("active");
+        summary.setDetailRoute("/pages/tianpan/index");
+
+        if (existing != null) {
+            summary.setId(existing.getId());
+            reportSummaryMapper.updateById(summary);
+        } else {
+            reportSummaryMapper.insert(summary);
+        }
+    }
+
+    public void markDiscarded(String sourceTable, Long sourceId) {
+        reportSummaryMapper.update(
+                null,
+                new LambdaUpdateWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, sourceTable)
+                        .eq(ReportSummary::getSourceId, sourceId)
+                        .set(ReportSummary::getStatus, "discarded")
+        );
+    }
+
+    public Page<ReportSummaryVO> pageList(ReportListQueryDTO query) {
+        Page<ReportSummary> page = new Page<>(query.getPage(), query.getSize());
+
+        LambdaQueryWrapper<ReportSummary> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(ReportSummary::getUserId, query.getUserId());
+        wrapper.ne(ReportSummary::getStatus, "discarded");
+
+        if ("all".equals(query.getType()) || query.getType() == null || query.getType().isEmpty()) {
+            // no type filter
+        } else {
+            wrapper.eq(ReportSummary::getReportType, query.getType());
+        }
+
+        if (query.getKeyword() != null && !query.getKeyword().isEmpty()) {
+            wrapper.and(w -> w
+                    .like(ReportSummary::getTitle, query.getKeyword())
+                    .or().like(ReportSummary::getSubjectName, query.getKeyword())
+            );
+        }
+
+        wrapper.orderByDesc(ReportSummary::getReportDate);
+        reportSummaryMapper.selectPage(page, wrapper);
+
+        Page<ReportSummaryVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
+        List<ReportSummaryVO> voList = new ArrayList<>();
+        for (ReportSummary s : page.getRecords()) {
+            ReportSummaryVO vo = new ReportSummaryVO();
+            vo.setId(s.getId());
+            vo.setReportType(s.getReportType());
+            vo.setReportTypeName(ReportSummaryVO.getReportTypeName(s.getReportType()));
+            vo.setTitle(s.getTitle());
+            vo.setSubjectName(s.getSubjectName());
+            vo.setSummary(s.getSummary());
+            vo.setFileUrl(s.getFileUrl());
+            vo.setFileSize(s.getFileSize());
+            vo.setFileName(s.getFileName());
+            vo.setHasFile(s.getHasFile() != null && s.getHasFile() == 1);
+            vo.setOverallScore(s.getOverallScore());
+            vo.setReportDate(s.getReportDate() != null ? s.getReportDate().toString() : "");
+            vo.setStatus(s.getStatus());
+            vo.setDetailRoute(s.getDetailRoute());
+            vo.setCreatedAt(s.getCreatedAt());
+            voList.add(vo);
+        }
+        voPage.setRecords(voList);
+        return voPage;
+    }
+
+    public ReportSummaryVO getDetail(Long summaryId) {
+        ReportSummary s = reportSummaryMapper.selectById(summaryId);
+        if (s == null) return null;
+
+        ReportSummaryVO vo = new ReportSummaryVO();
+        vo.setId(s.getId());
+        vo.setReportType(s.getReportType());
+        vo.setReportTypeName(ReportSummaryVO.getReportTypeName(s.getReportType()));
+        vo.setTitle(s.getTitle());
+        vo.setSubjectName(s.getSubjectName());
+        vo.setSummary(s.getSummary());
+        vo.setFileUrl(s.getFileUrl());
+        vo.setFileSize(s.getFileSize());
+        vo.setFileName(s.getFileName());
+        vo.setHasFile(s.getHasFile() != null && s.getHasFile() == 1);
+        vo.setOverallScore(s.getOverallScore());
+        vo.setReportDate(s.getReportDate() != null ? s.getReportDate().toString() : "");
+        vo.setStatus(s.getStatus());
+        vo.setDetailRoute(s.getDetailRoute());
+        vo.setCreatedAt(s.getCreatedAt());
+        return vo;
+    }
+
+    public Page<ReportSummaryVO> adminPageList(ReportListQueryDTO query) {
+        Page<ReportSummary> page = new Page<>(query.getPage(), query.getSize());
+
+        LambdaQueryWrapper<ReportSummary> wrapper = new LambdaQueryWrapper<>();
+        wrapper.ne(ReportSummary::getStatus, "discarded");
+
+        if ("all".equals(query.getType()) || query.getType() == null || query.getType().isEmpty()) {
+            // no type filter
+        } else {
+            wrapper.eq(ReportSummary::getReportType, query.getType());
+        }
+
+        if (query.getKeyword() != null && !query.getKeyword().isEmpty()) {
+            wrapper.and(w -> w
+                    .like(ReportSummary::getTitle, query.getKeyword())
+                    .or().like(ReportSummary::getSubjectName, query.getKeyword())
+                    .or().like(ReportSummary::getSummary, query.getKeyword())
+            );
+        }
+
+        if (query.getStartDate() != null && !query.getStartDate().isEmpty()) {
+            wrapper.ge(ReportSummary::getReportDate, query.getStartDate());
+        }
+        if (query.getEndDate() != null && !query.getEndDate().isEmpty()) {
+            wrapper.le(ReportSummary::getReportDate, query.getEndDate());
+        }
+
+        wrapper.orderByDesc(ReportSummary::getReportDate);
+        reportSummaryMapper.selectPage(page, wrapper);
+
+        Page<ReportSummaryVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
+        List<ReportSummaryVO> voList = new ArrayList<>();
+        for (ReportSummary rs : page.getRecords()) {
+            ReportSummaryVO vo = new ReportSummaryVO();
+            vo.setId(rs.getId());
+            vo.setReportType(rs.getReportType());
+            vo.setReportTypeName(ReportSummaryVO.getReportTypeName(rs.getReportType()));
+            vo.setTitle(rs.getTitle());
+            vo.setSubjectName(rs.getSubjectName());
+            vo.setSummary(rs.getSummary());
+            vo.setHasFile(rs.getHasFile() != null && rs.getHasFile() == 1);
+            vo.setOverallScore(rs.getOverallScore());
+            vo.setReportDate(rs.getReportDate() != null ? rs.getReportDate().toString() : "");
+            vo.setStatus(rs.getStatus());
+            vo.setCreatedAt(rs.getCreatedAt());
+            voList.add(vo);
+        }
+        voPage.setRecords(voList);
+        return voPage;
+    }
+
+    public ReportSyncResultDTO fullSync() {
+        ReportSyncResultDTO result = new ReportSyncResultDTO();
+        result.setNewCount(0);
+        result.setUpdateCount(0);
+        result.setDiscardedCount(0);
+        result.setErrorCount(0);
+
+        // 1. sync health_reports
+        try {
+            List<HealthReport> healthReports = healthReportMapper.selectList(new LambdaQueryWrapper<HealthReport>());
+            List<ReportSummary> existingHealth = reportSummaryMapper.selectList(
+                    new LambdaQueryWrapper<ReportSummary>().eq(ReportSummary::getSourceTable, "health_reports"));
+            for (HealthReport hr : healthReports) {
+                try {
+                    ReportSummary existing = reportSummaryMapper.selectOne(
+                            new LambdaQueryWrapper<ReportSummary>()
+                                    .eq(ReportSummary::getSourceTable, "health_reports")
+                                    .eq(ReportSummary::getSourceId, hr.getId()));
+                    if (existing == null) {
+                        syncFromHealthReport(hr);
+                        result.setNewCount(result.getNewCount() + 1);
+                    } else {
+                        syncFromHealthReport(hr);
+                        result.setUpdateCount(result.getUpdateCount() + 1);
+                    }
+                } catch (Exception e) {
+                    result.setErrorCount(result.getErrorCount() + 1);
+                    log.error("同步 health_reports 记录失败 id={}", hr.getId(), e);
+                }
+            }
+            for (ReportSummary es : existingHealth) {
+                if (healthReportMapper.selectById(es.getSourceId()) == null) {
+                    markDiscarded("health_reports", es.getSourceId());
+                    result.setDiscardedCount(result.getDiscardedCount() + 1);
+                }
+            }
+        } catch (Exception e) {
+            log.error("全量同步 health_reports 失败", e);
+            result.setErrorCount(result.getErrorCount() + 1);
+        }
+
+        // 2. sync dan_report_uploads
+        try {
+            List<DanReportUpload> danUploads = danReportUploadMapper.selectList(new LambdaQueryWrapper<DanReportUpload>());
+            List<ReportSummary> existingDan = reportSummaryMapper.selectList(
+                    new LambdaQueryWrapper<ReportSummary>().eq(ReportSummary::getSourceTable, "dan_report_uploads"));
+            for (DanReportUpload du : danUploads) {
+                try {
+                    ReportSummary existing = reportSummaryMapper.selectOne(
+                            new LambdaQueryWrapper<ReportSummary>()
+                                    .eq(ReportSummary::getSourceTable, "dan_report_uploads")
+                                    .eq(ReportSummary::getSourceId, du.getId()));
+                    if (existing == null) {
+                        syncFromDanReport(du);
+                        result.setNewCount(result.getNewCount() + 1);
+                    } else {
+                        syncFromDanReport(du);
+                        result.setUpdateCount(result.getUpdateCount() + 1);
+                    }
+                } catch (Exception e) {
+                    result.setErrorCount(result.getErrorCount() + 1);
+                    log.error("同步 dan_report_uploads 失败 id={}", du.getId(), e);
+                }
+            }
+            for (ReportSummary es : existingDan) {
+                if (danReportUploadMapper.selectById(es.getSourceId()) == null) {
+                    markDiscarded("dan_report_uploads", es.getSourceId());
+                    result.setDiscardedCount(result.getDiscardedCount() + 1);
+                }
+            }
+        } catch (Exception e) {
+            log.error("全量同步 dan_report_uploads 失败", e);
+            result.setErrorCount(result.getErrorCount() + 1);
+        }
+
+        // 3. sync insurance_policies
+        try {
+            List<InsurancePolicy> policies = insurancePolicyMapper.selectList(new LambdaQueryWrapper<InsurancePolicy>());
+            List<ReportSummary> existingIns = reportSummaryMapper.selectList(
+                    new LambdaQueryWrapper<ReportSummary>().eq(ReportSummary::getSourceTable, "insurance_policies"));
+            for (InsurancePolicy p : policies) {
+                try {
+                    ReportSummary existing = reportSummaryMapper.selectOne(
+                            new LambdaQueryWrapper<ReportSummary>()
+                                    .eq(ReportSummary::getSourceTable, "insurance_policies")
+                                    .eq(ReportSummary::getSourceId, p.getId()));
+                    if (existing == null) {
+                        syncFromInsurancePolicy(p);
+                        result.setNewCount(result.getNewCount() + 1);
+                    } else {
+                        syncFromInsurancePolicy(p);
+                        result.setUpdateCount(result.getUpdateCount() + 1);
+                    }
+                } catch (Exception e) {
+                    result.setErrorCount(result.getErrorCount() + 1);
+                    log.error("同步 insurance_policies 失败 id={}", p.getId(), e);
+                }
+            }
+            for (ReportSummary es : existingIns) {
+                if (insurancePolicyMapper.selectById(es.getSourceId()) == null) {
+                    markDiscarded("insurance_policies", es.getSourceId());
+                    result.setDiscardedCount(result.getDiscardedCount() + 1);
+                }
+            }
+        } catch (Exception e) {
+            log.error("全量同步 insurance_policies 失败", e);
+            result.setErrorCount(result.getErrorCount() + 1);
+        }
+
+        // 4. sync family_fortune_report via JdbcTemplate
+        try {
+            String sql = "SELECT id, family_id, element, lucky_direction, pdf_path, file_size, created_at FROM family_fortune_report";
+            List<FamilyFortuneReport> reports = jdbcTemplate.query(sql, (rs, rowNum) -> {
+                FamilyFortuneReport r = new FamilyFortuneReport();
+                r.setId(rs.getLong("id"));
+                r.setFamilyId(rs.getInt("family_id"));
+                r.setElement(rs.getString("element"));
+                r.setLuckyDirection(rs.getString("lucky_direction"));
+                r.setPdfPath(rs.getString("pdf_path"));
+                r.setFileSize(rs.getLong("file_size"));
+                r.setCreatedAt(rs.getTimestamp("created_at"));
+                return r;
+            });
+
+            List<ReportSummary> existingFortune = reportSummaryMapper.selectList(
+                    new LambdaQueryWrapper<ReportSummary>().eq(ReportSummary::getSourceTable, "family_fortune_report"));
+            for (FamilyFortuneReport r : reports) {
+                try {
+                    ReportSummary existing = reportSummaryMapper.selectOne(
+                            new LambdaQueryWrapper<ReportSummary>()
+                                    .eq(ReportSummary::getSourceTable, "family_fortune_report")
+                                    .eq(ReportSummary::getSourceId, r.getId()));
+                    if (existing == null) {
+                        syncFromFamilyFortuneReport(r);
+                        result.setNewCount(result.getNewCount() + 1);
+                    } else {
+                        syncFromFamilyFortuneReport(r);
+                        result.setUpdateCount(result.getUpdateCount() + 1);
+                    }
+                } catch (Exception e) {
+                    result.setErrorCount(result.getErrorCount() + 1);
+                    log.error("同步 family_fortune_report 失败 id={}", r.getId(), e);
+                }
+            }
+            for (ReportSummary es : existingFortune) {
+                boolean exists = reports.stream().anyMatch(r -> r.getId().equals(es.getSourceId()));
+                if (!exists) {
+                    markDiscarded("family_fortune_report", es.getSourceId());
+                    result.setDiscardedCount(result.getDiscardedCount() + 1);
+                }
+            }
+        } catch (Exception e) {
+            log.error("全量同步 family_fortune_report 失败", e);
+            result.setErrorCount(result.getErrorCount() + 1);
+        }
+
+        log.info("全量同步完成: new={}, update={}, discarded={}, error={}",
+                result.getNewCount(), result.getUpdateCount(), result.getDiscardedCount(), result.getErrorCount());
+        return result;
+    }
+}

+ 32 - 0
cfc-backend/src/main/java/com/etotem/cfc/task/ReportSummarySyncTask.java

@@ -0,0 +1,32 @@
+package com.etotem.cfc.task;
+
+import com.etotem.cfc.dto.ReportSyncResultDTO;
+import com.etotem.cfc.service.ReportSummaryService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@Component
+public class ReportSummarySyncTask {
+
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
+    /**
+     * 每日凌晨 2:00 全量校准汇总表
+     */
+    @Scheduled(cron = "0 0 2 * * ?")
+    public void scheduledSync() {
+        log.info("开始定时同步报告汇总表...");
+        try {
+            ReportSyncResultDTO result = reportSummaryService.fullSync();
+            log.info("定时同步完成: new={}, update={}, discarded={}, error={}",
+                    result.getNewCount(), result.getUpdateCount(), result.getDiscardedCount(), result.getErrorCount());
+        } catch (Exception e) {
+            log.error("定时同步失败", e);
+        }
+    }
+}

+ 26 - 0
cfc-backend/src/main/resources/mapper/ReportSummaryMapper.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.etotem.cfc.mapper.ReportSummaryMapper">
+  <resultMap id="baseMap" type="com.etotem.cfc.entity.ReportSummary">
+    <id column="id" property="id"/>
+    <result column="report_type" property="reportType"/>
+    <result column="source_table" property="sourceTable"/>
+    <result column="source_id" property="sourceId"/>
+    <result column="user_id" property="userId"/>
+    <result column="family_id" property="familyId"/>
+    <result column="subject_id" property="subjectId"/>
+    <result column="subject_name" property="subjectName"/>
+    <result column="title" property="title"/>
+    <result column="summary" property="summary"/>
+    <result column="file_url" property="fileUrl"/>
+    <result column="file_size" property="fileSize"/>
+    <result column="file_name" property="fileName"/>
+    <result column="has_file" property="hasFile"/>
+    <result column="overall_score" property="overallScore"/>
+    <result column="report_date" property="reportDate"/>
+    <result column="status" property="status"/>
+    <result column="detail_route" property="detailRoute"/>
+    <result column="created_at" property="createdAt"/>
+    <result column="updated_at" property="updatedAt"/>
+  </resultMap>
+</mapper>

+ 47 - 13
cfc-backend/src/main/resources/schema.sql

@@ -3464,17 +3464,51 @@ CREATE TABLE IF NOT EXISTS points_exchange_records (
 
 -- 文章阅读记录表
 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 '阅读时长(秒)',
-    task_id BIGINT DEFAULT NULL COMMENT '关联的任务ID',
-    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)
+  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 '阅读时长(秒)',
+  task_id BIGINT DEFAULT NULL COMMENT '关联的任务ID',
+  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='文章阅读记录表';
+
+-- ============================================================
+-- 统一报告汇总表(report_summary)
+-- ============================================================
+CREATE TABLE IF NOT EXISTS report_summary (
+  id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
+  report_type VARCHAR(32) NOT NULL COMMENT '报告类型',
+  source_table VARCHAR(32) NOT NULL COMMENT '来源表',
+  source_id BIGINT NOT NULL COMMENT '来源表记录ID',
+  user_id BIGINT NOT NULL COMMENT '创建者用户ID',
+  family_id BIGINT COMMENT '家庭ID',
+  subject_id BIGINT COMMENT '报告主体ID',
+  subject_name VARCHAR(50) COMMENT '报告主体名称',
+  title VARCHAR(200) COMMENT '报告标题',
+  summary TEXT COMMENT '摘要',
+  file_url VARCHAR(500) COMMENT '文件URL',
+  file_size BIGINT COMMENT '文件大小',
+  file_name VARCHAR(200) COMMENT '文件名',
+  has_file TINYINT DEFAULT 0 COMMENT '是否有文件',
+  overall_score INT COMMENT '综合评分',
+  report_date DATE COMMENT '报告日期',
+  status VARCHAR(20) COMMENT '状态',
+  detail_route VARCHAR(100) COMMENT '详情页路由',
+  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  INDEX idx_user_id (user_id),
+  INDEX idx_family_id (family_id),
+  INDEX idx_subject_id (subject_id),
+  INDEX idx_report_type (report_type),
+  INDEX idx_report_date (report_date),
+  INDEX idx_status (status),
+  UNIQUE KEY uk_source (source_table, source_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一报告汇总表';
+-- report_summary 表已由 DatabaseInitializer 迁移创建

+ 68 - 139
cfc-frontend/components/DimensionActivities.vue

@@ -10,7 +10,7 @@
       </view>
       <view class="section-more" @click="$emit('moreActivities')">
         <text class="section-more-text">更多</text>
-        <text class="section-more-arrow">&#x203A;</text>
+        <text class="section-more-arrow">›</text>
       </view>
     </view>
 
@@ -20,7 +20,7 @@
       <text class="empty-tip">{{ isLoggedIn ? '暂无相关活动' : '登录后查看更多活动' }}</text>
     </view>
 
-    <!-- 活动卡片轮播 -->
+    <!-- 活动卡片轮播(行布局) -->
     <view class="activity-carousel" v-if="displayActivities && displayActivities.length > 0">
       <swiper
         class="activity-swiper"
@@ -34,32 +34,18 @@
       >
         <swiper-item v-for="(act, index) in displayActivities" :key="act.id">
           <view class="activity-card" @click.stop="$emit('activityClick', act)">
-            <!-- 封面图区域 -->
-            <view class="card-cover">
-              <image
-                class="card-cover-img"
-                :src="act.coverImage || '/static/default-activity.png'"
-                mode="aspectFill"
-              />
-              <!-- 渐变遮罩 -->
-              <view class="card-cover-mask"></view>
-              <!-- 状态标签 -->
-              <view class="card-status" :class="'status-' + (act.status || 'upcoming')">
-                <text class="card-status-dot" v-if="act.status === 'ongoing'"></text>
-                <text class="card-status-text">{{ statusText(act.status) }}</text>
-              </view>
-              <!-- 标题 -->
-              <view class="card-title-wrap">
-                <text class="card-title">{{ act.title }}</text>
-              </view>
-            </view>
-
-            <!-- 内容区域 -->
-            <view class="card-content">
-              <!-- 时间和维度标签 -->
+            <!-- 封面图(占满卡片高度) -->
+            <image
+              class="card-cover-img"
+              :src="act.coverImage || '/static/default-activity.png'"
+              mode="aspectFill"
+            />
+            <!-- 右侧信息区 -->
+            <view class="card-info">
+              <text class="card-title line-clamp-1">{{ act.title }}</text>
               <view class="card-meta-row">
                 <view class="card-time">
-                  <text class="meta-icon">&#x1F552;</text>
+                  <text class="meta-icon">🕐</text>
                   <text class="meta-text">{{ formatTime(act.startTime) }}</text>
                 </view>
                 <view class="card-badges" v-if="act._badges && act._badges.length">
@@ -73,17 +59,13 @@
                   </text>
                 </view>
               </view>
-
-              <!-- 底部:价格和按钮 -->
-              <view class="card-footer">
-                <view class="card-price-wrap">
-                  <text class="card-price" v-if="act.priceLabel">{{ act.priceLabel }}</text>
-                  <text class="card-price" v-else-if="act.price && act.price > 0">{{ formatPriceWithSymbol(act.price) }}</text>
-                  <text class="card-price-free" v-else>免费</text>
-                </view>
-                <view class="card-action-btn">
-                  <text class="card-action-text">了解详情</text>
-                </view>
+              <view class="card-bottom">
+                <text class="card-status" :class="'status-' + (act.status || 'upcoming')">
+                  {{ statusText(act.status) }}
+                </text>
+                <text class="card-price" v-if="act.priceLabel">{{ act.priceLabel }}</text>
+                <text class="card-price" v-else-if="act.price && act.price > 0">{{ formatPriceWithSymbol(act.price) }}</text>
+                <text class="card-price-free" v-else>免费</text>
               </view>
             </view>
           </view>
@@ -256,7 +238,7 @@ export default {
 
 .activity-swiper {
   width: 100%;
-  height: 300rpx;
+  height: 160rpx;
 }
 
 .activity-swiper ::deep .swiper-slide {
@@ -287,94 +269,49 @@ export default {
   background: #F97316;
 }
 
-/* ===== 活动卡片 ===== */
+/* ===== 活动卡片(行布局) ===== */
 .activity-card {
   background: #FFFFFF;
-  border-radius: 24rpx;
+  border-radius: 16rpx;
   overflow: hidden;
-  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
+  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
+  display: flex;
+  flex-direction: row;
+  height: 100%;
 }
-
-/* 封面图 */
-.card-cover {
-  position: relative;
-  width: 100%;
-  height: 160rpx;
-  overflow: hidden;
+.activity-card:active {
+  opacity: 0.8;
 }
 
+/* 封面图 — 占满卡片高度 */
 .card-cover-img {
-  width: 100%;
+  width: 200rpx;
   height: 100%;
+  flex-shrink: 0;
   background: #F1F5F9;
 }
 
-/* 渐变遮罩 */
-.card-cover-mask {
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  height: 80rpx;
-  background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
-}
-
-/* 状态标签 */
-.card-status {
-  position: absolute;
-  top: 16rpx;
-  right: 16rpx;
+/* 右侧信息区 */
+.card-info {
+  flex: 1;
+  padding: 16rpx;
   display: flex;
-  flex-direction: row;
-  align-items: center;
-  padding: 6rpx 16rpx;
-  border-radius: 20rpx;
-  background: rgba(0, 0, 0, 0.4);
-  backdrop-filter: blur(10rpx);
-}
-
-.card-status-dot {
-  width: 8rpx;
-  height: 8rpx;
-  border-radius: 50%;
-  background: #22C55E;
-  margin-right: 6rpx;
-  animation: pulse 2s infinite;
-}
-
-@keyframes pulse {
-  0%, 100% { opacity: 1; }
-  50% { opacity: 0.4; }
-}
-
-.card-status-text {
-  font-size: 22rpx;
-  color: #FFFFFF;
-  font-weight: 500;
+  flex-direction: column;
+  justify-content: space-between;
+  min-width: 0;
 }
 
 /* 标题 */
-.card-title-wrap {
-  position: absolute;
-  bottom: 20rpx;
-  left: 20rpx;
-  right: 20rpx;
-}
-
 .card-title {
-  font-size: 26rpx;
-  font-weight: 700;
-  color: #FFFFFF;
-  text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3);
-  display: -webkit-box;
-  -webkit-line-clamp: 1;
-  -webkit-box-orient: vertical;
-  overflow: hidden;
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 8rpx;
 }
-
-/* 内容区域 */
-.card-content {
-  padding: 12rpx 16rpx;
+.line-clamp-1 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
 
 /* 时间和维度标签 */
@@ -417,47 +354,39 @@ export default {
   font-weight: 500;
 }
 
-/* 底部 */
-.card-footer {
+/* 底部:状态 + 价格 */
+.card-bottom {
   display: flex;
   flex-direction: row;
-  justify-content: space-between;
   align-items: center;
-  padding-top: 8rpx;
-  border-top: 1rpx solid #F1F5F9;
+  gap: 12rpx;
 }
 
-.card-price-wrap {
-  display: flex;
-  flex-direction: row;
-  align-items: baseline;
+.card-status {
+  font-size: 20rpx;
+  padding: 2rpx 10rpx;
+  border-radius: 8rpx;
+  background: #E8F5E9;
+  color: #2E7D32;
+}
+.card-status.status-upcoming {
+  background: #E3F2FD;
+  color: #1565C0;
+}
+.card-status.status-ended {
+  background: #F5F5F5;
+  color: #999;
 }
 
 .card-price {
-  font-size: 30rpx;
-  font-weight: 700;
-  color: #FF6B9D;
+  font-size: 24rpx;
+  font-weight: bold;
+  color: #F97316;
 }
 
 .card-price-free {
-  font-size: 28rpx;
+  font-size: 24rpx;
   font-weight: 600;
   color: #22C55E;
 }
-
-.card-action-btn {
-  padding: 10rpx 24rpx;
-  background: linear-gradient(135deg, #FF6B9D, #FF8FB1);
-  border-radius: 24rpx;
-}
-
-.card-action-text {
-  font-size: 22rpx;
-  color: #FFFFFF;
-  font-weight: 600;
-}
-
-.card-action-btn:active {
-  opacity: 0.8;
-}
-</style>
+</style>

+ 12 - 0
cfc-frontend/pages.json

@@ -1364,6 +1364,18 @@
           "style": {
             "navigationBarTitleText": "我的"
           }
+        },
+        {
+          "path": "report-management",
+          "style": {
+            "navigationBarTitleText": "报告管理"
+          }
+        },
+        {
+          "path": "report-detail",
+          "style": {
+            "navigationBarTitleText": "报告详情"
+          }
         }
       ]
     }

+ 7 - 0
cfc-frontend/pages/profile/components/ProfileMenu.vue

@@ -20,6 +20,10 @@
           <text>🎯 新手任务</text>
           <text class="arrow">›</text>
         </view>
+        <view class="menu-item" @click="goToReportManagement">
+          <text>📋 报告管理</text>
+          <text class="arrow">›</text>
+        </view>
       </view>
 
       <!-- ===== 服务 ===== -->
@@ -138,6 +142,9 @@ export default {
     goToOnboarding() {
       uni.navigateTo({ url: '/pages/profile-extra/onboarding' })
     },
+    goToReportManagement() {
+      uni.navigateTo({ url: '/pages/profile/report-management' })
+    },
     goToFamilyMembers() {
       uni.navigateTo({ url: '/pages/profile-extra/family-members' })
     },

+ 232 - 0
cfc-frontend/pages/profile/report-detail.vue

@@ -0,0 +1,232 @@
+<template>
+  <view class="container">
+    <view class="report-header">
+      <view class="type-badge" :class="'badge-' + getBadgeClass(detail.reportType)">
+        <text>{{ detail.reportTypeName || '报告' }}</text>
+      </view>
+      <text class="report-title">{{ detail.title }}</text>
+      <view class="report-meta">
+        <text class="meta-item">{{ detail.subjectName }}</text>
+        <text class="meta-item">{{ formatDate(detail.reportDate) }}</text>
+      </view>
+      <view class="score-section" v-if="detail.overallScore !== null && detail.overallScore !== undefined">
+        <text class="score">{{ detail.overallScore }}</text>
+        <text class="score-label">综合评分</text>
+      </view>
+    </view>
+
+    <view class="summary-section" v-if="detail.summary">
+      <text class="section-title">报告摘要</text>
+      <text class="summary-text">{{ detail.summary }}</text>
+    </view>
+
+    <view class="action-section" v-if="detail.hasFile">
+      <button class="action-btn" @click="downloadReport">下载报告</button>
+      <button class="action-btn secondary" @click="goToOriginalDetail">查看原始详情</button>
+    </view>
+
+    <view class="action-section" v-else>
+      <button class="action-btn secondary" @click="goToOriginalDetail">查看原始详情</button>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getReportSummaryDetail, downloadReportSummary } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      reportId: null,
+      detail: {}
+    }
+  },
+  onLoad(options) {
+    if (options && options.id) {
+      this.reportId = parseInt(options.id)
+      this.loadDetail()
+    }
+  },
+  onShareAppMessage() {
+    return {
+      title: '浠艾福报告',
+      path: '/pages/profile/report-detail?id=' + this.reportId
+    }
+  },
+  methods: {
+    loadDetail() {
+      var self = this
+      getReportSummaryDetail(self.reportId).then(function(res) {
+        if (res && res.data) {
+          self.detail = res.data
+          uni.setNavigationBarTitle({ title: self.detail.title || '报告详情' })
+        }
+      }).catch(function(e) {
+        console.log('加载详情失败', e)
+        uni.showToast({ title: '加载失败', icon: 'none' })
+      })
+    },
+    downloadReport() {
+      var self = this
+      uni.showLoading({ title: '下载中...' })
+      downloadReportSummary(self.reportId).then(function(res) {
+        if (res && res.data && res.data.fileUrl) {
+          var url = res.data.fileUrl
+          var name = res.data.fileName || 'report'
+          uni.downloadFile({
+            url: url,
+            filePath: wx.env.USER_DATA_PATH + '/' + name,
+            success: function(res2) {
+              uni.saveFileToDisk({
+                filePath: res2.filePath,
+                success: function() {
+                  uni.showToast({ title: '保存成功', icon: 'success' })
+                },
+                fail: function() {
+                  uni.showToast({ title: '保存失败', icon: 'none' })
+                }
+              })
+            },
+            fail: function() {
+              uni.showToast({ title: '下载失败', icon: 'none' })
+            },
+            complete: function() {
+              uni.hideLoading()
+            }
+          })
+        }
+      }).catch(function(e) {
+        uni.hideLoading()
+        uni.showToast({ title: '下载失败', icon: 'none' })
+      })
+    },
+    goToOriginalDetail() {
+      var route = this.detail.detailRoute || ''
+      if (route) {
+        var url = route + (this.detail.sourceId ? '?reportId=' + this.detail.sourceId : '')
+        uni.navigateTo({ url: url })
+      } else {
+        uni.showToast({ title: '暂无详情页面', icon: 'none' })
+      }
+    },
+    getBadgeClass(reportType) {
+      return reportType || 'default'
+    },
+    formatDate(dateStr) {
+      if (!dateStr) return '--'
+      return dateStr
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container {
+  min-height: 100vh;
+  background: #f5f7fa;
+  padding: 24rpx;
+}
+
+.report-header {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 32rpx 24rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+  margin-bottom: 16rpx;
+}
+
+.type-badge {
+  display: inline-block;
+  font-size: 22rpx;
+  padding: 6rpx 20rpx;
+  border-radius: 12rpx;
+  margin-bottom: 16rpx;
+}
+
+.badge-gut_flora { background: #E8F5E9; color: #2E7D32; }
+.badge-physical_exam { background: #E3F2FD; color: #1565C0; }
+.badge-dan { background: #F3E5F5; color: #7B1FA2; }
+.badge-insurance { background: #FFF3E0; color: #E65100; }
+.badge-fortune { background: #FCE4EC; color: #C62828; }
+
+.report-title {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+  display: block;
+  margin-bottom: 12rpx;
+}
+
+.report-meta {
+  display: flex;
+  flex-direction: row;
+  margin-bottom: 16rpx;
+}
+
+.meta-item {
+  font-size: 24rpx;
+  color: #888;
+  margin-right: 24rpx;
+}
+
+.score-section {
+  display: flex;
+  flex-direction: row;
+  align-items: baseline;
+}
+
+.score {
+  font-size: 56rpx;
+  font-weight: bold;
+  color: #5B9BD5;
+}
+
+.score-label {
+  font-size: 24rpx;
+  color: #888;
+  margin-left: 12rpx;
+}
+
+.summary-section {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+  margin-bottom: 16rpx;
+}
+
+.section-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  display: block;
+  margin-bottom: 12rpx;
+}
+
+.summary-text {
+  font-size: 26rpx;
+  color: #666;
+  line-height: 1.6;
+}
+
+.action-section {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+}
+
+.action-btn {
+  background: #5B9BD5;
+  color: #fff;
+  font-size: 28rpx;
+  border-radius: 12rpx;
+  margin-bottom: 16rpx;
+}
+
+.action-btn.secondary {
+  background: #f0f0f0;
+  color: #333;
+  margin-bottom: 0;
+}
+</style>

+ 302 - 0
cfc-frontend/pages/profile/report-management.vue

@@ -0,0 +1,302 @@
+<template>
+  <view class="container">
+    <view class="tab-bar">
+      <view
+        v-for="(tab, idx) in tabs"
+        :key="idx"
+        class="tab-item"
+        :class="{ 'tab-active': activeTab === idx }"
+        @click="activeTab = idx; loadData()"
+      >
+        <text>{{ tab.label }}</text>
+      </view>
+    </view>
+
+    <view class="search-bar">
+      <input
+        class="search-input"
+        v-model="keyword"
+        placeholder="搜索报告标题或成员名称..."
+        @confirm="loadData()"
+      />
+    </view>
+
+    <scroll-view scroll-y class="report-list" @scrolltolower="loadMore">
+      <view
+        v-for="item in reports"
+        :key="item.id"
+        class="report-card"
+        :class="'card-' + getCardClass(item.reportType)"
+        @click="goToDetail(item)"
+      >
+        <view class="card-left">
+          <view class="type-badge" :class="'badge-' + getCardClass(item.reportType)">
+            <text>{{ item.reportTypeName }}</text>
+          </view>
+          <text class="card-title">{{ item.title }}</text>
+          <view class="card-meta">
+            <text class="subject-name">{{ item.subjectName }}</text>
+            <text class="date-text">{{ formatDate(item.reportDate) }}</text>
+          </view>
+        </view>
+        <view class="card-right">
+          <text v-if="item.overallScore !== null && item.overallScore !== undefined" class="score">{{ item.overallScore }}<text class="score-unit">分</text></text>
+          <text v-else class="score-text">{{ item.hasFile ? '有文件' : '结构化数据' }}</text>
+          <text class="arrow">›</text>
+        </view>
+      </view>
+
+      <view v-if="reports.length === 0 && !loading" class="empty">
+        <text class="empty-icon">📋</text>
+        <text class="empty-title">暂无报告</text>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import { getReportSummaryList } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      tabs: [
+        { label: '全部', type: 'all' },
+        { label: '菌群', type: 'gut_flora' },
+        { label: '体检', type: 'physical_exam' },
+        { label: 'DAN', type: 'dan' },
+        { label: '保单', type: 'insurance' },
+        { label: '周报', type: 'fortune' }
+      ],
+      activeTab: 0,
+      keyword: '',
+      reports: [],
+      page: 1,
+      loading: false,
+      hasMore: true
+    }
+  },
+  onLoad() {
+    this.loadData()
+  },
+  onPullDownRefresh() {
+    this.page = 1
+    this.reports = []
+    this.hasMore = true
+    this.loadData()
+  },
+  methods: {
+    loadData() {
+      var self = this
+      if (self.loading) return
+      if (!self.hasMore) return
+      self.loading = true
+      var params = {
+        page: self.page,
+        size: 20,
+        type: self.tabs[self.activeTab].type,
+        keyword: self.keyword
+      }
+      getReportSummaryList(params).then(function(res) {
+        var list = res && res.data && res.data.records ? res.data.records : []
+        if (self.page === 1) {
+          self.reports = list
+        } else {
+          self.reports = self.reports.concat(list)
+        }
+        self.hasMore = list.length >= 20
+        self.page++
+      }).catch(function(e) {
+        console.log('获取报告列表失败', e)
+        uni.showToast({ title: '加载失败', icon: 'none' })
+      }).finally(function() {
+        self.loading = false
+        uni.stopPullDownRefresh()
+      })
+    },
+    loadMore() {
+      if (!this.loading && this.hasMore) {
+        this.loadData()
+      }
+    },
+    goToDetail(item) {
+      uni.navigateTo({
+        url: '/pages/profile/report-detail?id=' + item.id + '&title=' + encodeURIComponent(item.title)
+      })
+    },
+    getCardClass(reportType) {
+      return reportType || 'default'
+    },
+    formatDate(dateStr) {
+      if (!dateStr) return '--'
+      try {
+        var d = new Date(dateStr)
+        var y = d.getFullYear()
+        var m = ('' + (d.getMonth() + 1)).padStart(2, '0')
+        var day = ('' + d.getDate()).padStart(2, '0')
+        return y + '-' + m + '-' + day
+      } catch (e) {
+        return dateStr
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container {
+  min-height: 100vh;
+  background: #f5f7fa;
+  display: flex;
+  flex-direction: column;
+}
+
+.tab-bar {
+  display: flex;
+  flex-direction: row;
+  background: #fff;
+  padding: 20rpx 0;
+  border-bottom: 1rpx solid #eee;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.tab-item {
+  padding: 12rpx 24rpx;
+  font-size: 26rpx;
+  color: #666;
+  margin: 0 8rpx;
+  border-radius: 20rpx;
+}
+
+.tab-active {
+  background: #5B9BD5;
+  color: #fff;
+}
+
+.search-bar {
+  padding: 16rpx 24rpx;
+  background: #fff;
+}
+
+.search-input {
+  background: #f5f5f5;
+  border-radius: 12rpx;
+  padding: 16rpx 24rpx;
+  font-size: 26rpx;
+}
+
+.report-list {
+  flex: 1;
+  padding: 16rpx 24rpx;
+}
+
+.report-card {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  margin-bottom: 16rpx;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+}
+
+.card-left {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  min-width: 0;
+}
+
+.type-badge {
+  display: inline-block;
+  font-size: 20rpx;
+  padding: 4rpx 16rpx;
+  border-radius: 12rpx;
+  align-self: flex-start;
+  margin-bottom: 8rpx;
+}
+
+.badge-gut_flora { background: #E8F5E9; color: #2E7D32; }
+.badge-physical_exam { background: #E3F2FD; color: #1565C0; }
+.badge-dan { background: #F3E5F5; color: #7B1FA2; }
+.badge-insurance { background: #FFF3E0; color: #E65100; }
+.badge-fortune { background: #FCE4EC; color: #C62828; }
+
+.card-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 8rpx;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.card-meta {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.subject-name {
+  font-size: 24rpx;
+  color: #888;
+  margin-right: 16rpx;
+}
+
+.date-text {
+  font-size: 24rpx;
+  color: #aaa;
+}
+
+.card-right {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap: 8rpx;
+  margin-left: 16rpx;
+}
+
+.score {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+}
+
+.score-unit {
+  font-size: 20rpx;
+  color: #999;
+  font-weight: normal;
+  margin-left: 4rpx;
+}
+
+.score-text {
+  font-size: 24rpx;
+  color: #888;
+}
+
+.arrow {
+  font-size: 32rpx;
+  color: #ccc;
+}
+
+.empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 100rpx 0;
+}
+
+.empty-icon {
+  font-size: 80rpx;
+  margin-bottom: 20rpx;
+}
+
+.empty-title {
+  font-size: 28rpx;
+  color: #999;
+}
+</style>

+ 6 - 1
cfc-frontend/utils/api.js

@@ -2148,4 +2148,9 @@ export const getMyAppointments = () => {
 // 取消测评预约
 export const cancelAppointment = (id) => {
   return request('/api/dan-assessment/appointment/cancel', 'POST', { id: id })
-}
+}
+
+// 统一报告管理
+export const getReportSummaryList = (params) => request('/api/report-summary/list', 'POST', params)
+export const getReportSummaryDetail = (id) => request('/api/report-summary/detail/' + id, 'POST', {})
+export const downloadReportSummary = (id) => request('/api/report-summary/download/' + id, 'POST', {})

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-f49bcdded60a12db42c31d656685c964f36505b5
+547a52162f4d63427f374ebb3c8f317d71a36d9c

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
   "name": "cfc-web",
-  "version": "1.0.604",
+  "version": "1.0.605",
   "private": true,
   "scripts": {
     "dev": "vue-cli-service serve",

+ 6 - 0
cfc-web/public/CHANGELOG-v1.0.md

@@ -4,6 +4,12 @@
 
 ---
 
+## v1.0.605 (2026-07-28)
+
+### 文档
+- add report management design spec
+
+
 ## v1.0.604 (2026-07-28)
 
 ### 新功能

+ 7 - 1
cfc-web/public/CHANGELOG.md

@@ -1,6 +1,6 @@
 # 更新日志
 
-> 当前版本: v1.0.604
+> 当前版本: v1.0.605
 
 ## 历史版本
 
@@ -8,6 +8,12 @@
 
 ---
 
+## v1.0.605 (2026-07-28)
+
+### 文档
+- add report management design spec
+
+
 ## v1.0.604 (2026-07-28)
 
 ### 新功能

+ 8 - 0
cfc-web/src/api/admin.js

@@ -534,6 +534,14 @@ export function deleteProduct(id) {
   })
 }
 
+export function copyProduct(id) {
+  return request({
+    url: '/api/admin/product/copy',
+    method: 'post',
+    data: { productId: id }
+  })
+}
+
 // ========== SKU 管理 API ==========
 
 export function getProductSkuList(productId) {

+ 6 - 0
cfc-web/src/router/index.js

@@ -447,6 +447,12 @@ const routes = [
         component: () => import('@/views/nutritionist/HealthReports.vue'),
         meta: { title: '健康报告', perm: 'health:reports' }
       },
+      {
+        path: 'report-management',
+        name: 'ReportManagement',
+        component: () => import('@/views/admin/ReportManagement.vue'),
+        meta: { title: '报告管理', perm: 'report:management' }
+      },
       {
         path: 'health-report-audit',
         name: 'HealthReportAudit',

+ 1 - 2
cfc-web/src/views/Layout.vue

@@ -225,6 +225,7 @@ export default {
         { title: '健康饮食', icon: 'el-icon-first-aid-kit', perm: 'health',
           children: [
             { path: '/health-reports', label: '健康报告', icon: 'el-icon-document', perm: 'health:reports' },
+            { path: '/report-management', label: '报告管理', icon: 'el-icon-files', perm: 'report:management' },
             { path: '/health-report-audit', label: '报告审核', icon: 'el-icon-document-checked', perm: 'health:reports' },
             { path: '/health-indicators', label: '健康指标', icon: 'el-icon-data-line', perm: 'health:indicators' },
             { path: '/health-checkins', label: '健康打卡', icon: 'el-icon-s-order', perm: 'health:checkins' },
@@ -644,7 +645,6 @@ export default {
 }
 
 .username {
-  margin-right: 20px;
   color: #606266;
 }
 
@@ -840,7 +840,6 @@ export default {
   }
 
   .username {
-    margin-right: 10px !important;
     font-size: 13px;
   }
 

+ 1 - 1
cfc-web/src/views/admin/ProductEdit.vue

@@ -199,7 +199,7 @@
 
         <el-form-item>
           <el-button type="primary" :loading="saving" @click="handleSave">
-            {{ isEdit ? '保存修改' : '创建商品' }}
+            保存
           </el-button>
           <el-button @click="$router.back()">取消</el-button>
         </el-form-item>

+ 13 - 1
cfc-web/src/views/admin/ProductManage.vue

@@ -91,6 +91,7 @@
               更多<i class="el-icon-arrow-down el-icon--right"></i>
             </el-button>
             <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item command="copy" icon="el-icon-document-copy">复制</el-dropdown-item>
               <el-dropdown-item v-if="row.status === 'pending'" command="approve" icon="el-icon-circle-check">通过</el-dropdown-item>
               <el-dropdown-item v-if="row.status === 'pending'" command="reject" icon="el-icon-circle-close">拒绝</el-dropdown-item>
               <el-dropdown-item v-if="row.status === 'on_shelf'" command="offShelf" icon="el-icon-download">下架</el-dropdown-item>
@@ -144,7 +145,7 @@
 </template>
 
 <script>
-import { getProductList, reviewProduct, shelveProduct } from '@/api/admin'
+import { getProductList, reviewProduct, shelveProduct, copyProduct } from '@/api/admin'
 
 export default {
   name: 'ProductManage',
@@ -283,8 +284,19 @@ export default {
         closeOnClickModal: true
       })
     },
+    async handleCopy(row) {
+      try {
+        await this.$confirm('确认复制商品 "' + row.name + '"?', '提示')
+        await copyProduct(row.id)
+        this.$message.success('复制成功')
+        this.loadList()
+      } catch (e) {
+        if (e !== 'cancel') this.$message.error('复制失败')
+      }
+    },
     handleActionCmd(row, cmd) {
       switch (cmd) {
+        case 'copy': this.handleCopy(row); break;
         case 'approve': this.handleReview(row, 'approve'); break;
         case 'reject': this.showRejectDialog(row); break;
         case 'offShelf': this.handleShelve(row, false); break;

+ 176 - 0
cfc-web/src/views/admin/ReportManagement.vue

@@ -0,0 +1,176 @@
+<template>
+  <div class="report-management">
+    <el-card>
+      <div class="filter-bar">
+        <el-select v-model="query.type" placeholder="报告类型" clearable style="width:140px">
+          <el-option label="全部" value=""></el-option>
+          <el-option label="菌群检测" value="gut_flora"></el-option>
+          <el-option label="体检报告" value="physical_exam"></el-option>
+          <el-option label="DAN测评" value="dan"></el-option>
+          <el-option label="保单" value="insurance"></el-option>
+          <el-option label="家庭周报" value="fortune"></el-option>
+        </el-select>
+        <el-input v-model="query.keyword" placeholder="搜索标题/名称" style="width:200px" @keyup.enter.native="loadData"></el-input>
+        <el-date-picker
+          v-model="dateRange"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          style="width:240px"
+        ></el-date-picker>
+        <el-button type="primary" @click="loadData">查询</el-button>
+        <el-button @click="doSync">同步数据</el-button>
+      </div>
+
+      <el-table :data="tableData" border stripe v-loading="loading" style="margin-top:16px">
+        <el-table-column prop="id" label="ID" width="80"></el-table-column>
+        <el-table-column label="报告类型" width="100">
+          <template slot-scope="scope">
+            <el-tag size="small" :type="getTypeTag(scope.row.reportType)">{{ scope.row.reportTypeName }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="title" label="标题"></el-table-column>
+        <el-table-column prop="subjectName" label="报告主体" width="120"></el-table-column>
+        <el-table-column prop="reportDate" label="报告日期" width="120"></el-table-column>
+        <el-table-column prop="overallScore" label="综合评分" width="100">
+          <template slot-scope="scope">
+            <span v-if="scope.row.overallScore">{{ scope.row.overallScore }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" width="100">
+          <template slot-scope="scope">
+            <el-tag size="small">{{ scope.row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="createdAt" label="创建时间" width="160"></el-table-column>
+        <el-table-column label="操作" width="120">
+          <template slot-scope="scope">
+            <el-button size="mini" type="text" @click="viewDetail(scope.row.id)">详情</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <el-pagination
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="query.page"
+        :page-size="query.size"
+        :total="total"
+        layout="total, prev, pager, next"
+        style="margin-top:16px"
+      ></el-pagination>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+
+export default {
+  name: 'ReportManagement',
+  data() {
+    return {
+      tableData: [],
+      total: 0,
+      loading: false,
+      query: {
+        page: 1,
+        size: 20,
+        type: '',
+        keyword: ''
+      },
+      dateRange: []
+    }
+  },
+  created() {
+    this.loadData()
+  },
+  methods: {
+    loadData() {
+      var self = this
+      self.loading = true
+      var params = {
+        page: self.query.page,
+        size: self.query.size,
+        type: self.query.type,
+        keyword: self.query.keyword
+      }
+      if (self.dateRange && self.dateRange.length === 2) {
+        params.startDate = self.dateRange[0].format ? self.dateRange[0].format('YYYY-MM-DD') : self.dateRange[0]
+        params.endDate = self.dateRange[1].format ? self.dateRange[1].format('YYYY-MM-DD') : self.dateRange[1]
+      }
+      axios.post('/api/admin/report-summary/list', params, {
+        headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
+      }).then(function(res) {
+        if (res.data && res.data.data) {
+          self.tableData = res.data.data.records || []
+          self.total = res.data.data.total || 0
+        }
+      }).catch(function(e) {
+        console.error(e)
+      }).finally(function() {
+        self.loading = false
+      })
+    },
+    handleSizeChange(size) {
+      this.query.size = size
+      this.query.page = 1
+      this.loadData()
+    },
+    handleCurrentChange(page) {
+      this.query.page = page
+      this.loadData()
+    },
+    doSync() {
+      var self = this
+      axios.post('/api/admin/report-summary/sync', {}, {
+        headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
+      }).then(function(res) {
+        if (res.data && res.data.code === 200) {
+          self.$message.success('同步完成: ' + res.data.message)
+          self.loadData()
+        }
+      }).catch(function(e) {
+        self.$message.error('同步失败')
+      })
+    },
+    viewDetail(id) {
+      var self = this
+      axios.post('/api/report-summary/detail/' + id, {}, {
+        headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
+      }).then(function(res) {
+        if (res.data && res.data.data) {
+          var d = res.data.data
+          self.$alert(
+            '<p>标题: ' + d.title + '</p>' +
+            '<p>类型: ' + d.reportTypeName + '</p>' +
+            '<p>主体: ' + d.subjectName + '</p>' +
+            '<p>日期: ' + d.reportDate + '</p>' +
+            '<p>评分: ' + (d.overallScore || '-') + '</p>' +
+            '<p>状态: ' + d.status + '</p>',
+            '报告详情',
+            { dangerouslyUseHTMLString: true }
+          )
+        }
+      })
+    },
+    getTypeTag(type) {
+      var map = { gut_flora: 'success', physical_exam: 'primary', dan: 'warning', insurance: 'info', fortune: 'danger' }
+      return map[type] || 'info'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.report-management { padding: 16px; }
+.filter-bar {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap: 12px;
+  flex-wrap: wrap;
+}
+</style>

+ 1931 - 0
docs/superpowers/plans/2026-07-28-report-management-plan.md

@@ -0,0 +1,1931 @@
+# 统一报告管理中心 — 实施计划
+
+> **Plan author:** Sisyphus (orchestrator)
+> **Date:** 2026-07-28
+> **Spec:** [2026-07-28-report-management-design.md](./specs/2026-07-28-report-management-design.md)
+
+**Goal:** 新建 `report_summary` 汇总表 + 同步机制 + 统一查询API + 小程序/Web管理端报告管理页面。
+
+**Architecture:** 汇总表 `report_summary` 通过 `source_table + source_id` 关联4类源表;Service 层 `ReportSummaryService` 负责即时同步 + 查询;`@Scheduled` 定时任务兜底;小程序 `pages/profile/report-management.vue` + Web `ReportManagement.vue` 双端覆盖。
+
+**Tech Stack:** Spring Boot 2.7.18 + MyBatis-Plus + Java 8 + uni-app Vue 2 + Element UI
+
+## Global Constraints
+
+- 接口统一 `@PostMapping`,禁止 `@GetMapping/@PutMapping/@DeleteMapping`
+- 响应统一 `Result<T>` (code/message/data)
+- 小程序禁用可选链 `?.`(用 `&&` 替代)、禁用 CSS Grid
+- Vue 2 Options API,禁止 Composition API
+- 定时任务使用 Spring `@Scheduled`
+- 汇总表通过 `UNIQUE KEY uk_source (source_table, source_id)` 去重
+- 源表不修改,汇总表只做 `INSERT ... ON DUPLICATE KEY UPDATE`
+
+---
+
+## Task 1: 数据库 — 建表 + 迁移
+
+**Files:**
+- Create: `cfc-backend/src/main/resources/schema.sql` (append)
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java`
+
+**Produces:** `report_summary` 表创建 + 幂等迁移
+
+- [ ] Step 1: 在 `cfc-backend/src/main/resources/schema.sql` 末尾追加 `CREATE TABLE IF NOT EXISTS report_summary` 语句(使用 spec §2.1 的 SQL)
+- [ ] Step 2: 在 `DatabaseInitializer.runMigrations()` 末尾添加建表迁移,使用 try-catch 包裹(表已存在则忽略):
+
+```java
+// 创建统一报告汇总表(report_summary)
+try {
+    jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS report_summary (" +
+            "id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', " +
+            "report_type VARCHAR(32) NOT NULL COMMENT '报告类型', " +
+            "source_table VARCHAR(32) NOT NULL COMMENT '来源表', " +
+            "source_id BIGINT NOT NULL COMMENT '来源表记录ID', " +
+            "user_id BIGINT NOT NULL COMMENT '创建者用户ID', " +
+            "family_id BIGINT COMMENT '家庭ID', " +
+            "subject_id BIGINT COMMENT '报告主体ID', " +
+            "subject_name VARCHAR(50) COMMENT '报告主体名称', " +
+            "title VARCHAR(200) COMMENT '报告标题', " +
+            "summary TEXT COMMENT '摘要', " +
+            "file_url VARCHAR(500) COMMENT '文件URL', " +
+            "file_size BIGINT COMMENT '文件大小', " +
+            "file_name VARCHAR(200) COMMENT '文件名', " +
+            "has_file TINYINT DEFAULT 0 COMMENT '是否有文件', " +
+            "overall_score INT COMMENT '综合评分', " +
+            "report_date DATE COMMENT '报告日期', " +
+            "status VARCHAR(20) COMMENT '状态', " +
+            "detail_route VARCHAR(100) COMMENT '详情页路由', " +
+            "created_at DATETIME DEFAULT CURRENT_TIMESTAMP, " +
+            "updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, " +
+            "INDEX idx_user_id (user_id), " +
+            "INDEX idx_family_id (family_id), " +
+            "INDEX idx_subject_id (subject_id), " +
+            "INDEX idx_report_type (report_type), " +
+            "INDEX idx_report_date (report_date), " +
+            "INDEX idx_status (status), " +
+            "UNIQUE KEY uk_source (source_table, source_id)" +
+            ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一报告汇总表'");
+    log.info("已创建 report_summary 表");
+} catch (Exception e) {
+    log.info("report_summary 表已存在,跳过创建");
+}
+```
+
+- [ ] Step 3: 在 `schema.sql` 末尾(`ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='统一报告汇总表';` 后)添加注释 `-- report_summary 表已由 DatabaseInitializer 迁移创建`
+- [ ] Step 4: `cd cfc-backend && mvn clean compile` 验证编译通过
+
+---
+
+## Task 2: 后端 — Entity + Mapper
+
+**Files:**
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/entity/ReportSummary.java`
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/mapper/ReportSummaryMapper.java`
+- Create: `cfc-backend/src/main/resources/mapper/ReportSummaryMapper.xml`
+
+**Produces:** MyBatis-Plus 实体 + Mapper + XML 映射
+
+- [ ] Step 1: 创建 `ReportSummary.java` — 使用 Lombok `@Data`、`@TableName("report_summary")`、`@TableId(type = IdType.AUTO)`。字段与 spec §2.1 一致:
+
+```java
+package com.etotem.cfc.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("report_summary")
+public class ReportSummary implements Serializable {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String reportType;
+    private String sourceTable;
+    private Long sourceId;
+    private Long userId;
+    private Long familyId;
+    private Long subjectId;
+    private String subjectName;
+    private String title;
+    private String summary;
+    private String fileUrl;
+    private Long fileSize;
+    private String fileName;
+    private Integer hasFile;
+    private Integer overallScore;
+    private Date reportDate;
+    private String status;
+    private String detailRoute;
+    private Date createdAt;
+    private Date updatedAt;
+}
+```
+
+- [ ] Step 2: 创建 `ReportSummaryMapper.java` — 继承 `BaseMapper<ReportSummary>`:
+
+```java
+package com.etotem.cfc.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.etotem.cfc.entity.ReportSummary;
+
+public interface ReportSummaryMapper extends BaseMapper<ReportSummary> {
+}
+```
+
+- [ ] Step 3: 创建 `ReportSummaryMapper.xml` — 参考 `ActivityFeedbackMapper.xml` 的 resultMap 模式(虽然这里字段名与数据库列名驼峰一致,XML 可为空结构):
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.etotem.cfc.mapper.ReportSummaryMapper">
+    <resultMap id="baseMap" type="com.etotem.cfc.entity.ReportSummary">
+        <id column="id" property="id"/>
+        <result column="report_type" property="reportType"/>
+        <result column="source_table" property="sourceTable"/>
+        <result column="source_id" property="sourceId"/>
+        <result column="user_id" property="userId"/>
+        <result column="family_id" property="familyId"/>
+        <result column="subject_id" property="subjectId"/>
+        <result column="subject_name" property="subjectName"/>
+        <result column="title" property="title"/>
+        <result column="summary" property="summary"/>
+        <result column="file_url" property="fileUrl"/>
+        <result column="file_size" property="fileSize"/>
+        <result column="file_name" property="fileName"/>
+        <result column="has_file" property="hasFile"/>
+        <result column="overall_score" property="overallScore"/>
+        <result column="report_date" property="reportDate"/>
+        <result column="status" property="status"/>
+        <result column="detail_route" property="detailRoute"/>
+        <result column="created_at" property="createdAt"/>
+        <result column="updated_at" property="updatedAt"/>
+    </resultMap>
+</mapper>
+```
+
+- [ ] Step 4: `cd cfc-backend && mvn clean compile` 验证编译通过
+
+---
+
+## Task 3: 后端 — DTO/VO
+
+**Files:**
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/dto/ReportSummaryVO.java`
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/dto/ReportListQueryDTO.java`
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/dto/ReportSyncResultDTO.java`
+
+**Produces:** 列表查询入参 DTO、列表 VO、同步结果 DTO
+
+- [ ] Step 1: 创建 `ReportListQueryDTO.java`:
+
+```java
+package com.etotem.cfc.dto;
+
+import lombok.Data;
+
+@Data
+public class ReportListQueryDTO {
+    private Integer page = 1;
+    private Integer size = 20;
+    private Long userId;
+    private String type;       // gut_flora|physical_exam|dan|insurance|fortune|all
+    private String keyword;
+    private Long subjectId;
+    private String startDate;
+    private String endDate;
+}
+```
+
+- [ ] Step 2: 创建 `ReportSummaryVO.java` — 展示用,添加 `reportTypeName`(中文类型名):
+
+```java
+package com.etotem.cfc.dto;
+
+import lombok.Data;
+import java.util.Date;
+
+@Data
+public class ReportSummaryVO {
+    private Long id;
+    private String reportType;
+    private String reportTypeName;
+    private String sourceTable;
+    private Long sourceId;
+    private String title;
+    private String subjectName;
+    private String summary;
+    private String fileUrl;
+    private Long fileSize;
+    private String fileName;
+    private Boolean hasFile;
+    private Integer overallScore;
+    private String reportDate;
+    private String status;
+    private String detailRoute;
+    private String detailParams;  // JSON 字符串,前端用
+    private Date createdAt;
+
+    public static String getReportTypeName(String reportType) {
+        if (reportType == null) return "未知";
+        switch (reportType) {
+            case "gut_flora": return "菌群检测";
+            case "physical_exam": return "体检报告";
+            case "dan": return "DAN测评";
+            case "insurance": return "保单";
+            case "fortune": return "家庭周报";
+            default: return reportType;
+        }
+    }
+}
+```
+
+- [ ] Step 3: 创建 `ReportSyncResultDTO.java`:
+
+```java
+package com.etotem.cfc.dto;
+
+import lombok.Data;
+
+@Data
+public class ReportSyncResultDTO {
+    private int newCount;
+    private int updateCount;
+    private int discardedCount;
+    private int errorCount;
+    private String errorMsg;
+}
+```
+
+- [ ] Step 4: `mvn clean compile` 验证
+
+---
+
+## Task 4: 后端 — ReportSummaryService(核心)
+
+**Files:**
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/service/ReportSummaryService.java`
+
+**Consumes:** Task 1 (表存在), Task 2 (Mapper), Task 3 (DTO)
+
+**Produces:** `sync*()` 各源表同步方法 + `pageList()` 分页查询 + `getDetail()` + `adminPageList()` + `fullSync()`
+
+- [ ] Step 1: 创建 `ReportSummaryService.java`。注入 Mapper:
+
+```java
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.etotem.cfc.dto.ReportListQueryDTO;
+import com.etotem.cfc.dto.ReportSummaryVO;
+import com.etotem.cfc.dto.ReportSyncResultDTO;
+import com.etotem.cfc.entity.ReportSummary;
+import com.etotem.cfc.entity.*;
+import com.etotem.cfc.mapper.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+@Service
+@Slf4j
+public class ReportSummaryService {
+
+    @Resource
+    private ReportSummaryMapper reportSummaryMapper;
+
+    @Resource
+    private HealthReportMapper healthReportMapper;
+
+    @Resource
+    private DanReportUploadMapper danReportUploadMapper;
+
+    @Resource
+    private InsurancePolicyMapper insurancePolicyMapper;
+
+    @Resource
+    private UserMapper userMapper;
+
+    @Resource
+    private FamilyMemberMapper familyMemberMapper;
+}
+```
+
+- [ ] Step 2: 实现 `syncFromHealthReport(HealthReport report)` — 按 spec §2.2 映射:
+
+```java
+public void syncFromHealthReport(HealthReport report) {
+    ReportSummary existing = reportSummaryMapper.selectOne(
+        new LambdaQueryWrapper<ReportSummary>()
+            .eq(ReportSummary::getSourceTable, "health_reports")
+            .eq(ReportSummary::getSourceId, report.getId())
+    );
+
+    ReportSummary summary = new ReportSummary();
+    summary.setReportType(report.getReportType());
+    summary.setSourceTable("health_reports");
+    summary.setSourceId(report.getId());
+    summary.setUserId(report.getUserId());
+    summary.setFamilyId(report.getFamilyId());
+    summary.setSubjectId(report.getSubjectId());
+    if (report.getSubjectId() != null) {
+        User subject = userMapper.selectById(report.getSubjectId());
+        summary.setSubjectName(subject != null ? subject.getNickname() : "");
+    }
+    String typeLabel = "gut_flora".equals(report.getReportType()) ? "菌群检测报告" : "体检报告";
+    String name = summary.getSubjectName() != null && !summary.getSubjectName().isEmpty() ? summary.getSubjectName() : "用户";
+    summary.setTitle(name + "的" + typeLabel);
+    summary.setFileUrl(report.getFileUrl());
+    summary.setHasFile(report.getFileUrl() != null && !report.getFileUrl().isEmpty() ? 1 : 0);
+    summary.setOverallScore(report.getOverallScore());
+    summary.setReportDate(report.getReportDate());
+    summary.setStatus(report.getStatus());
+    summary.setDetailRoute("gut_flora".equals(report.getReportType())
+        ? "/pages/health/gut-flora-detail"
+        : "/pages/health/physical-exam-detail");
+
+    if (existing != null) {
+        summary.setId(existing.getId());
+        reportSummaryMapper.updateById(summary);
+    } else {
+        reportSummaryMapper.insert(summary);
+    }
+}
+```
+
+- [ ] Step 3: 实现 `syncFromDanReport(DanReportUpload upload)`:
+
+```java
+public void syncFromDanReport(DanReportUpload upload) {
+    ReportSummary existing = reportSummaryMapper.selectOne(
+        new LambdaQueryWrapper<ReportSummary>()
+            .eq(ReportSummary::getSourceTable, "dan_report_uploads")
+            .eq(ReportSummary::getSourceId, upload.getId())
+    );
+
+    ReportSummary summary = new ReportSummary();
+    summary.setReportType("dan");
+    summary.setSourceTable("dan_report_uploads");
+    summary.setSourceId(upload.getId());
+    summary.setUserId(upload.getUploaderUserId());
+    summary.setFamilyId(upload.getFamilyId());
+    summary.setSubjectId(upload.getFamilyMemberId());
+
+    FamilyMember member = familyMemberMapper.selectById(upload.getFamilyMemberId());
+    summary.setSubjectName(member != null ? member.getMemberName() : "");
+
+    String dimLabel = "mind".equals(upload.getDimension()) ? "心智" : "智慧";
+    String name = summary.getSubjectName() != null && !summary.getSubjectName().isEmpty() ? summary.getSubjectName() : "成员";
+    summary.setTitle(name + "的" + dimLabel + "测评");
+
+    summary.setFileUrl(upload.getFileUrl());
+    summary.setHasFile(upload.getFileUrl() != null && !upload.getFileUrl().isEmpty() ? 1 : 0);
+
+    // 从 dan_assessment_results 关联查 overall_score
+    if (upload.getLinkedResultId() != null) {
+        DanAssessmentResult result = danAssessmentResultMapper.selectById(upload.getLinkedResultId());
+        if (result != null) summary.setOverallScore(result.getOverallScore());
+    }
+
+    summary.setReportDate(upload.getUploadedAt());
+    String status = upload.getDraftStatus();
+    if ("confirmed".equals(status)) status = "confirmed";
+    else if ("pending".equals(status)) status = "active";
+    else if ("discarded".equals(status)) status = "discarded";
+    else status = "active";
+    summary.setStatus(status);
+    summary.setDetailRoute("/pages/dan-assessment/report-upload");
+
+    if (existing != null) {
+        summary.setId(existing.getId());
+        reportSummaryMapper.updateById(summary);
+    } else {
+        reportSummaryMapper.insert(summary);
+    }
+}
+```
+
+- [ ] Step 4: 实现 `syncFromInsurancePolicy(InsurancePolicy policy)`:
+
+```java
+public void syncFromInsurancePolicy(InsurancePolicy policy) {
+    ReportSummary existing = reportSummaryMapper.selectOne(
+        new LambdaQueryWrapper<ReportSummary>()
+            .eq(ReportSummary::getSourceTable, "insurance_policies")
+            .eq(ReportSummary::getSourceId, policy.getId())
+    );
+
+    ReportSummary summary = new ReportSummary();
+    summary.setReportType("insurance");
+    summary.setSourceTable("insurance_policies");
+    summary.setSourceId(policy.getId());
+    summary.setUserId(policy.getUserId());
+    summary.setSubjectId(policy.getChildId());
+    summary.setSubjectName(policy.getInsuredPerson());
+    summary.setTitle(policy.getPolicyName());
+    String company = policy.getInsuranceCompany() != null ? policy.getInsuranceCompany() : "未知公司";
+    String type = policy.getPolicyType() != null ? policy.getPolicyType() : "保险";
+    Integer sum = policy.getSumInsured() != null ? policy.getSumInsured() : 0;
+    summary.setSummary(company + " · " + type + " · 保额 ¥" + sum / 100);
+    summary.setHasFile(0);
+    summary.setReportDate(policy.getStartDate());
+    summary.setStatus(policy.getStatus());
+    summary.setDetailRoute("/pages/wealth-sub/insurance-list");
+
+    if (existing != null) {
+        summary.setId(existing.getId());
+        reportSummaryMapper.updateById(summary);
+    } else {
+        reportSummaryMapper.insert(summary);
+    }
+}
+```
+
+- [ ] Step 5: 实现 `syncFromFamilyFortuneReport(FamilyFortuneReport report)`:
+
+```java
+public void syncFromFamilyFortuneReport(FamilyFortuneReport report) {
+    ReportSummary existing = reportSummaryMapper.selectOne(
+        new LambdaQueryWrapper<ReportSummary>()
+            .eq(ReportSummary::getSourceTable, "family_fortune_report")
+            .eq(ReportSummary::getSourceId, report.getId())
+    );
+
+    ReportSummary summary = new ReportSummary();
+    summary.setReportType("fortune");
+    summary.setSourceTable("family_fortune_report");
+    summary.setSourceId(report.getId());
+    summary.setFamilyId(Long.valueOf(report.getFamilyId()));
+
+    FamilyMember member = familyMemberMapper.selectOne(
+        new LambdaQueryWrapper<FamilyMember>()
+            .eq(FamilyMember::getFamilyId, report.getFamilyId())
+            .eq(FamilyMember::getRole, "parent")
+            .last("LIMIT 1")
+    );
+    summary.setUserId(member != null ? member.getUserId() : null);
+
+    String element = report.getElement() != null ? report.getElement() : "天盘";
+    summary.setTitle(element + "天盘周报");
+    summary.setSummary(report.getLuckyDirection() != null ? report.getLuckyDirection() : "");
+    summary.setFileUrl(report.getPdfPath());
+    summary.setFileSize(report.getFileSize());
+    summary.setHasFile(report.getPdfPath() != null && !report.getPdfPath().isEmpty() ? 1 : 0);
+    summary.setReportDate(report.getCreatedAt());
+    summary.setStatus("active");
+    summary.setDetailRoute("/pages/tianpan/index");
+
+    if (existing != null) {
+        summary.setId(existing.getId());
+        reportSummaryMapper.updateById(summary);
+    } else {
+        reportSummaryMapper.insert(summary);
+    }
+}
+```
+
+- [ ] Step 6: 实现 `markDiscarded(String sourceTable, Long sourceId)` — 源表删除时调用:
+
+```java
+public void markDiscarded(String sourceTable, Long sourceId) {
+    reportSummaryMapper.update(
+        null,
+        new LambdaUpdateWrapper<ReportSummary>()
+            .eq(ReportSummary::getSourceTable, sourceTable)
+            .eq(ReportSummary::getSourceId, sourceId)
+            .set(ReportSummary::getStatus, "discarded")
+    );
+}
+```
+
+- [ ] Step 7: 实现 `pageList(ReportListQueryDTO query)` — 小程序端分页查询,过滤当前用户 + 家庭:
+
+```java
+public Page<ReportSummaryVO> pageList(ReportListQueryDTO query) {
+    Page<ReportSummary> page = new Page<>(query.getPage(), query.getSize());
+
+    LambdaQueryWrapper<ReportSummary> wrapper = new LambdaQueryWrapper<>();
+    wrapper.eq(ReportSummary::getUserId, query.getUserId());
+    wrapper.ne(ReportSummary::getStatus, "discarded");
+
+    if ("all".equals(query.getType()) || query.getType() == null || query.getType().isEmpty()) {
+        // 不限类型
+    } else {
+        wrapper.eq(ReportSummary::getReportType, query.getType());
+    }
+
+    if (query.getKeyword() != null && !query.getKeyword().isEmpty()) {
+        wrapper.and(w -> w
+            .like(ReportSummary::getTitle, query.getKeyword())
+            .or().like(ReportSummary::getSubjectName, query.getKeyword())
+        );
+    }
+
+    if (query.getSubjectId() != null) {
+        wrapper.eq(ReportSummary::getSubjectId, query.getSubjectId());
+    }
+
+    wrapper.orderByDesc(ReportSummary::getReportDate);
+    reportSummaryMapper.selectPage(page, wrapper);
+
+    Page<ReportSummaryVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
+    List<ReportSummaryVO> voList = new ArrayList<>();
+    for (ReportSummary s : page.getRecords()) {
+        ReportSummaryVO vo = new ReportSummaryVO();
+        vo.setId(s.getId());
+        vo.setReportType(s.getReportType());
+        vo.setReportTypeName(ReportSummaryVO.getReportTypeName(s.getReportType()));
+        vo.setTitle(s.getTitle());
+        vo.setSubjectName(s.getSubjectName());
+        vo.setSummary(s.getSummary());
+        vo.setFileUrl(s.getFileUrl());
+        vo.setFileSize(s.getFileSize());
+        vo.setFileName(s.getFileName());
+        vo.setHasFile(s.getHasFile() != null && s.getHasFile() == 1);
+        vo.setOverallScore(s.getOverallScore());
+        vo.setReportDate(s.getReportDate() != null ? s.getReportDate().toString() : "");
+        vo.setStatus(s.getStatus());
+        vo.setDetailRoute(s.getDetailRoute());
+        vo.setCreatedAt(s.getCreatedAt());
+        voList.add(vo);
+    }
+    voPage.setRecords(voList);
+    return voPage;
+}
+```
+
+- [ ] Step 8: 实现 `getDetail(Long summaryId)` — 单条详情:
+
+```java
+public ReportSummaryVO getDetail(Long summaryId) {
+    ReportSummary s = reportSummaryMapper.selectById(summaryId);
+    if (s == null) return null;
+
+    ReportSummaryVO vo = new ReportSummaryVO();
+    vo.setId(s.getId());
+    vo.setReportType(s.getReportType());
+    vo.setReportTypeName(ReportSummaryVO.getReportTypeName(s.getReportType()));
+    vo.setTitle(s.getTitle());
+    vo.setSubjectName(s.getSubjectName());
+    vo.setSummary(s.getSummary());
+    vo.setFileUrl(s.getFileUrl());
+    vo.setFileSize(s.getFileSize());
+    vo.setFileName(s.getFileName());
+    vo.setHasFile(s.getHasFile() != null && s.getHasFile() == 1);
+    vo.setOverallScore(s.getOverallScore());
+    vo.setReportDate(s.getReportDate() != null ? s.getReportDate().toString() : "");
+    vo.setStatus(s.getStatus());
+    vo.setDetailRoute(s.getDetailRoute());
+    vo.setCreatedAt(s.getCreatedAt());
+    return vo;
+}
+```
+
+- [ ] Step 9: 实现 `adminPageList(ReportListQueryDTO query)` — 管理端,不限 userId:
+
+```java
+public Page<ReportSummaryVO> adminPageList(ReportListQueryDTO query) {
+    Page<ReportSummary> page = new Page<>(query.getPage(), query.getSize());
+    LambdaQueryWrapper<ReportSummary> wrapper = new LambdaQueryWrapper<>();
+    wrapper.ne(ReportSummary::getStatus, "discarded");
+
+    if ("all".equals(query.getType()) || query.getType() == null || query.getType().isEmpty()) {
+        // 不限
+    } else {
+        wrapper.eq(ReportSummary::getReportType, query.getType());
+    }
+
+    if (query.getKeyword() != null && !query.getKeyword().isEmpty()) {
+        wrapper.and(w -> w
+            .like(ReportSummary::getTitle, query.getKeyword())
+            .or().like(ReportSummary::getSubjectName, query.getKeyword())
+            .or().like(ReportSummary::getSummary, query.getKeyword())
+        );
+    }
+
+    if (query.getStartDate() != null && !query.getStartDate().isEmpty()) {
+        wrapper.ge(ReportSummary::getReportDate, query.getStartDate());
+    }
+    if (query.getEndDate() != null && !query.getEndDate().isEmpty()) {
+        wrapper.le(ReportSummary::getReportDate, query.getEndDate());
+    }
+
+    wrapper.orderByDesc(ReportSummary::getReportDate);
+    reportSummaryMapper.selectPage(page, wrapper);
+
+    Page<ReportSummaryVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
+    List<ReportSummaryVO> voList = new ArrayList<>();
+    for (ReportSummary rs : page.getRecords()) {
+        ReportSummaryVO vo = new ReportSummaryVO();
+        vo.setId(rs.getId());
+        vo.setReportType(rs.getReportType());
+        vo.setReportTypeName(ReportSummaryVO.getReportTypeName(rs.getReportType()));
+        vo.setTitle(rs.getTitle());
+        vo.setSubjectName(rs.getSubjectName());
+        vo.setSummary(rs.getSummary());
+        vo.setHasFile(rs.getHasFile() != null && rs.getHasFile() == 1);
+        vo.setOverallScore(rs.getOverallScore());
+        vo.setReportDate(rs.getReportDate() != null ? rs.getReportDate().toString() : "");
+        vo.setStatus(rs.getStatus());
+        vo.setCreatedAt(rs.getCreatedAt());
+        voList.add(vo);
+    }
+    voPage.setRecords(voList);
+    return voPage;
+}
+```
+
+- [ ] Step 10: 实现 `fullSync()` — 全量校准,供定时任务和手动触发调用:
+
+```java
+public ReportSyncResultDTO fullSync() {
+    ReportSyncResultDTO result = new ReportSyncResultDTO();
+    result.setNewCount(0);
+    result.setUpdateCount(0);
+    result.setDiscardedCount(0);
+    result.setErrorCount(0);
+
+    try {
+        // 1. 同步 health_reports
+        List<HealthReport> healthReports = healthReportMapper.selectList(new LambdaQueryWrapper<HealthReport>());
+        List<ReportSummary> existingHealth = reportSummaryMapper.selectList(
+            new LambdaQueryWrapper<ReportSummary>().eq(ReportSummary::getSourceTable, "health_reports"));
+        for (HealthReport hr : healthReports) {
+            try {
+                ReportSummary existing = reportSummaryMapper.selectOne(
+                    new LambdaQueryWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, "health_reports")
+                        .eq(ReportSummary::getSourceId, hr.getId()));
+                if (existing == null) {
+                    syncFromHealthReport(hr);
+                    result.setNewCount(result.getNewCount() + 1);
+                } else {
+                    syncFromHealthReport(hr);
+                    result.setUpdateCount(result.getUpdateCount() + 1);
+                }
+            } catch (Exception e) {
+                result.setErrorCount(result.getErrorCount() + 1);
+                log.error("同步 health_reports 记录失败 id={}", hr.getId(), e);
+            }
+        }
+        // 标记已删除的
+        for (ReportSummary es : existingHealth) {
+            if (healthReportMapper.selectById(es.getSourceId()) == null) {
+                markDiscarded("health_reports", es.getSourceId());
+                result.setDiscardedCount(result.getDiscardedCount() + 1);
+            }
+        }
+    } catch (Exception e) {
+        log.error("全量同步 health_reports 失败", e);
+        result.setErrorCount(result.getErrorCount() + 1);
+    }
+
+    // 2. 同步 dan_report_uploads
+    try {
+        List<DanReportUpload> danUploads = danReportUploadMapper.selectList(new LambdaQueryWrapper<DanReportUpload>());
+        List<ReportSummary> existingDan = reportSummaryMapper.selectList(
+            new LambdaQueryWrapper<ReportSummary>().eq(ReportSummary::getSourceTable, "dan_report_uploads"));
+        for (DanReportUpload du : danUploads) {
+            try {
+                ReportSummary existing = reportSummaryMapper.selectOne(
+                    new LambdaQueryWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, "dan_report_uploads")
+                        .eq(ReportSummary::getSourceId, du.getId()));
+                if (existing == null) {
+                    syncFromDanReport(du);
+                    result.setNewCount(result.getNewCount() + 1);
+                } else {
+                    syncFromDanReport(du);
+                    result.setUpdateCount(result.getUpdateCount() + 1);
+                }
+            } catch (Exception e) {
+                result.setErrorCount(result.getErrorCount() + 1);
+                log.error("同步 dan_report_uploads 失败 id={}", du.getId(), e);
+            }
+        }
+        for (ReportSummary es : existingDan) {
+            if (danReportUploadMapper.selectById(es.getSourceId()) == null) {
+                markDiscarded("dan_report_uploads", es.getSourceId());
+                result.setDiscardedCount(result.getDiscardedCount() + 1);
+            }
+        }
+    } catch (Exception e) {
+        log.error("全量同步 dan_report_uploads 失败", e);
+        resultsetErrorCount(result.getErrorCount() + 1);
+    }
+
+    // 3. 同步 insurance_policies
+    try {
+        List<InsurancePolicy> policies = insurancePolicyMapper.selectList(new LambdaQueryWrapper<InsurancePolicy>());
+        List<ReportSummary> existingIns = reportSummaryMapper.selectList(
+            new LambdaQueryWrapper<ReportSummary>().eq(ReportSummary::getSourceTable, "insurance_policies"));
+        for (InsurancePolicy p : policies) {
+            try {
+                ReportSummary existing = reportSummaryMapper.selectOne(
+                    new LambdaQueryWrapper<ReportSummary>()
+                        .eq(ReportSummary::getSourceTable, "insurance_policies")
+                        .eq(ReportSummary::getSourceId, p.getId()));
+                if (existing == null) {
+                    syncFromInsurancePolicy(p);
+                    result.setNewCount(result.getNewCount() + 1);
+                } else {
+                    syncFromInsurancePolicy(p);
+                    result.setUpdateCount(result.getUpdateCount() + 1);
+                }
+            } catch (Exception e) {
+                result.setErrorCount(result.getErrorCount() + 1);
+                log.error("同步 insurance_policies 失败 id={}", p.getId(), e);
+            }
+        }
+        for (ReportSummary es : existingIns) {
+            if (insurancePolicyMapper.selectById(es.getSourceId()) == null) {
+                markDiscarded("insurance_policies", es.getSourceId());
+                result.setDiscardedCount(result.getDiscardedCount() + 1);
+            }
+        }
+    } catch (Exception e) {
+        log.error("全量同步 insurance_policies 失败", e);
+        result.setErrorCount(result.getErrorCount() + 1);
+    }
+
+    // 4. 同步 family_fortune_report(无标准 Mapper,用 JdbcTemplate 查询)
+    try {
+        // 注意:FamilyFortuneReport 无 Mapper,使用 jdbcTemplate 查询
+        String sql = "SELECT id, family_id, element, lucky_direction, pdf_path, file_size, created_at FROM family_fortune_report";
+        // 实际实现需用 JdbcTemplate 查询并调用 syncFromFamilyFortuneReport
+        // 此步骤在 task 实现时补充 JdbcTemplate 注入和完整实现
+    } catch (Exception e) {
+        log.error("全量同步 family_fortune_report 失败", e);
+        result.setErrorCount(result.getErrorCount() + 1);
+    }
+
+    log.info("全量同步完成: new={}, update={}, discarded={}, error={}",
+        result.getNewCount(), result.getUpdateCount(), result.getDiscardedCount(), result.getErrorCount());
+    return result;
+}
+```
+
+> ⚠️ `fullSync()` 中 `family_fortune_report` 的同步需额外注入 `JdbcTemplate`(因该表无 MyBatis Mapper)。实现时添加 `@Resource private JdbcTemplate jdbcTemplate;` 并用 `jdbcTemplate.query()` 查询。
+
+- [ ] Step 11: `mvn clean compile` 验证编译通过
+
+---
+
+## Task 5: 后端 — 注入 sync 调用到各源 Service
+
+**Files:**
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/service/HealthReportService.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/service/DanReportUploadService.java`
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/service/InsurancePolicyService.java`
+- Find: FamilyFortuneReport 相关 Service(搜索 `family_fortune_report` 写入逻辑)
+
+**Consumes:** Task 4 (ReportSummaryService)
+
+**Produces:** 源表 CRUD 末尾自动同步到汇总表
+
+- [ ] Step 1: 在 `HealthReportService` 中注入 `ReportSummaryService`:
+
+```java
+@Resource
+private ReportSummaryService reportSummaryService;
+```
+
+在 `createReport()` 方法末尾(`healthReportMapper.insert(report)` 之后)添加:
+
+```java
+try {
+    reportSummaryService.syncFromHealthReport(report);
+} catch (Exception e) {
+    log.warn("同步报告摘要失败 reportId={}", report.getId(), e);
+}
+```
+
+> ⚠️ 需确认 `HealthReportService` 的 `createReport` 方法是否在保存后能拿到 report 的 id(MyBatis-Plus insert 后 id 会自动回填)。
+
+- [ ] Step 2: 在 `DanReportUploadService.confirmUpload()` 方法末尾(确认保存后)添加:
+
+```java
+try {
+    reportSummaryService.syncFromDanReport(upload);
+} catch (Exception e) {
+    log.warn("同步DAN报告摘要失败 uploadId={}", uploadId, e);
+}
+```
+
+在 `DanReportUploadService` 也需注入 `ReportSummaryService`。
+
+- [ ] Step 3: 在 `InsurancePolicyService.createPolicy()` 末尾添加:
+
+```java
+try {
+    reportSummaryService.syncFromInsurancePolicy(policy);
+} catch (Exception e) {
+    log.warn("同步保单摘要失败 policyId={}", policy.getId(), e);
+}
+```
+
+在 `InsurancePolicyService.updatePolicy()` 末尾也添加同样的 sync 调用。
+
+在 `InsurancePolicyService.deletePolicy()` 中,将物理删除改为标记废弃(可选,保持物理删除但同步标记):
+
+```java
+try {
+    reportSummaryService.markDiscarded("insurance_policies", id);
+} catch (Exception e) {
+    log.warn("标记保单废弃失败 policyId={}", id, e);
+}
+```
+
+注入 `@Resource private ReportSummaryService reportSummaryService;`
+
+- [ ] Step 4: 查找 `family_fortune_report` 表的写入逻辑(搜索 `family_fortune_report` 或 `FamilyFortuneReport` 的 INSERT/UPDATE),在写入后调用 `reportSummaryService.syncFromFamilyFortuneReport(report)`。
+
+- [ ] Step 5: `mvn clean compile` 验证
+
+---
+
+## Task 6: 后端 — ReportSummaryController(小程序端 API)
+
+**Files:**
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/controller/ReportSummaryController.java`
+
+**Consumes:** Task 4 (ReportSummaryService)
+
+**Produces:** `POST /api/report-summary/list`, `POST /api/report-summary/detail/{id}`, `POST /api/report-summary/download/{id}`
+
+- [ ] Step 1: 创建 `ReportSummaryController.java`:
+
+```java
+package com.etotem.cfc.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.dto.ReportListQueryDTO;
+import com.etotem.cfc.dto.ReportSummaryVO;
+import com.etotem.cfc.service.ReportSummaryService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/report-summary")
+public class ReportSummaryController {
+
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
+    @PostMapping("/list")
+    public Result<Object> list(@RequestBody ReportListQueryDTO query, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        if (userId == null) return Result.error("用户未登录");
+        query.setUserId(userId);
+
+        Page<ReportSummaryVO> page = reportSummaryService.pageList(query);
+        return Result.success(page);
+    }
+
+    @PostMapping("/detail/{id}")
+    public Result<ReportSummaryVO> detail(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        if (userId == null) return Result.error("用户未登录");
+
+        ReportSummaryVO vo = reportSummaryService.getDetail(id);
+        if (vo == null) return Result.error("报告不存在");
+        return Result.success(vo);
+    }
+
+    @PostMapping("/download/{id}")
+    public Result<Object> download(@PathVariable Long id, HttpServletRequest request) {
+        Long userId = (Long) request.getAttribute("userId");
+        if (userId == null) return Result.error("用户未登录");
+
+        ReportSummaryVO vo = reportSummaryService.getDetail(id);
+        if (vo == null) return Result.error("报告不存在");
+        if (!vo.getHasFile()) return Result.error("该报告无文件可下载");
+
+        Object data = new Object() {
+            public String getFileUrl() { return vo.getFileUrl(); }
+            public String getFileName() { return vo.getFileName(); }
+            public Long getFileSize() { return vo.getFileSize(); }
+        };
+        return Result.success(data);
+    }
+}
+```
+
+- [ ] Step 2: `mvn clean compile` 验证
+
+---
+
+## Task 7: 后端 — Admin Controller + Sync Task
+
+**Files:**
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/controller/admin/ReportSummaryAdminController.java`
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/task/ReportSummarySyncTask.java`
+
+**Consumes:** Task 4 (ReportSummaryService)
+
+**Produces:** 管理端 API + 定时任务
+
+- [ ] Step 1: 创建 `ReportSummaryAdminController.java`:
+
+```java
+package com.etotem.cfc.controller.admin;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.etotem.cfc.common.Result;
+import com.etotem.cfc.dto.ReportListQueryDTO;
+import com.etotem.cfc.dto.ReportSummaryVO;
+import com.etotem.cfc.dto.ReportSyncResultDTO;
+import com.etotem.cfc.service.ReportSummaryService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@RestController
+@RequestMapping("/api/admin/report-summary")
+public class ReportSummaryAdminController {
+
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
+    @PostMapping("/list")
+    public Result<Object> adminList(@RequestBody ReportListQueryDTO query) {
+        Page<ReportSummaryVO> page = reportSummaryService.adminPageList(query);
+        return Result.success(page);
+    }
+
+    @PostMapping("/sync")
+    public Result<ReportSyncResultDTO> sync() {
+        ReportSyncResultDTO result = reportSummaryService.fullSync();
+        String msg = "同步完成: 新增" + result.getNewCount() +
+                     "条, 更新" + result.getUpdateCount() +
+                     "条, 废弃" + result.getDiscardedCount() +
+                     "条, 错误" + result.getErrorCount() + "条";
+        return Result.success(msg, result);
+    }
+}
+```
+
+- [ ] Step 2: 创建 `ReportSummarySyncTask.java` — 参考 `OrderTimeoutCancelTask.java` 的 `@Scheduled` 模式:
+
+```java
+package com.etotem.cfc.task;
+
+import com.etotem.cfc.dto.ReportSyncResultDTO;
+import com.etotem.cfc.service.ReportSummaryService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+@Slf4j
+@Component
+public class ReportSummarySyncTask {
+
+    @Resource
+    private ReportSummaryService reportSummaryService;
+
+    /**
+     * 每日凌晨 2:00 全量校准汇总表
+     */
+    @Scheduled(cron = "0 0 2 * * ?")
+    public void scheduledSync() {
+        log.info("开始定时同步报告汇总表...");
+        try {
+            ReportSyncResultDTO result = reportSummaryService.fullSync();
+            log.info("定时同步完成: new={}, update={}, discarded={}, error={}",
+                result.getNewCount(), result.getUpdateCount(),
+                result.getDiscardedCount(), result.getErrorCount());
+        } catch (Exception e) {
+            log.error("定时同步失败", e);
+        }
+    }
+}
+```
+
+- [ ] Step 3: `mvn clean compile` 验证
+
+---
+
+## Task 8: 小程序前端 — ProfileMenu 入口
+
+**Files:**
+- Modify: `cfc-frontend/pages/profile/components/ProfileMenu.vue`
+
+**Produces:** 在「个人」分组添加「📋 报告管理」菜单项
+
+- [ ] Step 1: 在 `ProfileMenu.vue` 的「个人」分组(`menu-group` 内)添加菜单项。参考现有 `goToDailyTasks` 模式:
+
+在 `pages/profile/components/ProfileMenu.vue` 的「个人」分组的 `</view>` 之前(第 22 行 `</view>` 之后、第 23 行 `</view>` 之前),添加:
+
+```vue
+<view class="menu-item" @click="goToReportManagement">
+  <text>📋 报告管理</text>
+  <text class="arrow">›</text>
+</view>
+```
+
+> 位置:在 `goToOnboarding` 的 `</view>` 之后,`</view>`(个人分组结束)之前。即第 21 行 `</view>` 之后。
+
+- [ ] Step 2: 在 `ProfileMenu.vue` 的 methods 中添加导航方法:
+
+```js
+goToReportManagement() {
+  uni.navigateTo({ url: '/pages/profile/report-management' })
+}
+```
+
+- [ ] Step 3: 确认修改正确,无语法错误
+
+---
+
+## Task 9: 小程序前端 — report-management.vue 主页面
+
+**Files:**
+- Create: `cfc-frontend/pages/profile/report-management.vue`
+- Modify: `cfc-frontend/utils/api.js` (添加 API 函数)
+- Modify: `cfc-frontend/pages.json` (注册新页面)
+
+**Produces:** 报告管理页面(类型Tab + 搜索 + 卡片列表 + 分页)
+
+- [ ] Step 1: 在 `utils/api.js` 末尾添加 API 函数:
+
+```js
+export const getReportSummaryList = (params) => request('/api/report-summary/list', 'POST', params)
+export const getReportSummaryDetail = (id) => request('/api/report-summary/detail/' + id, 'POST', {})
+export const downloadReportSummary = (id) => request('/api/report-summary/download/' + id, 'POST', {})
+```
+
+- [ ] Step 2: 在 `pages.json` 中注册新页面。找到 `pages/profile` 所在的 subPackage(搜索 `"root": "pages/profile"`),在其 `pages` 数组中添加:
+
+```json
+{
+  "path": "report-management",
+  "style": { "navigationBarTitleText": "报告管理" }
+},
+{
+  "path": "report-detail",
+  "style": { "navigationBarTitleText": "报告详情" }
+}
+```
+
+- [ ] Step 3: 创建 `pages/profile/report-management.vue`:
+
+```vue
+<template>
+  <view class="container">
+    <!-- 类型 Tab -->
+    <view class="tab-bar">
+      <view
+        v-for="(tab, idx) in tabs"
+        :key="idx"
+        class="tab-item"
+        :class="{ 'tab-active': activeTab === idx }"
+        @click="activeTab = idx; loadData()"
+      >
+        <text>{{ tab.label }}</text>
+      </view>
+    </view>
+
+    <!-- 搜索栏 -->
+    <view class="search-bar">
+      <input
+        class="search-input"
+        v-model="keyword"
+        placeholder="搜索报告标题或成员名称..."
+        @confirm="loadData()"
+      />
+    </view>
+
+    <!-- 报告列表 -->
+    <scroll-view scroll-y class="report-list" @scrolltolower="loadMore">
+      <view
+        v-for="item in reports"
+        :key="item.id"
+        class="report-card"
+        :class="'card-' + getCardClass(item.reportType)"
+        @click="goToDetail(item)"
+      >
+        <view class="card-left">
+          <view class="type-badge" :class="'badge-' + getCardClass(item.reportType)">
+            <text>{{ item.reportTypeName }}</text>
+          </view>
+          <text class="card-title">{{ item.title }}</text>
+          <view class="card-meta">
+            <text class="subject-name">{{ item.subjectName }}</text>
+            <text class="date-text">{{ formatDate(item.reportDate) }}</text>
+          </view>
+        </view>
+        <view class="card-right">
+          <text v-if="item.overallScore !== null && item.overallScore !== undefined" class="score">{{ item.overallScore }}<text class="score-unit">分</text></text>
+          <text v-else class="score-text">{{ item.hasFile ? '有文件' : '结构化数据' }}</text>
+          <text class="arrow">›</text>
+        </view>
+      </view>
+
+      <view v-if="reports.length === 0 && !loading" class="empty">
+        <text class="empty-icon">📋</text>
+        <text class="empty-title">暂无报告</text>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<script>
+import { getReportSummaryList } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      tabs: [
+        { label: '全部', type: 'all' },
+        { label: '菌群', type: 'gut_flora' },
+        { label: '体检', type: 'physical_exam' },
+        { label: 'DAN', type: 'dan' },
+        { label: '保单', type: 'insurance' },
+        { label: '周报', type: 'fortune' }
+      ],
+      activeTab: 0,
+      keyword: '',
+      reports: [],
+      page: 1,
+      loading: false,
+      hasMore: true
+    }
+  },
+  onLoad() {
+    this.loadData()
+  },
+  onPullDownRefresh() {
+    this.page = 1
+    this.reports = []
+    this.hasMore = true
+    this.loadData()
+  },
+  methods: {
+    loadData() {
+      var self = this
+      if (self.loading) return
+      if (!self.hasMore) return
+      self.loading = true
+      var params = {
+        page: self.page,
+        size: 20,
+        type: self.tabs[self.activeTab].type,
+        keyword: self.keyword
+      }
+      getReportSummaryList(params).then(function(res) {
+        var list = res && res.data && res.data.records ? res.data.records : []
+        if (self.page === 1) {
+          self.reports = list
+        } else {
+          self.reports = self.reports.concat(list)
+        }
+        self.hasMore = list.length >= 20
+        self.page++
+      }).catch(function(e) {
+        console.log('获取报告列表失败', e)
+        uni.showToast({ title: '加载失败', icon: 'none' })
+      }).finally(function() {
+        self.loading = false
+        uni.stopPullDownRefresh()
+      })
+    },
+    loadMore() {
+      if (!this.loading && this.hasMore) {
+        this.loadData()
+      }
+    },
+    goToDetail(item) {
+      uni.navigateTo({
+        url: '/pages/profile/report-detail?id=' + item.id + '&title=' + encodeURIComponent(item.title)
+      })
+    },
+    getCardClass(reportType) {
+      return reportType || 'default'
+    },
+    formatDate(dateStr) {
+      if (!dateStr) return '--'
+      try {
+        var d = new Date(dateStr)
+        var y = d.getFullYear()
+        var m = ('' + (d.getMonth() + 1)).padStart(2, '0')
+        var day = ('' + d.getDate()).padStart(2, '0')
+        return y + '-' + m + '-' + day
+      } catch (e) {
+        return dateStr
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container {
+  min-height: 100vh;
+  background: #f5f7fa;
+  display: flex;
+  flex-direction: column;
+}
+
+.tab-bar {
+  display: flex;
+  flex-direction: row;
+  background: #fff;
+  padding: 20rpx 0;
+  border-bottom: 1rpx solid #eee;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.tab-item {
+  padding: 12rpx 24rpx;
+  font-size: 26rpx;
+  color: #666;
+  margin: 0 8rpx;
+  border-radius: 20rpx;
+}
+
+.tab-active {
+  background: #5B9BD5;
+  color: #fff;
+}
+
+.search-bar {
+  padding: 16rpx 24rpx;
+  background: #fff;
+}
+
+.search-input {
+  background: #f5f5f5;
+  border-radius: 12rpx;
+  padding: 16rpx 24rpx;
+  font-size: 26rpx;
+}
+
+.report-list {
+  flex: 1;
+  padding: 16rpx 24rpx;
+}
+
+.report-card {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  margin-bottom: 16rpx;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+}
+
+.card-left {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  min-width: 0;
+}
+
+.type-badge {
+  display: inline-block;
+  font-size: 20rpx;
+  padding: 4rpx 16rpx;
+  border-radius: 12rpx;
+  align-self: flex-start;
+  margin-bottom: 8rpx;
+}
+
+.badge-gut_flora { background: #E8F5E9; color: #2E7D32; }
+.badge-physical_exam { background: #E3F2FD; color: #1565C0; }
+.badge-dan { background: #F3E5F5; color: #7B1FA2; }
+.badge-insurance { background: #FFF3E0; color: #E65100; }
+.badge-fortune { background: #FCE4EC; color: #C62828; }
+
+.card-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 8rpx;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.card-meta {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+
+.subject-name {
+  font-size: 24rpx;
+  color: #888;
+  margin-right: 16rpx;
+}
+
+.date-text {
+  font-size: 24rpx;
+  color: #aaa;
+}
+
+.card-right {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap: 8rpx;
+  margin-left: 16rpx;
+}
+
+.score {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+}
+
+.score-unit {
+  font-size: 20rpx;
+  color: #999;
+  font-weight: normal;
+  margin-left: 4rpx;
+}
+
+.score-text {
+  font-size: 24rpx;
+  color: #888;
+}
+
+.arrow {
+  font-size: 32rpx;
+  color: #ccc;
+}
+
+.empty {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 100rpx 0;
+}
+
+.empty-icon {
+  font-size: 80rpx;
+  margin-bottom: 20rpx;
+}
+
+.empty-title {
+  font-size: 28rpx;
+  color: #999;
+}
+</style>
+```
+
+- [ ] Step 4: 编译验证(`npm run build:mp-weixin` 在 `cfc-frontend/` 目录)
+
+---
+
+## Task 10: 小程序前端 — report-detail.vue 统一详情视图
+
+**Files:**
+- Create: `cfc-frontend/pages/profile/report-detail.vue`
+
+**Consumes:** Task 9 (API 函数)
+
+**Produces:** 统一报告详情页(有文件显示预览/下载,无文件显示结构化数据,支持分享)
+
+- [ ] Step 1: 在 `utils/api.js` 中确保 `getReportSummaryDetail` 和 `downloadReportSummary` 已定义(Task 9 Step 1 已添加)
+
+- [ ] Step 2: 创建 `pages/profile/report-detail.vue`:
+
+```vue
+<template>
+  <view class="container">
+    <!-- 报告头部 -->
+    <view class="report-header">
+      <view class="type-badge" :class="'badge-' + getBadgeClass(detail.reportType)">
+        <text>{{ detail.reportTypeName || '报告' }}</text>
+      </view>
+      <text class="report-title">{{ detail.title }}</text>
+      <view class="report-meta">
+        <text class="meta-item">{{ detail.subjectName }}</text>
+        <text class="meta-item">{{ formatDate(detail.reportDate) }}</text>
+      </view>
+      <view class="score-section" v-if="detail.overallScore !== null && detail.overallScore !== undefined">
+        <text class="score">{{ detail.overallScore }}</text>
+        <text class="score-label">综合评分</text>
+      </view>
+    </view>
+
+    <!-- 摘要 -->
+    <view class="summary-section" v-if="detail.summary">
+      <text class="section-title">报告摘要</text>
+      <text class="summary-text">{{ detail.summary }}</text>
+    </view>
+
+    <!-- 文件操作区 -->
+    <view class="action-section" v-if="detail.hasFile">
+      <button class="action-btn" @click="downloadReport">📥 下载报告</button>
+      <button class="action-btn secondary" @click="goToOriginalDetail">📄 查看原始详情</button>
+    </view>
+
+    <!-- 无文件 -->
+    <view class="action-section" v-else>
+      <button class="action-btn secondary" @click="goToOriginalDetail">📄 查看原始详情</button>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getReportSummaryDetail, downloadReportSummary } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      reportId: null,
+      detail: {}
+    }
+  },
+  onLoad(options) {
+    if (options && options.id) {
+      this.reportId = parseInt(options.id)
+      this.loadDetail()
+    }
+  },
+  onShareAppMessage() {
+    return {
+      title: '浠艾福报告',
+      path: '/pages/profile/report-detail?id=' + this.reportId
+    }
+  },
+  methods: {
+    loadDetail() {
+      var self = this
+      getReportSummaryDetail(self.reportId).then(function(res) {
+        if (res && res.data) {
+          self.detail = res.data
+          uni.setNavigationBarTitle({ title: self.detail.title || '报告详情' })
+        }
+      }).catch(function(e) {
+        console.log('加载详情失败', e)
+        uni.showToast({ title: '加载失败', icon: 'none' })
+      })
+    },
+    downloadReport() {
+      var self = this
+      uni.showLoading({ title: '下载中...' })
+      downloadReportSummary(self.reportId).then(function(res) {
+        if (res && res.data && res.data.fileUrl) {
+          var url = res.data.fileUrl
+          var name = res.data.fileName || 'report'
+          uni.downloadFile({
+            url: url,
+            filePath: wx.env.USER_DATA_PATH + '/' + name,
+            success: function(res2) {
+              uni.saveFileToDisk({
+                filePath: res2.filePath,
+                success: function() {
+                  uni.showToast({ title: '保存成功', icon: 'success' })
+                },
+                fail: function() {
+                  uni.showToast({ title: '保存失败', icon: 'none' })
+                }
+              })
+            },
+            fail: function() {
+              uni.showToast({ title: '下载失败', icon: 'none' })
+            },
+            complete: function() {
+              uni.hideLoading()
+            }
+          })
+        }
+      }).catch(function(e) {
+        uni.hideLoading()
+        uni.showToast({ title: '下载失败', icon: 'none' })
+      })
+    },
+    goToOriginalDetail() {
+      var route = this.detail.detailRoute || ''
+      if (route) {
+        var url = route + (this.detail.sourceId ? '?reportId=' + this.detail.sourceId : '')
+        uni.navigateTo({ url: url })
+      } else {
+        uni.showToast({ title: '暂无详情页面', icon: 'none' })
+      }
+    },
+    getBadgeClass(reportType) {
+      return reportType || 'default'
+    },
+    formatDate(dateStr) {
+      if (!dateStr) return '--'
+      return dateStr
+    }
+  }
+}
+</script>
+
+<style scoped>
+.container {
+  min-height: 100vh;
+  background: #f5f7fa;
+  padding: 24rpx;
+}
+
+.report-header {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 32rpx 24rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+  margin-bottom: 16rpx;
+}
+
+.type-badge {
+  display: inline-block;
+  font-size: 22rpx;
+  padding: 6rpx 20rpx;
+  border-radius: 12rpx;
+  margin-bottom: 16rpx;
+}
+
+.badge-gut_flora { background: #E8F5E9; color: #2E7D32; }
+.badge-physical_exam { background: #E3F2FD; color: #1565C0; }
+.badge-dan { background: #F3E5F5; color: #7B1FA2; }
+.badge-insurance { background: #FFF3E0; color: #E65100; }
+.badge-fortune { background: #FCE4EC; color: #C62828; }
+
+.report-title {
+  font-size: 32rpx;
+  font-weight: bold;
+  color: #333;
+  display: block;
+  margin-bottom: 12rpx;
+}
+
+.report-meta {
+  display: flex;
+  flex-direction: row;
+  margin-bottom: 16rpx;
+}
+
+.meta-item {
+  font-size: 24rpx;
+  color: #888;
+  margin-right: 24rpx;
+}
+
+.score-section {
+  display: flex;
+  flex-direction: row;
+  align-items: baseline;
+}
+
+.score {
+  font-size: 56rpx;
+  font-weight: bold;
+  color: #5B9BD5;
+}
+
+.score-label {
+  font-size: 24rpx;
+  color: #888;
+  margin-left: 12rpx;
+}
+
+.summary-section {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+  margin-bottom: 16rpx;
+}
+
+.section-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  display: block;
+  margin-bottom: 12rpx;
+}
+
+.summary-text {
+  font-size: 26rpx;
+  color: #666;
+  line-height: 1.6;
+}
+
+.action-section {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 24rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+}
+
+.action-btn {
+  background: #5B9BD5;
+  color: #fff;
+  font-size: 28rpx;
+  border-radius: 12rpx;
+  margin-bottom: 16rpx;
+}
+
+.action-btn.secondary {
+  background: #f0f0f0;
+  color: #333;
+  margin-bottom: 0;
+}
+</style>
+```
+
+- [ ] Step 3: `npm run build:mp-weixin` 编译验证
+
+---
+
+## Task 11: Web管理端 — ReportManagement.vue
+
+**Files:**
+- Create: `cfc-web/src/views/admin/ReportManagement.vue`
+- Modify: `cfc-web/src/views/Layout.vue` (菜单入口)
+- Modify: `cfc-web/src/router/index.js` (路由)
+
+**Produces:** 管理端报告管理页面(表格 + 筛选 + 分页)
+
+- [ ] Step 1: 创建 `cfc-web/src/views/admin/ReportManagement.vue`:
+
+```vue
+<template>
+  <div class="report-management">
+    <el-card>
+      <div class="filter-bar">
+        <el-select v-model="query.type" placeholder="报告类型" clearable style="width:140px">
+          <el-option label="全部" value=""></el-option>
+          <el-option label="菌群检测" value="gut_flora"></el-option>
+          <el-option label="体检报告" value="physical_exam"></el-option>
+          <el-option label="DAN测评" value="dan"></el-option>
+          <el-option label="保单" value="insurance"></el-option>
+          <el-option label="家庭周报" value="fortune"></el-option>
+        </el-select>
+        <el-input v-model="query.keyword" placeholder="搜索标题/名称" style="width:200px" @keyup.enter.native="loadData"></el-input>
+        <el-date-picker
+          v-model="dateRange"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          style="width:240px"
+        ></el-date-picker>
+        <el-button type="primary" @click="loadData">查询</el-button>
+        <el-button @click="doSync">同步数据</el-button>
+      </div>
+
+      <el-table :data="tableData" border stripe v-loading="loading" style="margin-top:16px">
+        <el-table-column prop="id" label="ID" width="80"></el-table-column>
+        <el-table-column label="报告类型" width="100">
+          <template slot-scope="scope">
+            <el-tag size="small" :type="getTypeTag(scope.row.reportType)">{{ scope.row.reportTypeName }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="title" label="标题"></el-table-column>
+        <el-table-column prop="subjectName" label="报告主体" width="120"></el-table-column>
+        <el-table-column prop="reportDate" label="报告日期" width="120"></el-table-column>
+        <el-table-column prop="overallScore" label="综合评分" width="100">
+          <template slot-scope="scope">
+            <span v-if="scope.row.overallScore">{{ scope.row.overallScore }}</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="状态" width="100">
+          <template slot-scope="scope">
+            <el-tag size="small">{{ scope.row.status }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column prop="createdAt" label="创建时间" width="160"></el-table-column>
+        <el-table-column label="操作" width="120">
+          <template slot-scope="scope">
+            <el-button size="mini" type="text" @click="viewDetail(scope.row.id)">详情</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <el-pagination
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+        :current-page="query.page"
+        :page-size="query.size"
+        :total="total"
+        layout="total, prev, pager, next"
+        style="margin-top:16px"
+      ></el-pagination>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import axios from 'axios'
+
+export default {
+  name: 'ReportManagement',
+  data() {
+    return {
+      tableData: [],
+      total: 0,
+      loading: false,
+      query: {
+        page: 1,
+        size: 20,
+        type: '',
+        keyword: ''
+      },
+      dateRange: []
+    }
+  },
+  created() {
+    this.loadData()
+  },
+  methods: {
+    loadData() {
+      var self = this
+      self.loading = true
+      var params = {
+        page: self.query.page,
+        size: self.query.size,
+        type: self.query.type,
+        keyword: self.query.keyword
+      }
+      if (self.dateRange && self.dateRange.length === 2) {
+        params.startDate = self.dateRange[0].format ? self.dateRange[0].format('YYYY-MM-DD') : self.dateRange[0]
+        params.endDate = self.dateRange[1].format ? self.dateRange[1].format('YYYY-MM-DD') : self.dateRange[1]
+      }
+      axios.post('/api/admin/report-summary/list', params, {
+        headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
+      }).then(function(res) {
+        if (res.data && res.data.data) {
+          self.tableData = res.data.data.records || []
+          self.total = res.data.data.total || 0
+        }
+      }).catch(function(e) {
+        console.error(e)
+      }).finally(function() {
+        self.loading = false
+      })
+    },
+    handleSizeChange(size) {
+      this.query.size = size
+      this.query.page = 1
+      this.loadData()
+    },
+    handleCurrentChange(page) {
+      this.query.page = page
+      this.loadData()
+    },
+    doSync() {
+      var self = this
+      axios.post('/api/admin/report-summary/sync', {}, {
+        headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
+      }).then(function(res) {
+        if (res.data && res.data.code === 200) {
+          self.$message.success('同步完成: ' + res.data.message)
+          self.loadData()
+        }
+      }).catch(function(e) {
+        self.$message.error('同步失败')
+      })
+    },
+    viewDetail(id) {
+      var self = this
+      axios.post('/api/report-summary/detail/' + id, {}, {
+        headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
+      }).then(function(res) {
+        if (res.data && res.data.data) {
+          self.$alert(
+            '<p>标题: ' + res.data.data.title + '</p>' +
+            '<p>类型: ' + res.data.data.reportTypeName + '</p>' +
+            '<p>主体: ' + res.data.data.subjectName + '</p>' +
+            '<p>日期: ' + res.data.data.reportDate + '</p>' +
+            '<p>评分: ' + (res.data.data.overallScore || '-') + '</p>' +
+            '<p>状态: ' + res.data.data.status + '</p>',
+            '报告详情',
+            { dangerouslyUseHTMLString: true }
+          )
+        }
+      })
+    },
+    getTypeTag(type) {
+      var map = { gut_flora: 'success', physical_exam: 'primary', dan: 'warning', insurance: 'info', fortune: 'danger' }
+      return map[type] || 'info'
+    }
+  }
+}
+</script>
+
+<style scoped>
+.report-management { padding: 16px; }
+.filter-bar {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  gap: 12px;
+  flex-wrap: wrap;
+}
+</style>
+```
+
+- [ ] Step 2: 在 `cfc-web/src/router/index.js` 中添加路由:
+
+```js
+{
+  path: 'report-management',
+  name: 'ReportManagement',
+  component: () => import('@/views/admin/ReportManagement.vue'),
+  meta: { title: '报告管理', perm: 'report:management' }
+}
+```
+
+- [ ] Step 3: 在 `cfc-web/src/views/Layout.vue` 左侧菜单中添加菜单项,参考现有菜单结构(搜索 `health-reports` 找到分组位置),添加:
+
+```js
+// 在 menuItems 数组中找到合适位置(如"健康"或"系统管理"分组),添加:
+{
+  path: '/admin/report-management',
+  title: '报告管理',
+  icon: 'el-icon-document',
+  perm: 'report:management'
+}
+```
+
+> ⚠️ 需确认 `Layout.vue` 中菜单数据的具体结构(可能是一个数组或对象),根据现有模式适配。
+
+- [ ] Step 4: `cd cfc-web && npm run serve` 验证
+
+---
+
+## 最终验证
+
+- [ ] `cd cfc-backend && mvn clean compile` — 后端编译通过
+- [ ] 检查所有 4 个源 Service 的 sync 调用已正确注入
+- [ ] 小程序端:从「我的」页点击「报告管理」→ 看到报告列表 → 点击卡片 → 看到详情
+- [ ] Web 管理端:从菜单点击「报告管理」→ 看到表格 → 点击「同步数据」→ 看到同步结果
+- [ ] `fullSync()` 能正确处理已删除的源记录(标记为 discarded)
+- [ ] 小程序禁用 `?.` 可选链、禁用 CSS Grid — 全部使用 `&&` 和 flexbox
+
+---
+
+## 文件清单汇总
+
+### 后端 (cfc-backend)
+
+| 文件 | 操作 | Task |
+|------|------|------|
+| `entity/ReportSummary.java` | 新建 | 2 |
+| `mapper/ReportSummaryMapper.java` | 新建 | 2 |
+| `mapper/ReportSummaryMapper.xml` | 新建 | 2 |
+| `dto/ReportSummaryVO.java` | 新建 | 3 |
+| `dto/ReportListQueryDTO.java` | 新建 | 3 |
+| `dto/ReportSyncResultDTO.java` | 新建 | 3 |
+| `service/ReportSummaryService.java` | 新建 | 4 |
+| `controller/ReportSummaryController.java` | 新建 | 6 |
+| `controller/admin/ReportSummaryAdminController.java` | 新建 | 7 |
+| `task/ReportSummarySyncTask.java` | 新建 | 7 |
+| `config/DatabaseInitializer.java` | 修改 | 1 |
+| `resources/schema.sql` | 修改 | 1 |
+| `service/HealthReportService.java` | 修改 | 5 |
+| `service/DanReportUploadService.java` | 修改 | 5 |
+| `service/InsurancePolicyService.java` | 修改 | 5 |
+
+### 小程序 (cfc-frontend)
+
+| 文件 | 操作 | Task |
+|------|------|------|
+| `pages/profile/report-management.vue` | 新建 | 9 |
+| `pages/profile/report-detail.vue` | 新建 | 10 |
+| `pages/profile/components/ProfileMenu.vue` | 修改 | 8 |
+| `utils/api.js` | 修改 | 9 |
+| `pages.json` | 修改 | 9 |
+
+### Web管理端 (cfc-web)
+
+| 文件 | 操作 | Task |
+|------|------|------|
+| `src/views/admin/ReportManagement.vue` | 新建 | 11 |
+| `src/views/Layout.vue` | 修改 | 11 |
+| `src/router/index.js` | 修改 | 11 |

+ 607 - 1060
web/ppt/index.html

@@ -1,1073 +1,620 @@
 <!DOCTYPE html>
 <html lang="zh-CN">
 <head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>浠艾福 会员卡</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { font-family:'Inter',-apple-system,sans-serif; background:#111; overflow:hidden; width:100vw; height:100vh; }
-        .slide-container { width:100vw; height:100vh; display:flex; align-items:center; justify-content:center; }
-        .slide { width:1280px; height:720px; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%) scale(var(--scale)); opacity:0; pointer-events:none; transition:opacity .35s ease, transform .35s ease; background:#fff; overflow:hidden; }
-        .slide.active { opacity:1; pointer-events:auto; }
-        .nav { position:fixed; bottom:24px; left:50%; transform:translateX(-50%); display:flex; align-items:center; gap:16px; z-index:100; }
-        .nav-dot { width:8px; height:8px; border-radius:50%; background:rgba(255,255,255,.3); cursor:pointer; transition:all .25s; }
-        .nav-dot.active { background:#4ecdc4; transform:scale(1.4); }
-        .nav-dot:hover { background:rgba(255,255,255,.7); }
-        .nav-btn { background:rgba(255,255,255,.15); border:1px solid rgba(255,255,255,.25); border-radius:50%; width:36px; height:36px; display:flex; align-items:center; justify-content:center; cursor:pointer; transition:background .2s; color:#fff; font-size:14px; }
-        .nav-btn:hover { background:rgba(255,255,255,.3); }
-        .slide-counter { position:fixed; top:20px; right:24px; font-size:13px; color:rgba(255,255,255,.5); z-index:100; font-weight:500; }
-        .fullscreen-btn { position:fixed; top:20px; left:24px; background:rgba(255,255,255,.15); border:1px solid rgba(255,255,255,.25); border-radius:6px; padding:6px 12px; cursor:pointer; z-index:100; color:#fff; font-size:12px; transition:background .2s; }
-        .fullscreen-btn:hover { background:rgba(255,255,255,.3); }
-    </style>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>浠艾福 · 主动健康</title>
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800;900&family=Inter:wght@300;400;500;600;700&display=swap');
+*{margin:0;padding:0;box-sizing:border-box}
+body{font-family:Inter,sans-serif;background:#0a0e27;overflow:hidden;width:100vw;height:100vh}
+.slide-container{width:100vw;height:100vh;display:flex;align-items:center;justify-content:center}
+.slide{width:1280px;height:720px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) scale(var(--scale));opacity:0;pointer-events:none;transition:opacity .4s,transform .4s;overflow:hidden;border-radius:12px;box-shadow:0 20px 60px rgba(0,0,0,.5)}
+.slide.active{opacity:1;pointer-events:auto}
+.nav{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:16px;z-index:100}
+.nav-dot{width:7px;height:7px;border-radius:50%;background:rgba(255,255,255,.2);cursor:pointer;transition:all .3s}
+.nav-dot.active{background:#14b8a6;transform:scale(1.5);box-shadow:0 0 12px rgba(20,184,166,.4)}
+.nav-btn{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.15);border-radius:50%;width:36px;height:36px;display:flex;align-items:center;justify-content:center;cursor:pointer;color:#fff;font-size:14px;backdrop-filter:blur(8px);transition:all .2s}
+.nav-btn:hover{background:rgba(255,255,255,.2);transform:scale(1.1)}
+.slide-counter{position:fixed;top:20px;right:24px;font-size:12px;color:rgba(255,255,255,.3);z-index:100;font-weight:500}
+.fullscreen-btn{position:fixed;top:20px;left:24px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);border-radius:6px;padding:6px 14px;cursor:pointer;z-index:100;color:rgba(255,255,255,.5);font-size:11px;transition:all .2s;backdrop-filter:blur(8px)}
+.fullscreen-btn:hover{background:rgba(255,255,255,.15);color:#fff}
+</style>
 </head>
 <body>
-    <button class="fullscreen-btn" onclick="toggleFullscreen()">⛶ 全屏</button>
-    <div class="slide-counter"><span id="currentSlide">1</span> / 10</div>
-    <div class="slide-container" id="slideContainer"></div>
-    <div class="nav">
-        <div class="nav-btn" onclick="prevSlide()">◀</div>
-        <div class="nav-dots" id="navDots"></div>
-        <div class="nav-btn" onclick="nextSlide()">▶</div>
-    </div>
-    <script>
-    const slides = [
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>浠艾福 会员卡</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin: 0; padding: 0; box-sizing: border-box; }
-        body {
-            width: 1280px; height: 720px;
-            background: linear-gradient(135deg, #ffffff 0%, #f0fdfa 100%);
-            font-family: 'Inter', -apple-system, sans-serif;
-            overflow: hidden; position: relative;
-        }
-        .top-bar { position: absolute; top: 0; left: 0; right: 0; height: 4px; background: linear-gradient(90deg, #00b4d8, #4ecdc4, #95e1d3); }
-        .ecg { position: absolute; top: 50px; right: 80px; width: 300px; height: 60px; opacity: 0.12; }
-        .ecg path { stroke: #00b4d8; stroke-width: 2; fill: none; stroke-dasharray: 400; stroke-dashoffset: 400; animation: draw 3s ease-in-out infinite; }
-        @keyframes draw { 0%{stroke-dashoffset:400} 50%{stroke-dashoffset:0} 100%{stroke-dashoffset:-400} }
-        .cross { position: absolute; top: 50px; left: 80px; width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; }
-        .cross::before, .cross::after { content:''; position: absolute; background: #dc2626; border-radius: 3px; }
-        .cross::before { width: 22px; height: 7px; }
-        .cross::after { width: 7px; height: 22px; }
-        .content { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); text-align: center; width: 80%; }
-        .label { font-size: 13px; font-weight: 600; letter-spacing: 0.25em; color: #4ecdc4; margin-bottom: 28px; display: flex; align-items: center; justify-content: center; gap: 10px; }
-        .label::before { content:''; width: 8px; height: 8px; background: #4ecdc4; border-radius: 50%; animation: pulse 2s ease-in-out infinite; }
-        @keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.5;transform:scale(1.3)} }
-        .title { font-size: 80px; font-weight: 800; letter-spacing: -0.04em; color: #0a2540; margin-bottom: 12px; }
-        .subtitle { font-size: 32px; font-weight: 500; color: #5a7a96; margin-bottom: 40px; letter-spacing: 0.12em; }
-        .tagline { display: inline-block; background: linear-gradient(135deg, #f0fdfa, #e0f5f1); border: 2px solid #4ecdc4; border-radius: 8px; padding: 18px 48px; }
-        .tagline-text { font-size: 20px; font-weight: 700; color: #00b4d8; letter-spacing: 0.08em; }
-        .page { position: absolute; bottom: 30px; right: 40px; font-size: 12px; color: #5a7a96; opacity: 0.5; font-weight: 500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <svg class="ecg" viewBox="0 0 300 60"><path d="M0,30 L50,30 L60,10 L70,50 L80,20 L90,40 L100,30 L150,30 L160,10 L170,50 L180,20 L190,40 L200,30 L250,30 L260,10 L270,50 L280,20 L290,40 L300,30"/></svg>
-    <div class="cross"></div>
-    <div class="content">
-        <div class="label">HEALTH MEMBERSHIP</div>
-        <div class="title">浠艾福</div>
-        <div class="subtitle">会 员 卡</div>
-        <div class="tagline"><div class="tagline-text">精准营养 · 主动健康 · 家庭守护</div></div>
-    </div>
-    <div class="page">01 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>触目惊心的数据</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        .ecg { position:absolute; top:40px; right:60px; width:220px; height:44px; opacity:0.1; }
-        .ecg path { stroke:#00b4d8; stroke-width:2; fill:none; stroke-dasharray:280; stroke-dashoffset:280; animation:draw 3s ease-in-out infinite; }
-        @keyframes draw { 0%{stroke-dashoffset:280} 50%{stroke-dashoffset:0} 100%{stroke-dashoffset:-280} }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#4ecdc4; display:flex; align-items:center; gap:10px; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; }
-        .content { position:absolute; top:100px; left:80px; right:80px; }
-        .title { font-size:38px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:8px; }
-        .sub { font-size:16px; color:#5a7a96; margin-bottom:32px; }
-        .big-stat { display:flex; align-items:center; gap:40px; margin-bottom:36px; }
-        .big-number { font-size:120px; font-weight:800; color:#dc2626; line-height:1; letter-spacing:-0.04em; }
-        .big-number span { font-size:48px; color:#dc2626; }
-        .big-label { font-size:22px; font-weight:600; color:#0a2540; line-height:1.6; }
-        .big-label em { font-style:normal; color:#dc2626; }
-        .grid { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-bottom:28px; }
-        .card { background:#f8fffe; border:1px solid #e0f5f1; border-radius:8px; padding:20px 22px; position:relative; overflow:hidden; }
-        .card::before { content:''; position:absolute; top:0; left:0; right:0; height:3px; background:linear-gradient(90deg,#00b4d8,#4ecdc4); }
-        .card-num { font-size:36px; font-weight:800; color:#00b4d8; font-variant-numeric:tabular-nums; }
-        .card-num .unit { font-size:18px; font-weight:600; color:#4ecdc4; }
-        .card-label { font-size:14px; font-weight:600; color:#0a2540; margin-top:4px; }
-        .card-desc { font-size:12px; color:#5a7a96; margin-top:4px; line-height:1.4; }
-        .bottom { position:absolute; bottom:70px; left:80px; right:80px; background:linear-gradient(135deg,#fee2e2,#fef2f2); border:1px solid #fca5a5; border-radius:8px; padding:18px 24px; display:flex; align-items:center; gap:14px; }
-        .bottom-icon { width:40px; height:40px; background:#dc2626; border-radius:50%; display:flex; align-items:center; justify-content:center; color:#fff; flex-shrink:0; }
-        .bottom-text { font-size:15px; font-weight:600; color:#991b1b; line-height:1.5; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <svg class="ecg" viewBox="0 0 220 44"><path d="M0,22 L35,22 L44,6 L53,38 L62,15 L71,29 L80,22 L120,22 L129,6 L138,38 L147,15 L156,29 L165,22 L200,22 L209,6 L218,38 L220,22"/></svg>
-    <div class="label">HEALTH CRISIS</div>
-    <div class="content">
-        <div class="title">触目惊心的数据</div>
-        <div class="sub">中国心脑血管疾病死亡占总死亡人数 40% 以上,癌症占近 30%</div>
-        <div class="big-stat">
-            <div class="big-number">1/5</div>
-            <div class="big-label">每 <em>5 个</em>成年人死亡<br/>就有 <em>1 人</em>死于心脑血管疾病</div>
-        </div>
-        <div class="grid">
-            <div class="card">
-                <div class="card-num">508<span class="unit">万/年</span></div>
-                <div class="card-label">心脑血管疾病死亡</div>
-                <div class="card-desc">2025年,超过新冠全球年度死亡的5倍</div>
-            </div>
-            <div class="card">
-                <div class="card-num">3.3<span class="unit">亿</span></div>
-                <div class="card-label">心脑血管患病人群</div>
-                <div class="card-desc">高血压2.7亿·高血脂3.8亿·糖尿病1.5亿</div>
-            </div>
-            <div class="card">
-                <div class="card-num">515<span class="unit">万/年</span></div>
-                <div class="card-label">新发癌症</div>
-                <div class="card-desc">全球占比25%,发病年轻化趋势明显</div>
-            </div>
-            <div class="card">
-                <div class="card-num">1.8<span class="unit">亿</span></div>
-                <div class="card-label">高尿酸人群</div>
-                <div class="card-desc">肾脏排泄障碍,约30%经肠道代谢</div>
-            </div>
-            <div class="card">
-                <div class="card-num">7000<span class="unit">万</span></div>
-                <div class="card-label">骨质疏松人群</div>
-                <div class="card-desc">50岁以上患病率19.2%,女性32.1%</div>
-            </div>
-            <div class="card">
-                <div class="card-num">1.7<span class="unit">亿</span></div>
-                <div class="card-label">老年痴呆人群</div>
-                <div class="card-desc">全球患者,神经炎症与肠脑轴关联</div>
-            </div>
-        </div>
-    </div>
-    <div class="bottom">
-        <div class="bottom-icon">
-            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12" y2="16"></line><circle cx="12" cy="12" r="10"></circle></svg>
-        </div>
-        <div class="bottom-text">慢性病不再遥远——它就在你我身边,90%的心脑血管疾病由肠道菌群紊乱引发</div>
-    </div>
-    <div class="page">02 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>健康风险自检</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#4ecdc4; display:flex; align-items:center; gap:10px; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; }
-        .content { position:absolute; top:100px; left:80px; right:80px; }
-        .title { font-size:38px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:8px; }
-        .sub { font-size:16px; color:#5a7a96; margin-bottom:28px; }
-        .cols { display:grid; grid-template-columns:repeat(2,1fr); gap:14px; }
-        .item { background:#f8fffe; border:1px solid #e0f5f1; border-radius:8px; padding:16px 20px; display:flex; align-items:center; gap:14px; transition:border-color .2s; }
-        .item:hover { border-color:#4ecdc4; }
-        .check { width:28px; height:28px; border:2px solid #4ecdc4; border-radius:6px; display:flex; align-items:center; justify-content:center; flex-shrink:0; font-size:14px; color:#4ecdc4; font-weight:700; }
-        .item-text { font-size:15px; font-weight:500; color:#0a2540; }
-        .bottom { position:absolute; bottom:60px; left:80px; right:80px; display:flex; gap:20px; align-items:stretch; }
-        .score-box { flex:1; background:#f0fdfa; border:1px solid #4ecdc4; border-radius:8px; padding:18px 24px; }
-        .score-title { font-size:15px; font-weight:700; color:#0a2540; margin-bottom:6px; }
-        .score-desc { font-size:13px; color:#5a7a96; line-height:1.5; }
-        .badges { display:flex; gap:10px; flex-shrink:0; align-items:center; }
-        .badge { padding:8px 16px; border-radius:20px; font-size:12px; font-weight:700; }
-        .b-green { background:#dcfce7; color:#16a34a; }
-        .b-yellow { background:#fef3c7; color:#d97706; }
-        .b-red { background:#fee2e2; color:#dc2626; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <div class="label">SELF CHECK</div>
-    <div class="content">
-        <div class="title">健康风险自检清单</div>
-        <div class="sub">对照以下症状,看看你中了几条?</div>
-        <div class="cols">
-            <div class="item"><div class="check">1</div><div class="item-text">经常疲劳、精力不足,睡醒也累</div></div>
-            <div class="item"><div class="check">2</div><div class="item-text">消化不良,经常腹胀、便秘或腹泻</div></div>
-            <div class="item"><div class="check">3</div><div class="item-text">睡眠质量差,失眠、多梦、早醒</div></div>
-            <div class="item"><div class="check">4</div><div class="item-text">情绪波动大,容易焦虑、烦躁或低落</div></div>
-            <div class="item"><div class="check">5</div><div class="item-text">免疫力下降,换季必感冒或反复感染</div></div>
-            <div class="item"><div class="check">6</div><div class="item-text">皮肤问题:痤疮、湿疹、暗沉、长斑</div></div>
-            <div class="item"><div class="check">7</div><div class="item-text">体重管理困难,怎么减都不掉秤</div></div>
-            <div class="item"><div class="check">8</div><div class="item-text">记忆力下降,注意力不集中,脑子不清醒</div></div>
-        </div>
-    </div>
-    <div class="bottom">
-        <div class="score-box">
-            <div class="score-title">评分标准</div>
-            <div class="score-desc">出现 0-2 项 → 健康状态 &nbsp;|&nbsp; 3-5 项 → 亚健康警报 &nbsp;|&nbsp; 6 项以上 → 慢病高风险</div>
-        </div>
-        <div class="badges">
-            <div class="badge b-green">0-2 项</div>
-            <div class="badge b-yellow">3-5 项</div>
-            <div class="badge b-red">6+ 项</div>
-        </div>
-    </div>
-    <div class="page">03 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>肠道菌群科学</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#4ecdc4; display:flex; align-items:center; gap:10px; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; }
-        .content { position:absolute; top:100px; left:80px; right:80px; }
-        .title { font-size:38px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:8px; }
-        .sub { font-size:16px; color:#5a7a96; margin-bottom:28px; }
-        .top-card { background:linear-gradient(135deg,#f0fdfa,#e0f5f1); border:1px solid #4ecdc4; border-radius:10px; padding:24px 32px; margin-bottom:24px; display:flex; gap:32px; align-items:center; }
-        .top-card-icon { font-size:48px; flex-shrink:0; }
-        .top-card-text { flex:1; }
-        .top-card-title { font-size:20px; font-weight:700; color:#0a2540; margin-bottom:8px; }
-        .top-card-desc { font-size:14px; color:#5a7a96; line-height:1.7; }
-        .evidence { display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-bottom:24px; }
-        .ev-card { background:#f8fffe; border:1px solid #e0f5f1; border-radius:8px; padding:18px 20px; position:relative; overflow:hidden; }
-        .ev-card::before { content:''; position:absolute; top:0; left:0; right:0; height:3px; background:linear-gradient(90deg,#00b4d8,#4ecdc4); }
-        .ev-year { font-size:12px; font-weight:700; color:#4ecdc4; margin-bottom:6px; letter-spacing:0.05em; }
-        .ev-title { font-size:14px; font-weight:700; color:#0a2540; margin-bottom:6px; }
-        .ev-desc { font-size:12px; color:#5a7a96; line-height:1.5; }
-        .ev-journal { font-size:11px; color:#4ecdc4; font-weight:600; margin-top:6px; }
-        .conclusion { background:#f0fdfa; border-left:4px solid #4ecdc4; border-radius:0 8px 8px 0; padding:16px 24px; }
-        .conclusion p { font-size:15px; font-weight:600; color:#0a2540; line-height:1.6; }
-        .conclusion strong { color:#00b4d8; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <div class="label">GUT SCIENCE</div>
-    <div class="content">
-        <div class="title">肠道菌群:人体健康的"总开关"</div>
-        <div class="sub">顶尖医学期刊持续证实:肠道微生态决定全身健康走向</div>
-        <div class="top-card">
-            <div class="top-card-icon"><svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20M7 5l5-3 5 3M7 19l5 3 5-3M7 11l5-3 5 3M7 13l5 3 5-3"/></svg></div>
-            <div class="top-card-text">
-                <div class="top-card-title">你的第二基因组</div>
-                <div class="top-card-desc">肠道菌群基因总量约为人类基因的 150 倍,被称为人体"第二基因组"。它调控免疫、代谢、情绪、炎症等几乎所有生理过程,是你健康的底层操作系统。</div>
-            </div>
-        </div>
-        <div class="evidence">
-            <div class="ev-card">
-                <div class="ev-year">2011 · NATURE</div>
-                <div class="ev-title">肠道-心脑血管关联</div>
-                <div class="ev-desc">肠道菌群代谢直接驱动心脑血管疾病发生,正式建立病理关联体系</div>
-                <div class="ev-journal">Nature</div>
-            </div>
-            <div class="ev-card">
-                <div class="ev-year">2013 · 临床突破</div>
-                <div class="ev-title">菌群移植治愈率</div>
-                <div class="ev-desc">菌群移植对特定肠道疾病治愈率超 90%,可显著改善自闭症等神经问题</div>
-                <div class="ev-journal">多中心临床研究</div>
-            </div>
-            <div class="ev-card">
-                <div class="ev-year">2020 · CELL</div>
-                <div class="ev-title">PAG 致病机制</div>
-                <div class="ev-desc">菌群代谢产物 PAG 直接加速血管炎症、斑块不稳定,诱发急性心脑血管事件</div>
-                <div class="ev-journal">Cell</div>
-            </div>
-        </div>
-        <div class="conclusion">
-            <p>核心结论:<strong>肠道微生态是维持人体健康的"总开关"</strong>——菌群失衡引发肠漏 → 毒素入血 → 全身慢性炎症 → 心脑血管病、糖尿病、癌症……</p>
-        </div>
-    </div>
-    <div class="page">04 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>传统医疗的困境</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#4ecdc4; display:flex; align-items:center; gap:10px; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; }
-        .content { position:absolute; top:100px; left:80px; right:80px; }
-        .title { font-size:38px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:8px; }
-        .sub { font-size:16px; color:#5a7a96; margin-bottom:28px; }
-        .pain-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; margin-bottom:24px; }
-        .pain-card { background:#fff; border:1px solid #fee2e2; border-radius:8px; padding:20px 24px; display:flex; gap:16px; align-items:flex-start; }
-        .pain-icon { width:44px; height:44px; background:#fee2e2; border-radius:8px; display:flex; align-items:center; justify-content:center; font-size:22px; flex-shrink:0; }
-        .pain-title { font-size:16px; font-weight:700; color:#991b1b; margin-bottom:4px; }
-        .pain-desc { font-size:13px; color:#5a7a96; line-height:1.5; }
-        .flow { display:flex; align-items:center; justify-content:center; gap:0; margin-bottom:24px; }
-        .flow-step { background:#fee2e2; border-radius:8px; padding:12px 20px; text-align:center; }
-        .flow-step-label { font-size:13px; font-weight:700; color:#991b1b; }
-        .flow-step-sub { font-size:11px; color:#5a7a96; margin-top:2px; }
-        .flow-arrow { font-size:20px; color:#dc2626; padding:0 6px; }
-        .bottom { position:absolute; bottom:60px; left:80px; right:80px; background:linear-gradient(135deg,#f0fdfa,#e0f5f1); border:1px solid #4ecdc4; border-radius:8px; padding:18px 28px; display:flex; align-items:center; gap:16px; }
-        .bottom-icon { font-size:32px; flex-shrink:0; }
-        .bottom-text { font-size:15px; font-weight:600; color:#0a2540; line-height:1.5; }
-        .bottom-text strong { color:#00b4d8; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <div class="label">MEDICAL LIMITS</div>
-    <div class="content">
-        <div class="title">吃药治病吗?</div>
-        <div class="sub">传统医疗在慢性病管理上的困境</div>
-        <div class="pain-grid">
-            <div class="pain-card">
-                <div class="pain-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#991b1b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m10.5 20.5 10-10a4.95 4.95 0 1 0-7-7l-10 10a4.95 4.95 0 1 0 7 7Z"/><path d="m8.5 8 6 6"/></svg></div>
-                <div>
-                    <div class="pain-title">药一辈子不能停</div>
-                    <div class="pain-desc">高血压药、降糖药、他汀类——一旦开始就是终身服药,药物只控制指标,不解决根因</div>
-                </div>
-            </div>
-            <div class="pain-card">
-                <div class="pain-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#991b1b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/></svg></div>
-                <div>
-                    <div class="pain-title">治好一种,引发另一种</div>
-                    <div class="pain-desc">利尿剂降压→血粘稠度升高;降糖药→胃肠道副作用→菌群进一步紊乱</div>
-                </div>
-            </div>
-            <div class="pain-card">
-                <div class="pain-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#991b1b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="m19 9-5 5-4-4-3 3"/></svg></div>
-                <div>
-                    <div class="pain-title">发病率越来越高</div>
-                    <div class="pain-desc">原发高血压从1959年的5.1%飙升到2025年的26.4%——医疗进步了,病人却更多了</div>
-                </div>
-            </div>
-            <div class="pain-card">
-                <div class="pain-icon"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#991b1b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></div>
-                <div>
-                    <div class="pain-title">有病才去医院</div>
-                    <div class="pain-desc">等到确诊已是中晚期,冠脉堵塞70%才有心绞痛症状,30岁斑块就开始加速</div>
-                </div>
-            </div>
-        </div>
-        <div class="flow">
-            <div class="flow-step"><div class="flow-step-label">吃药</div><div class="flow-step-sub">控制指标</div></div>
-            <div class="flow-arrow"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg></div>
-            <div class="flow-step"><div class="flow-step-label">药量加大</div><div class="flow-step-sub">耐药性</div></div>
-            <div class="flow-arrow"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg></div>
-            <div class="flow-step"><div class="flow-step-label">联合用药</div><div class="flow-step-sub">副作用叠加</div></div>
-            <div class="flow-arrow"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg></div>
-            <div class="flow-step"><div class="flow-step-label">并发症</div><div class="flow-step-sub">心梗/脑梗</div></div>
-            <div class="flow-arrow"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg></div>
-            <div class="flow-step"><div class="flow-step-label">手术/搭桥</div><div class="flow-step-sub">应急方案</div></div>
-        </div>
-        <div class="bottom">
-            <div class="bottom-icon"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1.2.5 2.4 1.5 3.4.8.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg></div>
-            <div class="bottom-text"><strong>核心问题:</strong>传统医疗处理的是"结果"(症状和指标),而非"原因"(菌群失衡和慢性炎症)。真正的健康,需要从源头主动干预。</div>
-        </div>
-    </div>
-    <div class="page">05 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>主动健康理念</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#4ecdc4; display:flex; align-items:center; gap:10px; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; }
-        .content { position:absolute; top:100px; left:80px; right:80px; }
-        .title { font-size:38px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:8px; }
-        .sub { font-size:16px; color:#5a7a96; margin-bottom:36px; }
-        .vs { display:flex; gap:32px; margin-bottom:32px; }
-        .vs-card { flex:1; border-radius:12px; padding:32px 28px; position:relative; overflow:hidden; }
-        .vs-left { background:#fee2e2; border:1px solid #fca5a5; }
-        .vs-right { background:#f0fdfa; border:2px solid #4ecdc4; }
-        .vs-badge { position:absolute; top:16px; right:16px; font-size:11px; font-weight:700; padding:4px 12px; border-radius:12px; }
-        .vs-left .vs-badge { background:#dc2626; color:#fff; }
-        .vs-right .vs-badge { background:#4ecdc4; color:#fff; }
-        .vs-icon { font-size:36px; margin-bottom:12px; display:flex; justify-content:center; }
-        .micro-section { position:absolute; bottom:100px; left:50%; transform:translateX(-50%); display:flex; flex-direction:column; align-items:center; gap:10px; }
-        .micro-label { font-size:12px; font-weight:700; color:#4ecdc4; letter-spacing:0.15em; display:flex; align-items:center; gap:8px; }
-        .micro-label::before, .micro-label::after { content:''; width:40px; height:1px; background:#4ecdc4; }
-        .micro-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:12px; width:620px; justify-content:center; }
-        .micro-card { background:#f8fffe; border:1px solid #e0f5f1; border-radius:8px; padding:14px; width:140px; display:flex; flex-direction:column; gap:6px; }
-        .micro-card-header { display:flex; align-items:center; gap:6px; }
-        .micro-card-title { font-size:13px; font-weight:700; color:#0a2540; }
-        .micro-card-desc { font-size:11px; color:#5a7a96; line-height:1.4; }
-        .vs-title { font-size:22px; font-weight:800; margin-bottom:8px; }
-        .vs-left .vs-title { color:#991b1b; }
-        .vs-right .vs-title { color:#0a2540; }
-        .vs-desc { font-size:14px; line-height:1.7; }
-        .vs-left .vs-desc { color:#7f1d1d; }
-        .vs-right .vs-desc { color:#5a7a96; }
-        .vs-list { list-style:none; margin-top:12px; }
-        .vs-list li { font-size:13px; padding:4px 0; display:flex; align-items:center; gap:8px; }
-        .vs-left .vs-list li { color:#991b1b; }
-        .vs-right .vs-list li { color:#0a2540; }
-        .vs-left .vs-list li .icon-x { width:14px; height:14px; flex-shrink:0; }
-        .vs-right .vs-list li .icon-check { width:14px; height:14px; flex-shrink:0; }
-        .quote { position:absolute; bottom:40px; left:80px; right:80px; background:#f8fffe; border-left:4px solid #4ecdc4; border-radius:0 8px 8px 0; padding:18px 24px; }
-        .quote p { font-size:16px; color:#0a2540; font-weight:500; line-height:1.7; }
-        .quote strong { color:#00b4d8; font-weight:700; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <div class="label">PROACTIVE HEALTH</div>
-    <div class="content">
-        <div class="title">从"治病"到"不生病"</div>
-        <div class="sub">主动健康:在疾病发生之前,管理好你的健康</div>
-        <div class="vs">
-            <div class="vs-card vs-left">
-                <div class="vs-badge">传统模式</div>
-                <div class="vs-icon"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#991b1b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg></div>
-                <div class="vs-title">被动医疗</div>
-                <div class="vs-desc">等生病了才去医院,治疗的是"结果"</div>
-                <ul class="vs-list">
-                    <li><svg class="icon-x" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>有症状才就医,发现即中晚期</li>
-                    <li><svg class="icon-x" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>药一辈子不能停,越吃越多</li>
-                    <li><svg class="icon-x" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>治好一种病,引发另一种</li>
-                    <li><svg class="icon-x" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>只管指标,不管根因</li>
-                </ul>
-            </div>
-            <div class="vs-card vs-right">
-                <div class="vs-badge">浠艾福</div>
-                <div class="vs-icon"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 6.1 18 12 18 12s-2.5 5.9-8.2 5.9A7 7 0 0 1 11 20z"/><path d="M12 12v8"/><path d="m12 12 3-3"/><path d="m12 12-3-3"/></svg></div>
-                <div class="vs-title">主动健康</div>
-                <div class="vs-desc">在疾病发生之前,从根源管理健康</div>
-                <ul class="vs-list">
-                    <li><svg class="icon-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>提前检测,预判风险</li>
-                    <li><svg class="icon-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>精准营养干预,修复根因</li>
-                    <li><svg class="icon-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>肠道菌群管理,源头治理</li>
-                    <li><svg class="icon-check" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>全年跟踪,持续优化</li>
-                </ul>
-            </div>
-        </div>
-        <div class="micro-section">
-            <div class="micro-label">肠道菌群检测</div>
-            <div class="micro-grid">
-                <div class="micro-card">
-                    <div class="micro-card-header">
-                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
-                        <div class="micro-card-title">检测技术</div>
-                    </div>
-                    <div class="micro-card-desc">16S rRNA 基因测序技术,一次精准检测 300+ 肠道菌群种类</div>
-                </div>
-                <div class="micro-card">
-                    <div class="micro-card-header">
-                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg>
-                        <div class="micro-card-title">核心指标</div>
-                    </div>
-                    <div class="micro-card-desc">α多样性、β多样性、菌群丰度与均匀度、共生代谢通路综合评估</div>
-                </div>
-                <div class="micro-card">
-                    <div class="micro-card-header">
-                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 18h16z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
-                        <div class="micro-card-title">风险预警</div>
-                    </div>
-                    <div class="micro-card-desc">条件致病菌丰度实时监控,关联12类慢病风险模型预测</div>
-                </div>
-                <div class="micro-card">
-                    <div class="micro-card-header">
-                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m16 12-4-4-4 4M12 16V8"/></svg>
-                        <div class="micro-card-title">闭环管理</div>
-                    </div>
-                    <div class="micro-card-desc">检测→评估→干预→复测闭环,数据驱动精准调整</div>
-                </div>
-            </div>
-        </div>
-        <div class="quote">
-            <p>健康,非无病即安。而是以<strong>简易悦心之法,主动调控</strong>,养充沛生机,去身、心痛楚,得长寿安康。</p>
-        </div>
-    </div>
-    <div class="page">06 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>五维健康体系</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#4ecdc4; display:flex; align-items:center; gap:10px; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; }
-        .content { position:absolute; top:100px; left:80px; right:80px; }
-        .title { font-size:38px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:8px; }
-        .sub { font-size:16px; color:#5a7a96; margin-bottom:32px; }
-        .pentagon { display:flex; justify-content:center; gap:20px; margin-bottom:28px; }
-        .dim { width:200px; border-radius:12px; padding:24px 16px; text-align:center; position:relative; overflow:hidden; transition:transform .2s; }
-        .dim:hover { transform:translateY(-4px); }
-        .dim::before { content:''; position:absolute; top:0; left:0; right:0; height:4px; }
-        .dim-body { background:#f0fdfa; border:1px solid #e0f5f1; }
-        .dim-body::before { background:linear-gradient(90deg,#00b4d8,#4ecdc4); }
-        .dim-mind { background:#f5f3ff; border:1px solid #e0e7ff; }
-        .dim-mind::before { background:linear-gradient(90deg,#8b5cf6,#a78bfa); }
-        .dim-brain { background:#fffbeb; border:1px solid #fde68a; }
-        .dim-brain::before { background:linear-gradient(90deg,#f59e0b,#fbbf24); }
-        .dim-action { background:#fef2f2; border:1px solid #fecaca; }
-        .dim-action::before { background:linear-gradient(90deg,#ef4444,#f87171); }
-        .dim-wealth { background:#f0fdf4; border:1px solid #bbf7d0; }
-        .dim-wealth::before { background:linear-gradient(90deg,#22c55e,#4ade80); }
-        .dim-icon { font-size:36px; margin-bottom:10px; }
-        .dim-zh { font-size:20px; font-weight:800; color:#0a2540; margin-bottom:2px; }
-        .dim-en { font-size:11px; font-weight:600; color:#5a7a96; letter-spacing:0.1em; text-transform:uppercase; margin-bottom:10px; }
-        .dim-desc { font-size:12px; color:#5a7a96; line-height:1.5; }
-        .center-label { text-align:center; margin-bottom:20px; }
-        .center-label span { display:inline-block; background:#f0fdfa; border:1px solid #4ecdc4; border-radius:20px; padding:6px 24px; font-size:13px; font-weight:700; color:#00b4d8; }
-        .bottom { display:flex; gap:16px; }
-        .bottom-card { flex:1; background:#f8fffe; border:1px solid #e0f5f1; border-radius:8px; padding:16px 20px; }
-        .bottom-title { font-size:13px; font-weight:700; color:#0a2540; margin-bottom:4px; }
-        .bottom-desc { font-size:12px; color:#5a7a96; line-height:1.5; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <div class="label">5-DIMENSION SYSTEM</div>
-    <div class="content">
-        <div class="title">浠艾福五维健康体系</div>
-        <div class="sub">健康不是单一指标,而是五个维度的动态平衡</div>
-        <div class="center-label"><span>五维能量 · 全面守护 · 精准干预</span></div>
-        <div class="pentagon">
-            <div class="dim dim-body">
-                <div class="dim-icon"><svg width="36" height="36" viewBox="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" fill="none"><path d="M6 12h12M6 8h12M6 16h12"/></svg></div>
-                <div class="dim-zh">身</div>
-                <div class="dim-en">PHYSICAL</div>
-                <div class="dim-desc">身体机能·营养均衡<br/>免疫力·肠道健康<br/>慢性病预防与管理</div>
-            </div>
-            <div class="dim dim-mind">
-                <div class="dim-icon"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.2-3.01 2.5 2.5 0 0 1 2.2-3.01 2.5 2.5 0 0 1 4.96-.44V4.5A2.5 2.5 0 0 1 9.5 2z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.2-3.01 2.5 2.5 0 0 0-2.2-3.01 2.5 2.5 0 0 0-4.96-.44V4.5A2.5 2.5 0 0 0 14.5 2z"/></svg></div>
-                <div class="dim-zh">心</div>
-                <div class="dim-en">MENTAL</div>
-                <div class="dim-desc">情绪管理·压力调节<br/>睡眠质量·心理韧性<br/>焦虑抑郁预防</div>
-            </div>
-            <div class="dim dim-brain">
-                <div class="dim-icon"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg></div>
-                <div class="dim-zh">智</div>
-                <div class="dim-en">INTELLECTUAL</div>
-                <div class="dim-desc">认知功能·学习能力<br/>专注力·记忆力<br/>脑健康与抗衰</div>
-            </div>
-            <div class="dim dim-action">
-                <div class="dim-icon"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 4v16M17 4v16M19 4h-1c-1 0-2 1-2 2v12c0 1 1 2 2 2h1"/><path d="M11 4h-1c-1 0-2 1-2 2v12c0 1 1 2 2 2h1"/><path d="M7 4h-1c-1 0-2 1-2 2v12c0 1 1 2 2 2h1"/></svg></div>
-                <div class="dim-zh">行</div>
-                <div class="dim-en">BEHAVIORAL</div>
-                <div class="dim-desc">运动习惯·饮食习惯<br/>作息规律·行为改变<br/>健康生活方式养成</div>
-            </div>
-            <div class="dim dim-wealth">
-                <div class="dim-icon"><svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a10 10 0 1 0 10 10H12V2z"/><path d="M12 12L2.1 12.1"/><path d="M12 12l9.9.1"/></svg></div>
-                <div class="dim-zh">富</div>
-                <div class="dim-en">WEALTH</div>
-                <div class="dim-desc">健康财富·时间自由<br/>家庭和谐·生命质量<br/>健康即最大的财富</div>
-            </div>
-        </div>
-        <div class="bottom">
-            <div class="bottom-card">
-                <div class="bottom-title"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle; margin-right:6px;"><path d="M15 14c.2-1 .7-2 1.3-2.5a6.6 6.6 0 0 0-1.7-4.3c-1.2-.5-2.4-.5-3.6 0a6.6 6.6 0 0 0-1.7 4.3c.6.5 1.1 1.5 1.3 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg> 核心理念</div>
-                <div class="bottom-desc">五维不是独立的,而是相互关联的整体。肠道健康(身)影响情绪(心),情绪影响认知(智),认知决定行为(行),行为创造健康财富(富)。</div>
-            </div>
-            <div class="bottom-card">
-                <div class="bottom-title"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle; margin-right:6px;"><circle cx="12" cy="12" r="10"/><path d="M12 8v8M8 12h8"/></svg> 干预策略</div>
-                <div class="bottom-desc">浠艾福从"身"维度切入——通过精准营养和肠道菌群管理,修复身体底层操作系统,带动其他四个维度全面提升。</div>
-            </div>
-            <div class="bottom-card">
-                <div class="bottom-title"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ecdc4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle; margin-right:6px;"><path d="M18 20V10M12 20V4M6 20v-6"/></svg> 评估体系</div>
-                <div class="bottom-desc">五维能量评估,量化你的健康状态。每个维度独立评分,找到短板,精准干预,而非头痛医头。</div>
-            </div>
-        </div>
-    </div>
-    <div class="page">07 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>会员权益</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#4ecdc4; display:flex; align-items:center; gap:10px; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; }
-        .content { position:absolute; top:100px; left:80px; right:80px; display:flex; gap:36px; }
-        .left { flex:1; }
-        .right { width:400px; }
-        .title { font-size:38px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:24px; }
-        .benefits { display:grid; grid-template-columns:repeat(2,1fr); gap:12px; margin-bottom:24px; }
-        .ben { background:#f8fffe; border:1px solid #e0f5f1; border-radius:8px; padding:16px 18px; display:flex; gap:12px; align-items:flex-start; }
-        .ben-icon { width:36px; height:36px; background:linear-gradient(135deg,#00b4d8,#4ecdc4); border-radius:8px; display:flex; align-items:center; justify-content:center; font-size:18px; flex-shrink:0; }
-        .ben-title { font-size:14px; font-weight:700; color:#0a2540; margin-bottom:2px; }
-        .ben-desc { font-size:12px; color:#5a7a96; line-height:1.4; }
-        .seed { background:linear-gradient(135deg,#fef2f2,#fff7ed); border:1px solid #fca5a5; border-radius:10px; padding:20px 24px; }
-        .seed-badge { display:inline-block; background:#dc2626; color:#fff; font-size:11px; font-weight:700; padding:4px 14px; border-radius:12px; margin-bottom:12px; }
-        .seed-title { font-size:18px; font-weight:800; color:#0a2540; margin-bottom:10px; }
-        .seed-list { list-style:none; }
-        .seed-list li { font-size:13px; color:#5a7a96; padding:5px 0; display:flex; align-items:center; gap:8px; line-height:1.4; }
-        .seed-list li::before { content:''; display:inline-block; width:14px; height:14px; background:url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"%234ecdc4\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"8\"/><path d=\"M12 6v12M8 12h8\"/></svg>') no-repeat center/contain; margin-right:4px; }
-        .seed-list li strong { color:#dc2626; font-weight:700; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <div class="label">MEMBERSHIP</div>
-    <div class="content">
-        <div class="left">
-            <div class="title">会员权益</div>
-            <div class="benefits">
-                <div class="ben">
-                    <div class="ben-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h20M12 2v20M4.93 4.93l14.14 14.14M4.93 19.07L19.07 4.93"/></svg></div>
-                    <div>
-                        <div class="ben-title">菌群基因检测</div>
-                        <div class="ben-desc">价值 ¥1314 · 今日下单即送</div>
-                    </div>
-                </div>
-                <div class="ben">
-                    <div class="ben-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 8h10M7 12h10M7 16h10"/></svg></div>
-                    <div>
-                        <div class="ben-title">精准营养方案</div>
-                        <div class="ben-desc">基于检测结果定制</div>
-                    </div>
-                </div>
-                <div class="ben">
-                    <div class="ben-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="//9" cy="7" r="4"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg></div>
-                    <div>
-                        <div class="ben-title">一对一健康管理师</div>
-                        <div class="ben-desc">全年跟踪 · 随时咨询</div>
-                    </div>
-                </div>
-                <div class="ben">
-                    <div class="ben-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 20V10M12 20V4M6 20v-6"/></svg></div>
-                    <div>
-                        <div class="ben-title">健康数据档案</div>
-                        <div class="ben-desc">完整记录 · 趋势分析</div>
-                    </div>
-                </div>
-                <div class="ben">
-                    <div class="ben-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 10v6M2 10l10-5 10 5-10 5z"/><path d="M6 12v5c3 3 6 3 6 3s3 0 6-3v-5"/></svg></div>
-                    <div>
-                        <div class="ben-title">健康教育课程</div>
-                        <div class="ben-desc">肠道科学 · 营养知识</div>
-                    </div>
-                </div>
-                <div class="ben">
-                    <div class="ben-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 5H19"/></svg></div>
-                    <div>
-                        <div class="ben-title">会员专属折扣</div>
-                        <div class="ben-desc">营养品会员专享价</div>
-                    </div>
-                </div>
-            </div>
-        </div>
-        <div class="right">
-            <div class="seed">
-                <div class="seed-badge"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle; margin-right:4px;"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg> 种子用户 · 限首批 100 名</div>
-                <div class="seed-title">首批加入,解锁长期收益</div>
-                <ul class="seed-list">
-                    <li><strong>分享提成:</strong>推荐好友加入,每推荐 1 人即获提成</li>
-                    <li><strong>消费返佣:</strong>被推荐人后续每次消费,你持续获得返佣</li>
-                    <li><strong>优先体验:</strong>新品上线、新服务推出,优先试用</li>
-                    <li><strong>核心社群:</strong>对接创始团队,参与产品共建</li>
-                </ul>
-            </div>
-        </div>
-    </div>
-    <div class="page">08 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>限时优惠</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:#fff; font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#dc2626,#f97316,#facc15); }
-        /* SVG 背景装饰 */
-        .bg-circles { position:absolute; top:-80px; right:-80px; width:400px; height:400px; opacity:0.06; }
-        .bg-dna { position:absolute; bottom:0; left:0; width:300px; height:720px; opacity:0.04; }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.22em; color:#dc2626; display:flex; align-items:center; gap:10px; z-index:2; }
-        .label::before { content:''; width:8px; height:8px; background:#dc2626; border-radius:50%; animation:pulse 1.5s ease-in-out infinite; }
-        @keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.4;transform:scale(1.4)} }
-        .content { position:absolute; top:100px; left:80px; right:80px; display:flex; gap:36px; z-index:2; }
-        .left { flex:1; }
-        .right { width:400px; display:flex; flex-direction:column; gap:16px; }
-        .title { font-size:36px; font-weight:800; letter-spacing:-0.035em; color:#0a2540; margin-bottom:6px; }
-        .title em { font-style:normal; color:#dc2626; }
-        .sub { font-size:15px; color:#5a7a96; margin-bottom:24px; }
-        .offer-card { background:linear-gradient(135deg,#fef2f2,#fff7ed); border:2px solid #fca5a5; border-radius:12px; padding:24px 28px; margin-bottom:16px; position:relative; overflow:hidden; }
-        .offer-card::after { content:''; position:absolute; top:-30px; right:-30px; width:120px; height:120px; background:radial-gradient(circle,rgba(220,38,38,0.08) 0%,transparent 70%); border-radius:50%; }
-        .offer-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
-        .offer-title { font-size:20px; font-weight:800; color:#0a2540; }
-        .offer-tag { background:#dc2626; color:#fff; font-size:12px; font-weight:700; padding:5px 14px; border-radius:16px; display:flex; align-items:center; gap:6px; }
-        .offer-tag svg { width:14px; height:14px; }
-        .offer-price { display:flex; align-items:baseline; gap:10px; margin-bottom:14px; }
-        .price-old { font-size:18px; color:#9ca3af; text-decoration:line-through; }
-        .price-new { font-size:44px; font-weight:800; color:#dc2626; }
-        .price-unit { font-size:15px; color:#5a7a96; }
-        .offer-list { list-style:none; }
-        .offer-list li { font-size:14px; color:#0a2540; padding:7px 0; border-bottom:1px solid #fee2e2; display:flex; align-items:center; gap:8px; }
-        .offer-list li:last-child { border-bottom:none; }
-        .offer-list li svg { width:16px; height:16px; flex-shrink:0; }
-        .urgency { background:#fef3c7; border:1px solid #fcd34d; border-radius:8px; padding:14px 18px; text-align:center; display:flex; align-items:center; justify-content:center; gap:10px; }
-        .urgency svg { width:20px; height:20px; flex-shrink:0; }
-        .urgency-text { font-size:14px; font-weight:700; color:#92400e; }
-        /* 右侧礼物卡 */
-        .gift-box { background:linear-gradient(135deg,#00b4d8,#4ecdc4); border-radius:12px; padding:28px; color:#fff; text-align:center; position:relative; overflow:hidden; flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; }
-        .gift-box::before { content:''; position:absolute; top:-40px; left:-40px; width:160px; height:160px; background:radial-gradient(circle,rgba(255,255,255,0.15) 0%,transparent 70%); border-radius:50%; }
-        .gift-box::after { content:''; position:absolute; bottom:-60px; right:-60px; width:200px; height:200px; background:radial-gradient(circle,rgba(255,255,255,0.1) 0%,transparent 70%); border-radius:50%; }
-        .gift-icon { position:relative; z-index:1; margin-bottom:12px; }
-        .gift-label { font-size:13px; font-weight:600; opacity:0.9; margin-bottom:6px; letter-spacing:0.1em; position:relative; z-index:1; }
-        .gift-value { font-size:52px; font-weight:800; margin-bottom:2px; position:relative; z-index:1; }
-        .gift-unit { font-size:15px; opacity:0.85; margin-bottom:12px; position:relative; z-index:1; }
-        .gift-desc { font-size:13px; opacity:0.9; line-height:1.6; position:relative; z-index:1; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; z-index:2; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <!-- SVG 背景装饰:同心圆 -->
-    <svg class="bg-circles" viewBox="0 0 400 400">
-        <circle cx="200" cy="200" r="180" fill="none" stroke="#dc2626" stroke-width="1"/>
-        <circle cx="200" cy="200" r="140" fill="none" stroke="#dc2626" stroke-width="1"/>
-        <circle cx="200" cy="200" r="100" fill="none" stroke="#dc2626" stroke-width="1"/>
-        <circle cx="200" cy="200" r="60" fill="none" stroke="#dc2626" stroke-width="1.5"/>
-    </svg>
-    <!-- SVG 背景装饰:DNA双螺旋 -->
-    <svg class="bg-dna" viewBox="0 0 300 720">
-        <path d="M50,0 Q150,60 50,120 Q-50,180 50,240 Q150,300 50,360 Q-50,420 50,480 Q150,540 50,600 Q-50,660 50,720" fill="none" stroke="#dc2626" stroke-width="2"/>
-        <path d="M250,0 Q150,60 250,120 Q350,180 250,240 Q150,300 250,360 Q350,420 250,480 Q150,540 250,600 Q350,660 250,720" fill="none" stroke="#dc2626" stroke-width="2"/>
-        <line x1="90" y1="60" x2="210" y2="60" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="60" y1="120" x2="240" y2="120" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="90" y1="180" x2="210" y2="180" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="60" y1="240" x2="240" y2="240" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="90" y1="300" x2="210" y2="300" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="60" y1="360" x2="240" y2="360" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="90" y1="420" x2="210" y2="420" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="60" y1="480" x2="240" y2="480" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="90" y1="540" x2="210" y2="540" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-        <line x1="60" y1="600" x2="240" y2="600" stroke="#dc2626" stroke-width="1" opacity="0.5"/>
-    </svg>
-    <div class="label">
-        <svg viewBox="0 0 16 16" fill="none"><circle cx="8" cy="8" r="3" fill="#dc2626"><animate attributeName="r" values="3;5;3" dur="1.5s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;0.4;1" dur="1.5s" repeatCount="indefinite"/></circle></svg>
-        LIMITED OFFER
-    </div>
-    <div class="content">
-        <div class="left">
-            <div class="title">今日限定 · <em>买年度会员</em></div>
-            <div class="sub">首批种子用户专享,名额有限,先到先得</div>
-            <div class="offer-card">
-                <div class="offer-header">
-                    <div class="offer-title">浠艾福年度会员</div>
-                    <div class="offer-tag"><svg viewBox="0 0 16 16" fill="none"><path d="M8 1l2.2 4.5L15 6.2l-3.5 3.4.8 4.8L8 12l-4.3 2.4.8-4.8L1 6.2l4.8-.7L8 1z" fill="#fff"/></svg> 今日特惠</div>
-                </div>
-                <div class="offer-price">
-                    <div class="price-old">原价 ¥XXX</div>
-                    <div class="price-new">¥XXX</div>
-                    <div class="price-unit">/年</div>
-                </div>
-                <ul class="offer-list">
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        全年 1 对 1 健康管理师跟踪服务
-                    </li>
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        个性化精准营养方案
-                    </li>
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        会员专属产品折扣
-                    </li>
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        健康教育课程无限畅学
-                    </li>
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        种子用户分享提成 + 消费返佣资格
-                    </li>
-                    <li style="color:#dc2626; font-weight:700; border-bottom:none;">
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#fef2f2" stroke="#dc2626" stroke-width="1"/><path d="M8 4v5M8 11v1" stroke="#dc2626" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        PLUS ↓ 赠送以下检测
-                    </li>
-                </ul>
-            </div>
-            <div class="urgency"><svg viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="10" stroke="#92400e" stroke-width="2"/><path d="M12 7v5l3 3" stroke="#92400e" stroke-width="2" stroke-linecap="round"/></svg><div class="urgency-text"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#92400e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle; margin-right:4px;"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 种子用户仅限首批 100 名,额满即止</div></div>
-        </div>
-        <div class="right">
-            <div class="gift-box">
-                <!-- 礼物盒 SVG 图标 -->
-                <div class="gift-icon">
-                    <svg width="64" height="64" viewBox="0 0 64 64" fill="none">
-                        <rect x="8" y="28" width="48" height="30" rx="4" fill="rgba(255,255,255,0.2)" stroke="#fff" stroke-width="2"/>
-                        <rect x="4" y="22" width="56" height="10" rx="3" fill="rgba(255,255,255,0.3)" stroke="#fff" stroke-width="2"/>
-                        <path d="M32 22V58" stroke="#fff" stroke-width="2"/>
-                        <path d="M32 22C32 22 24 10 18 10C12 10 12 18 18 22" stroke="#fff" stroke-width="2" fill="none" stroke-linecap="round"/>
-                        <path d="M32 22C32 22 40 10 46 10C52 10 52 18 46 22" stroke="#fff" stroke-width="2" fill="none" stroke-linecap="round"/>
-                        <circle cx="18" cy="10" r="3" fill="rgba(255,255,255,0.4)"/>
-                        <circle cx="46" cy="10" r="3" fill="rgba(255,255,255,0.4)"/>
-                    </svg>
-                </div>
-                <div class="gift-label"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:middle; margin-right:6px;"><rect x="3" y="8" width="18" height="12" rx="2"/><path d="M12 8V4M8 4h8M12 4l-2 2M12 4l2 2"/></svg> 今日下单额外赠送</div>
-                <div class="gift-value">¥1314</div>
-                <div class="gift-unit">肠道菌群基因检测</div>
-                <div class="gift-desc">全面分析肠道菌群构成<br/>了解你的"第二基因组"<br/>精准指导后续营养方案</div>
-            </div>
-            <div style="text-align:center; font-size:13px; color:#5a7a96; line-height:1.6; background:#f8fffe; border:1px solid #e0f5f1; border-radius:8px; padding:14px;">
-                检测包含:<strong style="color:#0a2540;">菌群多样性分析 · 肠道年龄评估 · 有害菌/有益菌占比 · 慢病风险预警 · 个性化饮食建议</strong>
-            </div>
-        </div>
-    </div>
-    <div class="page">09 / 10</div>
-</body>
-</html>`,
-`<!DOCTYPE html>
-<html lang="zh-CN">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>立即加入</title>
-    <style>
-        @import url('https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;600;700;800&family=Inter:wght@400;500;600;700&display=swap');
-        * { margin:0; padding:0; box-sizing:border-box; }
-        body { width:1280px; height:720px; background:linear-gradient(135deg,#f0fdfa 0%,#ffffff 50%,#f0fdfa 100%); font-family:'Inter',-apple-system,sans-serif; overflow:hidden; position:relative; }
-        .top-bar { position:absolute; top:0; left:0; right:0; height:4px; background:linear-gradient(90deg,#00b4d8,#4ecdc4,#95e1d3); }
-        /* SVG 背景装饰 */
-        .bg-wave { position:absolute; bottom:0; left:0; right:0; height:200px; opacity:0.06; }
-        .bg-molecule { position:absolute; top:40px; right:120px; width:200px; height:200px; opacity:0.08; }
-        .bg-hex { position:absolute; bottom:100px; left:60px; width:160px; height:160px; opacity:0.05; }
-        .label { position:absolute; top:56px; left:80px; font-size:13px; font-weight:600; letter-spacing:0.25em; color:#4ecdc4; display:flex; align-items:center; gap:10px; z-index:2; }
-        .label::before { content:''; width:8px; height:8px; background:#4ecdc4; border-radius:50%; animation:pulse 2s ease-in-out infinite; }
-        @keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.5;transform:scale(1.3)} }
-        .content { position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); text-align:center; width:82%; z-index:2; }
-        .title { font-size:52px; font-weight:800; letter-spacing:-0.04em; color:#0a2540; margin-bottom:10px; }
-        .subtitle { font-size:18px; color:#5a7a96; margin-bottom:36px; line-height:1.6; }
-        .cta-row { display:flex; align-items:center; justify-content:center; gap:48px; margin-bottom:32px; }
-        .qr-box { background:#fff; border:2px solid #4ecdc4; border-radius:12px; padding:24px; width:200px; height:200px; display:flex; align-items:center; justify-content:center; box-shadow:0 4px 24px rgba(78,205,196,0.15); position:relative; }
-        .qr-box::before { content:''; position:absolute; inset:-4px; border:1px dashed #4ecdc4; border-radius:14px; opacity:0.4; }
-        .cta-text { text-align:left; }
-        .cta-main { font-size:26px; font-weight:800; color:#0a2540; margin-bottom:6px; }
-        .cta-main em { font-style:normal; color:#dc2626; }
-        .cta-sub { font-size:15px; color:#5a7a96; line-height:1.6; margin-bottom:14px; }
-        .cta-features { list-style:none; }
-        .cta-features li { font-size:14px; color:#0a2540; padding:4px 0; display:flex; align-items:center; gap:8px; }
-        .cta-features li svg { width:16px; height:16px; flex-shrink:0; }
-        .divider { width:100%; height:1px; background:linear-gradient(90deg,transparent,#4ecdc4,transparent); margin:20px 0; }
-        .quote { font-size:16px; color:#5a7a96; font-style:italic; max-width:640px; margin:0 auto; line-height:1.7; }
-        .quote strong { color:#00b4d8; font-weight:600; font-style:normal; }
-        .brand-row { position:absolute; bottom:44px; left:50%; transform:translateX(-50%); display:flex; align-items:center; gap:12px; z-index:2; }
-        .brand-text { font-size:14px; font-weight:600; color:#4ecdc4; letter-spacing:0.12em; }
-        .page { position:absolute; bottom:28px; right:40px; font-size:12px; color:#5a7a96; opacity:0.5; font-weight:500; z-index:2; }
-    </style>
-</head>
-<body>
-    <div class="top-bar"></div>
-    <!-- SVG 波浪背景 -->
-    <svg class="bg-wave" viewBox="0 0 1280 200" preserveAspectRatio="none">
-        <path d="M0,100 C200,40 400,160 640,100 C880,40 1080,160 1280,100 L1280,200 L0,200 Z" fill="#4ecdc4"/>
-        <path d="M0,130 C200,70 400,190 640,130 C880,70 1080,190 1280,130 L1280,200 L0,200 Z" fill="#00b4d8" opacity="0.5"/>
-    </svg>
-    <!-- SVG 分子结构装饰 -->
-    <svg class="bg-molecule" viewBox="0 0 200 200">
-        <circle cx="100" cy="60" r="12" fill="none" stroke="#4ecdc4" stroke-width="2"/>
-        <circle cx="50" cy="130" r="12" fill="none" stroke="#4ecdc4" stroke-width="2"/>
-        <circle cx="150" cy="130" r="12" fill="none" stroke="#4ecdc4" stroke-width="2"/>
-        <circle cx="100" cy="170" r="8" fill="none" stroke="#4ecdc4" stroke-width="1.5"/>
-        <line x1="100" y1="72" x2="56" y2="122" stroke="#4ecdc4" stroke-width="1.5"/>
-        <line x1="100" y1="72" x2="144" y2="122" stroke="#4ecdc4" stroke-width="1.5"/>
-        <line x1="56" y1="136" x2="94" y2="166" stroke="#4ecdc4" stroke-width="1.5"/>
-        <line x1="144" y1="136" x2="106" y2="166" stroke="#4ecdc4" stroke-width="1.5"/>
-    </svg>
-    <!-- SVG 六边形装饰 -->
-    <svg class="bg-hex" viewBox="0 0 160 160">
-        <polygon points="80,10 145,40 145,100 80,130 15,100 15,40" fill="none" stroke="#4ecdc4" stroke-width="2"/>
-        <polygon points="80,30 125,50 125,90 80,110 35,90 35,50" fill="none" stroke="#4ecdc4" stroke-width="1.5"/>
-        <polygon points="80,50 105,62 105,82 80,94 55,82 55,62" fill="none" stroke="#4ecdc4" stroke-width="1"/>
-    </svg>
-    <div class="label">
-        <svg viewBox="0 0 16 16" width="16" height="16" fill="none"><circle cx="8" cy="8" r="3" fill="#4ecdc4"><animate attributeName="r" values="3;5;3" dur="2s" repeatCount="indefinite"/><animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/></circle></svg>
-        JOIN US NOW
-    </div>
-    <div class="content">
-        <div class="title">立即加入浠艾福</div>
-        <div class="subtitle">精准营养,主动健康,让每一个家庭都拥有健康守护者</div>
-        <div class="cta-row">
-            <div class="qr-box">
-                <!-- 二维码占位 SVG -->
-                <svg width="140" height="140" viewBox="0 0 140 140">
-                    <rect x="10" y="10" width="40" height="40" rx="4" fill="#0a2540"/>
-                    <rect x="16" y="16" width="28" height="28" rx="2" fill="#fff"/>
-                    <rect x="22" y="22" width="16" height="16" rx="1" fill="#0a2540"/>
-                    <rect x="90" y="10" width="40" height="40" rx="4" fill="#0a2540"/>
-                    <rect x="96" y="16" width="28" height="28" rx="2" fill="#fff"/>
-                    <rect x="102" y="22" width="16" height="16" rx="1" fill="#0a2540"/>
-                    <rect x="10" y="90" width="40" height="40" rx="4" fill="#0a2540"/>
-                    <rect x="16" y="96" width="28" height="28" rx="2" fill="#fff"/>
-                    <rect x="22" y="102" width="16" height="16" rx="1" fill="#0a2540"/>
-                    <rect x="60" y="60" width="20" height="20" rx="2" fill="#4ecdc4"/>
-                    <rect x="60" y="10" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="72" y="10" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="60" y="22" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="10" y="60" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="22" y="60" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="10" y="72" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="90" y="60" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="102" y="60" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="114" y="60" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="90" y="72" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="60" y="90" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="72" y="90" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="60" y="102" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="90" y="100" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="102" y="112" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="114" y="100" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                    <rect x="120" y="112" width="8" height="8" fill="#0a2540" opacity="0.3"/>
-                </svg>
-            </div>
-            <div class="cta-text">
-                <div class="cta-main">今日下单 · <em>赠送 ¥1314 菌群检测</em></div>
-                <div class="cta-sub">成为种子用户,解锁分享提成与消费返佣权益</div>
-                <ul class="cta-features">
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        年度会员全年健康管理服务
-                    </li>
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        价值 ¥1314 肠道菌群基因检测
-                    </li>
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#dcfce7" stroke="#16a34a" stroke-width="1"/><path d="M5 8l2 2 4-4" stroke="#16a34a" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        种子用户专属收益权
-                    </li>
-                    <li>
-                        <svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="#fef2f2" stroke="#dc2626" stroke-width="1"/><path d="M8 4v5M8 11v1" stroke="#dc2626" stroke-width="1.5" fill="none" stroke-linecap="round"/></svg>
-                        仅限首批 <strong style="color:#dc2626">100</strong> 名
-                    </li>
-                </ul>
-            </div>
-        </div>
-        <div class="divider"></div>
-        <div class="quote">
-            "用最适合你自己的、且最简单的方法,<br/><strong>心情愉快、活得舒服、活得长久!</strong>"
-        </div>
-    </div>
-    <div class="brand-row">
-        <!-- 品牌 SVG 标志 -->
-        <svg width="28" height="28" viewBox="0 0 28 28" fill="none">
-            <circle cx="14" cy="14" r="12" stroke="#4ecdc4" stroke-width="2"/>
-            <path d="M8 14 C8 10 11 7 14 7 C17 7 20 10 20 14 C20 18 17 21 14 21" stroke="#4ecdc4" stroke-width="2" fill="none" stroke-linecap="round"/>
-            <circle cx="14" cy="14" r="3" fill="#4ecdc4"/>
-        </svg>
-        <div class="brand-text">浠艾福 · CARE FAMILY CLUB</div>
-    </div>
-    <div class="page">10 / 10</div>
-</body>
-</html>`
-];
+<button class="fullscreen-btn" onclick="toggleFullscreen()">⛶ 全屏</button>
+<div class="slide-counter"><span id="currentSlide">1</span> / 08</div>
+<div class="slide-container" id="slideContainer"></div>
+<div class="nav">
+<div class="nav-btn" onclick="prevSlide()">◀</div>
+<div class="nav-dots" id="navDots"></div>
+<div class="nav-btn" onclick="nextSlide()">▶</div>
+</div>
+<script>
+const slides=[
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(180deg,#0a0e27,#0d2a28);font-family:Inter,sans-serif;overflow:hidden;position:relative;display:flex;align-items:center;justify-content:center}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <defs><linearGradient id="wg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="rgba(20,184,166,.08)"/><stop offset="100%" stop-color="rgba(20,184,166,0)"/></linearGradient>
+  <linearGradient id="sg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="rgba(20,184,166,0)"/><stop offset="100%" stop-color="rgba(20,184,166,.12)"/></linearGradient>
+  <linearGradient id="ic" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#14b8a6"/><stop offset="100%" stop-color="#0d9488"/></linearGradient>
+  </defs>
+  <!-- Sky gradient -->
+  <rect x="0" y="0" width="1280" height="300" fill="url(#wg)"/>
+  <!-- Water surface -->
+  <rect x="0" y="300" width="1280" height="420" fill="url(#sg)"/>
+  <!-- Water line -->
+  <path d="M0,300 Q100,290 200,300 Q300,310 400,300 Q500,290 600,300 Q700,310 800,300 Q900,290 1000,300 Q1100,310 1200,300 L1280,300" fill="none" stroke="rgba(20,184,166,.15)" stroke-width="2"/>
+  <!-- Iceberg above water (症) -->
+  <path d="M540,300 L580,180 L640,160 L700,180 L740,300 Z" fill="rgba(20,184,166,.15)" stroke="rgba(20,184,166,.3)" stroke-width="1.5" class="a1"/>
+  <text x="640" y="240" text-anchor="middle" fill="rgba(255,255,255,.5)" font-size="11" font-weight="600" letter-spacing=".2em" class="a2">症</text>
+  <text x="640" y="258" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">你感受到的不舒服</text>
+  <!-- Iceberg below water (病) - much larger -->
+  <path d="M540,300 L500,420 L460,520 L480,600 L520,640 L640,660 L760,640 L800,600 L820,520 L780,420 L740,300 Z" fill="rgba(20,184,166,.08)" stroke="rgba(20,184,166,.15)" stroke-width="1" class="a3"/>
+  <text x="640" y="420" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="12" font-weight="700" letter-spacing=".2em" class="a3">病</text>
+  <text x="640" y="440" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">身体底层的病理改变</text>
+  <text x="640" y="500" text-anchor="middle" fill="rgba(255,255,255,.1)" font-size="8">高血压→动脉粥样硬化</text>
+  <text x="640" y="518" text-anchor="middle" fill="rgba(255,255,255,.1)" font-size="8">血糖高→胰岛素抵抗</text>
+  <text x="640" y="536" text-anchor="middle" fill="rgba(255,255,255,.1)" font-size="8">治疗指标≠治疗疾病</text>
+  <!-- Label -->
+  <text x="640" y="60" text-anchor="middle" fill="rgba(20,184,166,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">WHAT IS HEALTH</text>
+  <text x="640" y="100" text-anchor="middle" fill="#fff" font-size="48" font-weight="800" class="a2">健康,是<span fill="#14b8a6">完好</span></text>
+  <text x="640" y="135" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="16" font-weight="300" class="a3">WHO 1948:身体、心理、社交都处于完好状态</text>
+  <!-- Quote box -->
+  <rect x="340" y="680" rx="8" width="600" height="28" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1" class="a4"/>
+  <text x="640" y="698" text-anchor="middle" fill="rgba(255,255,255,.25)" font-size="10" class="a4">症是冰山一角,病是水下巨冰——治症不治病,指标正常了,人还在生病的路上</text>
+  <!-- Page -->
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">01/08</text>
+  <text x="640" y="700" text-anchor="middle" fill="rgba(255,255,255,.04)" font-size="10" letter-spacing=".15em">浠艾福 · CARE FAMILY</text>
+</svg></body></html>`,
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(135deg,#0a0e27,#1a0a20);font-family:Inter,sans-serif;overflow:hidden;position:relative}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <defs><linearGradient id="b1"><stop offset="0%" stop-color="rgba(20,184,166,.15)"/><stop offset="100%" stop-color="rgba(20,184,166,.05)"/></linearGradient>
+  <linearGradient id="b2"><stop offset="0%" stop-color="rgba(244,63,94,.15)"/><stop offset="100%" stop-color="rgba(244,63,94,.05)"/></linearGradient>
+  </defs>
+  <!-- Title -->
+  <text x="640" y="60" text-anchor="middle" fill="rgba(244,63,94,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">SYMPTOM VS DISEASE</text>
+  <text x="640" y="95" text-anchor="middle" fill="#fff" font-size="36" font-weight="800" class="a2">治症,还是治病?</text>
+  <text x="640" y="125" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="14" class="a3">对病溯源,才能找到因</text>
+  <!-- Flow diagram: 4 rounded rectangles connected by arrows -->
+  <!-- Box 1: 症 -->
+  <rect x="120" y="170" width="220" height="120" rx="12" fill="url(#b1)" stroke="rgba(20,184,166,.3)" stroke-width="2" class="a1"/>
+  <text x="230" y="210" text-anchor="middle" fill="#fff" font-size="28" font-weight="700">😫</text>
+  <text x="230" y="238" text-anchor="middle" fill="#fff" font-size="16" font-weight="700">症</text>
+  <text x="230" y="260" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="11">你感受到的不舒服</text>
+  <!-- Arrow 1 -->
+  <line x1="340" y1="230" x2="400" y2="230" stroke="rgba(255,255,255,.08)" stroke-width="2" marker-end="url(#arrow)" class="a2"/>
+  <polygon points="400,225 415,230 400,235" fill="rgba(255,255,255,.08)"/>
+  <!-- Box 2: 病 -->
+  <rect x="420" y="170" width="220" height="120" rx="12" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.08)" stroke-width="1" class="a2"/>
+  <text x="530" y="210" text-anchor="middle" fill="#fff" font-size="28">🔬</text>
+  <text x="530" y="238" text-anchor="middle" fill="#fff" font-size="16" font-weight="700">病</text>
+  <text x="530" y="260" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="11">身体底层的病理改变</text>
+  <!-- Arrow 2 -->
+  <line x1="640" y1="230" x2="700" y2="230" stroke="rgba(255,255,255,.08)" stroke-width="2" class="a3"/>
+  <polygon points="700,225 715,230 700,235" fill="rgba(255,255,255,.08)"/>
+  <!-- Box 3: 因 -->
+  <rect x="720" y="170" width="220" height="120" rx="12" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.08)" stroke-width="1" class="a3"/>
+  <text x="830" y="210" text-anchor="middle" fill="#fff" font-size="28">🔍</text>
+  <text x="830" y="238" text-anchor="middle" fill="#fff" font-size="16" font-weight="700">因</text>
+  <text x="830" y="260" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="11">驱动疾病的根本原因</text>
+  <!-- Arrow 3 -->
+  <line x1="940" y1="230" x2="1000" y2="230" stroke="rgba(20,184,166,.2)" stroke-width="2" class="a4"/>
+  <polygon points="1000,225 1015,230 1000,235" fill="rgba(20,184,166,.2)"/>
+  <!-- Box 4: 主动健康 -->
+  <rect x="1020" y="170" width="200" height="120" rx="12" fill="url(#b1)" stroke="rgba(20,184,166,.3)" stroke-width="2" class="a4"/>
+  <text x="1120" y="210" text-anchor="middle" fill="#14b8a6" font-size="28">✅</text>
+  <text x="1120" y="238" text-anchor="middle" fill="#14b8a6" font-size="16" font-weight="700">主动健康</text>
+  <text x="1120" y="260" text-anchor="middle" fill="rgba(20,184,166,.5)" font-size="11">从因入手逆转病程</text>
+  <!-- 3 example cards -->
+  <g class="a5">
+    <rect x="80" y="340" width="360" height="130" rx="10" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="100" y="370" fill="#f43f5e" font-size="13" font-weight="700">症:血压高</text>
+    <text x="100" y="390" fill="#14b8a6" font-size="13" font-weight="700">病:动脉粥样硬化</text>
+    <text x="100" y="415" fill="rgba(255,255,255,.3)" font-size="11">因:慢性炎症→血管内皮损伤→斑块形成→</text>
+    <text x="100" y="435" fill="rgba(255,255,255,.3)" font-size="11">管腔狭窄→血压升高</text>
+    <rect x="460" y="340" width="360" height="130" rx="10" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="480" y="370" fill="#f43f5e" font-size="13" font-weight="700">症:血糖高</text>
+    <text x="480" y="390" fill="#14b8a6" font-size="13" font-weight="700">病:胰岛素抵抗</text>
+    <text x="480" y="415" fill="rgba(255,255,255,.3)" font-size="11">因:慢性炎症→IRS-1丝氨酸磷酸化→</text>
+    <text x="480" y="435" fill="rgba(255,255,255,.3)" font-size="11">胰岛素信号受阻→血糖失控</text>
+    <rect x="840" y="340" width="360" height="130" rx="10" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="860" y="370" fill="#f43f5e" font-size="13" font-weight="700">症:尿酸高</text>
+    <text x="860" y="390" fill="#14b8a6" font-size="13" font-weight="700">病:嘌呤代谢紊乱</text>
+    <text x="860" y="415" fill="rgba(255,255,255,.3)" font-size="11">因:菌群失调→肠道尿酸排泄下降+</text>
+    <text x="860" y="435" fill="rgba(255,255,255,.3)" font-size="11">果糖过量→内源性尿酸生成增加</text>
+  </g>
+  <!-- Bottom bar -->
+  <rect x="80" y="505" width="1120" height="48" rx="8" fill="rgba(245,158,11,.04)" stroke="rgba(245,158,11,.1)" stroke-width="1" class="a5"/>
+  <text x="640" y="530" text-anchor="middle" fill="rgba(255,255,255,.35)" font-size="12">传统医疗处理的是症—降压药降的是数字,不是血管的炎症。主动健康追踪的是因—修复肠漏、降低炎症,</text>
+  <text x="640" y="545" text-anchor="middle" fill="rgba(255,255,255,.35)" font-size="12">血压和血糖自然回落,才是真正的治愈</text>
+  <!-- Page -->
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">02/08</text>
+</svg></body></html>`,
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(135deg,#0a0e27,#0d2a28);font-family:Inter,sans-serif;overflow:hidden;position:relative}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <!-- Title -->
+  <text x="640" y="60" text-anchor="middle" fill="rgba(20,184,166,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">ROOT OF ALL DISEASES</text>
+  <text x="640" y="95" text-anchor="middle" fill="#fff" font-size="36" font-weight="800" class="a2">万病之源,是慢性炎症</text>
+  <text x="640" y="120" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="12" class="a3">不同的器官、不同的表现,背后是同一个敌人</text>
+  <!-- Central target: chronic inflammation -->
+  <circle cx="640" cy="300" r="60" fill="rgba(244,63,94,.15)" stroke="#f43f5e" stroke-width="2" class="a1"/>
+  <circle cx="640" cy="300" r="40" fill="rgba(244,63,94,.1)" class="a2"/>
+  <circle cx="640" cy="300" r="20" fill="rgba(244,63,94,.2)" class="a3"/>
+  <text x="640" y="296" text-anchor="middle" fill="#f43f5e" font-size="10" font-weight="800">慢性</text>
+  <text x="640" y="310" text-anchor="middle" fill="#f43f5e" font-size="10" font-weight="800">炎症</text>
+  <!-- Surrounding disease nodes -->
+  <g class="a4">
+    <circle cx="350" cy="180" r="36" fill="rgba(20,184,166,.08)" stroke="rgba(20,184,166,.2)" stroke-width="1"/>
+    <text x="350" y="176" text-anchor="middle" fill="#14b8a6" font-size="20">❤️</text>
+    <text x="350" y="194" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="8">心脑血管</text>
+    <line x1="386" y1="195" x2="600" y2="280" stroke="rgba(244,63,94,.1)" stroke-width="1" stroke-dasharray="4,3"/>
+  </g>
+  <g class="a5">
+    <circle cx="930" cy="180" r="36" fill="rgba(245,158,11,.08)" stroke="rgba(245,158,11,.2)" stroke-width="1"/>
+    <text x="930" y="176" text-anchor="middle" fill="#f59e0b" font-size="20">🍬</text>
+    <text x="930" y="194" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="8">糖尿病</text>
+    <line x1="894" y1="195" x2="680" y2="280" stroke="rgba(244,63,94,.1)" stroke-width="1" stroke-dasharray="4,3"/>
+  </g>
+  <g class="a4">
+    <circle cx="350" cy="420" r="36" fill="rgba(139,92,246,.08)" stroke="rgba(139,92,246,.2)" stroke-width="1"/>
+    <text x="350" y="416" text-anchor="middle" fill="#8b5cf6" font-size="20">🧠</text>
+    <text x="350" y="434" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="8">阿尔茨海默</text>
+    <line x1="386" y1="405" x2="600" y2="320" stroke="rgba(244,63,94,.1)" stroke-width="1" stroke-dasharray="4,3"/>
+  </g>
+  <g class="a5">
+    <circle cx="930" cy="420" r="36" fill="rgba(236,72,153,.08)" stroke="rgba(236,72,153,.2)" stroke-width="1"/>
+    <text x="930" y="416" text-anchor="middle" fill="#ec4899" font-size="20">🦴</text>
+    <text x="930" y="434" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="8">骨质疏松</text>
+    <line x1="894" y1="405" x2="680" y2="320" stroke="rgba(244,63,94,.1)" stroke-width="1" stroke-dasharray="4,3"/>
+  </g>
+  <g class="a4">
+    <circle cx="180" cy="300" r="36" fill="rgba(234,179,8,.08)" stroke="rgba(234,179,8,.2)" stroke-width="1"/>
+    <text x="180" y="296" text-anchor="middle" fill="#eab308" font-size="20">🫀</text>
+    <text x="180" y="314" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="8">高血压</text>
+    <line x1="216" y1="300" x2="580" y2="300" stroke="rgba(244,63,94,.1)" stroke-width="1" stroke-dasharray="4,3"/>
+  </g>
+  <g class="a5">
+    <circle cx="1100" cy="300" r="36" fill="rgba(34,197,94,.08)" stroke="rgba(34,197,94,.2)" stroke-width="1"/>
+    <text x="1100" y="296" text-anchor="middle" fill="#22c55e" font-size="20">🦠</text>
+    <text x="1100" y="314" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="8">自身免疫病</text>
+    <line x1="1064" y1="300" x2="700" y2="300" stroke="rgba(244,63,94,.1)" stroke-width="1" stroke-dasharray="4,3"/>
+  </g>
+  <!-- Two-column explanation -->
+  <g class="a5">
+    <rect x="80" y="500" width="540" height="100" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="100" y="525" fill="#fff" font-size="14" font-weight="700">🧬 第一基因组:决定可能得什么病</text>
+    <text x="100" y="548" fill="rgba(255,255,255,.3)" font-size="11">基因决定了薄弱环节—APOE4与阿尔茨海默、</text>
+    <text x="100" y="565" fill="rgba(255,255,255,.3)" font-size="11">BRCA与乳腺癌。但基因不是命运—它只决定</text>
+    <text x="100" y="582" fill="rgba(255,255,255,.3)" font-size="11">子弹上了膛,扣不扣扳机取决于环境</text>
+    <rect x="660" y="500" width="540" height="100" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="680" y="525" fill="#fff" font-size="14" font-weight="700">🔥 扣动扳机的:慢性炎症的水平</text>
+    <text x="680" y="548" fill="rgba(255,255,255,.3)" font-size="11">炎症首先攻击最弱的细胞—血管内皮弱的→</text>
+    <text x="680" y="565" fill="rgba(255,255,255,.3)" font-size="11">动脉粥样硬化;胰岛β细胞弱的→糖尿病;</text>
+    <text x="680" y="582" fill="rgba(255,255,255,.3)" font-size="11">关节滑膜弱的→痛风/类风湿</text>
+  </g>
+  <!-- Page -->
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">03/08</text>
+</svg></body></html>`,
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(135deg,#0a0e27,#0d1a30);font-family:Inter,sans-serif;overflow:hidden;position:relative}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <defs><linearGradient id="cc1"><stop offset="0%" stop-color="rgba(20,184,166,.12)"/><stop offset="100%" stop-color="rgba(20,184,166,.04)"/></linearGradient>
+  <linearGradient id="cc2"><stop offset="0%" stop-color="rgba(244,63,94,.12)"/><stop offset="100%" stop-color="rgba(244,63,94,.04)"/></linearGradient>
+  </defs>
+  <text x="640" y="50" text-anchor="middle" fill="rgba(244,63,94,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">WHERE INFLAMMATION COMES FROM</text>
+  <text x="640" y="80" text-anchor="middle" fill="#fff" font-size="32" font-weight="800" class="a2">慢性炎症从哪儿来?</text>
+  <text x="640" y="105" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="12" class="a3">找到源头,才能从源头干预</text>
+  <!-- Chain: 7 nodes connected by arrows -->
+  <g class="a1">
+    <rect x="30" y="140" width="150" height="90" rx="8" fill="url(#cc1)" stroke="rgba(20,184,166,.2)" stroke-width="1"/>
+    <text x="105" y="170" text-anchor="middle" font-size="24">🍔</text>
+    <text x="105" y="190" text-anchor="middle" fill="#fff" font-size="12" font-weight="700">饮食不当</text>
+    <text x="105" y="208" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">高糖·低纤维·加工食品</text>
+  </g>
+  <g class="a2">
+    <line x1="180" y1="185" x2="210" y2="185" stroke="rgba(255,255,255,.06)" stroke-width="1.5"/>
+    <polygon points="210,181 220,185 210,189" fill="rgba(255,255,255,.06)"/>
+    <rect x="225" y="140" width="150" height="90" rx="8" fill="url(#cc1)" stroke="rgba(20,184,166,.2)" stroke-width="1"/>
+    <text x="300" y="170" text-anchor="middle" font-size="24">💊</text>
+    <text x="300" y="190" text-anchor="middle" fill="#fff" font-size="12" font-weight="700">抗生素/药物</text>
+    <text x="300" y="208" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">滥用·PPI·NSAIDs</text>
+  </g>
+  <g class="a3">
+    <line x1="375" y1="185" x2="405" y2="185" stroke="rgba(255,255,255,.06)" stroke-width="1.5"/>
+    <polygon points="405,181 415,185 405,189" fill="rgba(255,255,255,.06)"/>
+    <rect x="420" y="140" width="150" height="90" rx="8" fill="url(#cc1)" stroke="rgba(20,184,166,.2)" stroke-width="1"/>
+    <text x="495" y="170" text-anchor="middle" font-size="24">😰</text>
+    <text x="495" y="190" text-anchor="middle" fill="#fff" font-size="12" font-weight="700">压力/熬夜</text>
+    <text x="495" y="208" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">皮质醇↑·HPA轴紊乱</text>
+  </g>
+  <g class="a4">
+    <line x1="570" y1="185" x2="600" y2="185" stroke="rgba(244,63,94,.12)" stroke-width="1.5"/>
+    <polygon points="600,181 610,185 600,189" fill="rgba(244,63,94,.12)"/>
+    <rect x="615" y="140" width="150" height="90" rx="8" fill="url(#cc2)" stroke="rgba(244,63,94,.25)" stroke-width="1.5"/>
+    <text x="690" y="170" text-anchor="middle" font-size="24">🦠</text>
+    <text x="690" y="190" text-anchor="middle" fill="#f43f5e" font-size="12" font-weight="700">菌群失调</text>
+    <text x="690" y="208" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">有益菌↓·致病菌↑</text>
+  </g>
+  <g class="a5">
+    <line x1="765" y1="185" x2="795" y2="185" stroke="rgba(244,63,94,.12)" stroke-width="1.5"/>
+    <polygon points="795,181 805,185 795,189" fill="rgba(244,63,94,.12)"/>
+    <rect x="810" y="140" width="150" height="90" rx="8" fill="url(#cc2)" stroke="rgba(244,63,94,.25)" stroke-width="1.5"/>
+    <text x="885" y="170" text-anchor="middle" font-size="24">🧱</text>
+    <text x="885" y="190" text-anchor="middle" fill="#f43f5e" font-size="12" font-weight="700">肠漏</text>
+    <text x="885" y="208" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">紧密连接破坏</text>
+  </g>
+  <g class="a6">
+    <line x1="960" y1="185" x2="990" y2="185" stroke="rgba(244,63,94,.12)" stroke-width="1.5"/>
+    <polygon points="990,181 1000,185 990,189" fill="rgba(244,63,94,.12)"/>
+    <rect x="1005" y="140" width="150" height="90" rx="8" fill="url(#cc2)" stroke="rgba(244,63,94,.25)" stroke-width="1.5"/>
+    <text x="1080" y="170" text-anchor="middle" font-size="24">🔥</text>
+    <text x="1080" y="190" text-anchor="middle" fill="#f43f5e" font-size="12" font-weight="700">慢性炎症</text>
+    <text x="1080" y="208" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">LPS→TLR4→NF-κB</text>
+  </g>
+  <g class="a7">
+    <line x1="1155" y1="185" x2="1185" y2="185" stroke="rgba(244,63,94,.12)" stroke-width="1.5"/>
+    <polygon points="1185,181 1195,185 1185,189" fill="rgba(244,63,94,.12)"/>
+    <rect x="1200" y="140" width="70" height="90" rx="8" fill="url(#cc2)" stroke="rgba(244,63,94,.25)" stroke-width="1.5"/>
+    <text x="1235" y="170" text-anchor="middle" font-size="24">🏥</text>
+    <text x="1235" y="190" text-anchor="middle" fill="#f43f5e" font-size="11" font-weight="700">慢性病</text>
+    <text x="1235" y="208" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="8">心梗·脑梗</text>
+  </g>
+  <!-- 3 evidence cards -->
+  <g class="a5">
+    <rect x="80" y="280" width="360" height="130" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="100" y="305" fill="#fff" font-size="13" font-weight="700">📊 70%的免疫系统在肠道</text>
+    <text x="100" y="330" fill="rgba(255,255,255,.3)" font-size="11">肠道是人体最大的免疫器官。肠道菌群训练</text>
+    <text x="100" y="348" fill="rgba(255,255,255,.3)" font-size="11">免疫细胞区分朋友和敌人——菌群一乱,</text>
+    <text x="100" y="366" fill="rgba(255,255,255,.3)" font-size="11">免疫就乱,过敏和自身免疫病随之而来。</text>
+    <text x="100" y="390" fill="rgba(20,184,166,.3)" font-size="9">Gut 2014 · Nature 2011</text>
+    <rect x="460" y="280" width="360" height="130" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="480" y="305" fill="#fff" font-size="13" font-weight="700">🧬 150倍基因优势</text>
+    <text x="480" y="330" fill="rgba(255,255,255,.3)" font-size="11">肠道菌群330万独特基因 vs 人类2.3万个</text>
+    <text x="480" y="348" fill="rgba(255,255,255,.3)" font-size="11">基因。它们编码的酶决定了你的代谢能力、</text>
+    <text x="480" y="366" fill="rgba(255,255,255,.3)" font-size="11">炎症水平和营养状态。</text>
+    <text x="480" y="390" fill="rgba(20,184,166,.3)" font-size="9">Zhu et al., Protein & Cell 2010</text>
+    <rect x="840" y="280" width="360" height="130" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="860" y="305" fill="#fff" font-size="13" font-weight="700">🧠 90%的血清素在肠道产生</text>
+    <text x="860" y="330" fill="rgba(255,255,255,.3)" font-size="11">情绪、睡眠、食欲、注意力——都受肠道</text>
+    <text x="860" y="348" fill="rgba(255,255,255,.3)" font-size="11">菌群调控。特定菌群直接调节肠壁细胞的</text>
+    <text x="860" y="366" fill="rgba(255,255,255,.3)" font-size="11">血清素合成。</text>
+    <text x="860" y="390" fill="rgba(20,184,166,.3)" font-size="9">Yano et al., Cell 2015</text>
+  </g>
+  <!-- Bottom -->
+  <rect x="80" y="440" width="1120" height="40" rx="8" fill="rgba(20,184,166,.04)" stroke="rgba(20,184,166,.08)" stroke-width="1" class="a7"/>
+  <text x="640" y="465" text-anchor="middle" fill="rgba(255,255,255,.35)" font-size="12">这就是主动健康的<text fill="#14b8a6" font-weight="700">抓手</text>——肠道菌群是可测量、可干预、可逆转的。<text fill="#14b8a6" font-weight="700">一份菌群报告,就能看清你的体内炎症地图</text></text>
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">04/08</text>
+</svg></body></html>`,
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(135deg,#0a0e27,#0d2a28);font-family:Inter,sans-serif;overflow:hidden;position:relative}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <text x="640" y="45" text-anchor="middle" fill="rgba(20,184,166,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">GUT REPORT</text>
+  <text x="640" y="75" text-anchor="middle" fill="#fff" font-size="28" font-weight="800" class="a2">一份菌群报告,<text fill="#14b8a6">8大维度</text>看清你的第二基因组</text>
+  <text x="640" y="95" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="11" class="a3">16S rRNA基因测序 — 一次检测全面评估肠道生态</text>
+  <!-- 8 dimension nodes arranged in a circle -->
+  <!-- Center circle -->
+  <circle cx="640" cy="350" r="50" fill="rgba(20,184,166,.06)" stroke="rgba(20,184,166,.15)" stroke-width="1" class="a1"/>
+  <text x="640" y="345" text-anchor="middle" fill="#14b8a6" font-size="12" font-weight="800">菌群</text>
+  <text x="640" y="360" text-anchor="middle" fill="#14b8a6" font-size="12" font-weight="800">报告</text>
+  <!-- 8 nodes around the circle at 45-degree intervals -->
+  <g class="a2"><circle cx="640" cy="180" r="38" fill="rgba(20,184,166,.1)" stroke="rgba(20,184,166,.2)" stroke-width="1"/><text x="640" y="176" text-anchor="middle" font-size="20">🌿</text><text x="640" y="194" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">多样性</text><line x1="640" y1="218" x2="640" y2="298" stroke="rgba(20,184,166,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <g class="a3"><circle cx="870" cy="210" r="38" fill="rgba(245,158,11,.1)" stroke="rgba(245,158,11,.2)" stroke-width="1"/><text x="870" y="206" text-anchor="middle" font-size="20">⏳</text><text x="870" y="224" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">肠道年龄</text><line x1="850" y1="240" x2="760" y2="300" stroke="rgba(245,158,11,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <g class="a4"><circle cx="1020" cy="330" r="38" fill="rgba(139,92,246,.1)" stroke="rgba(139,92,246,.2)" stroke-width="1"/><text x="1020" y="326" text-anchor="middle" font-size="20">🧱</text><text x="1020" y="344" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">屏障功能</text><line x1="990" y1="350" x2="700" y2="350" stroke="rgba(139,92,246,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <g class="a5"><circle cx="870" cy="460" r="38" fill="rgba(244,63,94,.1)" stroke="rgba(244,63,94,.2)" stroke-width="1"/><text x="870" y="456" text-anchor="middle" font-size="20">🛡️</text><text x="870" y="474" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">菌群占比</text><line x1="760" y1="400" x2="700" y2="350" stroke="rgba(244,63,94,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <g class="a6"><circle cx="410" cy="460" r="38" fill="rgba(244,63,94,.1)" stroke="rgba(244,63,94,.2)" stroke-width="1"/><text x="410" y="456" text-anchor="middle" font-size="20">🔥</text><text x="410" y="474" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">SCFA</text><line x1="520" y1="400" x2="580" y2="350" stroke="rgba(244,63,94,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <g class="a7"><circle cx="260" cy="330" r="38" fill="rgba(245,158,11,.1)" stroke="rgba(245,158,11,.2)" stroke-width="1"/><text x="260" y="326" text-anchor="middle" font-size="20">⚠️</text><text x="260" y="344" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">风险预警</text><line x1="290" y1="350" x2="580" y2="350" stroke="rgba(245,158,11,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <g class="a8"><circle cx="410" cy="210" r="38" fill="rgba(20,184,166,.1)" stroke="rgba(20,184,166,.2)" stroke-width="1"/><text x="410" y="206" text-anchor="middle" font-size="20">🧪</text><text x="410" y="224" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">营养建议</text><line x1="430" y1="240" x2="520" y2="300" stroke="rgba(20,184,166,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <g class="a4"><circle cx="640" cy="520" r="38" fill="rgba(139,92,246,.1)" stroke="rgba(139,92,246,.2)" stroke-width="1"/><text x="640" y="516" text-anchor="middle" font-size="20">⚙️</text><text x="640" y="534" text-anchor="middle" fill="rgba(255,255,255,.4)" font-size="8">代谢通路</text><line x1="640" y1="480" x2="640" y2="402" stroke="rgba(139,92,246,.08)" stroke-width="1" stroke-dasharray="3,3"/></g>
+  <!-- Bottom stats -->
+  <g class="a9">
+    <rect x="80" y="590" width="260" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="210" y="612" text-anchor="middle" fill="#14b8a6" font-size="24" font-weight="800">300+</text>
+    <text x="210" y="630" text-anchor="middle" fill="rgba(255,255,255,.25)" font-size="10">菌群种类检测</text>
 
-let current = 0;
-const total = slides.length;
-const container = document.getElementById('slideContainer');
-const dotsEl = document.getElementById('navDots');
-const counterEl = document.getElementById('currentSlide');
+    <rect x="360" y="590" width="260" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="490" y="612" text-anchor="middle" fill="#14b8a6" font-size="24" font-weight="800">12</text>
+    <text x="490" y="630" text-anchor="middle" fill="rgba(255,255,255,.25)" font-size="10">慢病风险模型</text>
 
-slides.forEach((html, i) => {
-    const iframe = document.createElement('iframe');
-    iframe.className = 'slide' + (i === 0 ? ' active' : '');
-    iframe.id = 'slide-' + i;
-    iframe.srcdoc = html;
-    iframe.style.border = 'none';
-    iframe.setAttribute('scrolling', 'no');
-    iframe.setAttribute('frameborder', '0');
-    container.appendChild(iframe);
-    const dot = document.createElement('div');
-    dot.className = 'nav-dot' + (i === 0 ? ' active' : '');
-    dot.onclick = () => goToSlide(i);
-    dotsEl.appendChild(dot);
-});
+    <rect x="640" y="590" width="260" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="770" y="612" text-anchor="middle" fill="#14b8a6" font-size="24" font-weight="800">8</text>
+    <text x="770" y="630" text-anchor="middle" fill="rgba(255,255,255,.25)" font-size="10">核心代谢通路</text>
 
-function goToSlide(n) {
-    const prev = current;
-    current = Math.max(0, Math.min(total - 1, n));
-    if (current === prev) return;
-    document.getElementById('slide-' + prev).classList.remove('active');
-    document.getElementById('slide-' + current).classList.add('active');
-    dotsEl.children[prev].classList.remove('active');
-    dotsEl.children[current].classList.add('active');
-    counterEl.textContent = current + 1;
-}
-function nextSlide() { goToSlide(current + 1); }
-function prevSlide() { goToSlide(current - 1); }
+    <rect x="920" y="590" width="260" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="1050" y="612" text-anchor="middle" fill="#14b8a6" font-size="24" font-weight="800">1</text>
+    <text x="1050" y="630" text-anchor="middle" fill="rgba(255,255,255,.25)" font-size="10">肠道年龄评估</text>
+  </g>
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">05/08</text>
+</svg></body></html>`,
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(135deg,#0a0e27,#0d2a28);font-family:Inter,sans-serif;overflow:hidden;position:relative}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <text x="640" y="50" text-anchor="middle" fill="rgba(20,184,166,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">CARE FAMILY SYSTEM</text>
+  <text x="640" y="80" text-anchor="middle" fill="#fff" font-size="30" font-weight="800" class="a2">浠艾福的<text fill="#14b8a6">主动健康</text>体系</text>
+  <text x="640" y="105" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="12" class="a3">测·调·查·评·跟·升 — 六阶段闭环,让健康不再靠感觉</text>
+  <!-- 4 circular nodes in a row -->
+  <g class="a1">
+    <circle cx="200" cy="280" r="70" fill="rgba(20,184,166,.06)" stroke="rgba(20,184,166,.2)" stroke-width="2"/>
+    <text x="200" y="245" text-anchor="middle" fill="#14b8a6" font-size="28" font-weight="800">1</text>
+    <text x="200" y="270" text-anchor="middle" fill="#fff" font-size="14" font-weight="700">测</text>
+    <text x="200" y="290" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="10">菌群基因检测</text>
+    <text x="200" y="310" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">16S rRNA测序</text>
+  </g>
+  <g class="a2">
+    <circle cx="460" cy="280" r="70" fill="rgba(20,184,166,.06)" stroke="rgba(20,184,166,.2)" stroke-width="2"/>
+    <text x="460" y="245" text-anchor="middle" fill="#14b8a6" font-size="28" font-weight="800">2</text>
+    <text x="460" y="270" text-anchor="middle" fill="#fff" font-size="14" font-weight="700">调</text>
+    <text x="460" y="290" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="10">精准营养干预</text>
+    <text x="460" y="310" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">AI+专家定制方案</text>
+  </g>
+  <g class="a3">
+    <circle cx="720" cy="280" r="70" fill="rgba(20,184,166,.06)" stroke="rgba(20,184,166,.2)" stroke-width="2"/>
+    <text x="720" y="245" text-anchor="middle" fill="#14b8a6" font-size="28" font-weight="800">3</text>
+    <text x="720" y="270" text-anchor="middle" fill="#fff" font-size="14" font-weight="700">查</text>
+    <text x="720" y="290" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="10">复测追踪效果</text>
+    <text x="720" y="310" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">数据对比·趋势分析</text>
+  </g>
+  <g class="a4">
+    <circle cx="980" cy="280" r="70" fill="rgba(20,184,166,.06)" stroke="rgba(20,184,166,.2)" stroke-width="2"/>
+    <text x="980" y="245" text-anchor="middle" fill="#14b8a6" font-size="28" font-weight="800">4</text>
+    <text x="980" y="270" text-anchor="middle" fill="#fff" font-size="14" font-weight="700">评·跟·升</text>
+    <text x="980" y="290" text-anchor="middle" fill="rgba(255,255,255,.3)" font-size="10">多维评估·全年跟踪</text>
+    <text x="980" y="310" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">动态优化升级</text>
+  </g>
+  <!-- Connecting arrows -->
+  <line x1="270" y1="280" x2="385" y2="280" stroke="rgba(255,255,255,.06)" stroke-width="2" class="a2"/>
+  <polygon points="385,275 395,280 385,285" fill="rgba(255,255,255,.06)"/>
+  <line x1="530" y1="280" x2="645" y2="280" stroke="rgba(255,255,255,.06)" stroke-width="2" class="a3"/>
+  <polygon points="645,275 655,280 645,285" fill="rgba(255,255,255,.06)"/>
+  <line x1="790" y1="280" x2="905" y2="280" stroke="rgba(255,255,255,.06)" stroke-width="2" class="a4"/>
+  <polygon points="905,275 915,280 905,285" fill="rgba(255,255,255,.06)"/>
+  <!-- Descriptions below -->
+  <g class="a5">
+    <rect x="80" y="400" width="260" height="130" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="100" y="425" fill="rgba(255,255,255,.3)" font-size="11">通过16S rRNA基因测序,</text>
+    <text x="100" y="443" fill="rgba(255,255,255,.3)" font-size="11">一次检测300+菌群种类、</text>
+    <text x="100" y="461" fill="rgba(255,255,255,.3)" font-size="11">多样性指数、肠道年龄、</text>
+    <text x="100" y="479" fill="rgba(255,255,255,.3)" font-size="11">慢病风险预警。</text>
 
-function updateScale() {
-    const sx = window.innerWidth / 1280;
-    const sy = window.innerHeight / 720;
-    const s = Math.min(sx, sy) * 0.95;
-    document.documentElement.style.setProperty('--scale', s);
-}
-window.addEventListener('resize', updateScale);
-updateScale();
+    <rect x="360" y="400" width="260" height="130" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="380" y="425" fill="rgba(255,255,255,.3)" font-size="11">AI+专家双重制定个性化</text>
+    <text x="380" y="443" fill="rgba(255,255,255,.3)" font-size="11">方案。不是人人都吃益生</text>
+    <text x="380" y="461" fill="rgba(255,255,255,.3)" font-size="11">菌,而是你的菌群缺什么</text>
+    <text x="380" y="479" fill="rgba(255,255,255,.3)" font-size="11">就补什么。</text>
 
-document.addEventListener('keydown', e => {
-    if (e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); nextSlide(); }
-    else if (e.key === 'ArrowLeft') { e.preventDefault(); prevSlide(); }
-    else if (e.key === 'PageDown') { e.preventDefault(); goToSlide(current + 3); }
-    else if (e.key === 'PageUp') { e.preventDefault(); goToSlide(current - 3); }
-    else if (e.key === 'Home') { e.preventDefault(); goToSlide(0); }
-    else if (e.key === 'End') { e.preventDefault(); goToSlide(total - 1); }
-});
+    <rect x="640" y="400" width="260" height="130" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="660" y="425" fill="rgba(255,255,255,.3)" font-size="11">定期复测,数据对比。</text>
+    <text x="660" y="443" fill="rgba(255,255,255,.3)" font-size="11">菌群多样性有没有改善?</text>
+    <text x="660" y="461" fill="rgba(255,255,255,.3)" font-size="11">炎症指标有没有下降?</text>
+    <text x="660" y="479" fill="rgba(255,255,255,.3)" font-size="11">每个月的进步清清楚楚。</text>
 
-function toggleFullscreen() {
-    if (!document.fullscreenElement) document.documentElement.requestFullscreen();
-    else document.exitFullscreen();
-}
+    <rect x="920" y="400" width="260" height="130" rx="8" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.05)" stroke-width="1"/>
+    <text x="940" y="425" fill="rgba(255,255,255,.3)" font-size="11">多维健康评估→全年管理</text>
+    <text x="940" y="443" fill="rgba(255,255,255,.3)" font-size="11">师跟踪→动态优化升级。</text>
+    <text x="940" y="461" fill="rgba(255,255,255,.3)" font-size="11">从一次性干预到持续优化</text>
+    <text x="940" y="479" fill="rgba(255,255,255,.3)" font-size="11">的终身健康管理。</text>
+  </g>
+  <!-- Bottom bar -->
+  <rect x="80" y="565" width="1120" height="40" rx="8" fill="rgba(20,184,166,.04)" stroke="rgba(20,184,166,.08)" stroke-width="1" class="a6"/>
+  <text x="640" y="590" text-anchor="middle" fill="rgba(255,255,255,.35)" font-size="11">基于<text fill="#14b8a6" font-weight="700">CANTOS研究</text>(NEJM 2017 · 10,061名患者):单纯抗炎可将心血管事件风险降低15%。肠道菌群调控,就是控制炎症最上游的杠杆。</text>
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">06/08</text>
+</svg></body></html>`,
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(135deg,#0a0e27,#0f0a30);font-family:Inter,sans-serif;overflow:hidden;position:relative}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <text x="640" y="45" text-anchor="middle" fill="rgba(139,92,246,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">EVOLUTION OF HEALTH</text>
+  <text x="640" y="75" text-anchor="middle" fill="#fff" font-size="28" font-weight="800" class="a2">从WHO三维,到浠艾福的<text fill="#8b5cf6">五维六阶段</text></text>
+  <!-- Evolution bars -->
+  <g class="a3">
+    <rect x="100" y="110" width="300" height="50" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <text x="250" y="130" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">WHO 1948</text>
+    <text x="250" y="148" text-anchor="middle" fill="#fff" font-size="16" font-weight="700">三维</text>
+    <text x="250" y="165" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">身体·心理·社交</text>
+    <text x="420" y="135" text-anchor="middle" fill="rgba(255,255,255,.06)" font-size="20">→</text>
+    <rect x="460" y="110" width="300" height="50" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <text x="610" y="130" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">中国"健康中国"</text>
+    <text x="610" y="148" text-anchor="middle" fill="#fff" font-size="16" font-weight="700">四维</text>
+    <text x="610" y="165" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="9">生理·心理·道德·社会适应</text>
+    <text x="780" y="135" text-anchor="middle" fill="rgba(139,92,246,.15)" font-size="20">→</text>
+    <rect x="820" y="110" width="340" height="50" rx="8" fill="rgba(139,92,246,.06)" stroke="rgba(139,92,246,.15)" stroke-width="1.5"/>
+    <text x="990" y="130" text-anchor="middle" fill="rgba(139,92,246,.15)" font-size="9">浠艾福</text>
+    <text x="990" y="148" text-anchor="middle" fill="#8b5cf6" font-size="16" font-weight="700">五维六阶段</text>
+  </g>
+  <!-- 5 dimension pentagon -->
+  <g class="a4">
+    <polygon points="640,190 520,250 560,380 720,380 760,250" fill="rgba(139,92,246,.03)" stroke="rgba(139,92,246,.08)" stroke-width="1"/>
+    <circle cx="640" cy="190" r="28" fill="rgba(20,184,166,.1)" stroke="rgba(20,184,166,.2)" stroke-width="1"/>
+    <text x="640" y="188" text-anchor="middle" font-size="16">🌏</text><text x="640" y="204" text-anchor="middle" fill="#14b8a6" font-size="10" font-weight="700">身</text>
+    <text x="640" y="170" text-anchor="middle" fill="rgba(255,255,255,.12)" font-size="7">Physical</text>
+    <circle cx="520" cy="250" r="28" fill="rgba(244,63,94,.1)" stroke="rgba(244,63,94,.2)" stroke-width="1"/>
+    <text x="520" y="248" text-anchor="middle" font-size="16">🔥</text><text x="520" y="264" text-anchor="middle" fill="#f43f5e" font-size="10" font-weight="700">心</text>
+    <text x="520" y="230" text-anchor="middle" fill="rgba(255,255,255,.12)" font-size="7">Mental</text>
+    <circle cx="560" cy="380" r="28" fill="rgba(245,158,11,.1)" stroke="rgba(245,158,11,.2)" stroke-width="1"/>
+    <text x="560" y="378" text-anchor="middle" font-size="16">⚔️</text><text x="560" y="394" text-anchor="middle" fill="#f59e0b" font-size="10" font-weight="700">智</text>
+    <text x="560" y="360" text-anchor="middle" fill="rgba(255,255,255,.12)" font-size="7">Intellectual</text>
+    <circle cx="720" cy="380" r="28" fill="rgba(139,92,246,.1)" stroke="rgba(139,92,246,.2)" stroke-width="1"/>
+    <text x="720" y="378" text-anchor="middle" font-size="16">🌿</text><text x="720" y="394" text-anchor="middle" fill="#8b5cf6" font-size="10" font-weight="700">行</text>
+    <text x="720" y="360" text-anchor="middle" fill="rgba(255,255,255,.12)" font-size="7">Behavioral</text>
+    <circle cx="760" cy="250" r="28" fill="rgba(236,72,153,.1)" stroke="rgba(236,72,153,.2)" stroke-width="1"/>
+    <text x="760" y="248" text-anchor="middle" font-size="16">💧</text><text x="760" y="264" text-anchor="middle" fill="#ec4899" font-size="10" font-weight="700">富</text>
+    <text x="760" y="230" text-anchor="middle" fill="rgba(255,255,255,.12)" font-size="7">Wealth</text>
+  </g>
+  <!-- 6 stages -->
+  <g class="a5">
+    <rect x="80" y="450" width="180" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.04)" stroke-width="1"/>
+    <text x="170" y="470" text-anchor="middle" fill="rgba(20,184,166,.35)" font-size="9">阶段一</text>
+    <text x="170" y="488" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">测 · 16S rRNA菌群检测</text>
+    <rect x="275" y="450" width="180" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.04)" stroke-width="1"/>
+    <text x="365" y="470" text-anchor="middle" fill="rgba(20,184,166,.35)" font-size="9">阶段二</text>
+    <text x="365" y="488" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">调 · 精准营养干预</text>
+    <rect x="470" y="450" width="180" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.04)" stroke-width="1"/>
+    <text x="560" y="470" text-anchor="middle" fill="rgba(20,184,166,.35)" font-size="9">阶段三</text>
+    <text x="560" y="488" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">查 · 复测追踪效果</text>
+    <rect x="665" y="450" width="180" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.04)" stroke-width="1"/>
+    <text x="755" y="470" text-anchor="middle" fill="rgba(20,184,166,.35)" font-size="9">阶段四</text>
+    <text x="755" y="488" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">评 · 多维健康评估</text>
+    <rect x="860" y="450" width="180" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.04)" stroke-width="1"/>
+    <text x="950" y="470" text-anchor="middle" fill="rgba(20,184,166,.35)" font-size="9">阶段五</text>
+    <text x="950" y="488" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">跟 · 全年管理师跟踪</text>
+    <rect x="1055" y="450" width="180" height="50" rx="6" fill="rgba(255,255,255,.02)" stroke="rgba(255,255,255,.04)" stroke-width="1"/>
+    <text x="1145" y="470" text-anchor="middle" fill="rgba(20,184,166,.35)" font-size="9">阶段六</text>
+    <text x="1145" y="488" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">升 · 动态优化升级</text>
+  </g>
+  <!-- Dimension descriptions -->
+  <g class="a6">
+    <rect x="80" y="530" width="224" height="100" rx="6" fill="rgba(20,184,166,.03)" stroke="rgba(20,184,166,.08)" stroke-width="1"/>
+    <text x="192" y="555" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">身体机能·营养均衡</text>
+    <text x="192" y="572" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">免疫力·肠道健康</text>
+    <rect x="320" y="530" width="224" height="100" rx="6" fill="rgba(244,63,94,.03)" stroke="rgba(244,63,94,.08)" stroke-width="1"/>
+    <text x="432" y="555" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">情绪管理·压力调节</text>
+    <text x="432" y="572" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">睡眠质量·心理韧性</text>
+    <rect x="560" y="530" width="224" height="100" rx="6" fill="rgba(245,158,11,.03)" stroke="rgba(245,158,11,.08)" stroke-width="1"/>
+    <text x="672" y="555" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">认知功能·专注力</text>
+    <text x="672" y="572" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">记忆力·脑健康</text>
+    <rect x="800" y="530" width="224" height="100" rx="6" fill="rgba(139,92,246,.03)" stroke="rgba(139,92,246,.08)" stroke-width="1"/>
+    <text x="912" y="555" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">运动习惯·饮食规律</text>
+    <text x="912" y="572" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">作息管理·关系和谐</text>
+    <rect x="1040" y="530" width="224" height="100" rx="6" fill="rgba(236,72,153,.03)" stroke="rgba(236,72,153,.08)" stroke-width="1"/>
+    <text x="1152" y="555" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">健康财富·生命质量</text>
+    <text x="1152" y="572" text-anchor="middle" fill="rgba(255,255,255,.2)" font-size="9">家庭和谐·时间自由</text>
+  </g>
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">07/08</text>
+</svg></body></html>`,
+`<html><head><style>@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');*{margin:0;padding:0;box-sizing:border-box}body{width:1280px;height:720px;background:linear-gradient(135deg,#0a0e27,#0d2a28);font-family:Inter,sans-serif;overflow:hidden;position:relative}
+@keyframes fIn{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}
+@keyframes fScale{0%{opacity:0;transform:scale(.8)}100%{opacity:1;transform:scale(1)}}
+@keyframes draw{0%{stroke-dashoffset:1000}100%{stroke-dashoffset:0}}
+@keyframes pulse{0%,100%{opacity:.3}50%{opacity:.8}}
+@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
+@keyframes glow{0%,100%{opacity:.4;filter:blur(8px)}50%{opacity:.8;filter:blur(4px)}}
+.a1{animation:fIn .6s ease both}.a2{animation:fIn .6s ease .2s both}.a3{animation:fIn .6s ease .4s both}
+.a4{animation:fIn .6s ease .6s both}.a5{animation:fIn .6s ease .8s both}
+</style></head><body>
+<svg style="position:absolute;top:0;left:0;width:100%;height:100%" viewBox="0 0 1280 720">
+  <text x="640" y="45" text-anchor="middle" fill="rgba(20,184,166,.4)" font-size="10" font-weight="600" letter-spacing=".3em" class="a1">MEMBERSHIP</text>
+  <text x="640" y="75" text-anchor="middle" fill="#fff" font-size="28" font-weight="800" class="a2">加入浠艾福,<text fill="#14b8a6">开始主动健康的践行</text></text>
+  <!-- 6 benefit cards in 2x3 grid -->
+  <g class="a1">
+    <rect x="80" y="110" width="340" height="70" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <line x1="120" y1="145" x2="120" y2="145" stroke="#14b8a6" stroke-width="2"/>
+    <text x="100" y="140" fill="rgba(255,255,255,.3)" font-size="11">肠道菌群基因检测</text>
+    <text x="100" y="160" fill="rgba(255,255,255,.15)" font-size="10">价值¥1314 · 入会即送</text>
+  </g>
+  <g class="a2">
+    <rect x="440" y="110" width="340" height="70" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <text x="460" y="140" fill="rgba(255,255,255,.3)" font-size="11">个性化精准营养方案</text>
+    <text x="460" y="160" fill="rgba(255,255,255,.15)" font-size="10">AI+专家双重定制</text>
+  </g>
+  <g class="a3">
+    <rect x="800" y="110" width="340" height="70" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <text x="820" y="140" fill="rgba(255,255,255,.3)" font-size="11">1对1健康管理师</text>
+    <text x="820" y="160" fill="rgba(255,255,255,.15)" font-size="10">全年跟踪 · 随时咨询</text>
+  </g>
+  <g class="a4">
+    <rect x="80" y="200" width="340" height="70" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <text x="100" y="230" fill="rgba(255,255,255,.3)" font-size="11">健康数据档案</text>
+    <text x="100" y="250" fill="rgba(255,255,255,.15)" font-size="10">完整记录 · 趋势分析</text>
+  </g>
+  <g class="a5">
+    <rect x="440" y="200" width="340" height="70" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <text x="460" y="230" fill="rgba(255,255,255,.3)" font-size="11">健康教育课程</text>
+    <text x="460" y="250" fill="rgba(255,255,255,.15)" font-size="10">肠道科学·营养知识</text>
+  </g>
+  <g class="a6">
+    <rect x="800" y="200" width="340" height="70" rx="8" fill="rgba(255,255,255,.03)" stroke="rgba(255,255,255,.06)" stroke-width="1"/>
+    <text x="820" y="230" fill="rgba(255,255,255,.3)" font-size="11">会员专属折扣</text>
+    <text x="820" y="250" fill="rgba(255,255,255,.15)" font-size="10">营养品会员专享价</text>
+  </g>
+  <!-- Right side: Seed member card + offer -->
+  <g class="a3">
+    <rect x="80" y="310" width="540" height="160" rx="10" fill="rgba(244,63,94,.04)" stroke="rgba(244,63,94,.12)" stroke-width="1"/>
+    <rect x="100" y="325" width="100" height="20" rx="8" fill="rgba(244,63,94,.1)"/>
+    <text x="150" y="339" text-anchor="middle" fill="#f43f5e" font-size="9" font-weight="700">🌱 种子用户</text>
+    <text x="100" y="365" fill="#fff" font-size="14" font-weight="700">首批加入,解锁长期收益</text>
+    <text x="100" y="390" fill="rgba(255,255,255,.25)" font-size="11">✓ 分享提成:推荐好友加入,每推荐1人即获提成</text>
+    <text x="100" y="410" fill="rgba(255,255,255,.25)" font-size="11">✓ 消费返佣:被推荐人消费,你持续获得返佣</text>
+    <text x="100" y="430" fill="rgba(255,255,255,.25)" font-size="11">✓ 优先体验:新品上线、新服务,优先试用</text>
+    <text x="100" y="450" fill="rgba(255,255,255,.25)" font-size="11">✓ 核心社群:对接创始团队,参与产品共建</text>
+  </g>
+  <g class="a4">
+    <rect x="660" y="310" width="540" height="160" rx="10" fill="rgba(244,63,94,.04)" stroke="rgba(244,63,94,.12)" stroke-width="1"/>
+    <text x="930" y="340" text-anchor="middle" fill="rgba(244,63,94,.4)" font-size="10">🎁 今日下单 · 赠送菌群检测</text>
+    <text x="930" y="380" text-anchor="middle" fill="#f43f5e" font-size="32" font-weight="800">¥1314</text>
+    <text x="930" y="405" text-anchor="middle" fill="rgba(255,255,255,.15)" font-size="10">300+菌群种类检测 · 12类慢病风险预警</text>
+    <rect x="850" y="420" width="160" height="22" rx="6" fill="rgba(244,63,94,.1)"/>
+    <text x="930" y="435" text-anchor="middle" fill="rgba(244,63,94,.5)" font-size="9">仅限首批100名</text>
+    <text x="930" y="460" text-anchor="middle" fill="rgba(255,255,255,.12)" font-size="10" font-style="italic">用最适合你自己的、且最简单的方法</text>
+  </g>
+  <text x="1240" y="700" text-anchor="end" fill="rgba(255,255,255,.08)" font-size="10">08/08</text>
+</svg></body></html>`
+];
+let c=0;const t=slides.length,con=document.getElementById("slideContainer"),dots=document.getElementById("navDots"),cnt=document.getElementById("currentSlide");
+slides.forEach((h,i)=>{const f=document.createElement("iframe");f.className="slide"+(i===0?" active":"");f.id="slide-"+i;f.srcdoc=h;f.style.border="none";f.setAttribute("scrolling","no");f.setAttribute("frameborder","0");con.appendChild(f);const d=document.createElement("div");d.className="nav-dot"+(i===0?" active":"");d.onclick=()=>go(i);dots.appendChild(d)});
+function go(n){const p=c;c=Math.max(0,Math.min(t-1,n));if(c===p)return;document.getElementById("slide-"+p).classList.remove("active");document.getElementById("slide-"+c).classList.add("active");dots.children[p].classList.remove("active");dots.children[c].classList.add("active");cnt.textContent=c+1}
+function nextSlide(){go(c+1)}
+function prevSlide(){go(c-1)}
+function updateScale(){const sx=window.innerWidth/1280,sy=window.innerHeight/720,s=Math.min(sx,sy)*.95;document.documentElement.style.setProperty("--scale",s)}
+window.addEventListener("resize",updateScale);updateScale();
+document.addEventListener("keydown",e=>{if(e.key==="ArrowRight"||e.key===" "){e.preventDefault();nextSlide()}else if(e.key==="ArrowLeft"){e.preventDefault();prevSlide()}});
+function toggleFullscreen(){if(!document.fullscreenElement)document.documentElement.requestFullscreen();else document.exitFullscreen()}
 </script>
 </body>
-</html>
+</html>