2026-07-28-report-management-design.md 17 KB

统一报告管理中心 — 设计规格

日期: 2026-07-28 状态: 待审核

1. 背景与目标

现状

当前系统有4类报告分散在不同模块:

报告类型 源表 当前前端入口 文件支持
菌群检测 / 体检 health_reports pages/health/report-list.vue ✅ PDF/图片
DAN测评 dan_report_uploads pages/dan-assessment/report-upload.vue ⚠️ 部分有PDF
保单 insurance_policies pages/wealth-sub/insurance-list.vue ❌ 纯结构化数据
家庭周报 family_fortune_report 无统一入口 ✅ PDF

用户需在多个入口之间切换查看不同类型报告,且无法统一搜索、下载、分享。

目标

  • 统一报告管理页面,一个入口查看全部报告
  • 支持按类型筛选、关键词搜索
  • 报告下载(PDF/图片)和转发分享
  • 小程序端 + Web管理端双端覆盖

2. 数据层:统一报告汇总表

2.1 表结构 report_summary

CREATE TABLE IF NOT EXISTS report_summary (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',

    -- 分类标识
    report_type     VARCHAR(32)  NOT NULL COMMENT '报告类型: gut_flora/physical_exam/dan/insurance/fortune',
    source_table    VARCHAR(32)  NOT NULL COMMENT '来源表: health_reports/dan_report_uploads/insurance_policies/family_fortune_report',
    source_id       BIGINT       NOT NULL COMMENT '来源表记录ID',

    -- 归属信息
    user_id         BIGINT       NOT NULL COMMENT '创建者/上传者用户ID',
    family_id       BIGINT       COMMENT '家庭ID',
    subject_id      BIGINT       COMMENT '报告主体ID(users/family_members)',
    subject_name    VARCHAR(50)  COMMENT '报告主体名称',

    -- 展示信息
    title           VARCHAR(200) COMMENT '报告标题',
    summary         TEXT         COMMENT '摘要(列表页展示)',

    -- 文件信息
    file_url        VARCHAR(500) COMMENT '报告文件URL(PDF/图片)',
    file_size       BIGINT       COMMENT '文件大小(字节)',
    file_name       VARCHAR(200) COMMENT '原始文件名',
    has_file        TINYINT      DEFAULT 0 COMMENT '是否有可下载文件: 1=有, 0=无',

    -- 评分/日期
    overall_score   INT          COMMENT '综合评分(0-100,如适用)',
    report_date     DATE         COMMENT '报告日期(体检日期/测评日期等)',

    -- 状态
    status          VARCHAR(20)  COMMENT '状态: active/confirmed/completed/expired/archived/discarded',

    -- 前端路由
    detail_route    VARCHAR(100) COMMENT '详情页路由(如 /pages/health/physical-exam-detail)',

    -- 时间
    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='统一报告汇总表';

2.2 各源表字段映射规则

菌群 / 体检 (health_reports)

report_summary 字段 映射来源 说明
report_type report_type (gut_flora/physical_exam) 直接映射
source_table 常量 health_reports
source_id id
user_id user_id 上传者
family_id family_id
subject_id subject_id 报告主体(可为null)
subject_name users 表关联查询 subject_id
title 拼接: {报告主体姓名} 的 {report_type} 报告 体检报告/菌群检测报告
file_url file_url
overall_score overall_score
report_date report_date
status status active/archived
detail_route gut_flora → /pages/health/gut-flora-detail, physical_exam → /pages/health/physical-exam-detail 路由映射
has_file file_url IS NOT NULL → 1, else 0

DAN测评 (dan_report_uploads)

report_summary 字段 映射来源 说明
report_type 常量 dan
source_table 常量 dan_report_uploads
source_id id
user_id uploader_user_id
family_id family_id
subject_id family_member_id 测评对象
subject_name family_members 表关联查询 family_member_id
title 拼接: {subject_name} 的 {dimension} 测评 dimension=mind/wisdom
file_url file_url
overall_score dan_assessment_results 关联查询 overall_score 通过 linked_result_id
report_date uploaded_at (DATE部分) 上传日期
status draft_status → 映射为 active/confirmed/discarded
detail_route /pages/dan-assessment/report-upload (带id参数)
has_file file_url IS NOT NULL → 1, else 0

保单 (insurance_policies)

report_summary 字段 映射来源 说明
report_type 常量 insurance
source_table 常量 insurance_policies
source_id id
user_id user_id
subject_name insured_person 被保人
title 拼接: {policy_name}
summary 拼接: {insurance_company} · {policy_type} · 保额 ¥{sum_insured}
file_url NULL 保单无PDF
has_file 0 纯结构化数据
report_date start_date 保单生效日期
status status active/expired/cancelled
detail_route /pages/wealth-sub/insurance-list 跳转到保单列表,标记选中
overall_score NULL 不适用

家庭周报 (family_fortune_report)

report_summary 字段 映射来源 说明
report_type 常量 fortune
source_table 常量 family_fortune_report
source_id id
user_id family_members 关联查询(family主用户)
family_id family_id
title 拼接: {element} 天盘周报
summary 拼接: {luckyDirection} · {createdAt}
file_url pdfPath
file_size fileSize
has_file pdfPath IS NOT NULL → 1, else 0
report_date createdAt (DATE部分)
status active
detail_route /pages/tianpan/index 跳转到天盘
overall_score NULL 不适用

3. 数据同步策略:Service层同步 + 定时任务兜底

3.1 Service层即时同步

新建 ReportSummaryService,提供 sync() 方法。在各源表的CRUD操作末尾调用。

// 各Service中的伪代码
// HealthReportService.save(report) 末尾:
reportSummaryService.syncFromHealthReport(report);

// DanReportUploadService.confirm(upload) 末尾:
reportSummaryService.syncFromDanReport(upload);

// InsurancePolicyService.create(policy) 末尾:
reportSummaryService.syncFromInsurancePolicy(policy);

// FamilyFortuneReport相关Service:
reportSummaryService.syncFromFortuneReport(report);

关键设计

  • sync() 使用 INSERT ... ON DUPLICATE KEY UPDATE 保证幂等
  • 通过 UNIQUE KEY uk_source (source_table, source_id) 去重
  • 源表删除时:更新 report_summary.status = 'discarded',不物理删除

3.2 定时任务全量校准

新建 ReportSummarySyncTask,每天凌晨 2:00 执行:

POST /api/admin/report-summary/sync  (管理员手动触发)
定时: cron 0 0 2 * * ?               (每日凌晨2点自动)

执行逻辑

  1. 扫描 health_reports → 更新/插入 report_summary(已删除的标记为 discarded)
  2. 扫描 dan_report_uploads → 同上
  3. 扫描 insurance_policies → 同上
  4. 扫描 family_fortune_report → 同上
  5. 统计并记录:新增 N 条、更新 N 条、标记废弃 N 条
  6. 日志输出同步结果

异常处理

  • 源表查询失败 → 记录错误日志,继续处理其他类型
  • 汇总写入失败 → 重试3次,仍失败则告警

4. 后端API设计

4.1 统一报告列表

POST /api/report-summary/list
请求体:
{
  "page": 1,
  "size": 20,
  "type": "all",          // gut_flora|physical_exam|dan|insurance|fortune|all
  "keyword": "",          // 搜索标题/主体名称
  "subjectId": null,      // 按报告主体筛选
  "startDate": null,      // 报告日期范围起
  "endDate": null
}
响应:
{
  "code": 200,
  "data": {
    "total": 156,
    "list": [
      {
        "id": 1,
        "reportType": "gut_flora",
        "reportTypeName": "菌群检测",
        "title": "小李的菌群检测报告",
        "subjectName": "小李",
        "reportDate": "2025-03-15",
        "overallScore": 78,
        "hasFile": true,
        "status": "active",
        "createdAt": "2025-03-16 10:30:00"
      }
    ]
  }
}

4.2 报告详情

POST /api/report-summary/detail/{summaryId}
响应:
{
  "code": 200,
  "data": {
    "id": 1,
    "reportType": "gut_flora",
    "reportTypeName": "菌群检测",
    "title": "小李的菌群检测报告",
    "subjectName": "小李",
    "summary": "肠道菌群健康评分78分...",
    "fileUrl": "https://cdn.xxx.com/reports/abc123.pdf",
    "fileName": "gut_flora_report_20250315.pdf",
    "fileSize": 2048576,
    "hasFile": true,
    "overallScore": 78,
    "reportDate": "2025-03-15",
    "status": "active",
    "detailRoute": "/pages/health/gut-flora-detail",
    "detailParams": { "reportId": 45 }  // 跳转到详情页的额外参数
  }
}

4.3 报告下载

POST /api/report-summary/download/{summaryId}
响应:
{
  "code": 200,
  "data": {
    "fileUrl": "https://cdn.xxx.com/reports/abc123.pdf",
    "fileName": "gut_flora_report_20250315.pdf",
    "fileSize": 2048576
  }
}

4.4 管理员手动同步

POST /api/admin/report-summary/sync
响应:
{
  "code": 200,
  "message": "同步完成",
  "data": {
    "newCount": 5,
    "updateCount": 12,
    "discardedCount": 2,
    "errorCount": 0
  }
}

5. 前端设计

5.1 小程序端

入口

pages/profile/components/ProfileMenu.vue 的「个人」分组末尾添加:

<view class="menu-item" @click="goToReportManagement">
  <text>📋 报告管理</text>
  <text class="arrow">›</text>
</view>

对应方法:

goToReportManagement() {
  uni.navigateTo({ url: '/pages/profile/report-management' })
}

页面:pages/profile/report-management.vue

页面结构

┌─────────────────────────────┐
│ ← 报告管理              📥  │  ← 搜索按钮
├─────────────────────────────┤
│ [全部] [菌群] [体检] [DAN] [保单] [周报]  │  ← 类型Tab
├─────────────────────────────┤
│ 🔍 搜索报告标题或成员名称...     │  ← 搜索栏
├─────────────────────────────┤
│ ┌─────────────────────────┐ │
│ │ 菌群检测       2025-03  │ │
│ │ 小李的菌群检测报告        │ │
│ │ 综合评分: 78分          │ │  → 点击查看详情
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ DAN测评      2025-02  │ │
│ │ 小王的心智测评            │ │
│ │ 综合评分: 82分          │ │
│ └─────────────────────────┘ │
│ ┌─────────────────────────┐ │
│ │ 保单         2024-01  │ │
│ │ 重疾险 - 中国平安        │ │  → 点击查看详情
│ │ 保额: ¥500,000        │ │
│ └─────────────────────────┘ │
└─────────────────────────────┘

点击卡片后的操作菜单(右滑/长按):

  • 📄 查看详情(跳转到对应详情页)
  • 📥 下载报告(有文件时显示)
  • 🔄 转发报告(小程序分享能力)
  • 🗑 删除报告(仅结构化数据,标记废弃)

底部Tab(可选):

  • 分页加载(上拉加载更多)

报告详情统一视图

新建 pages/profile/report-detail.vue,根据 hasFiledetailRoute 决定展示:

  • hasFile=1 → 显示PDF预览/下载按钮 + 报告摘要
  • hasFile=0 → 显示结构化数据详情(如保单明细)
  • 始终提供「转发」按钮(小程序 onShareAppMessage

5.2 Web管理端

入口

cfc-web/src/views/Layout.vue 左侧菜单新增「报告管理」分组:

  • /report-management — "报告管理"(perm: report:management

页面:cfc-web/src/views/admin/ReportManagement.vue

页面结构:Element UI表格 + 筛选 + 分页

字段
报告ID id
报告类型 菌群/体检/DAN/保单/周报 (tag)
标题 title
报告主体 subjectName
报告日期 reportDate
综合评分 overallScore (有条件色)
状态 status (tag)
操作 查看/下载/同步

顶部筛选

  • 类型下拉:全部/菌群/体检/DAN/保单/周报
  • 关键词搜索:标题/主体名称
  • 日期范围选择器
  • "同步数据"按钮(触发手动同步)

API

  • 列表:POST /api/report-summary/admin-list(分页查询,管理员可见所有用户)
  • 同步:POST /api/admin/report-summary/sync
  • 详情:POST /api/report-summary/detail/{id}
  • 下载:POST /api/report-summary/download/{id}

6. 安全与权限

6.1 小程序端权限

  • 只能查看自己(user_id)的报告
  • 家长可查看家庭成员的报告(通过 family_id 关联)
  • 报告转发:使用小程序 onShareAppMessage,生成分享卡片

6.2 Web管理端权限

  • admin 角色可查看全平台所有报告
  • teacher 角色可查看自己负责家庭的报告(通过 family_id 过滤)

7. 小程序转发/分享能力

利用微信小程序开放能力:

// report-detail.vue 中
onShareAppMessage() {
  return {
    title: this.reportTitle,
    path: '/pages/profile/report-detail?id=' + this.reportId,
    imageUrl: '/static/share-report.png'  // 分享封面图
  }
}

对于有文件的报告,下载后使用 wx.shareFileMessage 转发文件。


8. 新增/修改文件清单

后端(cfc-backend)

文件 操作 说明
entity/ReportSummary.java 新建 汇总表实体
mapper/ReportSummaryMapper.java 新建 MyBatis Mapper
mapper/ReportSummaryMapper.xml 新建 XML映射
service/ReportSummaryService.java 新建 同步+查询逻辑
controller/ReportSummaryController.java 新建 前端API
controller/admin/ReportSummaryAdminController.java 新建 管理端API
task/ReportSummarySyncTask.java 新建 定时同步任务
config/DatabaseInitializer.java 修改 添加建表迁移
resources/schema.sql 修改 追加建表语句
service/HealthReportService.java 修改 末尾加sync调用
service/DanReportUploadService.java 修改 末尾加sync调用
service/InsurancePolicyService.java 修改 末尾加sync调用
entity/FamilyFortuneReport.java 相关Service 修改 末尾加sync调用

前端小程序(cfc-frontend)

文件 操作 说明
pages/profile/components/ProfileMenu.vue 修改 添加「报告管理」菜单项
pages/profile/report-management.vue 新建 报告管理主页面
pages/profile/report-detail.vue 新建 统一报告详情视图
utils/api.js 修改 添加报告管理API函数
pages.json 修改 注册新页面

Web管理端(cfc-web)

文件 操作 说明
src/views/admin/ReportManagement.vue 新建 管理端报告管理页面
src/views/Layout.vue 修改 添加菜单入口
src/router/index.js 修改 添加路由

9. 设计约束

  • 遵循项目统一 @PostMapping 规范
  • 响应统一 Result<T> 包装
  • 小程序禁用可选链 ?.,禁用 CSS Grid
  • Vue 2 Options API
  • 汇总表与源表通过 source_table + source_id 唯一关联
  • 定时任务使用 Spring @Scheduled
  • 定时同步对源表只做 READ,不修改源表数据