|
@@ -1,5 +1,7 @@
|
|
|
package cn.iocoder.yudao.module.system.service.doctororder;
|
|
package cn.iocoder.yudao.module.system.service.doctororder;
|
|
|
|
|
|
|
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
|
@@ -8,24 +10,37 @@ import cn.iocoder.yudao.module.system.controller.admin.doctororder.vo.DoctorOrde
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.doctororder.vo.DoctorOrderExecutionSaveReqVO;
|
|
import cn.iocoder.yudao.module.system.controller.admin.doctororder.vo.DoctorOrderExecutionSaveReqVO;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.DoctorOrderExecutionDO;
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.DoctorOrderExecutionDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyInfoDO;
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyInfoDO;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.LongDoctorOrderDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.LongDoctorOrderEntryDO;
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.LongDoctorOrderEntryDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.DoctorOrderExecutionMapper;
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.DoctorOrderExecutionMapper;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyInfoMapper;
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyInfoMapper;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.LongDoctorOrderEntryMapper;
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.LongDoctorOrderEntryMapper;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.dal.mysql.biz.LongDoctorOrderMapper;
|
|
|
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
|
|
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
|
|
|
import cn.iocoder.yudao.module.system.enums.biz.DoctorOrderExecTypeEnum;
|
|
import cn.iocoder.yudao.module.system.enums.biz.DoctorOrderExecTypeEnum;
|
|
|
import cn.iocoder.yudao.module.system.enums.biz.DoctorOrderExecutionStatusEnum;
|
|
import cn.iocoder.yudao.module.system.enums.biz.DoctorOrderExecutionStatusEnum;
|
|
|
import cn.iocoder.yudao.module.system.enums.biz.DoctorOrderTypeEnum;
|
|
import cn.iocoder.yudao.module.system.enums.biz.DoctorOrderTypeEnum;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.enums.biz.LongOrderEntryStatusEnum;
|
|
|
|
|
+import cn.iocoder.yudao.module.system.service.sys.BusinessParamsConfigService;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.time.LocalTime;
|
|
|
import java.time.Period;
|
|
import java.time.Period;
|
|
|
import java.time.ZoneId;
|
|
import java.time.ZoneId;
|
|
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
|
|
+import java.time.format.DateTimeParseException;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
|
|
+import java.util.Collections;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -35,14 +50,28 @@ import java.util.stream.Collectors;
|
|
|
*/
|
|
*/
|
|
|
@Service
|
|
@Service
|
|
|
@Validated
|
|
@Validated
|
|
|
|
|
+@Slf4j
|
|
|
public class DoctorOrderExecutionServiceImpl implements DoctorOrderExecutionService {
|
|
public class DoctorOrderExecutionServiceImpl implements DoctorOrderExecutionService {
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计划执行时间支持的格式,兼容 HH:mm / H:mm / HH:mm:ss / H:mm:ss
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final List<DateTimeFormatter> PLAN_EXEC_TIME_FORMATTERS = Arrays.asList(
|
|
|
|
|
+ DateTimeFormatter.ofPattern("HH:mm"),
|
|
|
|
|
+ DateTimeFormatter.ofPattern("H:mm"),
|
|
|
|
|
+ DateTimeFormatter.ofPattern("HH:mm:ss"),
|
|
|
|
|
+ DateTimeFormatter.ofPattern("H:mm:ss"));
|
|
|
|
|
+
|
|
|
@Resource
|
|
@Resource
|
|
|
private DoctorOrderExecutionMapper doctorOrderExecutionMapper;
|
|
private DoctorOrderExecutionMapper doctorOrderExecutionMapper;
|
|
|
@Resource
|
|
@Resource
|
|
|
private ElderlyInfoMapper elderlyInfoMapper;
|
|
private ElderlyInfoMapper elderlyInfoMapper;
|
|
|
@Resource
|
|
@Resource
|
|
|
private LongDoctorOrderEntryMapper longDoctorOrderEntryMapper;
|
|
private LongDoctorOrderEntryMapper longDoctorOrderEntryMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private LongDoctorOrderMapper longDoctorOrderMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private BusinessParamsConfigService businessParamsConfigService;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public PageResult<DoctorOrderExecutionRespVO> getDoctorOrderExecutionPage(DoctorOrderExecutionPageReqVO reqVO) {
|
|
public PageResult<DoctorOrderExecutionRespVO> getDoctorOrderExecutionPage(DoctorOrderExecutionPageReqVO reqVO) {
|
|
@@ -106,6 +135,121 @@ public class DoctorOrderExecutionServiceImpl implements DoctorOrderExecutionServ
|
|
|
doctorOrderExecutionMapper.updateById(exist);
|
|
doctorOrderExecutionMapper.updateById(exist);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public int generateExecutionsForNewEntries(List<LongDoctorOrderEntryDO> entries) {
|
|
|
|
|
+ if (CollUtil.isEmpty(entries)) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 同一个主单的条目复用一份主单信息,避免重复查询
|
|
|
|
|
+ Map<Long, LongDoctorOrderDO> orderCache = new HashMap<>();
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ for (LongDoctorOrderEntryDO entry : entries) {
|
|
|
|
|
+ if (entry == null || entry.getLongDoctorOrderId() == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ LongDoctorOrderDO orderDO = orderCache.computeIfAbsent(entry.getLongDoctorOrderId(),
|
|
|
|
|
+ id -> longDoctorOrderMapper.selectById(id));
|
|
|
|
|
+ count += doGenerate(Collections.singletonList(entry), orderDO, LocalDate.now());
|
|
|
|
|
+ }
|
|
|
|
|
+ return count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public int generateLongOrderExecutions(LocalDate date) {
|
|
|
|
|
+ LocalDate targetDate = date == null ? LocalDate.now() : date;
|
|
|
|
|
+ List<LongDoctorOrderDO> orders = longDoctorOrderMapper.selectList();
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ for (LongDoctorOrderDO orderDO : orders) {
|
|
|
|
|
+ List<LongDoctorOrderEntryDO> entries = longDoctorOrderEntryMapper.selectListByOrderId(orderDO.getId());
|
|
|
|
|
+ count += doGenerate(entries, orderDO, targetDate);
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("[generateLongOrderExecutions][机构({}) 日期({}) 生成长期医嘱执行记录({})条]",
|
|
|
|
|
+ TenantContextHolder.getTenantId(), targetDate, count);
|
|
|
|
|
+ return count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按「日期 + 条目计划执行时间」生成常规执行记录,已存在(唯一键)则跳过
|
|
|
|
|
+ */
|
|
|
|
|
+ private int doGenerate(List<LongDoctorOrderEntryDO> entries, LongDoctorOrderDO orderDO, LocalDate date) {
|
|
|
|
|
+ if (CollUtil.isEmpty(entries) || orderDO == null) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long tenantId = orderDO.getTenantId() != null ? orderDO.getTenantId() : TenantContextHolder.getTenantId();
|
|
|
|
|
+ // 机构配置了「执行记录自动完成」,则生成的记录直接置为已执行
|
|
|
|
|
+ boolean autoComplete = businessParamsConfigService.isDoctorOrderExecAutoComplete(tenantId);
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ for (LongDoctorOrderEntryDO entry : entries) {
|
|
|
|
|
+ if (entry == null || LongOrderEntryStatusEnum.STOPPED.getStatus().equals(entry.getStatus())) {
|
|
|
|
|
+ // 已停止的条目不再生成执行记录(停止流程由停止确认任务闭环)
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ LocalTime planTimeOfDay = parsePlanExecTime(entry.getPlanExecTime());
|
|
|
|
|
+ if (planTimeOfDay == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ LocalDateTime planTime = date.atTime(planTimeOfDay);
|
|
|
|
|
+ Integer execType = DoctorOrderExecTypeEnum.NORMAL.getType();
|
|
|
|
|
+ // 是否必须?
|
|
|
|
|
+ if (doctorOrderExecutionMapper.selectByUniqueKey(
|
|
|
|
|
+ DoctorOrderTypeEnum.LONG.getType(), entry.getId(), planTime, execType) != null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ DoctorOrderExecutionDO execDO = new DoctorOrderExecutionDO();
|
|
|
|
|
+ fillNormalTask(execDO, entry, orderDO, planTime, tenantId);
|
|
|
|
|
+ if (autoComplete) {
|
|
|
|
|
+ execDO.setStatus(DoctorOrderExecutionStatusEnum.DONE.getStatus());
|
|
|
|
|
+ execDO.setExecTime(planTime);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ execDO.setStatus(DoctorOrderExecutionStatusEnum.PENDING.getStatus());
|
|
|
|
|
+ }
|
|
|
|
|
+ doctorOrderExecutionMapper.insert(execDO);
|
|
|
|
|
+ count++;
|
|
|
|
|
+ }
|
|
|
|
|
+ return count;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 填充常规执行记录字段(长期医嘱条目 -> 护士执行记录)
|
|
|
|
|
+ */
|
|
|
|
|
+ private void fillNormalTask(DoctorOrderExecutionDO execDO, LongDoctorOrderEntryDO entry,
|
|
|
|
|
+ LongDoctorOrderDO orderDO, LocalDateTime planTime, Long tenantId) {
|
|
|
|
|
+ execDO.setOrderType(DoctorOrderTypeEnum.LONG.getType());
|
|
|
|
|
+ execDO.setExecType(DoctorOrderExecTypeEnum.NORMAL.getType());
|
|
|
|
|
+ execDO.setOrderEntryId(entry.getId());
|
|
|
|
|
+ execDO.setElderId(orderDO.getElderId());
|
|
|
|
|
+ execDO.setElderName(orderDO.getElderName());
|
|
|
|
|
+ execDO.setBedName(orderDO.getBedName());
|
|
|
|
|
+ execDO.setOrderContent(entry.getOrderContent());
|
|
|
|
|
+ execDO.setFrequency(entry.getFrequency());
|
|
|
|
|
+ execDO.setMedTime(entry.getMedTime());
|
|
|
|
|
+ execDO.setRoute(entry.getRoute());
|
|
|
|
|
+ execDO.setPlanTime(planTime);
|
|
|
|
|
+ execDO.setDoctorSign(entry.getDoctorSign());
|
|
|
|
|
+ execDO.setTenantId(tenantId);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 解析计划执行时间(HH:mm)
|
|
|
|
|
+ */
|
|
|
|
|
+ private LocalTime parsePlanExecTime(String planExecTime) {
|
|
|
|
|
+ if (StrUtil.isBlank(planExecTime)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ String text = planExecTime.trim();
|
|
|
|
|
+ for (DateTimeFormatter formatter : PLAN_EXEC_TIME_FORMATTERS) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return LocalTime.parse(text, formatter);
|
|
|
|
|
+ } catch (DateTimeParseException ignored) {
|
|
|
|
|
+ // 尝试下一种格式
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ log.warn("[parsePlanExecTime][计划执行时间({}) 格式非法,跳过生成执行记录]", planExecTime);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 定位待执行/确认的记录:停止确认优先按 id、其次按条目查待确认任务;常规执行沿用唯一键 upsert
|
|
* 定位待执行/确认的记录:停止确认优先按 id、其次按条目查待确认任务;常规执行沿用唯一键 upsert
|
|
|
*/
|
|
*/
|