Files
hms/docs/superpowers/specs/2026-05-01-tri-platform-audit-fix-design.md

25 KiB
Raw Blame History

title, created, status, scope, estimated_effort, phases
title created status scope estimated_effort phases
三端联调审计问题修复设计规格 2026-05-01 draft 全量修复15 项) 23h 3

三端联调审计问题修复设计规格

基于 docs/discussions/2026-05-01-tri-platform-integration-audit.md 审计报告, 经 4 个专家组(统一性治理/功能孤岛/小程序/数据质量)代码级分析后整合的修复方案。

问题总览

级别 # 问题 专家组 工作量
P0 1 erp-plugin 测试编译失败 基础设施 5min
P0 2 品牌命名与主题设置联动 统一性 2h
P1 3 告警页面侧边栏无入口 统一性 1h
P1 4 行动收件箱侧边栏无入口 孤岛 0.5h
P1 5 危急值告警端点 500 基础设施 0.5h
P1 6 domain_events 堆积 1166 条 基础设施 1h
P2 7 患者详情关联导航缺失 统一性 2h
P2 8 AI 分析列表无患者 Link 统一性 1h
P2 9 小程序 AI 建议跳转错误 小程序 0.5h
P2 10 小程序通知 Tab 空壳 小程序 1h
P2 11 小程序咨询功能孤立 小程序 0.5h
P3 12 AI 分析 SSE 无触发入口 孤岛 4h
P3 13 家属管理无 UI 孤岛 3h
P3 14 E2E 测试数据污染 基础设施 1h
P4 15 统计仪表盘消费验证 孤岛 1h

实施阶段

  • Phase 1快速修复~5h: #1-#6 — 主题设置联动、菜单入口、测试修复、危急值 500、事件堆积
  • Phase 2体验补全~5.5h: #7-#11 — 导航关联、小程序修复
  • Phase 3功能闭环~9h: #12-#15 — AI SSE 入口、家属管理、E2E 清理、统计验证

#1: erp-plugin 测试编译失败 [P0]

根因

crates/erp-plugin/src/plugin_validator.rs#[cfg(test)] mod tests 中调用了 parse_manifest(),但该函数在同 crate 的 manifest.rs 中。测试模块的 use super::* 只引入 plugin_validator 模块的公开项。

修复

文件: crates/erp-plugin/src/plugin_validator.rs

在测试模块中添加 1 行导入:

#[cfg(test)]
mod tests {
    use super::*;
    use crate::manifest::parse_manifest;  // 新增

验证

cargo test -p erp-plugin --no-run

#2: 品牌命名与主题设置联动 [P0]

根因

  1. 登录页 Login.tsx 硬编码 "HMR Platform"、副标题、版权信息
  2. 后端 ThemeResp 只有 3 个字段(primary_color/logo_url/sidebar_style),缺少品牌名称、系统描述、版权等配置项
  3. 前端主题设置页面 ThemeSettings.tsx 只展示 3 个字段,主题设置实际未起到管理品牌信息的作用
  4. 侧边栏/页脚的品牌文字也是硬编码

设计思路

扩展现有主题设置体系,将品牌信息纳入主题配置。后端 ThemeResp 增加品牌字段,前端 ThemeSettings 页面增加品牌信息编辑区域Login/MainLayout 从主题配置读取品牌信息。

后端修改

文件: crates/erp-config/src/dto.rs — ThemeResp 增加字段

pub struct ThemeResp {
    // 已有字段
    pub primary_color: Option<String>,
    pub logo_url: Option<String>,
    pub sidebar_style: Option<String>,

    // 新增品牌字段
    #[serde(skip_serializing_if = "Option::is_none")]
    pub brand_name: Option<String>,           // 品牌名称,如 "HMS 健康管理平台"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub brand_slogan: Option<String>,         // 品牌标语,如 "新一代健康管理平台"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub brand_features: Option<String>,       // 功能亮点,如 "患者管理 · 健康监测 · 随访管理 · AI 智能分析"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub brand_copyright: Option<String>,      // 版权信息,如 "HMS 健康管理平台 · ©汕头市智界科技有限公司"
}

文件: crates/erp-config/src/handler/theme_handler.rs — 默认值更新

fn default_theme() -> ThemeResp {
    ThemeResp {
        primary_color: None,
        logo_url: None,
        sidebar_style: None,
        brand_name: Some("HMS 健康管理平台".into()),
        brand_slogan: Some("新一代健康管理平台".into()),
        brand_features: Some("患者管理 · 健康监测 · 随访管理 · AI 智能分析".into()),
        brand_copyright: Some("HMS 健康管理平台 · ©汕头市智界科技有限公司".into()),
    }
}

新增公开端点: crates/erp-config/src/handler/theme_handler.rs

/// GET /api/v1/public/theme — 公开主题端点(无需认证)
/// 只返回品牌信息,用于登录页等未认证场景。
pub async fn get_public_theme<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
) -> Result<JsonResponse<ApiResponse<PublicThemeResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    // 从 settings 读取,返回品牌字段即可
    // 如果租户上下文不存在,返回默认品牌信息
}

