Files
hms/docs/archive/superpowers-completed/2026-04-23-hms-miniprogram-plan.md
iven 18fa6ce6d4 docs: 全局文档梳理归档 — 删除过期文件 + 归档 V1/早期设计 + wiki 数据校正 + CLAUDE.md 规则优化
**根目录清理:**
- 删除 CLAUDE-1.md(ZCLAW 旧项目配置,HMS 已完全脱离)
- 移动 DESIGN.md → docs/archive/(ERP 旧设计系统)
- 删除 plans/ 98 个临时会话计划文件

**归档重组:**
- V1 审计(12 文件)→ docs/archive/audits-v1/
- 早期 CRM/插件迭代设计(13 文件)→ docs/archive/superpowers-early/
- 已完成/已取代设计(28 文件)→ docs/archive/superpowers-completed/
- 早期讨论/测试报告 → docs/archive/discussions-early/ + test-reports-early/
- QA 重复文件清理(3 个旧版 result 文件)

**wiki 数据校正:**
- 迁移数 137→145,源文件 599→649,提交数 720→800+
- 小程序文件 124→163,Web 前端 297→332
- 后端测试 999→943(实际统计),权限码 75+→128
- 文档索引新增归档目录说明

**CLAUDE.md 规则优化:**
- §2.5 闭环工作法:提交+文档+推送三合一 + wiki 更新触发条件
- §2.6 Feature DoD:新增文档一致性检查项
- §6 反模式:新增 wiki 更新滞后/推送不及时警告
2026-05-15 09:29:04 +08:00

34 KiB
Raw Blame History

HMS 患者小程序实施计划

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 基于 Taro 4 + React 19 构建微信小程序患者端,直连 erp-server 后端 API。

Architecture: 小程序代码位于 apps/miniprogram/,通过 Taro 编译为微信小程序。后端在 erp-auth 新增微信登录支持erp-health 新增小程序所需 API。前端用 Zustand 管理状态services 层封装 Taro.request 调用后端 /api/v1/ 端点。

Tech Stack: Taro 4, React 19, TypeScript 6, Zustand 5, SCSS, echarts-taro3-react, Rust/Axum/SeaORM (后端)

Spec: docs/superpowers/specs/2026-04-23-hms-miniprogram-design.md


Chunk 1: Phase 1 — 后端微信认证

Task 1: 创建 wechat_users 数据库迁移

Files:

  • Create: crates/erp-server/migration/src/m20260423_000001_wechat_users.rs

  • Step 1: 创建迁移文件

// m20260423_000001_wechat_users.rs
use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationAction)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(WechatUsers::Table)
                    .if_not_exists()
                    .col(ColumnDef::new(WechatUsers::Id).uuid().not_null().primary_key())
                    .col(ColumnDef::new(WechatUsers::TenantId).uuid().not_null())
                    .col(ColumnDef::new(WechatUsers::Openid).string().not_null())
                    .col(ColumnDef::new(WechatUsers::UnionId).string())
                    .col(ColumnDef::new(WechatUsers::UserId).uuid().not_null())
                    .col(ColumnDef::new(WechatUsers::Phone).string())
                    .col(ColumnDef::new(WechatUsers::CreatedAt).timestamp_with_time_zone().not_null().default(Expr::current_timestamp()))
                    .col(ColumnDef::new(WechatUsers::UpdatedAt).timestamp_with_time_zone().not_null().default(Expr::current_timestamp()))
                    .col(ColumnDef::new(WechatUsers::DeletedAt).timestamp_with_time_zone())
                    .to_owned(),
            )
            .await?;
        manager
            .create_index(
                Index::create()
                    .name("idx_wechat_users_openid")
                    .table(WechatUsers::Table)
                    .col(WechatUsers::Openid)
                    .col(WechatUsers::TenantId)
                    .unique()
                    .to_owned(),
            )
            .await
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager.drop_index(Index::drop().name("idx_wechat_users_openid").table(WechatUsers::Table).to_owned()).await?;
        manager.drop_table(Table::drop().table(WechatUsers::Table).to_owned()).await
    }
}

#[derive(DeriveIden)]
enum WechatUsers {
    Table,
    Id,
    TenantId,
    Openid,
    UnionId,
    UserId,
    Phone,
    CreatedAt,
    UpdatedAt,
    DeletedAt,
}
  • Step 2: 注册迁移

crates/erp-server/migration/src/lib.rs 的 migration 列表中添加新迁移。

  • Step 3: 验证迁移

Run: cargo check -p erp-server Expected: 编译通过

  • Step 4: 提交
git add crates/erp-server/migration/src/m20260423_000001_wechat_users.rs crates/erp-server/migration/src/lib.rs
git commit -m "feat(auth): 添加 wechat_users 表迁移"

