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

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

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

21 KiB
Raw Permalink Blame History

药物管理 API 文档 - 客户端版本

基础信息

Base URL: https://your-domain.com/api
认证方式: Bearer Token (JWT)
Content-Type: application/json

认证说明

所有接口都需要在 HTTP Header 中携带 JWT Token

Authorization: Bearer <your_jwt_token>

数据类型定义

MedicationForm药物剂型

enum MedicationForm {
  CAPSULE = "capsule", // 胶囊
  PILL = "pill", // 药片
  INJECTION = "injection", // 注射
  SPRAY = "spray", // 喷雾
  DROP = "drop", // 滴剂
  SYRUP = "syrup", // 糖浆
  OTHER = "other", // 其他
}

RepeatPattern重复模式

enum RepeatPattern {
  DAILY = "daily", // 每日(目前仅支持每日模式)
}

MedicationStatus服药状态

enum MedicationStatus {
  UPCOMING = "upcoming", // 待服用
  TAKEN = "taken", // 已服用
  MISSED = "missed", // 已错过
  SKIPPED = "skipped", // 已跳过
}

Medication药物信息

interface Medication {
  id: string; // 药物唯一标识
  userId: string; // 用户ID
  name: string; // 药物名称
  photoUrl?: string; // 药物照片URL可选
  form: MedicationForm; // 药物剂型
  dosageValue: number; // 剂量数值(如 1、0.5
  dosageUnit: string; // 剂量单位(片、粒、毫升等)
  timesPerDay: number; // 每日服用次数
  medicationTimes: string[]; // 服药时间列表,格式:["08:00", "12:00", "18:00"]
  repeatPattern: RepeatPattern; // 重复模式
  startDate: string; // 开始日期ISO 8601 格式
  endDate?: string; // 结束日期可选ISO 8601 格式
  note?: string; // 备注信息(可选)
  isActive: boolean; // 是否激活
  deleted: boolean; // 是否已删除
  createdAt: string; // 创建时间ISO 8601 格式
  updatedAt: string; // 更新时间ISO 8601 格式
}

MedicationRecord服药记录

interface MedicationRecord {
  id: string; // 记录唯一标识
  medicationId: string; // 关联的药物ID
  userId: string; // 用户ID
  scheduledTime: string; // 计划服药时间ISO 8601 格式
  actualTime?: string; // 实际服药时间可选ISO 8601 格式
  status: MedicationStatus; // 服药状态
  note?: string; // 备注(可选)
  deleted: boolean; // 是否已删除
  createdAt: string; // 创建时间ISO 8601 格式
  updatedAt: string; // 更新时间ISO 8601 格式
  medication?: Medication; // 关联的药物信息(可选,用于联表查询)
}

DailyMedicationStats每日统计

interface DailyMedicationStats {
  date: string; // 日期格式YYYY-MM-DD
  totalScheduled: number; // 计划服药总次数
  taken: number; // 已服用次数
  missed: number; // 已错过次数
  upcoming: number; // 待服用次数
  completionRate: number; // 完成率百分比0-100保留两位小数
}

统一响应格式

interface ApiResponse<T> {
  code: number; // 状态码200成功其他为错误
  message: string; // 响应消息
  data: T | null; // 响应数据
}

药物管理接口

1. 获取药物列表

获取当前用户的药物列表。

接口: GET /medications

请求参数:

参数 类型 必填 说明
isActive boolean 是否只获取激活的药物,默认获取全部
page number 页码,默认 1
pageSize number 每页数量,默认 20

请求示例:

GET /medications?isActive=true&page=1&pageSize=20
Authorization: Bearer <token>

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": [
    {
      "id": "med_001",
      "userId": "user_123",
      "name": "阿司匹林",
      "photoUrl": "https://cdn.example.com/medications/aspirin.jpg",
      "form": "pill",
      "dosageValue": 1,
      "dosageUnit": "片",
      "timesPerDay": 2,
      "medicationTimes": ["08:00", "20:00"],
      "repeatPattern": "daily",
      "startDate": "2025-01-01T00:00:00.000Z",
      "endDate": null,
      "note": "饭后服用",
      "isActive": true,
      "deleted": false,
      "createdAt": "2025-01-01T00:00:00.000Z",
      "updatedAt": "2025-01-01T00:00:00.000Z"
    }
  ]
}

2. 获取单个药物详情