此端点需要在 erp-server 的公开路由组(无需认证)中注册。

前端修改

文件: apps/web/src/api/themes.ts — 扩展 ThemeConfig

export interface ThemeConfig {
  primary_color?: string;
  logo_url?: string;
  sidebar_style?: 'light' | 'dark';
  // 新增
  brand_name?: string;
  brand_slogan?: string;
  brand_features?: string;
  brand_copyright?: string;
}

文件: apps/web/src/stores/app.ts — 缓存主题配置

在 app store 中增加 themeConfig: ThemeConfig | null 字段login 后自动加载并缓存到 localStorage。提供 loadThemeConfig() action。

文件: apps/web/src/pages/settings/ThemeSettings.tsx — 增加品牌信息区域

在现有表单下方增加"品牌信息"分组Divider 分隔):

<Divider>品牌信息</Divider>
<Form.Item name="brand_name" label="品牌名称">
  <Input placeholder="HMS 健康管理平台" />
</Form.Item>
<Form.Item name="brand_slogan" label="品牌标语">
  <Input placeholder="新一代健康管理平台" />
</Form.Item>
<Form.Item name="brand_features" label="功能亮点">
  <Input placeholder="患者管理 · 健康监测 · 随访管理 · AI 智能分析" />
</Form.Item>
<Form.Item name="brand_copyright" label="版权信息">
  <Input placeholder="HMS 健康管理平台 · ©汕头市智界科技有限公司" />
</Form.Item>

文件: apps/web/src/pages/Login.tsx — 从主题配置读取