Task 2: 创建 wechat_users Entity

Files:

  • Create: crates/erp-auth/src/entity/wechat_user.rs

  • Modify: crates/erp-auth/src/entity/mod.rs

  • Step 1: 创建 Entity 文件

参照 crates/erp-auth/src/entity/user_token.rs 的模式,创建 wechat_user.rs,包含 ModelRelationActiveModelBehavior

  • Step 2: 在 mod.rs 注册

crates/erp-auth/src/entity/mod.rs 添加 pub mod wechat_user;

  • Step 3: 验证

Run: cargo check -p erp-auth Expected: 编译通过

  • Step 4: 提交
git add crates/erp-auth/src/entity/wechat_user.rs crates/erp-auth/src/entity/mod.rs
git commit -m "feat(auth): 添加 wechat_user SeaORM entity"

Task 3: 微信登录 DTO

Files:

  • Modify: crates/erp-auth/src/dto.rs

  • Step 1: 添加微信登录相关 DTO

crates/erp-auth/src/dto.rs 中新增:

// 微信登录请求
#[derive(Deserialize, ToSchema)]
pub struct WechatLoginReq {
    pub code: String,
}

// 微信登录响应
#[derive(Serialize, ToSchema)]
pub struct WechatLoginResp {
    pub bound: bool,
    pub openid: String,
    pub token: Option<LoginResp>,  // 已绑定时返回
}

// 绑定手机号请求
#[derive(Deserialize, ToSchema)]
pub struct WechatBindPhoneReq {
    pub openid: String,
    pub encrypted_data: String,
    pub iv: String,
}
  • Step 2: 验证

Run: cargo check -p erp-auth

  • Step 3: 提交
git add crates/erp-auth/src/dto.rs
git commit -m "feat(auth): 添加微信登录 DTO"

Task 4: 微信登录 Service

Files:

  • Create: crates/erp-auth/src/service/wechat_service.rs

  • Modify: crates/erp-auth/src/service/mod.rs

  • Step 1: 实现 wechat_service.rs

核心逻辑:

  • login(state, tenant_id, code): 用 code 调用微信 API 换 openid → 查 wechat_users 表 → 返回绑定状态
  • bind_phone(state, tenant_id, req): 解密手机号 → 查找/创建 user → 创建 wechat_user 记录 → 签发 JWT
  • 微信 API 调用:GET https://api.weixin.qq.com/sns/jscode2session?appid=...&secret=...&js_code={code}&grant_type=authorization_code

注意appid 和 secret 从 AppConfig 中读取,需在 erp-server 配置中新增 wechat 段。

  • Step 2: 在 mod.rs 注册

添加 pub mod wechat_service;

  • Step 3: 验证

Run: cargo check -p erp-auth

  • Step 4: 提交
git add crates/erp-auth/src/service/wechat_service.rs crates/erp-auth/src/service/mod.rs
git commit -m "feat(auth): 实现微信登录/绑定手机号 service"

Task 5: 微信登录 Handler + 路由注册

Files:

  • Create: crates/erp-auth/src/handler/wechat_handler.rs

  • Modify: crates/erp-auth/src/handler/mod.rs

  • Modify: crates/erp-auth/src/lib.rs (路由注册)

  • Step 1: 创建 handler

// wechat_handler.rs
pub async fn wechat_login<S>(State(state): State<AuthState>, ...) -> Result<Json<ApiResponse<WechatLoginResp>>, AppError>
pub async fn wechat_bind_phone<S>(State(state): State<AuthState>, ...) -> Result<Json<ApiResponse<LoginResp>>, AppError>
  • Step 2: 注册公开路由

