# HMS 功能完善迭代设计规格 > 日期: 2026-04-25 > 状态: 已确认(审查修正版) > 关联: `docs/superpowers/specs/2026-04-25-erp-ai-module-design.md` ## 1. 背景与目标 ### 1.1 项目现状 HMS 健康管理平台已完成核心业务开发(237 次提交、57k 行 Rust + 174 前端文件),但存在以下功能缺口: - **按钮级权限控制缺失** — 路由守卫已有,但操作按钮(新增/编辑/删除)未做权限过滤;前端缺少权限数据源(`UserInfo` 接口不含 `permissions` 字段) - **AI 模块管理端空白** — 后端 6 个 API 端点中 4 个 SSE 流式端点可用,但 Prompt CRUD、分析历史查询、用量统计端点均为空壳或缺失 - **小程序端 AI 不可见** — 患者无法查看 AI 分析报告 ### 1.2 目标 通过纵向切片方式逐步交付三个功能域,每个切片从前到后完整打通: 1. **切片 1:按钮级权限** — 基础设施,后续页面的前置依赖 2. **切片 2:AI 管理端** — 3 个 PC 管理页面 3. **切片 3:小程序报告** — 患者端只读查看 --- ## 2. 切片 1:按钮级权限控制 ### 2.1 架构 ``` 后端新增 /api/v1/auth/me/permissions → 返回当前用户权限码列表 ↓ auth store (permissions: string[]) ← 登录时从新端点加载 ↓ usePermission(code) → { hasPermission: boolean } ↓ ... ↓ 无权限 → 不渲染(hidden 模式) 有权限 → 正常渲染子元素 ``` **前置依赖:** 当前 `UserInfo` 接口不含 `permissions` 字段(仅含 `roles`)。需后端新增 `/api/v1/auth/me/permissions` 端点,返回当前用户所有权限码的扁平列表(从角色 → 权限关联表聚合)。超级管理员(`is_system: true`)默认返回全部权限码。 ### 2.2 组件设计 **usePermission hook** 位置: `apps/web/src/hooks/usePermission.ts` ```typescript function usePermission(code: string): { hasPermission: boolean } ``` - 从 auth store 读取当前用户 permissions 数组 - 返回 code 是否在权限列表中 - 权限数据加载失败时默认无权限(安全降级) **AuthButton 组件** 位置: `apps/web/src/components/AuthButton.tsx` Props: - `code: string` — 权限码(如 `health.patient.manage`) - `children: ReactNode` — 受保护的按钮内容 行为: 无权限时不渲染 children(hidden 模式)。 **AuthGuard 组件** 位置: `apps/web/src/components/AuthGuard.tsx` Props: - `code: string` — 权限码 - `children: ReactNode` — 受保护的内容块 行为: 同 AuthButton,用于包裹非按钮内容(如整个 Tab、区块)。 ### 2.3 改造范围 优先改造健康模块 15 个页面中的操作按钮: | 页面 | 按钮权限码 | |------|-----------| | PatientList | health.patient.manage | | PatientDetail | health.patient.manage | | AppointmentList | health.appointment.manage | | DoctorList | health.doctor.manage | | DoctorSchedule | health.doctor.manage | | FollowUpTaskList | health.follow-up.manage | | FollowUpRecordList | health.follow-up.manage | | ConsultationList | health.consultation.manage | | ConsultationDetail | health.consultation.manage | | OfflineEventList | health.articles.manage | | PatientTagManage | health.patient.manage | | StatisticsDashboard | health.health-data.list (只读) | | PointsProductList | health.points.manage | | PointsOrderList | health.points.list | | PointsRuleList | health.points.manage | 扩展到基础模块页面(Users, Roles, Organizations, Workflow 等)。 ### 2.4 验证标准 - [ ] 无权限用户看不到操作按钮 - [ ] 有权限用户操作正常 - [ ] 权限变更后界面实时更新(无需刷新) --- ## 3. 切片 2:AI 管理端 3 页面 ### 3.1 路由设计 ``` /health/ai/prompts → Prompt 管理 /health/ai/analysis → 分析历史 /health/ai/usage → 用量统计 ``` ### 3.2 页面 A — Prompt 管理 位置: `apps/web/src/pages/health/AiPromptList.tsx` **功能清单:** | 功能 | 说明 | |------|------| | 列表展示 | 表格:名称/类型(化验单解读、趋势分析、体检方案、报告摘要)/版本号/状态(active/draft)/更新时间 | | 新建 Prompt | Modal 表单:名称、类型(下拉)、系统提示词、用户提示词模板(支持 `{{variable}}` 占位符) | | 编辑 Prompt | 同新建,自动递增版本号 | | 激活/停用 | 切换按钮,激活时停用同类型旧模板 | | 版本历史 | 展开行显示所有历史版本,支持一键回滚 | **API 封装:** 位置: `apps/web/src/api/ai/prompts.ts` ```typescript // 后端需新增 Prompt CRUD 端点(当前仅有 service 层的 get_active_prompt + create_prompt) getPrompts(params: ListParams): Promise> // GET /api/v1/ai/prompts createPrompt(data: CreatePromptDto): Promise // POST /api/v1/ai/prompts updatePrompt(id: string, data: UpdatePromptDto): Promise // PUT /api/v1/ai/prompts/{id} activatePrompt(id: string): Promise // POST /api/v1/ai/prompts/{id}/activate rollbackPrompt(id: string): Promise // POST /api/v1/ai/prompts/{id}/rollback ``` **版本回滚机制:** 每次编辑 Prompt 创建新记录(递增 version),回滚 = 将目标旧版本 `is_active` 设为 `true` 并将当前激活版本 `is_active` 设为 `false`。不删除任何版本记录。 **权限码:** `ai.prompt.list`(查看)、`ai.prompt.manage`(编辑/激活/回滚) ### 3.3 页面 B — 分析历史 位置: `apps/web/src/pages/health/AiAnalysisList.tsx` **功能清单:** | 功能 | 说明 | |------|------| | 列表展示 | 表格:分析类型/患者姓名/状态(streaming/completed/failed)/创建时间/token 用量 | | 详情查看 | 点击行展开/Modal 展示完整分析结果(Markdown 渲染) | | 筛选 | 按类型(4 种)、时间范围(DateRangePicker)、患者(PatientSelect 组件复用) | | 重新分析 | 对 failed 记录支持重新发起分析 | **API 封装:** 位置: `apps/web/src/api/ai/analysis.ts` ```typescript // 后端当前 list_analysis/get_analysis 为空壳(返回 ApiResponse::ok(())),需实现真实查询 getAnalysisHistory(params: AnalysisQueryParams): Promise> // GET /api/v1/ai/analysis/history getAnalysisDetail(id: string): Promise // GET /api/v1/ai/analysis/{id} ``` **权限码:** `ai.analysis.list`(查看)、`ai.analysis.manage`(重新分析) ### 3.4 页面 C — 用量统计 位置: `apps/web/src/pages/health/AiUsageDashboard.tsx` **功能清单:** | 功能 | 说明 | |------|------| | 概览卡片 | 4 张 StatCard:总用量/本月/今日/平均 token | | 趋势图 | Ant Design Charts 折线图,按日/周/月切换 | | 类型分布 | 饼图展示 4 种分析类型的占比 | | 用户排行 | 表格展示用户维度用量排名 | **API 封装:** 位置: `apps/web/src/api/ai/usage.ts` ```typescript // 后端需完全新增:路由、handler、聚合 service // ai_usage_logs 表需增加 created_by 列(当前缺失 user_id),或复用 ai_analysis_results.created_by 做用户排行 getUsageOverview(): Promise // GET /api/v1/ai/usage/overview getUsageTrend(params: TrendParams): Promise // GET /api/v1/ai/usage/trend getUsageByType(): Promise // GET /api/v1/ai/usage/by-type getUsageByUser(params: UserRankingParams): Promise> // GET /api/v1/ai/usage/by-user ``` **后端补充:** 需在 erp-ai 中新增用量统计聚合端点。优先方案:复用 `ai_analysis_results` 表的 `created_by` 字段做用户维度排行,避免修改 `ai_usage_logs` 表结构。如需精确 token 统计,后续可加迁移增加 `user_id` 列。 **权限码:** `ai.usage.list` ### 3.5 菜单注册 在 `apps/web/src/layouts/MainLayout.tsx` 健康管理菜单组下新增 AI 分析入口。当前菜单为扁平结构(无子菜单折叠),新增项直接追加为同级菜单项: ``` 健康管理 ├── 患者管理 ├── 医护管理 ├── 预约管理 ├── 随访管理 ├── 咨询管理 ├── 积分商城 ├── 统计看板 ├── AI Prompt 管理 ← 新增(扁平) ├── AI 分析历史 ← 新增(扁平) └── AI 用量统计 ← 新增(扁平) ``` ### 3.6 验证标准 - [ ] Prompt CRUD 全流程可用(创建/编辑/激活/回滚) - [ ] 分析历史可筛选、可查看详情(Markdown 正确渲染) - [ ] 用量统计图表数据正确 - [ ] 所有操作按钮受 AuthButton 权限控制 - [ ] 页面响应式布局正常 --- ## 4. 切片 3:小程序 AI 报告查看 ### 4.1 新增页面 **AI 报告列表页** 位置: `apps/miniprogram/src/pages/ai-report/list/index.tsx` - 调用 `GET /api/v1/ai/analysis/history` (后端需实现,根据 JWT user_id → patient_id 自动过滤) - 列表展示分析记录(类型图标 + 时间 + 状态标签) - 点击进入详情 **AI 报告详情页** 位置: `apps/miniprogram/src/pages/ai-report/detail/index.tsx` - 调用 `GET /api/v1/ai/analysis/{id}` - 使用 `taro-markdown` 组件渲染 Markdown 格式的分析结果(需先验证兼容性) - 底部展示分析时间和 token 用量信息 ### 4.2 路由集成 在首页(`pages/index/index.tsx`)健康数据区域增加"AI 报告"入口卡片。 ### 4.3 后端依赖 后端 `list_analysis` 和 `get_analysis` 当前为空壳(仅验证权限后返回空值),需实现: - 根据 JWT 中的 user_id 查找关联 patient_id - 从 `ai_analysis_results` 表查询该患者的分析记录 - 返回不含 PII 的脱敏结果 ### 4.4 验证标准 - [ ] 患者可查看自己的 AI 分析历史 - [ ] 详情页 Markdown 正确渲染 - [ ] 无法查看其他患者的报告 - [ ] 无报告时显示空状态提示 --- ## 5. 实施顺序 | 阶段 | 内容 | 依赖 | 预计工作量 | |------|------|------|-----------| | P0a | 后端:新增 `/api/v1/auth/me/permissions` 端点 | 无 | erp-auth handler + service | | P0b | 后端:实现 Prompt CRUD 端点(list/create/update/activate/rollback) | 无 | erp-ai handler + service | | P0c | 后端:实现分析历史查询(list_analysis/get_analysis 从空壳到真实查询) | 无 | erp-ai handler + service | | P0d | 后端:新增用量统计聚合端点(overview/trend/by-type/by-user) | 无 | erp-ai handler + service + 可能迁移 | | P1 | 前端:usePermission hook + AuthButton/AuthGuard 组件 + auth store 加载 permissions | P0a | 3 文件 | | P2 | 前端:健康模块页面按钮权限改造 | P1 | 15 文件 | | P3 | 前端:AI API 封装(3 个 service 文件) | P0b-d | 3 文件 | | P4 | 前端:AI Prompt 管理页面 | P1, P3 | 1 文件 | | P5 | 前端:AI 分析历史页面 | P1, P3 | 1 文件 | | P6 | 前端:AI 用量统计页面 | P1, P3 | 1 文件 | | P7 | 前端:菜单注册 + 路由配置 | P4-P6 | 2 文件 | | P8 | 小程序:验证 taro-markdown 兼容性 + AI 报告列表/详情页 | P0c | 3 文件 | | P9 | 小程序:首页入口集成 | P8 | 1 文件 | --- ## 6. 非目标(明确排除) - 不涉及 CI/CD 流水线建设(属于安全与稳定性方向) - 不涉及 erp-plugin unwrap 修复(属于代码质量方向) - 不涉及 TypeScript strict 模式开启(属于质量方向,单独处理) - 不涉及新的 AI 提供商接入(仅使用现有 Claude 提供商) - 不涉及用量配额/计费功能(后续迭代) --- ## 7. 技术约束 - 前端组件使用 Ant Design 6 现有组件 - 图表使用 Ant Design Charts(项目已有依赖) - 小程序 Markdown 渲染使用 taro-markdown 组件(P8 开始前验证兼容性) - 后端新增端点遵循现有 handler/service/entity 模式 - 所有新页面使用 i18n key(前缀约定:`health.ai.*`),不硬编码中文 - 权限数据加载失败时默认无权限(安全降级,宁可少显示按钮也不暴露越权操作)