门诊完诊审计日志错误:div_log 表中 pool_id 与 slot_id 存值与设计规范不符
400
门诊医生站点击【完诊】后,triage_queue_item 表 status 字段未按规范更新为 30
393
疾病报告管理-报告卡管理:状态为“审核失败”的报卡操作列缺失“审核”按钮
369
【住院管理】进入护理记录模块报错
361
三测单(体温单)住院第一日显示 1970-01-01,未正确获取入院日期
This commit is contained in:
Ranyunqiao
2026-04-21 11:38:05 +08:00
parent 994ffcb8b8
commit 88d9e19cc5
14 changed files with 245 additions and 76 deletions

View File

@@ -153,4 +153,16 @@ public class CurrentDayEncounterDto {
*/
private Boolean isFromAppointment;
/**
* 号源槽位ID关联 adm_schedule_slot.id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long slotId;
/**
* 号源池ID关联 adm_schedule_pool.id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long poolId;
}

View File

@@ -188,7 +188,7 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
encounterParticipant.setCreateTime(new Date());
iEncounterParticipantService.save(encounterParticipant);
// 更新 triage_queue_item 队列记录状态为 CALLING
// 更新 triage_queue_item 队列记录状态为 IN_CLINIC(诊中)
try {
TriageQueueItem queueItem = triageQueueItemService.getOne(
new LambdaQueryWrapper<TriageQueueItem>()
@@ -197,10 +197,10 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
.eq(TriageQueueItem::getDeleteFlag, "0")
);
if (queueItem != null) {
queueItem.setStatus("CALLING");
queueItem.setStatus(20); // 20=IN_CLINIC(诊中),患者进入诊室接诊
queueItem.setUpdateTime(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS));
triageQueueItemService.updateById(queueItem);
log.info("接诊时更新队列状态为CALLINGencounterId={}, queueItemId={}", encounterId, queueItem.getId());
log.info("接诊时更新队列状态为IN_CLINIC(诊中)encounterId={}, queueItemId={}", encounterId, queueItem.getId());
} else {
log.warn("接诊时未找到队列记录encounterId={}", encounterId);
}
@@ -263,10 +263,13 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
);
// 如果队列项存在,检查状态并更新
if (queueItem != null && "CALLING".equals(queueItem.getStatus())) {
// 允许从 CALLING(10) 或 IN_CLINIC(20) 完成就诊
if (queueItem != null &&
(Integer.valueOf(10).equals(queueItem.getStatus()) ||
Integer.valueOf(20).equals(queueItem.getStatus()))) {
// 更新队列状态为已完成
java.time.LocalDateTime nowLocal = java.time.LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
queueItem.setStatus("COMPLETED");
queueItem.setStatus(30); // 30=COMPLETED(已完成)
queueItem.setUpdateTime(nowLocal);
triageQueueItemService.updateById(queueItem);
@@ -275,13 +278,15 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
Long userId = SecurityUtils.getLoginUser().getUserId();
String divLogSql = "INSERT INTO hisdev.div_log "
+ "(pool_id, slot_id, queue_no, op_user_id, action, create_time) "
+ "VALUES (?, ?, ?, ?, 'COMPLETE', NOW()::timestamp(0))";
+ "VALUES (?, ?, ?, ?, 30, NOW()::timestamp(0))";
// action=30 表示 COMPLETED(已完成),与 triage_queue_item.status 数字编码保持一致
// 0=WAITING, 10=CALLING, 20=IN_CLINIC, 30=COMPLETED, 40=SKIPPED, 50=REFUNDED, 60=FOLLOW
jdbcTemplate.update(divLogSql,
queueItem.getOrganizationId(), // pool_id: 候选池ID科室
queueItem.getPractitionerId(), // slot_id: 槽位ID医生
queueItem.getQueueOrder(), // queue_no: 队列号
userId); // op_user_id: 操作用户ID
queueItem.getPoolId(), // pool_id: 号源池ID来自 adm_schedule_pool.id
queueItem.getSlotId(), // slot_id: 号源槽位ID来自 adm_schedule_slot.id
queueItem.getQueueOrder(), // queue_no: 队列
userId); // op_user_id: 操作用户ID
} catch (Exception e) {
log.error("写入div_log审计日志失败", e);
// 审计日志失败不影响主流程

View File

@@ -73,9 +73,21 @@ public class VitalSignsAppServiceImpl implements IVitalSignsAppService {
VitalSignsMedicalRecordDto medicalRecord = new VitalSignsMedicalRecordDto();
// 处理日期
// 处理出院日期
if (!vitalSignsInfoPage.getRecords().isEmpty()) {
medicalRecord.setHospDate(vitalSignsInfoPage.getRecords().get(0).getRecordingDate());
// 从第一条记录获取出院日期(如果存在)
Date dischargeDate = vitalSignsInfoPage.getRecords().get(0).getDischargeDate();
if (dischargeDate != null) {
medicalRecord.setOutdate(TimeUtils.dateToDateString(dischargeDate));
}
}
// 处理住院日期:优先使用第一条记录中的入院日期,如果没有则保持 null 让前端 fallback
if (!vitalSignsInfoPage.getRecords().isEmpty()) {
Date admissionDate = vitalSignsInfoPage.getRecords().get(0).getAdmissionDate();
if (admissionDate != null) {
medicalRecord.setHospDate(admissionDate);
}
}
// 处理生命体征数据

View File

@@ -57,6 +57,10 @@ import com.openhis.web.paymentmanage.mapper.PaymentMapper;
import com.openhis.web.personalization.dto.ActivityDeviceDto;
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
import com.openhis.appointmentmanage.domain.ScheduleSlot;
import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper;
import com.openhis.clinical.domain.Order;
import com.openhis.clinical.service.IOrderService;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.IDeviceDispenseService;
import com.openhis.workflow.service.IDeviceRequestService;
@@ -70,6 +74,7 @@ import com.openhis.yb.service.IClinicSettleService;
import com.openhis.yb.service.IInpatientSettleService;
import com.openhis.yb.service.IRegService;
import com.openhis.yb.service.YbManager;
import com.openhis.web.triageandqueuemanage.appservice.impl.TriageQueueAppServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.StringUtil;
@@ -186,6 +191,10 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
private YbManager ybManager;
@Autowired
private RedisCache redisCache;
@Autowired
private IOrderService iOrderService;
@Autowired
private ScheduleSlotMapper scheduleSlotMapper;
/**
* 【门诊预结算】
@@ -1985,6 +1994,31 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
}
// 创建队列项
// 尝试获取预约订单的 slot_id 和 pool_id
Long queuePoolId = null;
Long queueSlotId = null;
try {
// 查询患者当天的待签到预约订单status = 1 或 2 表示已预约或已取号)
Order order = iOrderService.getOne(
new LambdaQueryWrapper<Order>()
.eq(Order::getPatientId, encounter.getPatientId())
.in(Order::getStatus, 1, 2) // 1=BOOKED 已预约, 2=CHECKED_IN 已取号
.eq(Order::getDeleteFlag, "0")
.orderByDesc(Order::getCreateTime)
.last("LIMIT 1")
);
if (order != null && order.getSlotId() != null) {
queueSlotId = order.getSlotId();
// 通过 slot_id 获取 pool_id
ScheduleSlot slot = scheduleSlotMapper.selectById(queueSlotId);
if (slot != null) {
queuePoolId = slot.getPoolId();
}
logger.info("挂号时找到预约订单slotId={}, poolId={}, encounterId={}", queueSlotId, queuePoolId, encounterId);
}
} catch (Exception e) {
logger.warn("查询预约订单失败不影响挂号流程encounterId={}", encounterId, e);
}
TriageQueueItem queueItem = new TriageQueueItem()
.setTenantId(tenantId)
.setQueueDate(queueDate)
@@ -1997,7 +2031,9 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
.setPractitionerId(queuePractitionerId)
.setPractitionerName(practitionerName)
.setRoomNo(null)
.setStatus("WAITING")
.setPoolId(queuePoolId)
.setSlotId(queueSlotId)
.setStatus(TriageQueueAppServiceImpl.STATUS_WAITING) // 0=WAITING(等待中)
.setQueueOrder(maxOrder + 1)
.setDeleteFlag("0")
.setCreateTime(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS))

View File

@@ -5,16 +5,12 @@ import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.core.common.core.domain.R;
import com.core.common.utils.SecurityUtils;
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
import com.openhis.web.triageandqueuemanage.dto.CallNumberDisplayResp;
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
import com.openhis.web.triageandqueuemanage.dto.TriageQueueEncounterItem;
import com.openhis.web.triageandqueuemanage.dto.*;
import com.openhis.web.triageandqueuemanage.sse.CallNumberSseManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -28,10 +24,18 @@ import java.util.stream.Collectors;
@Service
public class TriageQueueAppServiceImpl implements TriageQueueAppService {
private static final String STATUS_WAITING = "WAITING";
private static final String STATUS_CALLING = "CALLING";
private static final String STATUS_SKIPPED = "SKIPPED";
private static final String STATUS_COMPLETED = "COMPLETED";
/**
* 分诊队列状态常量(数字编码)
* 0=WAITING(等待中), 10=CALLING(呼叫中), 20=IN_CLINIC(诊中),
* 30=COMPLETED(已完成), 40=SKIPPED(已跳过), 50=REFUNDED(已退费), 60=FOLLOW(已随访)
*/
public static final Integer STATUS_WAITING = 0;
public static final Integer STATUS_CALLING = 10;
public static final Integer STATUS_IN_CLINIC = 20;
public static final Integer STATUS_COMPLETED = 30;
public static final Integer STATUS_SKIPPED = 40;
public static final Integer STATUS_REFUNDED = 50;
public static final Integer STATUS_FOLLOW = 60;
@Resource
private TriageQueueItemService triageQueueItemService;
@@ -127,6 +131,8 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
.setPractitionerName(it.getPractitionerName())
.setPractitionerId(it.getPractitionerId()) // ✅ 新增字段(可选)
.setRoomNo(it.getRoomNo()) // ✅ 新增字段(可选)
.setPoolId(it.getPoolId()) // ✅ 号源池ID用于div_log审计
.setSlotId(it.getSlotId()) // ✅ 号源槽位ID用于div_log审计
.setStatus(STATUS_WAITING)
.setQueueOrder(++maxOrder)
.setDeleteFlag("0")

View File

@@ -59,7 +59,7 @@ public class CallNumberDisplayResp {
/** 患者姓名(脱敏) */
private String name;
/** 状态CALLING=就诊中WAITING=等待 */
private String status;
private Integer status;
/** 排队号 */
private Integer queueOrder;
}

View File

@@ -15,6 +15,10 @@ public class TriageQueueEncounterItem {
private Long practitionerId;
/** 诊室号(可选) */
private String roomNo;
/** 号源池ID关联 adm_schedule_pool.id用于 div_log 审计日志) */
private Long poolId;
/** 号源槽位ID关联 adm_schedule_slot.id用于 div_log 审计日志) */
private Long slotId;
}

View File

@@ -68,7 +68,9 @@
T9.picture_url AS pictureUrl,
T9.birth_date AS birthDate,
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo,
COALESCE(T9.order_id IS NOT NULL, false) AS isFromAppointment
COALESCE(T9.order_id IS NOT NULL, false) AS isFromAppointment,
T9.slot_id AS slotId,
T9.pool_id AS poolId
from (
SELECT T1.tenant_id AS tenant_id,
T1.id AS encounter_id,
@@ -95,8 +97,12 @@
T8.birth_date AS birth_date,
T8.bus_no AS patient_bus_no,
T18.identifier_no AS identifier_no,
T1.order_id AS order_id
T1.order_id AS order_id,
om.slot_id AS slot_id,
ss.pool_id AS pool_id
FROM adm_encounter AS T1
LEFT JOIN order_main AS om ON T1.order_id = om.id AND om.delete_flag = '0'
LEFT JOIN adm_schedule_slot AS ss ON om.slot_id = ss.id AND ss.delete_flag = '0'
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
LEFT JOIN (

View File

@@ -31,9 +31,15 @@ public class TriageQueueItem {
private String practitionerName; // 医生姓名
private Long practitionerId; // 医生ID新增字段
private String roomNo; // 诊室号(新增字段)
private Long poolId; // 号源池ID (关联 adm_schedule_pool.id)
private Long slotId; // 号源槽位ID (关联 adm_schedule_slot.id)
/** WAITING / CALLING / SKIPPED / COMPLETED */
private String status;
/**
* 分诊队列状态
* 0=WAITING(等待中), 10=CALLING(呼叫中), 20=IN_CLINIC(诊中),
* 30=COMPLETED(已完成), 40=SKIPPED(已跳过), 50=REFUNDED(已退费), 60=FOLLOW(已随访)
*/
private Integer status;
private Integer queueOrder; //“排队序号”,也就是患者在当前科室、当天队列里的 顺序号(从 1 开始递增)。
private LocalDateTime createTime;