微信登录端点是公开的(无需 JWTAuthModule::public_routes() 中添加:

  • POST /auth/wechat/login

  • POST /auth/wechat/bind-phone

  • Step 3: 在 AppConfig 中添加微信配置

crates/erp-server/src/config.rs 中新增:

pub wechat: WechatConfig,
pub struct WechatConfig { pub appid: String, pub secret: String }
  • Step 4: 验证

Run: cargo check -p erp-server

  • Step 5: 提交
git add crates/erp-auth/src/handler/wechat_handler.rs crates/erp-auth/src/handler/mod.rs crates/erp-auth/src/lib.rs crates/erp-server/src/config.rs
git commit -m "feat(auth): 添加微信登录 handler 和公开路由"

Task 6: 验证微信登录端到端

  • Step 1: 启动后端

Run: cargo run -p erp-server

  • Step 2: 测试端点

用 curl 测试:

curl -X POST http://localhost:3000/api/v1/auth/wechat/login -H "Content-Type: application/json" -d '{"code":"test_code"}'

Expected: 返回 { "success": true, "data": { "bound": false, "openid": "..." } } 或类似响应

  • Step 3: 确认迁移执行

检查 wechat_users 表已创建:

PGPASSWORD=123123 "D:\postgreSQL\bin\psql.exe" -U postgres -h localhost -d erp -c "\dt wechat_users"
  • Step 4: 提交确认

如果有修复:

git add -A && git commit -m "fix(auth): 微信登录端到端验证修复"

Chunk 2: Phase 1 — 小程序项目骨架 + 登录 + 首页

Task 7: 初始化 Taro 项目

  • Step 1: 用 Taro CLI 初始化项目
cd apps
npx @tarojs/cli init miniprogram --template react-ts

选择:

  • 框架React

  • TypeScriptYes

  • CSS 预处理Sass

  • 模板:默认模板

  • 编译工具Webpack5

  • Step 2: 安装核心依赖

cd apps/miniprogram
pnpm add zustand
pnpm add echarts echarts-taro3-react
  • Step 3: 配置 project.config.json

设置 appid先用测试号 touristappid、ES6 转 ES5、增强编译等。

  • Step 4: 验证
cd apps/miniprogram && pnpm dev:weapp

用微信开发者工具打开 dist/ 目录,确认空白小程序可运行。

  • Step 5: 提交
git add apps/miniprogram/
git commit -m "feat(miniprogram): 初始化 Taro 4 + React 项目骨架"

Task 8: 全局样式 + 主题变量

Files:

  • Modify: apps/miniprogram/src/app.scss

  • Create: apps/miniprogram/src/styles/variables.scss

  • Create: apps/miniprogram/src/styles/mixins.scss

  • Step 1: 创建主题变量

src/styles/variables.scss:

$pri: #0891B2;
$pri-l: #E0F7FA;
$pri-d: #065A73;
$pri-surface: #ECFEFF;
$acc: #059669;
$acc-l: #D1FAE5;
$bg: #F0FDFA;
$card: #FFFFFF;
$tx: #134E4A;
$tx2: #6B7280;
$tx3: #94A3B8;
$bd: #E5E7EB;
$bd-l: #F3F4F6;
$dan: #DC2626;
$dan-l: #FEE2E2;
$wrn: #D97706;
$wrn-l: #FEF3C7;
$r: 12px;
$r-sm: 8px;
$r-lg: 16px;
  • Step 2: 创建常用 mixins

src/styles/mixins.scss:

@mixin card {
  background: $card;
  border-radius: $r;
  box-shadow: 0 2px 8px rgba(0,0,0,.06);
  padding: 16px;
  margin: 0 16px 12px;
}
  • Step 3: 更新 app.scss

导入变量和全局基础样式(字体、背景色)。

  • Step 4: 提交
git add apps/miniprogram/src/styles/ apps/miniprogram/src/app.scss
git commit -m "feat(miniprogram): 添加医疗清新主题样式变量"

Task 9: services/request.ts — API 请求层

Files:

  • Create: apps/miniprogram/src/services/request.ts

  • Step 1: 实现 request 封装

import Taro from '@tarojs/taro';

const BASE_URL = process.env.TARO_APP_API_URL || 'http://localhost:3000/api/v1';

interface ApiResponse<T> {
  success: boolean;
  data?: T;
  message?: string;
}

async function getHeaders(): Promise<Record<string, string>> {
  const headers: Record<string, string> = { 'Content-Type': 'application/json' };
  const token = Taro.getStorageSync('access_token');
  if (token) headers['Authorization'] = `Bearer ${token}`;
  const patientId = Taro.getStorageSync('current_patient_id');
  if (patientId) headers['X-Patient-Id'] = patientId;
  const tenantId = Taro.getStorageSync('tenant_id');
  if (tenantId) headers['X-Tenant-Id'] = tenantId;
  return headers;
}

export async function request<T>(method: string, path: string, data?: unknown): Promise<T> {
  const headers = await getHeaders();
  const res = await Taro.request({ url: `${BASE_URL}${path}`, method, data, header: headers });

  if (res.statusCode === 401) {
    const refreshed = await tryRefreshToken();
    if (refreshed) return request(method, path, data);
    Taro.redirectTo({ url: '/pages/login/index' });
    throw new Error('登录已过期');
  }

  const body = res.data as ApiResponse<T>;
  if (!body.success) throw new Error(body.message || '请求失败');
  return body.data as T;
}

async function tryRefreshToken(): Promise<boolean> {
  const refreshToken = Taro.getStorageSync('refresh_token');
  if (!refreshToken) return false;
  try {
    const res = await Taro.request({
      url: `${BASE_URL}/auth/refresh`,
      method: 'POST',
      data: { refresh_token: refreshToken },
    });
    if (res.statusCode === 200 && res.data?.success) {
      Taro.setStorageSync('access_token', res.data.data.access_token);
      Taro.setStorageSync('refresh_token', res.data.data.refresh_token);
      return true;
    }
  } catch {}
  Taro.removeStorageSync('access_token');
  Taro.removeStorageSync('refresh_token');
  return false;
}

export const api = {
  get: <T>(path: string) => request<T>('GET', path),
  post: <T>(path: string, data?: unknown) => request<T>('POST', path, data),
  put: <T>(path: string, data?: unknown) => request<T>('PUT', path, data),
  delete: <T>(path: string) => request<T>('DELETE', path),
};
  • Step 2: 提交
git add apps/miniprogram/src/services/request.ts
git commit -m "feat(miniprogram): 实现 API 请求层封装"

Task 10: services/auth.ts + auth store

Files:

  • Create: apps/miniprogram/src/services/auth.ts

  • Create: apps/miniprogram/src/stores/auth.ts

  • Step 1: auth service

// services/auth.ts
import { api } from './request';

export interface LoginResp {
  bound: boolean;
  openid: string;
  token?: { access_token: string; refresh_token: string; user: UserInfo };
}

export interface UserInfo {
  id: string; name: string; phone: string; avatar?: string; tenant_id: string;
}

export interface PatientInfo {
  id: string; name: string; gender?: string; birthday?: string; relation: string;
}

export async function wechatLogin(code: string): Promise<LoginResp> {
  return api.post('/auth/wechat/login', { code });
}

export async function wechatBindPhone(openid: string, encryptedData: string, iv: string) {
  return api.post('/auth/wechat/bind-phone', { openid, encrypted_data: encryptedData, iv });
}

export async function getPatients() {
  return api.get<PatientInfo[]>('/health/patients');
}
  • Step 2: auth store

参照 Web 端 stores/auth.ts 模式,使用 Taro.getStorageSync / setStorageSync 替代 localStorage

State: token, refreshToken, user, currentPatient, patients, loading Actions: login(), bindPhone(), setCurrentPatient(), logout()

  • Step 3: 提交
git add apps/miniprogram/src/services/auth.ts apps/miniprogram/src/stores/auth.ts
git commit -m "feat(miniprogram): 实现 auth service 和 store"

Task 11: 登录页

Files:

  • Create: apps/miniprogram/src/pages/login/index.tsx

  • Create: apps/miniprogram/src/pages/login/index.scss

  • Step 1: 实现登录页 UI

登录页包含:

  • 品牌标识Logo + "健康管理" 标题)

  • 微信一键登录按钮(调用 wx.loginwechatLogin(code)

  • 如果返回 bound: false,显示手机号授权按钮(使用 <Button openType="getPhoneNumber">

  • 登录成功后 Taro.switchTab({ url: '/pages/index/index' })

  • Step 2: 实现样式

医疗清新风格:青色渐变背景、白色卡片、圆角按钮。

  • Step 3: 注册页面路由

src/app.config.tspages 数组中添加 /pages/login/index

  • Step 4: 提交
git add apps/miniprogram/src/pages/login/ apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现微信登录页面"

Task 12: 首页(静态数据)

Files:

  • Create: apps/miniprogram/src/pages/index/index.tsx

  • Create: apps/miniprogram/src/pages/index/index.scss

  • Create: apps/miniprogram/src/components/HealthCard/index.tsx

  • Modify: apps/miniprogram/src/app.config.ts (TabBar 配置)

  • Step 1: 配置 TabBar

app.config.ts 中定义 5 个 Tab

tabBar: {
  color: '#94A3B8',
  selectedColor: '#0891B2',
  backgroundColor: '#FFFFFF',
  list: [
    { pagePath: 'pages/index/index', text: '首页', iconPath: 'assets/tab-home.png', selectedIconPath: 'assets/tab-home-active.png' },
    { pagePath: 'pages/health/index', text: '健康', iconPath: 'assets/tab-health.png', selectedIconPath: 'assets/tab-health-active.png' },
    { pagePath: 'pages/appointment/index', text: '预约', iconPath: 'assets/tab-appt.png', selectedIconPath: 'assets/tab-appt-active.png' },
    { pagePath: 'pages/article/index', text: '资讯', iconPath: 'assets/tab-article.png', selectedIconPath: 'assets/tab-article-active.png' },
    { pagePath: 'pages/profile/index', text: '我的', iconPath: 'assets/tab-profile.png', selectedIconPath: 'assets/tab-profile-active.png' },
  ],
}
  • Step 2: 创建 Tab 图标

准备 10 个图标文件5 普通 + 5 选中态),放到 src/assets/

  • Step 3: 实现 HealthCard 组件

健康指标卡片:接收 title, value, unit, status,显示渐变背景 + 数值 + 状态标签。

  • Step 4: 实现首页

按设计规格布局:问候栏 + 今日健康2×2 网格)+ 快捷服务4 宫格)+ 即将到来 + 待办随访。 MVP 先用静态数据,后续 Phase 对接真实 API。

  • Step 5: 创建其他 Tab 占位页