获取指定药物的详细信息。

接口: GET /medications/{id}

路径参数:

参数 类型 必填 说明
id string 药物ID

请求示例:

GET /medications/med_001
Authorization: Bearer <token>

响应: 同"获取药物列表"中的单个药物对象


3. 创建药物

创建新的药物信息。

接口: POST /medications

请求体:

{
  "name": "阿司匹林",
  "photoUrl": "https://cdn.example.com/medications/aspirin.jpg",
  "form": "pill",
  "dosageValue": 1,
  "dosageUnit": "片",
  "timesPerDay": 2,
  "medicationTimes": ["08:00", "20:00"],
  "repeatPattern": "daily",
  "startDate": "2025-01-01T00:00:00.000Z",
  "endDate": null,
  "note": "饭后服用"
}

字段说明:

字段 类型 必填 说明
name string 药物名称最大100字符
photoUrl string 药物照片URL
form MedicationForm 药物剂型
dosageValue number 剂量数值必须大于0
dosageUnit string 剂量单位最大20字符
timesPerDay number 每日服用次数1-10次
medicationTimes string[] 服药时间列表,格式:"HH:mm"数量必须等于timesPerDay
repeatPattern RepeatPattern 重复模式,目前仅支持"daily"
startDate string 开始日期ISO 8601格式
endDate string 结束日期ISO 8601格式
note string 备注信息

响应示例:

{
  "code": 200,
  "message": "创建成功",
  "data": {
    "id": "med_001",
    "userId": "user_123",
    "name": "阿司匹林",
    "photoUrl": "https://cdn.example.com/medications/aspirin.jpg",
    "form": "pill",
    "dosageValue": 1,
    "dosageUnit": "片",
    "timesPerDay": 2,
    "medicationTimes": ["08:00", "20:00"],
    "repeatPattern": "daily",
    "startDate": "2025-01-01T00:00:00.000Z",
    "endDate": null,
    "note": "饭后服用",
    "isActive": true,
    "deleted": false,
    "createdAt": "2025-01-01T00:00:00.000Z",
    "updatedAt": "2025-01-01T00:00:00.000Z"
  }
}

4. 更新药物信息

更新指定药物的信息。

接口: PUT /medications/{id}

路径参数:

参数 类型 必填 说明
id string 药物ID

请求体: 与创建药物相同,所有字段均为可选

请求示例:

{
  "dosageValue": 2,
  "timesPerDay": 3,
  "medicationTimes": ["08:00", "14:00", "20:00"],
  "note": "饭后服用,加量"
}

响应: 同"创建药物"响应


5. 删除药物

删除指定药物(软删除)。

接口: DELETE /medications/{id}

路径参数:

参数 类型 必填 说明
id string 药物ID

请求示例:

DELETE /medications/med_001
Authorization: Bearer <token>

响应示例:

{
  "code": 200,
  "message": "删除成功",
  "data": null
}

6. 停用药物

停用指定药物(将 isActive 设为 false

接口: POST /medications/{id}/deactivate

路径参数:

参数 类型 必填 说明
id string 药物ID

请求示例:

POST /medications/med_001/deactivate
Authorization: Bearer <token>

响应示例:

{
  "code": 200,
  "message": "停用成功",
  "data": {
    "id": "med_001",
    "isActive": false,
    "updatedAt": "2025-01-15T00:00:00.000Z"
  }
}

服药记录接口

1. 获取服药记录

查询服药记录,支持多种筛选条件。

接口: GET /medication-records

请求参数:

参数 类型 必填 说明
date string 指定日期YYYY-MM-DD不填则返回所有
startDate string 开始日期YYYY-MM-DD
endDate string 结束日期YYYY-MM-DD
medicationId string 指定药物ID
status MedicationStatus 状态筛选

重要说明:

  • 首次查询某天的记录时,后端会自动生成该天的服药记录(惰性生成)
  • 建议使用 date 参数查询特定日期,效率更高

请求示例:

GET /medication-records?date=2025-01-15&status=upcoming
Authorization: Bearer <token>

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": [
    {
      "id": "record_001",
      "medicationId": "med_001",
      "userId": "user_123",
      "scheduledTime": "2025-01-15T08:00:00.000Z",
      "actualTime": null,
      "status": "upcoming",
      "note": null,
      "deleted": false,
      "createdAt": "2025-01-15T00:00:00.000Z",
      "updatedAt": "2025-01-15T00:00:00.000Z",
      "medication": {
        "id": "med_001",
        "name": "阿司匹林",
        "form": "pill",
        "dosageValue": 1,
        "dosageUnit": "片"
      }
    }
  ]
}