关键问题: 登录页在未认证状态下无法调用 /api/v1/themes(需要 token。解决方案

  1. 登录页先尝试从 localStorage('hms-theme-config') 读取缓存的主题配置
  2. 同时将后端 GET /api/v1/themes 端点增加一个无需认证的公开端点 GET /api/v1/public/theme(只返回品牌信息,不含 sidebar_style 等内部配置)
  3. Login 组件 mount 时调用公开端点获取品牌信息,失败则用 localStorage 缓存,再失败用硬编码默认值
// Login.tsx
const [brandConfig, setBrandConfig] = useState<{
  brand_name?: string;
  brand_slogan?: string;
  brand_features?: string;
  brand_copyright?: string;
} | null>(null);

useEffect(() => {
  // 尝试从公开端点获取品牌信息
  fetch('/api/v1/public/theme')
    .then(res => res.json())
    .then(data => {
      const config = data?.data;
      setBrandConfig(config);
      localStorage.setItem('hms-theme-config', JSON.stringify(config));
    })
    .catch(() => {
      // fallback: localStorage 缓存 → 默认值
      const cached = localStorage.getItem('hms-theme-config');
      if (cached) setBrandConfig(JSON.parse(cached));
    });
}, []);

<h1 className="brand-title">{brandConfig?.brand_name || 'HMS 健康管理平台'}</h1>
<p className="brand-desc">{brandConfig?.brand_slogan || '新一代健康管理平台'}</p>
<p className="brand-sub-desc">{brandConfig?.brand_features || '患者管理 · 健康监测 · 随访管理 · AI 智能分析'}</p>

底部版权同理。

文件: apps/web/src/layouts/MainLayout.tsx — Footer 和侧边栏

侧边栏 Logo 文字和 Footer 版权信息也从 themeConfig 读取,带默认值 fallback。

验证

  1. 在系统设置 → 主题设置中修改品牌名称为 "XX 医院健康管理"
  2. 保存后刷新页面,侧边栏/页脚品牌文字更新
  3. 登出后登录页显示新品牌名称
  4. 未配置时回退到默认值 "HMS 健康管理平台"

#3: 告警页面侧边栏无入口 [P1]

根因

数据库种子迁移 m20260429_000095_seed_alert_device_menus.rs 已插入告警菜单数据,但前端 MainLayout.tsxiconMap 中缺少 AlertOutlinedBellOutlinedControlOutlined 等图标映射。菜单数据返回后图标无法渲染。

修复

文件: apps/web/src/layouts/MainLayout.tsx

  1. 在 import 区域补充图标:
import {
  // ... 已有导入 ...
  AlertOutlined,
  BellOutlined,
  ControlOutlined,
  InboxOutlined,
  ApiOutlined,
  ReadOutlined,
  ExperimentOutlined,
} from '@ant-design/icons';
  1. iconMap 对象中补充映射:
AlertOutlined: <AlertOutlined />,
BellOutlined: <BellOutlined />,
ControlOutlined: <ControlOutlined />,
InboxOutlined: <InboxOutlined />,
ApiOutlined: <ApiOutlined />,
ReadOutlined: <ReadOutlined />,
ExperimentOutlined: <ExperimentOutlined />,

验证

登录后侧边栏"健康管理"分组下出现:告警仪表盘、告警列表、告警规则、设备管理、透析管理、资讯管理菜单项,图标正确渲染。


#4: 行动收件箱侧边栏无入口 [P1]

根因

前端代码已完整:ActionInbox.tsx(页面)、ActionThreadDrawer.tsx(组件)、actionInbox.tsAPIApp.tsx:260(路由注册)。仅缺数据库菜单种子数据。

修复

新建迁移: crates/erp-server/migration/src/m20260501_000100_seed_action_inbox_menu.rs

INSERT INTO menus (id, tenant_id, parent_id, title, path, icon, sort_order,
                   created_at, updated_at, is_active)
SELECT
    'b0000003-0000-7000-8000-000000000020'::uuid,
    t.id,
    (SELECT id FROM menus WHERE path = '/health' AND tenant_id = t.id LIMIT 1),
    '行动收件箱',
    '/health/action-inbox',
    'InboxOutlined',
    36,
    NOW(), NOW(), true
FROM tenants t
WHERE NOT EXISTS (
    SELECT 1 FROM menus
    WHERE path = '/health/action-inbox' AND tenant_id = t.id
);

同时需要种子关联 health.action-inbox.list 权限码(参照 m20260501_000097_seed_menu_permissions.rs 模式)。

验证

重启后端 → 刷新前端 → 侧边栏出现"行动收件箱"菜单项。


#5: 危急值告警端点 500 [P1]

根因

代码逻辑链路完整handler→service→entity 无 bug。最可能根因是 critical_alerts 表在 RLS 批量启用迁移m000086之后才由 m000090 创建,导致该表缺少 RLS 策略。如果手工启用了 RLS 却无策略,查询会被阻断。

修复

步骤 1: 确认迁移状态和根因(强制验证)

-- 确认迁移已执行
SELECT * FROM seaql_migrations WHERE name LIKE '%090%';

-- 确认表存在
SELECT table_name FROM information_schema.tables
WHERE table_name IN ('critical_alerts', 'critical_alert_responses');

-- 确认 RLS 状态(关键:判断是否为 RLS 导致 500
SELECT relname, relrowsecurity, relforcerowsecurity
FROM pg_class WHERE relname = 'critical_alerts';
-- 如果 relrowsecurity = false → 不是 RLS 问题,需查看 tracing 日志
-- 如果 relrowsecurity = true 且无 policy → 需要步骤 2

启动后端并调用端点,查看 tracing 日志中的具体错误信息:

cargo run
curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/health/critical-alerts
# 查看 tracing 输出确认根因

步骤 2: 根据步骤 1 结果决定是否补齐 RLS 策略

如果 RLS 已启用但缺少策略,新建迁移: crates/erp-server/migration/src/m20260501_000101_rls_for_post_migration_tables.rs

使用动态 SQL 扫描所有含 tenant_id 列但缺少 tenant_isolation 策略的表,自动补齐 RLS。

注意: 此迁移需在 migration/src/lib.rs 中注册。

步骤 3: 在 critical_alert_service.rs 添加 tracing 日志(无论根因是 RLS 还是其他,都应加日志)

.map_err(|e| {
    tracing::error!(error = %e, "查询危急值告警列表失败");
    HealthError::DbError(e.to_string())
})?;

验证

curl -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/v1/health/critical-alerts
# 期望: 200 + 空列表

#6: domain_events 堆积 1166 条 [P1]

根因

清理函数 cleanup_old_published_events() 保留 90 天,项目运行不到 2 周,所有事件未满 90 天。

修复

步骤 1: 立即手动清理

INSERT INTO domain_events_archive
SELECT * FROM domain_events WHERE status = 'published';
DELETE FROM domain_events WHERE status = 'published'
  AND id IN (SELECT id FROM domain_events_archive);

步骤 2: 调整保留期

文件: crates/erp-server/src/tasks.rs 第 26 行

// 修改前
"SELECT cleanup_old_published_events(90, 1000)"
// 修改后
"SELECT cleanup_old_published_events(7, 1000)"

步骤 3: 添加索引(可选)

CREATE INDEX IF NOT EXISTS idx_domain_events_status_created
ON domain_events (status, created_at ASC) WHERE status = 'pending';

验证

SELECT status, COUNT(*) FROM domain_events GROUP BY status;
-- 期望: published 数量大幅减少

#7: 患者详情关联导航缺失 [P2]

根因

PatientDetail.tsx 有 5 个 Tab基本信息/健康数据/随访/积分/AI建议但无法跳转到预约、咨询、透析、全局随访等关联功能页面。

修复

文件: apps/web/src/pages/health/PatientDetail.tsx

在基本信息卡片与 Tabs 卡片之间添加快捷导航卡片:

<Card style={{ marginBottom: 16, padding: '12px 16px' }}>
  <Space size={8} wrap>
    <Text type="secondary">快捷跳转:</Text>
    <Button type="link" size="small"
      onClick={() => navigate(`/health/appointments?patient_id=${id}`)}>
      预约记录
    </Button>
    <Button type="link" size="small"
      onClick={() => navigate(`/health/consultations?patient_id=${id}`)}>
      咨询记录
    </Button>
    <Button type="link" size="small"
      onClick={() => navigate(`/health/dialysis?patient_id=${id}`)}>
      透析记录
    </Button>
    <Button type="link" size="small"
      onClick={() => navigate(`/health/follow-up-tasks?patient_id=${id}`)}>
      随访任务
    </Button>
    <Button type="link" size="small"
      onClick={() => navigate(`/health/ai-analysis?patient_id=${id}`)}>
      AI 分析
    </Button>
  </Space>
</Card>

前提条件: 目标页面需支持 URL 参数 ?patient_id=xxx 过滤。需在以下页面添加 URL 参数读取:

  • AppointmentList.tsx
  • ConsultationList.tsx
  • DialysisManageList.tsx
  • FollowUpTaskList.tsx
  • AiAnalysisList.tsx

各页面在初始化查询参数时从 useSearchParams() 读取 patient_id 并设为默认筛选条件。

验证

在患者详情页点击各快捷跳转按钮,确认目标页面自动按患者 ID 过滤。


根因

AiAnalysisList.tsxpatient_id 列只显示截断 IDv.slice(0, 8)),无跳转。

修复

文件: apps/web/src/pages/health/AiAnalysisList.tsx

  1. 添加 import { Link } from 'react-router-dom';
  2. 修改列渲染:
{
  title: '患者',
  dataIndex: 'patient_id',
  key: 'patient_id',
  width: 140,
  render: (v: string) => (
    <Link to={`/health/patients/${v}`} style={{ fontFamily: 'monospace', fontSize: 12 }}>
      {v.slice(0, 8)}
    </Link>
  ),
},

验证

AI 分析列表中患者 ID 列变为可点击链接,跳转到对应患者详情页。


#9: 小程序 AI 建议跳转错误 [P2]

根因

apps/miniprogram/src/pages/health/index.tsx 第 179 行AI 建议卡片 onClick 统一跳转到设置页 /pages/pkg-profile/settings/index,而非根据 suggestion_typeappointment/followup跳转到对应功能页。

修复

文件: apps/miniprogram/src/pages/health/index.tsx

修改 onClick 处理逻辑:

// 修改前
onClick={() => Taro.navigateTo({ url: '/pages/pkg-profile/settings/index' })}

// 修改后
onClick={() => {
  if (item.suggestion_type === 'appointment') {
    Taro.navigateTo({ url: `/pages/pkg-appointment/create/index?patientId=${item.patient_id}` });
  } else if (item.suggestion_type === 'followup') {
    Taro.navigateTo({ url: `/pages/pkg-followup/detail/index?id=${item.related_id}` });
  } else {
    Taro.navigateTo({ url: `/pages/health/index` }); // 通用回退
  }
}}

验证

小程序健康页点击不同类型的 AI 建议卡片,确认跳转到正确页面。


#10: 小程序通知 Tab 空壳 [P2]

根因

apps/miniprogram/src/pages/messages/index.tsx 第 34-37 行 setNotifications([]) 硬编码空数组,注释"后续可对接独立通知 API"。

修复

文件: apps/miniprogram/src/pages/messages/index.tsx

  1. 新建通知 API 服务:apps/miniprogram/src/services/notification.ts

后端 erp-message 模块通知端点完整:GET /messages(列表)、PUT /messages/{id}/read(标记已读)、PUT /messages/read-all(全部已读)、GET /messages/unread-count(未读计数)、GET /messages/streamSSE 实时推送)。事件消费者覆盖 20 种事件类型。直接对接即可。