health/indexappointment/indexarticle/indexprofile/index 创建简单占位页,显示页面名称。

  • Step 6: 验证
pnpm dev:weapp

微信开发者工具中确认登录流程可用、首页布局正确、Tab 切换正常。

  • Step 7: 提交
git add apps/miniprogram/src/
git commit -m "feat(miniprogram): 实现首页布局 + TabBar + 登录流程"

Chunk 3: Phase 2 — 健康数据录入 + 趋势图

Task 13: 后端 — vital_signs 趋势查询 API

Files:

  • Modify: crates/erp-health/src/handler/health_data_handler.rs

  • Modify: crates/erp-health/src/service/health_data_service.rs

  • Modify: crates/erp-health/src/dto/health_data_dto.rs

  • Modify: crates/erp-health/src/module.rs (路由)

  • Step 1: 添加趋势查询 DTO

health_data_dto.rs 新增:

#[derive(Deserialize, ToSchema)]
pub struct TrendQueryParams {
    pub indicator: String,        // "blood_pressure_systolic" 等
    pub start_date: Option<chrono::NaiveDate>,
    pub end_date: Option<chrono::NaiveDate>,
    pub range: Option<String>,    // "7d", "30d", "90d"
}

#[derive(Serialize, ToSchema)]
pub struct TrendResp {
    pub indicator: String,
    pub data_points: Vec<DataPoint>,
}

