feat(medications): 新增药物激活状态管理及相关记录更新功能

This commit is contained in:
richarjiang
2025-11-10 11:04:36 +08:00
parent 188b4addca
commit e25002e018
4 changed files with 235 additions and 26 deletions

View File

@@ -6,9 +6,13 @@ import {
} from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { Medication } from './models/medication.model';
import { MedicationRecord } from './models/medication-record.model';
import { CreateMedicationDto } from './dto/create-medication.dto';
import { UpdateMedicationDto } from './dto/update-medication.dto';
import { MedicationStatusEnum } from './enums/medication-status.enum';
import { v4 as uuidv4 } from 'uuid';
import { Op } from 'sequelize';
import * as dayjs from 'dayjs';
/**
* 药物管理服务
@@ -20,6 +24,8 @@ export class MedicationsService {
constructor(
@InjectModel(Medication)
private readonly medicationModel: typeof Medication,
@InjectModel(MedicationRecord)
private readonly recordModel: typeof MedicationRecord,
) {}
/**
@@ -45,7 +51,7 @@ export class MedicationsService {
startDate: new Date(createDto.startDate),
endDate: createDto.endDate ? new Date(createDto.endDate) : null,
note: createDto.note,
isActive: true,
isActive: createDto.isActive !== undefined ? createDto.isActive : true,
deleted: false,
});
@@ -114,6 +120,9 @@ export class MedicationsService {
): Promise<Medication> {
const medication = await this.findOne(id, userId);
// 保存更新前的状态
const wasActive = medication.isActive;
// 更新字段
if (updateDto.name !== undefined) {
medication.name = updateDto.name;
@@ -148,9 +157,22 @@ export class MedicationsService {
if (updateDto.note !== undefined) {
medication.note = updateDto.note;
}
if (updateDto.isActive !== undefined) {
medication.isActive = updateDto.isActive;
}
await medication.save();
// 如果从激活状态变为停用状态,删除当天未服用的记录
if (updateDto.isActive !== undefined && wasActive && !updateDto.isActive) {
await this.deleteTodayUntakenRecords(medication);
}
// 如果更新了服药时间,更新当天相关的未服用记录
if (updateDto.medicationTimes) {
await this.updateTodayUntakenRecords(medication, updateDto.medicationTimes);
}
this.logger.log(`成功更新药物 ${id}`);
return medication;
}
@@ -173,9 +195,21 @@ export class MedicationsService {
async deactivate(id: string, userId: string): Promise<Medication> {
const medication = await this.findOne(id, userId);
// 如果已经是停用状态,不需要处理
if (!medication.isActive) {
this.logger.log(`药物 ${id} 已经是停用状态`);
return medication;
}
const wasActive = medication.isActive;
medication.isActive = false;
await medication.save();
// 删除当天未服用的记录
if (wasActive && !medication.isActive) {
await this.deleteTodayUntakenRecords(medication);
}
this.logger.log(`成功停用药物 ${id}`);
return medication;
}
@@ -186,10 +220,139 @@ export class MedicationsService {
async activate(id: string, userId: string): Promise<Medication> {
const medication = await this.findOne(id, userId);
// 如果已经是激活状态,不需要处理
if (medication.isActive) {
this.logger.log(`药物 ${id} 已经是激活状态`);
return medication;
}
const wasActive = medication.isActive;
medication.isActive = true;
await medication.save();
// 当药物从停用变为激活时RecordGeneratorService 会负责生成新记录
// 但这里不需要手动处理,因为采用的是惰性生成策略
this.logger.log(`成功激活药物 ${id}`);
return medication;
}
/**
* 更新当天相关的未服用记录
* 当药物的服药时间被更新时,将当天未服用的记录更新为新的服药时间
*/
private async updateTodayUntakenRecords(
medication: Medication,
newMedicationTimes: string[],
): Promise<void> {
const today = dayjs().format('YYYY-MM-DD');
const todayStart = dayjs(today).startOf('day').toDate();
const todayEnd = dayjs(today).endOf('day').toDate();
this.logger.log(
`开始更新药物 ${medication.id}${today} 的未服用记录`,
);
// 查询当天该药物的未服用记录(状态为 UPCOMING
const recordsToUpdate = await this.recordModel.findAll({
where: {
medicationId: medication.id,
userId: medication.userId,
status: MedicationStatusEnum.UPCOMING,
scheduledTime: {
[Op.between]: [todayStart, todayEnd],
},
deleted: false,
},
});
if (recordsToUpdate.length === 0) {
this.logger.log(`没有找到 ${medication.id} 需要更新的记录`);
return;
}
// 排序记录,按计划时间升序
recordsToUpdate.sort((a, b) => {
const timeA = dayjs(a.scheduledTime).format('HH:mm');
const timeB = dayjs(b.scheduledTime).format('HH:mm');
return timeA.localeCompare(timeB);
});
// 更新记录的计划时间
for (let i = 0; i < recordsToUpdate.length && i < newMedicationTimes.length; i++) {
const record = recordsToUpdate[i];
const newTime = newMedicationTimes[i];
// 解析新的时间字符串HH:mm
const [hours, minutes] = newTime.split(':').map(Number);
// 重新计算计划服药时间(基于今天)
const newScheduledTime = dayjs(today)
.hour(hours)
.minute(minutes)
.second(0)
.millisecond(0)
.toDate();
record.scheduledTime = newScheduledTime;
await record.save();
this.logger.log(
`更新记录 ${record.id} 的时间从 ${dayjs(record.scheduledTime).format('HH:mm')}${newTime}`,
);
}
this.logger.log(
`成功更新了 ${recordsToUpdate.length}${medication.id} 的当天记录`,
);
}
/**
* 删除当天未服用的药物记录
* 当药物被停用时,删除当天生成的但还未服用的记录
*/
private async deleteTodayUntakenRecords(medication: Medication): Promise<void> {
// 使用当前时间作为基准,查询当前时间及未来的未服用记录
const now = new Date();
this.logger.log(
`开始删除药物 ${medication.id} 从现在起(${dayjs(now).format('YYYY-MM-DD HH:mm')})的未服用记录`,
);
// 查询从现在开始的所有未服用记录(状态为 UPCOMING
const recordsToDelete = await this.recordModel.findAll({
where: {
medicationId: medication.id,
userId: medication.userId,
status: MedicationStatusEnum.UPCOMING,
scheduledTime: {
[Op.gte]: now, // 大于等于当前时间
},
deleted: false,
},
});
this.logger.log(
`找到 ${recordsToDelete.length} 条需要删除的 ${medication.id} 记录`,
);
if (recordsToDelete.length === 0) {
this.logger.log(`没有找到 ${medication.id} 需要删除的记录`);
return;
}
// 软删除记录
for (const record of recordsToDelete) {
const scheduledTime = dayjs(record.scheduledTime).format('YYYY-MM-DD HH:mm');
record.deleted = true;
await record.save();
this.logger.log(
`软删除记录 ${record.id},计划时间:${scheduledTime}`,
);
}
this.logger.log(
`成功删除了 ${recordsToDelete.length}${medication.id} 的未服用记录`,
);
}
}