import { api } from '@/services/request';

export const notificationService = {
  list: (params?: { page?: number; page_size?: number }) =>
    api.get('/messages', params),
  markRead: (id: string) =>
    api.put(`/messages/${id}/read`),
  markAllRead: () =>
    api.put('/messages/read-all'),
  getUnreadCount: () =>
    api.get('/messages/unread-count'),
};
  1. 替换硬编码空数组为 API 调用:
// 修改前
setNotifications([]);

// 修改后
try {
  const res = await notificationService.list({ page: 1, page_size: 20 });
  setNotifications(res?.data || []);
} catch {
  setNotifications([]);
}

验证

小程序消息页"通知"Tab 展示从后端获取的数据列表。


#11: 小程序咨询功能孤立 [P2]

根因

/pages/consultation/index 页面存在且已注册路由,但首页/健康页/个人中心均无入口。唯一入口在消息 Tab 的"咨询"子 Tab。

修复

文件: apps/miniprogram/src/pages/profile/index.tsx

MENU_ITEMS 数组中添加入口:

// 在"我的预约"项附近添加
{
  title: '在线咨询',
  icon: 'chat',
  url: '/pages/consultation/index',
},

文件: apps/miniprogram/src/pages/index/index.tsx

在首页快捷操作区添加咨询入口图标。