#[derive(Serialize, ToSchema)]
pub struct DataPoint {
    pub date: String,
    pub value: f64,
}
  • Step 2: 添加趋势查询 service 函数

health_data_service.rs 新增 get_vital_signs_trend(),基于已有的 get_indicator_timeseries() 封装,支持 range 参数自动计算日期范围。

  • Step 3: 添加 handler
pub async fn get_trend<S>(...) -> Result<Json<ApiResponse<TrendResp>>, AppError>
  • Step 4: 注册路由

module.rsprotected_routes() 中添加 GET /health/vital-signs/trend

  • Step 5: 验证

Run: cargo check -p erp-health

  • Step 6: 提交
git add crates/erp-health/src/
git commit -m "feat(health): 添加 vital_signs 趋势查询 API"

Task 14: 小程序 — 健康数据录入页

Files:

  • Create: apps/miniprogram/src/pages/health/index.tsx

  • Create: apps/miniprogram/src/pages/health/index.scss

  • Create: apps/miniprogram/src/pages/health/input/index.tsx

  • Create: apps/miniprogram/src/pages/health/input/index.scss

  • Create: apps/miniprogram/src/services/health.ts

  • Step 1: health service

// services/health.ts
import { api } from './request';

export interface VitalSignInput {
  indicator_type: string;
  value: number;
  measured_at?: string;
  note?: string;
}

export interface TodaySummary {
  blood_pressure?: { systolic: number; diastolic: number; status: string };
  heart_rate?: { value: number; status: string };
  blood_sugar?: { value: number; status: string };
  weight?: { value: number; status: string };
}

export async function getTodaySummary() {
  return api.get<TodaySummary>('/health/vital-signs?date=today');
}

export async function inputVitalSign(data: VitalSignInput) {
  return api.post('/health/vital-signs', data);
}

export async function getTrend(indicator: string, range: string) {
  return api.get<{ indicator: string; data_points: { date: string; value: number }[] }>(
    `/health/vital-signs/trend?indicator=${indicator}&range=${range}`
  );
}
  • Step 2: 健康数据首页

展示今日健康数据概览2×2 卡片网格)+ 最近趋势缩略图 + "录入数据" 按钮。

  • Step 3: 数据录入页

表单选择指标类型Picker→ 输入数值 → 选择测量时间DateTimePicker→ 备注(可选)→ 提交。 提交成功后 Taro.navigateBack() 并刷新上一页数据。

  • Step 4: 注册页面路由

app.config.ts 中添加 pages/health/input/index

  • Step 5: 验证

微信开发者工具中确认录入表单可操作,提交后数据出现在健康首页。

  • Step 6: 提交
git add apps/miniprogram/src/pages/health/ apps/miniprogram/src/services/health.ts apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现健康数据录入页面"

Task 15: 小程序 — 趋势图组件

