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

947 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 药物管理 API 文档 - 客户端版本
## 基础信息
**Base URL**: `https://your-domain.com/api`
**认证方式**: Bearer Token (JWT)
**Content-Type**: `application/json`
## 认证说明
所有接口都需要在 HTTP Header 中携带 JWT Token
```http
Authorization: Bearer <your_jwt_token>
```
## 数据类型定义
### MedicationForm药物剂型
```typescript
enum MedicationForm {
CAPSULE = "capsule", // 胶囊
PILL = "pill", // 药片
INJECTION = "injection", // 注射
SPRAY = "spray", // 喷雾
DROP = "drop", // 滴剂
SYRUP = "syrup", // 糖浆
OTHER = "other", // 其他
}
```
### RepeatPattern重复模式
```typescript
enum RepeatPattern {
DAILY = "daily", // 每日(目前仅支持每日模式)
}
```
### MedicationStatus服药状态
```typescript
enum MedicationStatus {
UPCOMING = "upcoming", // 待服用
TAKEN = "taken", // 已服用
MISSED = "missed", // 已错过
SKIPPED = "skipped", // 已跳过
}
```
### Medication药物信息
```typescript
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服药记录
```typescript
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每日统计
```typescript
interface DailyMedicationStats {
date: string; // 日期格式YYYY-MM-DD
totalScheduled: number; // 计划服药总次数
taken: number; // 已服用次数
missed: number; // 已错过次数
upcoming: number; // 待服用次数
completionRate: number; // 完成率百分比0-100保留两位小数
}
```
### 统一响应格式
```typescript
interface ApiResponse<T> {
code: number; // 状态码200成功其他为错误
message: string; // 响应消息
data: T | null; // 响应数据
}
```
---
## 药物管理接口
### 1. 获取药物列表
获取当前用户的药物列表。
**接口**: `GET /medications`
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
| -------- | ------- | ---- | ---------------------------------- |
| isActive | boolean | 否 | 是否只获取激活的药物,默认获取全部 |
| page | number | 否 | 页码,默认 1 |
| pageSize | number | 否 | 每页数量,默认 20 |
**请求示例**:
```http
GET /medications?isActive=true&page=1&pageSize=20
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"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 |
**请求示例**:
```http
GET /medications/med_001
Authorization: Bearer <token>
```
**响应**: 同"获取药物列表"中的单个药物对象
---
### 3. 创建药物
创建新的药物信息。
**接口**: `POST /medications`
**请求体**:
```json
{
"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 | 否 | 备注信息 |
**响应示例**:
```json
{
"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 |
**请求体**: 与创建药物相同,所有字段均为可选
**请求示例**:
```json
{
"dosageValue": 2,
"timesPerDay": 3,
"medicationTimes": ["08:00", "14:00", "20:00"],
"note": "饭后服用,加量"
}
```
**响应**: 同"创建药物"响应
---
### 5. 删除药物
删除指定药物(软删除)。
**接口**: `DELETE /medications/{id}`
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
| ---- | ------ | ---- | ------ |
| id | string | 是 | 药物ID |
**请求示例**:
```http
DELETE /medications/med_001
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"code": 200,
"message": "删除成功",
"data": null
}
```
---
### 6. 停用药物
停用指定药物(将 isActive 设为 false
**接口**: `POST /medications/{id}/deactivate`
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
| ---- | ------ | ---- | ------ |
| id | string | 是 | 药物ID |
**请求示例**:
```http
POST /medications/med_001/deactivate
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"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` 参数查询特定日期,效率更高
**请求示例**:
```http
GET /medication-records?date=2025-01-15&status=upcoming
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"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`
**请求示例**:
```http
GET /medication-records/today
Authorization: Bearer <token>
```
**响应**: 同"获取服药记录"响应
---
### 3. 获取服药记录详情
获取指定服药记录的详细信息。
**接口**: `GET /medication-records/{id}`
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
| ---- | ------ | ---- | ------ |
| id | string | 是 | 记录ID |
**请求示例**:
```http
GET /medication-records/record_001
Authorization: Bearer <token>
```
**响应**: 同"获取服药记录"中的单个记录对象
---
### 4. 标记为已服用
将服药记录标记为已服用。
**接口**: `POST /medication-records/{id}/take`
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
| ---- | ------ | ---- | ------ |
| id | string | 是 | 记录ID |
**请求体**:
```json
{
"actualTime": "2025-01-15T08:10:00.000Z"
}
```
**字段说明**:
| 字段 | 类型 | 必填 | 说明 |
| ---------- | ------ | ---- | ---------------------------------------------- |
| actualTime | string | 否 | 实际服药时间ISO 8601格式不填则使用当前时间 |
**响应示例**:
```json
{
"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 |
**请求体**:
```json
{
"note": "今天身体不适,暂时跳过"
}
```
**字段说明**:
| 字段 | 类型 | 必填 | 说明 |
| ---- | ------ | ---- | ------------ |
| note | string | 否 | 跳过原因备注 |
**响应示例**:
```json
{
"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 |
**请求体**:
```json
{
"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 |
**请求示例**:
```http
GET /medication-stats/daily?date=2025-01-15
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"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 |
**请求示例**:
```http
GET /medication-stats/range?startDate=2025-01-01&endDate=2025-01-15
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"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`
**请求示例**:
```http
GET /medication-stats/overall
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"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 | 服务器内部错误 |
**错误响应格式**:
```json
{
"code": 400,
"message": "请求参数错误:药物名称不能为空",
"data": null
}
```
---
## 业务逻辑说明
### 1. 服药记录的惰性生成
- 服药记录不会在创建药物时预先生成
- 当首次查询某天的记录时,后端会自动生成该天的所有服药记录
- 生成规则:根据药物的 `timesPerDay``medicationTimes` 生成对应数量的记录
**示例**
- 如果药物设置为每天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**:
```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. 获取今日服药记录
推荐使用专用接口而非通用查询:
```http
✅ 推荐
GET /medication-records/today
❌ 不推荐
GET /medication-records?date=2025-01-15
```
### 2. 批量操作
如果需要更新多个记录,建议单独调用每个接口,后端暂不支持批量操作。
### 3. 错误处理
建议在客户端统一处理 API 错误:
```javascript
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. 定时刷新
建议定时刷新今日服药记录以获取最新状态:
```javascript
// 每5分钟刷新一次今日记录
setInterval(
async () => {
const records = await getTodayRecords();
updateUI(records);
},
5 * 60 * 1000
);
```
---
## 完整使用流程示例
### 1. 创建药物并查看今日记录
```javascript
// 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. 标记服用并查看统计
```javascript
// 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. 查看历史统计
```javascript
// 获取最近7天的统计
const startDate = formatDate(daysAgo(7));
const endDate = formatDate(new Date());
const rangeStats = await getRangeStats(startDate, endDate);
// 绘制趋势图表
drawChart(rangeStats);
```
---
## 技术支持
如有疑问或需要帮助,请联系:
- **技术文档**: 本文档
- **API 基础URL**: https://your-domain.com/api
- **更新日期**: 2025-01-15
- **文档版本**: v1.0
---
## 更新日志
### v1.0 (2025-01-15)
- 初始版本发布
- 完整的药物管理功能
- 服药记录追踪
- 统计分析功能
- 自动状态更新
- 推送提醒支持