验证

小程序个人中心/首页出现"在线咨询"入口,点击进入咨询页面。


#12: AI 分析 SSE 无触发入口 [P3]

根因

后端 4 个 SSE 端点就绪lab-report/trends/checkup-plan/report-summary前端无调用入口。

修复

新建文件

1. apps/web/src/api/ai/analysisSse.ts — SSE 分析 API

export type AnalysisType = 'lab-report' | 'trends' | 'checkup-plan' | 'report-summary';

export interface SseAnalysisOptions {
  type: AnalysisType;
  reportId?: string;
  patientId?: string;
  metrics?: string[];
  onChunk: (content: string, index: number) => void;
  onError: (message: string) => void;
  onDone: (analysisId: string) => void;
}

export async function startAnalysis(options: SseAnalysisOptions): Promise<AbortController> {
  const controller = new AbortController();
  const endpoint = ENDPOINT_MAP[options.type];
  // fetch POST → 读取 ReadableStream → 解析 SSE → 回调
  return controller;
}

2. apps/web/src/components/AiAnalysisPanel.tsx — SSE 分析面板组件

展示分析类型选择、实时流式内容Markdown、进度指示器、完成后跳转。

修改文件

3. apps/web/src/pages/health/components/LabReportsTab.tsx — 每行添加"AI 解读"按钮

4. apps/web/src/pages/health/PatientDetail.tsx — AI 建议标签页添加"发起分析"按钮

5. apps/web/src/pages/health/AiAnalysisList.tsx — 顶部添加"新建分析"入口

验证

  1. 在化验报告列表点击"AI 解读",确认 SSE 流实时显示分析内容
  2. 在患者详情点击"趋势分析",确认分析完成并跳转历史详情

#13: 家属管理无 UI [P3]

根因

后端 4 个 API + 前端 API 封装全部就绪,仅缺 PatientDetail 中的 Tab 组件。

修复

新建文件: apps/web/src/pages/health/components/FamilyMembersTab.tsx

export function FamilyMembersTab({ patientId }: { patientId: string }) {
  // Table: 姓名 | 关系 | 电话 | 身份证号 | 备注 | 操作
  // DrawerForm: 添加/编辑家属(关系 Select: 父母/配偶/子女/兄弟姐妹/其他)
  // AuthButton code="health.patient.manage"
}

修改文件: apps/web/src/pages/health/PatientDetail.tsx