Files:

  • Create: apps/miniprogram/src/components/TrendChart/index.tsx

  • Create: apps/miniprogram/src/pages/health/trend/index.tsx

  • Create: apps/miniprogram/src/pages/health/trend/index.scss

  • Step 1: TrendChart 组件

使用 echarts-taro3-react 渲染折线图。接收 data: { date: string; value: number }[]title: string

  • Step 2: 趋势详情页

顶部:指标名称 + 时间范围切换7天/30天/90天 中部TrendChart 折线图 底部:数据表格(日期 + 数值)

  • Step 3: 注册路由

app.config.ts 添加 pages/health/trend/index

  • Step 4: 验证

切换时间范围,图表数据刷新。

  • Step 5: 提交
git add apps/miniprogram/src/components/TrendChart/ apps/miniprogram/src/pages/health/trend/ apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现健康趋势图页面"

Task 16: health store + 首页数据联动

Files:

  • Create: apps/miniprogram/src/stores/health.ts

  • Modify: apps/miniprogram/src/pages/index/index.tsx (对接真实数据)

  • Step 1: health store

// stores/health.ts
import { create } from 'zustand';
import * as healthApi from '../services/health';

interface HealthState {
  todaySummary: healthApi.TodaySummary | null;
  loading: boolean;
  refreshToday: () => Promise<void>;
}

export const useHealthStore = create<HealthState>((set) => ({
  todaySummary: null,
  loading: false,
  refreshToday: async () => {
    set({ loading: true });
    try {
      const data = await healthApi.getTodaySummary();
      set({ todaySummary: data, loading: false });
    } catch { set({ loading: false }); }
  },
}));
  • Step 2: 首页接入真实数据

替换首页静态数据,从 useHealthStore().todaySummary 读取。useDidShow 生命周期触发 refreshToday()

  • Step 3: 提交
git add apps/miniprogram/src/stores/health.ts apps/miniprogram/src/pages/index/
git commit -m "feat(miniprogram): 首页对接真实健康数据 API"

Chunk 4: Phase 3 — 预约挂号

Task 17: 后端 — 排班查询 API 适配

Files:

  • Modify: crates/erp-health/src/handler/health_data_handler.rs 或对应的排班 handler

  • Modify: crates/erp-health/src/dto/health_data_dto.rs

  • Step 1: 确认排班 API 支持按科室/医生查询

检查现有 GET /health/doctor-schedules 是否支持 department_iddoctor_id 查询参数。如不支持,添加相应过滤。

  • Step 2: 添加"可用时段"聚合

新增 DTO 和 handler输入医生 + 日期范围 → 返回每日可用时段及剩余名额。

  • Step 3: 验证

Run: cargo check -p erp-health

  • Step 4: 提交
git add crates/erp-health/src/
git commit -m "feat(health): 排班查询支持科室/医生过滤 + 可用时段聚合"

Task 18: 小程序 — 预约服务 + 列表页

Files:

  • Create: apps/miniprogram/src/services/appointment.ts

  • Modify: apps/miniprogram/src/pages/appointment/index.tsx

  • Create: apps/miniprogram/src/pages/appointment/index.scss

  • Step 1: appointment service

// services/appointment.ts
import { api } from './request';

export interface Appointment {
  id: string; doctor_name: string; department: string;
  date: string; time_slot: string; status: string;
}

export async function listAppointments(page = 1) {
  return api.get<{ data: Appointment[]; total: number }>(`/health/appointments?page=${page}`);
}

export async function createAppointment(data: {
  doctor_id: string; schedule_id: string; patient_id: string;
}) {
  return api.post('/health/appointments', data);
}

export async function cancelAppointment(id: string) {
  return api.put(`/health/appointments/${id}/cancel`);
}

export async function getDoctorSchedules(doctorId: string, startDate: string, endDate: string) {
  return api.get(`/health/doctor-schedules?doctor_id=${doctorId}&start_date=${startDate}&end_date=${endDate}`);
}
  • Step 2: 预约列表页

显示预约记录列表(按时间倒序),每项包含医生名、科室、日期、时段、状态标签。支持下拉刷新 + 上拉加载更多。

  • Step 3: 提交
git add apps/miniprogram/src/services/appointment.ts apps/miniprogram/src/pages/appointment/
git commit -m "feat(miniprogram): 实现预约列表页"

Task 19: 小程序 — 新建预约页

Files:

  • Create: apps/miniprogram/src/pages/appointment/create/index.tsx

  • Create: apps/miniprogram/src/pages/appointment/create/index.scss

  • Create: apps/miniprogram/src/pages/appointment/detail/index.tsx

  • Step 1: 新建预约页 — 步骤流程

