feat(medications): 新增完整的药物管理和服药提醒功能

实现了包含药物信息管理、服药记录追踪、统计分析、自动状态更新和推送提醒的完整药物管理系统。

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

技术实现:
- 使用 Sequelize ORM 管理 MySQL 数据表
- 集成 @nestjs/schedule 实现定时任务
- 复用现有推送通知系统发送提醒
- 采用软删除和权限验证保障数据安全
This commit is contained in:
richarjiang
2025-11-07 17:29:11 +08:00
parent 37cc2a729b
commit 188b4addca
27 changed files with 3464 additions and 0 deletions

379
src/medications/README.md Normal file
View File

@@ -0,0 +1,379 @@
# 药物管理模块
## 概述
药物管理模块提供完整的用药提醒和服药记录管理功能,包括:
- 药物信息管理CRUD
- 服药记录追踪
- 统计分析
- 自动状态更新
- 推送提醒
## 功能特性
### 1. 药物管理
- 支持多种药物剂型(胶囊、药片、注射、喷雾、滴剂、糖浆等)
- 灵活的服药时间设置
- 支持每日重复模式
- 可设置开始和结束日期
- 支持添加药物照片和备注
### 2. 服药记录
- **惰性生成策略**:查询时才生成当天记录,避免预先生成大量数据
- 四种状态:待服用(upcoming)、已服用(taken)、已错过(missed)、已跳过(skipped)
- 自动状态更新定时任务每30分钟检查并更新过期记录
- 支持标记服用、跳过服药、更新记录
### 3. 统计功能
- 每日统计:计划服药次数、已服用、已错过、待服用、完成率
- 日期范围统计:支持查询任意时间段的统计数据
- 总体统计概览:总记录数、完成率等
### 4. 推送提醒
- 定时任务每5分钟检查即将到来的服药时间提前15-20分钟
- 集成现有推送通知系统
- 支持自定义提醒消息
## API 接口
### 药物管理接口
#### 1. 获取药物列表
```http
GET /medications?isActive=true&page=1&pageSize=20
```
#### 2. 创建药物
```http
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. 更新药物
```http
PUT /medications/{id}
Content-Type: application/json
{
"dosageValue": 2,
"timesPerDay": 3,
"medicationTimes": ["08:00", "14:00", "20:00"]
}
```
#### 4. 删除药物
```http
DELETE /medications/{id}
```
#### 5. 停用药物
```http
POST /medications/{id}/deactivate
```
### 服药记录接口
#### 1. 获取服药记录
```http
GET /medication-records?date=2025-01-15&status=upcoming
```
#### 2. 获取今日服药记录
```http
GET /medication-records/today
```
#### 3. 标记为已服用
```http
POST /medication-records/{recordId}/take
Content-Type: application/json
{
"actualTime": "2025-01-15T08:10:00.000Z"
}
```
#### 4. 跳过服药
```http
POST /medication-records/{recordId}/skip
Content-Type: application/json
{
"note": "今天状态不好,暂时跳过"
}
```
#### 5. 更新服药记录
```http
PUT /medication-records/{recordId}
Content-Type: application/json
{
"status": "taken",
"actualTime": "2025-01-15T08:15:00.000Z",
"note": "延迟服用"
}
```
### 统计接口
#### 1. 获取每日统计
```http
GET /medication-stats/daily?date=2025-01-15
```
响应示例:
```json
{
"code": 200,
"message": "查询成功",
"data": {
"date": "2025-01-15",
"totalScheduled": 6,
"taken": 4,
"missed": 1,
"upcoming": 1,
"completionRate": 66.67
}
}
```
#### 2. 获取日期范围统计
```http
GET /medication-stats/range?startDate=2025-01-01&endDate=2025-01-15
```
#### 3. 获取总体统计
```http
GET /medication-stats/overall
```
## 数据模型
### Medication药物
```typescript
{
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服药记录
```typescript
{
id: string;
medicationId: string;
userId: string;
scheduledTime: Date; // 计划服药时间
actualTime?: Date; // 实际服药时间
status: MedicationStatus;
note?: string;
createdAt: Date;
updatedAt: Date;
}
```
## 业务逻辑说明
### 1. 惰性生成策略
服药记录采用惰性生成策略,而非预先生成大量记录:
```typescript
// 查询记录时自动生成当天记录
async findAll(userId: string, query: MedicationRecordQueryDto) {
// 1. 确保指定日期的记录存在
await this.recordGenerator.ensureRecordsExist(userId, query.date);
// 2. 查询并返回记录
return this.recordModel.findAll({...});
}
```
### 2. 状态自动更新
定时任务每30分钟检查并更新过期记录
```typescript
@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分钟检查即将到来的服药时间
```typescript
@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. 执行数据库迁移
```bash
# 连接到 MySQL 数据库
mysql -u your_username -p your_database
# 执行建表 SQL
source sql-scripts/medications-tables-create.sql
```
### 2. 环境变量
确保 `.env` 文件中包含以下配置:
```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. 启动应用
```bash
# 开发模式
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. **家庭账户**:支持为家人管理用药
## 相关文档
- [API 规范文档](../../docs/medication-api-spec.md)
- [数据库设计](../../sql-scripts/medications-tables-create.sql)
- [推送通知文档](../push-notifications/README_PUSH_TEST.md)