在 Tabs items 数组中注册:

{
  key: 'family',
  label: '家属管理',
  children: id ? <FamilyMembersTab patientId={id} /> : null,
},

验证

患者详情页出现"家属管理"Tab可添加/编辑/删除家属。


#14: E2E 测试数据污染 [P3]

根因

E2E 测试无 teardown30 个 E2E患者_* 永久残留。

修复

步骤 1: 立即清理

UPDATE patients SET deleted_at = NOW(), updated_at = NOW()
WHERE name LIKE 'E2E患者_%' AND deleted_at IS NULL;

步骤 2: 新建 apps/web/e2e/fixtures/cleanup.ts

export async function cleanupE2EData(api: ApiClient): Promise<void> {
  const patients = await api.get('/api/v1/health/patients?keyword=E2E');
  for (const p of patients?.data?.data ?? []) {
    if (p.name.startsWith('E2E')) await api.delete(`/api/v1/health/patients/${p.id}`);
  }
}

步骤 3: 每个 E2E spec 的 afterAll 中调用 cleanupE2EData

验证

数据库中 E2E 测试患者被清理,后续 E2E 测试运行后自动清理。


#15: 统计仪表盘消费验证 [P4]

根因

审计报告称"统计仪表盘消费不足"。经代码验证,DoctorDashboard.tsxNurseDashboard.tsx 已在消费 pointsApi.getPersonalStats()(分别在第 43 行和第 20 行。9 个后端统计端点中 6 个已被前端消费,仅 get_dashboard_stats(聚合端点,被分别调用替代)和 3 个单独端点(被 health-data 聚合端点覆盖)未被直接调用,但功能上已覆盖。

修复

降级为验证任务:

  1. 确认 DoctorDashboard/NurseDashboard 展示的个人统计指标是否完整(对比后端 personal_stats DTO 返回的全部字段)
  2. 如果有遗漏字段(如 abnormal_vital_signspending_lab_reviews),在对应仪表盘中补充展示
  3. 确认 useStatsData.tstryFetch 调用链无报错

验证

以医生角色登录 → 统计报表页 → 确认所有统计指标正常展示。


关键文件清单

文件 修改类型 问题#
crates/erp-plugin/src/plugin_validator.rs 添加 1 行导入 #1
apps/web/src/pages/Login.tsx 从主题配置读取品牌信息 #2
apps/web/src/stores/app.ts 缓存 themeConfig #2
apps/web/src/pages/settings/ThemeSettings.tsx 增加品牌信息表单 #2
crates/erp-config/src/dto.rs ThemeResp 增加品牌字段 #2
crates/erp-config/src/handler/theme_handler.rs 默认品牌值 #2
apps/web/src/api/themes.ts 扩展 ThemeConfig 接口 #2
apps/web/src/layouts/MainLayout.tsx Footer/Logo 从配置读取 #2,#3
apps/web/src/layouts/MainLayout.tsx 图标导入+映射 #3
migration/m20260501_000098_seed_action_inbox_menu.rs 新建迁移 #4
migration/m20260501_000099_rls_for_post_migration_tables.rs 新建迁移 #5
crates/erp-health/src/service/critical_alert_service.rs 添加 tracing #5
crates/erp-server/src/tasks.rs 保留期 90→7 #6
apps/web/src/pages/health/PatientDetail.tsx 快捷导航 #7
5 个列表页面 URL 参数支持 #7
apps/web/src/pages/health/AiAnalysisList.tsx Link 添加 #8
apps/miniprogram/src/pages/health/index.tsx 跳转逻辑 #9
apps/miniprogram/src/pages/messages/index.tsx API 对接 #10
apps/miniprogram/src/services/notification.ts 新建服务 #10
apps/miniprogram/src/pages/profile/index.tsx 菜单项 #11
apps/web/src/api/ai/analysisSse.ts 新建 SSE API #12
apps/web/src/components/AiAnalysisPanel.tsx 新建组件 #12
apps/web/src/pages/health/components/LabReportsTab.tsx AI 解读按钮 #12
apps/web/src/pages/health/components/FamilyMembersTab.tsx 新建组件 #13
apps/web/e2e/fixtures/cleanup.ts 新建清理 #14
apps/web/src/pages/health/StatisticsDashboard/DoctorDashboard.tsx 个人统计 #15

变更记录

日期 变更
2026-05-01 初始版本 — 4 专家组整合15 项修复3 阶段实施