4 步向导:

  1. 选择科室Picker
  2. 选择医生(列表,显示头像+姓名+职称+科室)
  3. 选择日期和时段(周视图日历 + 时段列表)
  4. 确认预约(显示摘要 + 提交按钮)
  • Step 2: 预约详情页

展示预约详情 + 取消预约按钮 + 导航到体检中心(地图)。

  • Step 3: 注册路由

app.config.ts 添加 pages/appointment/create/indexpages/appointment/detail/index

  • Step 4: 验证

完整走通:选科室 → 选医生 → 选时段 → 确认 → 查看详情 → 取消。

  • Step 5: 提交
git add apps/miniprogram/src/pages/appointment/ apps/miniprogram/src/app.config.ts
git commit -m "feat(miniprogram): 实现新建预约和预约详情页"

Chunk 5: Phase 4 — 报告查询 + 家庭管理

Task 20: 小程序 — 报告服务 + 列表/详情页

Files:

  • Create: apps/miniprogram/src/services/report.ts

  • Create: apps/miniprogram/src/pages/report/index.tsx

  • Modify: apps/miniprogram/src/app.config.ts

  • Step 1: report service

import { api } from './request';

export interface LabReport {
  id: string; title: string; type: string; date: string; status: string;
  indicators?: LabIndicator[];
}

export interface LabIndicator {
  name: string; value: number; unit: string; reference_min: number; reference_max: number;
  status: 'normal' | 'high' | 'low';
}

export async function listReports(page = 1) {
  return api.get<{ data: LabReport[]; total: number }>(`/health/lab-reports?page=${page}`);
}

export async function getReportDetail(id: string) {
  return api.get<LabReport>(`/health/lab-reports/${id}`);
}
  • Step 2: 报告列表页

卡片列表:报告名称 + 类型标签 + 日期。下拉刷新 + 上拉分页。

  • Step 3: 报告详情页

基本信息卡 + 指标列表(正常灰色、异常红色高亮 + 箭头)+ 附件预览区。

  • Step 4: 提交
git add apps/miniprogram/src/services/report.ts apps/miniprogram/src/pages/report/
git commit -m "feat(miniprogram): 实现报告查询列表和详情页"

Task 21: 小程序 — 家庭管理(就诊人切换)

Files:

  • Create: apps/miniprogram/src/components/FamilyPicker/index.tsx

  • Create: apps/miniprogram/src/pages/profile/family/index.tsx

  • Create: apps/miniprogram/src/pages/profile/family-add/index.tsx

  • Modify: apps/miniprogram/src/pages/profile/index.tsx

  • Step 1: FamilyPicker 组件

顶部下拉选择器:显示当前就诊人姓名 + 头像,点击展开列表切换。切换后更新 auth storecurrentPatient + Taro.setStorageSync('current_patient_id', id)

  • Step 2: 就诊人管理页

列表显示已添加的就诊人(本人 + 家属),支持添加新就诊人。

  • Step 3: 添加就诊人页

表单:姓名 + 与本人关系Picker+ 性别 + 出生日期 + 身份证号(可选)。提交 POST /health/patients

  • Step 4: 首页/健康/预约页接入 FamilyPicker

在这些页面顶部添加 FamilyPicker切换就诊人后重新拉取数据。

  • Step 5: 注册路由并提交
git add apps/miniprogram/src/components/FamilyPicker/ apps/miniprogram/src/pages/profile/family/ apps/miniprogram/src/pages/profile/family-add/
git commit -m "feat(miniprogram): 实现家庭管理 + 就诊人切换"

Chunk 6: Phase 5 — 随访 + 资讯 + 用药提醒

Task 22: 后端 — articles 表 + API

Files:

  • Create: crates/erp-server/migration/src/m20260423_000002_articles.rs

  • Create: crates/erp-health/src/entity/article.rs

  • Create: crates/erp-health/src/dto/article_dto.rs

  • Create: crates/erp-health/src/handler/article_handler.rs

  • Create: crates/erp-health/src/service/article_service.rs

  • Step 1: 创建 articles 迁移

表结构:id, tenant_id, title, summary, content(富文本), cover_image, category, author, published_at, created_at, updated_at, deleted_at, version

  • Step 2: Entity + DTO + Service + Handler

参照现有 health_data 的模式创建完整 CRUD。公开接口只需 listdetail

  • Step 3: 注册路由和权限

  • Step 4: 验证

Run: cargo check -p erp-health

  • Step 5: 提交
git add crates/erp-health/src/ crates/erp-server/migration/
git commit -m "feat(health): 添加健康资讯 articles 表和 API"

Task 23: 小程序 — 随访功能

Files:

  • Create: apps/miniprogram/src/services/followup.ts

  • Create: apps/miniprogram/src/pages/followup/index.tsx

  • Create: apps/miniprogram/src/pages/followup/detail/index.tsx

  • Step 1: followup service

  • Step 2: 随访任务列表页

