feat(medications): 优化药物删除功能,添加事务处理和批量操作

- 在药物删除时使用事务确保数据一致性
- 删除药物时同时软删除所有相关用药记录
- 优化删除操作性能,使用批量更新替代循环删除
- 扩展删除范围,从删除未服用记录改为删除当天所有记录
- 添加完善的错误处理和日志记录
This commit is contained in:
richarjiang
2025-11-10 14:46:10 +08:00
parent e25002e018
commit 2850eba7cf

View File

@@ -4,14 +4,15 @@ import {
ForbiddenException, ForbiddenException,
Logger, Logger,
} from '@nestjs/common'; } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize'; import { InjectModel, InjectConnection } from '@nestjs/sequelize';
import { Medication } from './models/medication.model'; import { Medication } from './models/medication.model';
import { MedicationRecord } from './models/medication-record.model'; import { MedicationRecord } from './models/medication-record.model';
import { CreateMedicationDto } from './dto/create-medication.dto'; import { CreateMedicationDto } from './dto/create-medication.dto';
import { UpdateMedicationDto } from './dto/update-medication.dto'; import { UpdateMedicationDto } from './dto/update-medication.dto';
import { MedicationStatusEnum } from './enums/medication-status.enum'; import { MedicationStatusEnum } from './enums/medication-status.enum';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { Op } from 'sequelize'; import { Op, Transaction } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import * as dayjs from 'dayjs'; import * as dayjs from 'dayjs';
/** /**
@@ -26,6 +27,8 @@ export class MedicationsService {
private readonly medicationModel: typeof Medication, private readonly medicationModel: typeof Medication,
@InjectModel(MedicationRecord) @InjectModel(MedicationRecord)
private readonly recordModel: typeof MedicationRecord, private readonly recordModel: typeof MedicationRecord,
@InjectConnection()
private readonly sequelize: Sequelize,
) {} ) {}
/** /**
@@ -181,12 +184,25 @@ export class MedicationsService {
* 删除药物(软删除) * 删除药物(软删除)
*/ */
async remove(id: string, userId: string): Promise<void> { async remove(id: string, userId: string): Promise<void> {
const medication = await this.findOne(id, userId); const transaction = await this.sequelize.transaction();
medication.deleted = true; try {
await medication.save(); const medication = await this.findOne(id, userId);
this.logger.log(`成功删除药物 ${id}`); // 软删除药物
medication.deleted = true;
await medication.save({ transaction });
// 软删除所有相关的用药记录
await this.deleteAllMedicationRecords(medication, transaction);
await transaction.commit();
this.logger.log(`成功删除药物 ${id} 及其所有记录`);
} catch (error) {
await transaction.rollback();
this.logger.error(`删除药物失败: ${error instanceof Error ? error.message : '未知错误'}`);
throw error;
}
} }
/** /**
@@ -307,52 +323,62 @@ export class MedicationsService {
); );
} }
/** /**
* 删除当天未服用的药物记录 * 删除当天的药物记录
* 当药物被停用时,删除当天生成的但还未服用的记录 * 当药物被停用时,删除当天生成的所有记录
*/ */
private async deleteTodayUntakenRecords(medication: Medication): Promise<void> { private async deleteTodayUntakenRecords(medication: Medication, transaction?: Transaction): Promise<void> {
// 使用当前时间作为基准,查询当前时间及未来的未服用记录 // 获取当天的开始和结束时间
const now = new Date(); const today = dayjs().format('YYYY-MM-DD');
const todayStart = dayjs(today).startOf('day').toDate();
const todayEnd = dayjs(today).endOf('day').toDate();
this.logger.log( this.logger.log(
`开始删除药物 ${medication.id} 从现在起(${dayjs(now).format('YYYY-MM-DD HH:mm')})的未服用记录`, `开始删除药物 ${medication.id} ${today} 的所有记录`,
); );
// 查询从现在开始的所有未服用记录(状态为 UPCOMING // 使用批量更新而不是循环删除,提高性能
const recordsToDelete = await this.recordModel.findAll({ const [affectedCount] = await this.recordModel.update(
where: { { deleted: true },
medicationId: medication.id, {
userId: medication.userId, where: {
status: MedicationStatusEnum.UPCOMING, medicationId: medication.id,
scheduledTime: { userId: medication.userId,
[Op.gte]: now, // 大于等于当前时间 scheduledTime: {
[Op.between]: [todayStart, todayEnd], // 当天的所有记录
},
deleted: false,
}, },
deleted: false, transaction,
}, }
});
this.logger.log(
`找到 ${recordsToDelete.length} 条需要删除的 ${medication.id} 记录`,
); );
if (recordsToDelete.length === 0) { this.logger.log(
this.logger.log(`没有找到 ${medication.id} 需要删除的记录`); `成功批量软删除了 ${affectedCount} ${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(); private async deleteAllMedicationRecords(medication: Medication, transaction?: Transaction): Promise<void> {
this.logger.log(`开始删除药物 ${medication.id} 的所有相关记录`);
this.logger.log( // 使用批量更新而不是循环删除,提高性能
`软删除记录 ${record.id},计划时间:${scheduledTime}`, const [affectedCount] = await this.recordModel.update(
); { deleted: true },
} {
where: {
medicationId: medication.id,
userId: medication.userId,
deleted: false,
},
transaction,
}
);
this.logger.log( this.logger.log(
`成功删除了 ${recordsToDelete.length}${medication.id}未服用记录`, `成功批量软删除了 ${affectedCount}${medication.id} 的记录`,
); );
} }
} }