Files
plates-server/src/medications/README.md
richarjiang 188b4addca feat(medications): 新增完整的药物管理和服药提醒功能
实现了包含药物信息管理、服药记录追踪、统计分析、自动状态更新和推送提醒的完整药物管理系统。

核心功能:
- 药物 CRUD 操作,支持多种剂型和自定义服药时间
- 惰性生成服药记录策略,查询时才生成当天记录
- 定时任务自动更新过期记录状态(每30分钟)
- 服药前15分钟自动推送提醒(每5分钟检查)
- 每日/范围/总体统计分析功能
- 完整的 API 文档和数据库建表脚本

技术实现:
- 使用 Sequelize ORM 管理 MySQL 数据表
- 集成 @nestjs/schedule 实现定时任务
- 复用现有推送通知系统发送提醒
- 采用软删除和权限验证保障数据安全
2025-11-07 17:29:11 +08:00

7.6 KiB
Raw Blame History

药物管理模块

概述

药物管理模块提供完整的用药提醒和服药记录管理功能,包括:

  • 药物信息管理CRUD
  • 服药记录追踪
  • 统计分析
  • 自动状态更新
  • 推送提醒

功能特性

1. 药物管理

  • 支持多种药物剂型(胶囊、药片、注射、喷雾、滴剂、糖浆等)
  • 灵活的服药时间设置
  • 支持每日重复模式
  • 可设置开始和结束日期
  • 支持添加药物照片和备注

2. 服药记录

  • 惰性生成策略:查询时才生成当天记录,避免预先生成大量数据
  • 四种状态:待服用(upcoming)、已服用(taken)、已错过(missed)、已跳过(skipped)
  • 自动状态更新定时任务每30分钟检查并更新过期记录
  • 支持标记服用、跳过服药、更新记录

3. 统计功能

  • 每日统计:计划服药次数、已服用、已错过、待服用、完成率
  • 日期范围统计:支持查询任意时间段的统计数据
  • 总体统计概览:总记录数、完成率等

4. 推送提醒

  • 定时任务每5分钟检查即将到来的服药时间提前15-20分钟
  • 集成现有推送通知系统
  • 支持自定义提醒消息

API 接口

药物管理接口

1. 获取药物列表

GET /medications?isActive=true&page=1&pageSize=20

2. 创建药物

POST /medications
Content-Type: application/json

{
  "name": "Metformin",
  "photoUrl": "https://cdn.example.com/med_001.jpg",
  "form": "capsule",
  "dosageValue": 1,
  "dosageUnit": "粒",
  "timesPerDay": 2,
  "medicationTimes": ["08:00", "20:00"],
  "repeatPattern": "daily",
  "startDate": "2025-01-01T00:00:00.000Z",
  "note": "饭后服用"
}

3. 更新药物

PUT /medications/{id}
Content-Type: application/json

{
  "dosageValue": 2,
  "timesPerDay": 3,
  "medicationTimes": ["08:00", "14:00", "20:00"]
}

4. 删除药物

DELETE /medications/{id}

5. 停用药物

POST /medications/{id}/deactivate

服药记录接口

1. 获取服药记录

GET /medication-records?date=2025-01-15&status=upcoming

2. 获取今日服药记录

GET /medication-records/today

3. 标记为已服用

POST /medication-records/{recordId}/take
Content-Type: application/json

{
  "actualTime": "2025-01-15T08:10:00.000Z"
}

4. 跳过服药

POST /medication-records/{recordId}/skip
Content-Type: application/json

{
  "note": "今天状态不好,暂时跳过"
}

5. 更新服药记录

PUT /medication-records/{recordId}
Content-Type: application/json

{
  "status": "taken",
  "actualTime": "2025-01-15T08:15:00.000Z",
  "note": "延迟服用"
}

统计接口

1. 获取每日统计

GET /medication-stats/daily?date=2025-01-15

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "date": "2025-01-15",
    "totalScheduled": 6,
    "taken": 4,
    "missed": 1,
    "upcoming": 1,
    "completionRate": 66.67
  }
}

2. 获取日期范围统计

GET /medication-stats/range?startDate=2025-01-01&endDate=2025-01-15

3. 获取总体统计

GET /medication-stats/overall

数据模型

Medication药物