按状态分组:待完成 / 已完成 / 已过期。每项显示任务名 + 截止日期 + 状态。

  • Step 3: 随访详情/填写页

动态表单渲染(后端定义字段)→ 提交 → 标记完成。

  • Step 4: 首页待办随访接入真实数据

  • Step 5: 提交

git add apps/miniprogram/src/services/followup.ts apps/miniprogram/src/pages/followup/
git commit -m "feat(miniprogram): 实现随访任务列表和填写页"

Task 24: 小程序 — 健康资讯

Files:

  • Create: apps/miniprogram/src/services/article.ts

  • Modify: apps/miniprogram/src/pages/article/index.tsx

  • Create: apps/miniprogram/src/pages/article/detail/index.tsx

  • Step 1: article service

  • Step 2: 资讯列表页

缩略图 + 标题 + 摘要 + 时间,下拉刷新 + 分页。

  • Step 3: 资讯详情页

使用 Taro RichText 渲染富文本内容。

  • Step 4: 提交
git add apps/miniprogram/src/services/article.ts apps/miniprogram/src/pages/article/
git commit -m "feat(miniprogram): 实现健康资讯列表和详情页"

Task 25: 小程序 — 用药提醒

Files:

  • Create: apps/miniprogram/src/pages/profile/medication/index.tsx

  • Create: apps/miniprogram/src/utils/medication-reminder.ts

  • Step 1: 用药提醒工具

本地 storage 存储提醒规则:{ id, name, frequency, times: string[], enabled }

  • Step 2: 用药提醒管理页

列表展示已添加的提醒 + 添加/编辑/删除/开关。

  • Step 3: 提交
git add apps/miniprogram/src/pages/profile/medication/ apps/miniprogram/src/utils/medication-reminder.ts
git commit -m "feat(miniprogram): 实现用药提醒管理页"

Chunk 7: Phase 6 — 打磨 + 真机测试 + 提审

Task 26: "我的" 页面完善

Files:

  • Modify: apps/miniprogram/src/pages/profile/index.tsx

  • Create: apps/miniprogram/src/pages/profile/settings/index.tsx

  • Step 1: 个人中心页

展示头像 + 姓名 + 手机号。功能入口:就诊人管理、我的报告、我的随访、用药提醒、设置。

  • Step 2: 设置页

清除缓存、关于我们、隐私政策、退出登录。

  • Step 3: 提交
git add apps/miniprogram/src/pages/profile/
git commit -m "feat(miniprogram): 完善个人中心页"

Task 27: 空状态 + 错误处理 + Loading

Files:

  • Create: apps/miniprogram/src/components/EmptyState/index.tsx

  • Create: apps/miniprogram/src/components/ErrorState/index.tsx

  • Create: apps/miniprogram/src/components/Loading/index.tsx

  • Step 1: 通用空状态组件

显示图标 + 文案 + 操作按钮(如"去预约")。

  • Step 2: 通用错误组件

网络错误 / 服务器错误 / 无权限,带重试按钮。

  • Step 3: 各页面接入

所有列表页添加空状态、错误状态、Loading 态。

  • Step 4: 提交
git add apps/miniprogram/src/components/EmptyState/ apps/miniprogram/src/components/ErrorState/ apps/miniprogram/src/components/Loading/
git commit -m "feat(miniprogram): 添加空状态/错误/Loading 通用组件"

Task 28: 分包 + 性能优化

  • Step 1: 配置分包

app.config.ts 中定义 subPackages

  • health/ — 健康数据相关页面
  • appointment/ — 预约相关
  • report/ — 报告相关
  • followup/ — 随访相关
  • article/ — 资讯相关

主包保留:首页 + 登录 + 我的 + 公共组件。

  • Step 2: 图表库按需引入

echarts 只引入折线图模块,减小体积。

  • Step 3: 提交
git add apps/miniprogram/src/app.config.ts
git commit -m "perf(miniprogram): 配置分包加载 + 图表按需引入"

Task 29: 真机测试 + 提审准备

  • Step 1: 真机调试

通过微信开发者工具预览/真机调试,在多款真机上验证。

  • Step 2: 检查清单

  • 登录流程正常(微信授权 + 手机号绑定)

  • 健康数据可录入并查看趋势

  • 预约可创建/取消

  • 报告可查看

  • 随访可填写

  • 就诊人可切换

  • 无白屏、无 JS 报错

  • 包体积 < 2MB主包

  • 隐私政策和用户协议已配置

  • Step 3: 构建生产版本

pnpm build:weapp
  • Step 4: 提交最终版本
git add -A
git commit -m "release(miniprogram): v1.0.0 提审版本"
git push