2. 获取今日服药记录

快捷获取今天的所有服药记录。

接口: GET /medication-records/today

请求示例:

GET /medication-records/today
Authorization: Bearer <token>

响应: 同"获取服药记录"响应


3. 获取服药记录详情

获取指定服药记录的详细信息。

接口: GET /medication-records/{id}

路径参数:

参数 类型 必填 说明
id string 记录ID

请求示例:

GET /medication-records/record_001
Authorization: Bearer <token>

响应: 同"获取服药记录"中的单个记录对象


4. 标记为已服用

将服药记录标记为已服用。

接口: POST /medication-records/{id}/take

路径参数:

参数 类型 必填 说明
id string 记录ID

请求体:

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

字段说明:

字段 类型 必填 说明
actualTime string 实际服药时间ISO 8601格式不填则使用当前时间

响应示例:

{
  "code": 200,
  "message": "已记录服药",
  "data": {
    "id": "record_001",
    "medicationId": "med_001",
    "userId": "user_123",
    "scheduledTime": "2025-01-15T08:00:00.000Z",
    "actualTime": "2025-01-15T08:10:00.000Z",
    "status": "taken",
    "note": null,
    "deleted": false,
    "createdAt": "2025-01-15T00:00:00.000Z",
    "updatedAt": "2025-01-15T08:10:00.000Z"
  }
}

5. 跳过服药

跳过本次服药,不计入已错过。

接口: POST /medication-records/{id}/skip

路径参数:

参数 类型 必填 说明
id string 记录ID

请求体:

{
  "note": "今天身体不适,暂时跳过"
}

字段说明:

字段 类型 必填 说明
note string 跳过原因备注

响应示例:

{
  "code": 200,
  "message": "已跳过服药",
  "data": {
    "id": "record_001",
    "medicationId": "med_001",
    "userId": "user_123",
    "scheduledTime": "2025-01-15T08:00:00.000Z",
    "actualTime": null,
    "status": "skipped",
    "note": "今天身体不适,暂时跳过",
    "deleted": false,
    "createdAt": "2025-01-15T00:00:00.000Z",
    "updatedAt": "2025-01-15T08:15:00.000Z"
  }
}

6. 更新服药记录

更新服药记录的状态和信息。

接口: PUT /medication-records/{id}

路径参数:

参数 类型 必填 说明
id string 记录ID

请求体:

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

字段说明: 所有字段均为可选

字段 类型 必填 说明
status MedicationStatus 服药状态
actualTime string 实际服药时间ISO 8601格式
note string 备注信息

响应: 同"标记为已服用"响应


统计接口

1. 获取每日统计

获取指定日期的服药统计数据。

接口: GET /medication-stats/daily

请求参数:

参数 类型 必填 说明
date string 日期格式YYYY-MM-DD

请求示例:

GET /medication-stats/daily?date=2025-01-15
Authorization: Bearer <token>

响应示例:

{
  "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 string 开始日期格式YYYY-MM-DD
endDate string 结束日期格式YYYY-MM-DD

请求示例:

GET /medication-stats/range?startDate=2025-01-01&endDate=2025-01-15
Authorization: Bearer <token>

响应示例:

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

3. 获取总体统计

获取用户的总体服药统计概览。

接口: GET /medication-stats/overall

请求示例:

GET /medication-stats/overall
Authorization: Bearer <token>

响应示例:

{
  "code": 200,
  "message": "查询成功",
  "data": {
    "totalMedications": 5,
    "totalRecords": 120,
    "completionRate": 85.5,
    "streak": 7
  }
}

字段说明:

字段 类型 说明
totalMedications number 药物总数
totalRecords number 服药记录总数
completionRate number 总体完成率(百分比,保留两位小数)
streak number 连续完成天数

错误码说明

错误码 说明
200 操作成功
400 请求参数错误
401 未授权Token无效或已过期
403 权限不足,无法访问该资源
404 资源不存在
409 资源冲突(如重复创建)
500 服务器内部错误

错误响应格式:

{
  "code": 400,
  "message": "请求参数错误:药物名称不能为空",
  "data": null
}

业务逻辑说明

1. 服药记录的惰性生成

  • 服药记录不会在创建药物时预先生成
  • 当首次查询某天的记录时,后端会自动生成该天的所有服药记录
  • 生成规则:根据药物的 timesPerDaymedicationTimes 生成对应数量的记录

示例

  • 如果药物设置为每天2次服药时间为 08:00 和 20:00
  • 首次查询 2025-01-15 的记录时,会自动生成该天 08:00 和 20:00 两条记录

2. 状态自动更新

  • 后端每30分钟运行一次定时任务
  • 自动将已过期的 upcoming 状态更新为 missed
  • 客户端无需手动更新状态

示例

  • 08:00 的服药记录,到了 08:30 还未标记为已服用
  • 定时任务会自动将其状态改为 missed

3. 推送提醒

  • 后端每5分钟检查一次即将到来的服药时间15-20分钟后
  • 自动发送推送通知提醒用户服药
  • 客户端需要正确配置推送通知权限

4. 时区处理

  • 重要:所有时间字段使用 UTC 时间存储和传输
  • 客户端需要:
    1. 发送请求时:将本地时间转换为 UTC
    2. 接收响应时:将 UTC 时间转换为本地时间显示

示例代码JavaScript:

// 本地时间转 UTC
const localTime = new Date("2025-01-15 08:00");
const utcTime = localTime.toISOString(); // "2025-01-15T00:00:00.000Z" (假设时区为UTC+8)

// UTC 转本地时间
const utcTime = "2025-01-15T00:00:00.000Z";
const localTime = new Date(utcTime);
console.log(localTime.toLocaleString()); // "2025-01-15 08:00:00" (UTC+8)

最佳实践建议

1. 获取今日服药记录

推荐使用专用接口而非通用查询:

✅ 推荐
GET /medication-records/today

❌ 不推荐
GET /medication-records?date=2025-01-15

2. 批量操作

如果需要更新多个记录,建议单独调用每个接口,后端暂不支持批量操作。

3. 错误处理

建议在客户端统一处理 API 错误:

async function callApi(url, options) {
  try {
    const response = await fetch(url, options);
    const data = await response.json();

    if (data.code !== 200) {
      // 显示错误提示
      showError(data.message);
      return null;
    }

    return data.data;
  } catch (error) {
    // 网络错误
    showError("网络连接失败,请稍后重试");
    return null;
  }
}

4. 数据缓存

建议在客户端缓存以下数据以提升性能:

  • 药物列表(有变更时刷新)
  • 今日服药记录(实时更新)
  • 统计数据(按天缓存)

5. 定时刷新

建议定时刷新今日服药记录以获取最新状态:

// 每5分钟刷新一次今日记录
setInterval(
  async () => {
    const records = await getTodayRecords();
    updateUI(records);
  },
  5 * 60 * 1000
);

完整使用流程示例

1. 创建药物并查看今日记录

// Step 1: 创建药物
const medication = await createMedication({
  name: "阿司匹林",
  form: "pill",
  dosageValue: 1,
  dosageUnit: "片",
  timesPerDay: 2,
  medicationTimes: ["08:00", "20:00"],
  repeatPattern: "daily",
  startDate: new Date().toISOString(),
  note: "饭后服用",
});

// Step 2: 获取今日服药记录(会自动生成)
const todayRecords = await getTodayRecords();

// Step 3: 显示记录列表
showRecordsList(todayRecords);

2. 标记服用并查看统计

// Step 1: 标记为已服用
await takeMedication(recordId, {
  actualTime: new Date().toISOString(),
});

// Step 2: 刷新今日记录
const todayRecords = await getTodayRecords();

// Step 3: 获取今日统计
const todayStats = await getDailyStats(formatDate(new Date()));

// Step 4: 显示完成率
showCompletionRate(todayStats.completionRate);

3. 查看历史统计

// 获取最近7天的统计
const startDate = formatDate(daysAgo(7));
const endDate = formatDate(new Date());

const rangeStats = await getRangeStats(startDate, endDate);

// 绘制趋势图表
drawChart(rangeStats);

技术支持

如有疑问或需要帮助,请联系:


更新日志

v1.0 (2025-01-15)

  • 初始版本发布
  • 完整的药物管理功能
  • 服药记录追踪
  • 统计分析功能
  • 自动状态更新
  • 推送提醒支持