{
  id: string;
  userId: string;
  name: string;              // 药物名称
  photoUrl?: string;         // 药物照片
  form: MedicationForm;      // 剂型
  dosageValue: number;       // 剂量数值
  dosageUnit: string;        // 剂量单位
  timesPerDay: number;       // 每日服用次数
  medicationTimes: string[]; // 服药时间
  repeatPattern: RepeatPattern;
  startDate: Date;
  endDate?: Date;
  note?: string;
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
}

MedicationRecord服药记录

{
  id: string;
  medicationId: string;
  userId: string;
  scheduledTime: Date;  // 计划服药时间
  actualTime?: Date;    // 实际服药时间
  status: MedicationStatus;
  note?: string;
  createdAt: Date;
  updatedAt: Date;
}

业务逻辑说明

1. 惰性生成策略

服药记录采用惰性生成策略,而非预先生成大量记录:

// 查询记录时自动生成当天记录
async findAll(userId: string, query: MedicationRecordQueryDto) {
  // 1. 确保指定日期的记录存在
  await this.recordGenerator.ensureRecordsExist(userId, query.date);

  // 2. 查询并返回记录
  return this.recordModel.findAll({...});
}

2. 状态自动更新

定时任务每30分钟检查并更新过期记录

@Cron(CronExpression.EVERY_30_MINUTES)
async updateExpiredRecords() {
  // 将已过期的 upcoming 记录更新为 missed
  await this.recordModel.update(
    { status: MedicationStatusEnum.MISSED },
    {
      where: {
        status: MedicationStatusEnum.UPCOMING,
        scheduledTime: { [Op.lt]: new Date() }
      }
    }
  );
}

3. 推送提醒

定时任务每5分钟检查即将到来的服药时间

@Cron('*/5 * * * *')
async checkAndSendReminders() {
  const now = new Date();
  const reminderStart = dayjs(now).add(15, 'minute').toDate();
  const reminderEnd = dayjs(now).add(20, 'minute').toDate();

  // 查找15-20分钟后需要服药的记录
  const upcomingRecords = await this.recordModel.findAll({
    where: {
      status: MedicationStatusEnum.UPCOMING,
      scheduledTime: { [Op.between]: [reminderStart, reminderEnd] }
    }
  });

  // 发送推送通知
  for (const record of upcomingRecords) {
    await this.sendReminder(record);
  }
}

部署说明

1. 执行数据库迁移

# 连接到 MySQL 数据库
mysql -u your_username -p your_database

# 执行建表 SQL
source sql-scripts/medications-tables-create.sql

2. 环境变量

确保 .env 文件中包含以下配置:

# 数据库配置
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=your_username
DB_PASSWORD=your_password
DB_DATABASE=pilates_db

# JWT 配置
JWT_SECRET=your_jwt_secret

# 推送通知配置(如需使用推送功能)
APPLE_KEY_ID=your_apple_key_id
APPLE_ISSUER_ID=your_apple_issuer_id
APPLE_PRIVATE_KEY_PATH=path/to/private/key.p8

3. 启动应用

# 开发模式
yarn start:dev

# 生产模式
yarn build
yarn start:prod

测试建议

1. 基础功能测试

  • 创建药物
  • 查询药物列表
  • 更新药物信息
  • 删除/停用药物

2. 记录管理测试

  • 查询今日记录(验证惰性生成)
  • 标记服用
  • 跳过服药
  • 更新记录

3. 统计功能测试

  • 每日统计计算准确性
  • 日期范围统计
  • 完成率计算

4. 定时任务测试

  • 状态自动更新等待30分钟后检查
  • 推送提醒发送创建15分钟后的服药记录

注意事项

  1. 时区处理:所有时间使用 UTC 存储,前端需要转换为本地时间
  2. 权限控制:所有接口需要 JWT 认证,用户只能访问自己的数据
  3. 惰性生成:首次查询某天记录时会自动生成,可能有轻微延迟
  4. 定时任务:依赖 @nestjs/schedule 模块,确保已启用
  5. 推送通知:需要正确配置 APNs 证书和密钥

未来扩展

  1. 周计划模式:支持每周特定日期服药
  2. 自定义周期支持间隔天数服药如每3天一次
  3. 剂量提醒:提醒用户剩余药量不足
  4. 服药历史:长期服药历史分析和可视化
  5. 多设备同步:支持多设备间的数据同步
  6. 家庭账户:支持为家人管理用药

相关文档