挂号补单功能的完善
This commit is contained in:
@@ -29,6 +29,8 @@ import com.openhis.web.chargemanage.dto.PatientMetadata;
|
||||
import com.openhis.web.chargemanage.dto.PractitionerMetadata;
|
||||
import com.openhis.web.chargemanage.dto.ReprintRegistrationDto;
|
||||
import com.openhis.web.chargemanage.mapper.OutpatientRegistrationAppMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import com.openhis.web.paymentmanage.appservice.IPaymentRecService;
|
||||
import com.openhis.web.paymentmanage.dto.CancelPaymentDto;
|
||||
import com.openhis.web.paymentmanage.dto.CancelRegPaymentDto;
|
||||
@@ -38,12 +40,15 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 门诊挂号 应用实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistrationAppService {
|
||||
|
||||
@@ -76,6 +81,9 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
|
||||
@Resource
|
||||
IPatientIdentifierService patientIdentifierService;
|
||||
|
||||
@Resource
|
||||
TriageCandidateExclusionService triageCandidateExclusionService;
|
||||
|
||||
/**
|
||||
* 门诊挂号 - 查询患者信息
|
||||
@@ -308,6 +316,47 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
||||
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
||||
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue());
|
||||
|
||||
// 过滤候选池排除列表(如果是从智能候选池查询,排除已加入队列的患者)
|
||||
// 检查请求参数 excludeFromCandidatePool,如果为 true 或未设置,则过滤排除列表
|
||||
String excludeParam = request.getParameter("excludeFromCandidatePool");
|
||||
boolean shouldExclude = excludeParam == null || "true".equalsIgnoreCase(excludeParam);
|
||||
if (shouldExclude && currentDayEncounter != null && !currentDayEncounter.getRecords().isEmpty()) {
|
||||
try {
|
||||
// 获取当前租户和日期
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
// 查询排除列表
|
||||
List<TriageCandidateExclusion> exclusions = triageCandidateExclusionService.list(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, today)
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
|
||||
if (exclusions != null && !exclusions.isEmpty()) {
|
||||
// 构建排除的 encounterId 集合
|
||||
Set<Long> excludedEncounterIds = exclusions.stream()
|
||||
.map(TriageCandidateExclusion::getEncounterId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 过滤结果
|
||||
List<CurrentDayEncounterDto> filteredRecords = currentDayEncounter.getRecords().stream()
|
||||
.filter(e -> e.getEncounterId() == null || !excludedEncounterIds.contains(e.getEncounterId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 更新分页结果
|
||||
currentDayEncounter.setRecords(filteredRecords);
|
||||
currentDayEncounter.setTotal(filteredRecords.size());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果过滤失败,记录日志但不影响正常查询
|
||||
log.warn("过滤候选池排除列表失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
currentDayEncounter.getRecords().forEach(e -> {
|
||||
// 性别
|
||||
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.openhis.web.triageandqueuemanage.appservice;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public interface TriageQueueAppService {
|
||||
R<?> list(Long organizationId, LocalDate date);
|
||||
R<?> add(TriageQueueAddReq req);
|
||||
R<?> remove(Long id);
|
||||
R<?> adjust(TriageQueueAdjustReq req);
|
||||
/** 选呼:将之前叫号中置为完成,选中的置为叫号中 */
|
||||
R<?> call(TriageQueueActionReq req);
|
||||
/** 完成:叫号中 -> 完成(移出列表),并自动推进下一个等待为叫号中 */
|
||||
R<?> complete(TriageQueueActionReq req);
|
||||
/** 过号重排:叫号中 -> 跳过并移到末尾,并自动推进下一个等待为叫号中 */
|
||||
R<?> requeue(TriageQueueActionReq req);
|
||||
/** 跳过:兼容前端按钮(当前实现等同于过号重排) */
|
||||
R<?> skip(TriageQueueActionReq req);
|
||||
/** 下一患者:当前叫号中 -> 完成,下一位等待 -> 叫号中 */
|
||||
R<?> next(TriageQueueActionReq req);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
package com.openhis.web.triageandqueuemanage.appservice.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
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.service.TriageCandidateExclusionService;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
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 org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@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";
|
||||
|
||||
@Resource
|
||||
private TriageQueueItemService triageQueueItemService;
|
||||
|
||||
@Resource
|
||||
private TriageCandidateExclusionService triageCandidateExclusionService;
|
||||
|
||||
@Override
|
||||
public R<?> list(Long organizationId, LocalDate date) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
// 只查询今天的患者
|
||||
LocalDate qd = date != null ? date : LocalDate.now();
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> wrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder);
|
||||
|
||||
// 如果指定了科室,按科室过滤;否则查询所有科室(全科模式)
|
||||
if (organizationId != null) {
|
||||
wrapper.eq(TriageQueueItem::getOrganizationId, organizationId);
|
||||
}
|
||||
|
||||
List<TriageQueueItem> list = triageQueueItemService.list(wrapper);
|
||||
|
||||
// 双重保险:再次过滤掉 COMPLETED 状态的患者(防止数据库中有异常数据)
|
||||
if (list != null && !list.isEmpty()) {
|
||||
int beforeSize = list.size();
|
||||
list = list.stream()
|
||||
.filter(item -> !STATUS_COMPLETED.equals(item.getStatus()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
if (beforeSize != list.size()) {
|
||||
System.out.println(">>> [TriageQueue] list() 警告:过滤掉了 " + (beforeSize - list.size()) + " 条 COMPLETED 状态的记录");
|
||||
}
|
||||
}
|
||||
|
||||
// 调试日志:检查状态值
|
||||
if (list != null && !list.isEmpty()) {
|
||||
System.out.println(">>> [TriageQueue] list() 返回 " + list.size() + " 条记录(已排除 COMPLETED)");
|
||||
for (int i = 0; i < Math.min(3, list.size()); i++) {
|
||||
TriageQueueItem item = list.get(i);
|
||||
System.out.println(" [" + i + "] patientName=" + item.getPatientName()
|
||||
+ ", status=" + item.getStatus()
|
||||
+ ", organizationId=" + item.getOrganizationId());
|
||||
}
|
||||
}
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> add(TriageQueueAddReq req) {
|
||||
if (req == null || ObjectUtil.isNull(req.getOrganizationId())) {
|
||||
return R.fail("organizationId 不能为空");
|
||||
}
|
||||
if (CollUtil.isEmpty(req.getItems())) {
|
||||
return R.fail("items 不能为空");
|
||||
}
|
||||
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LocalDate qd = LocalDate.now();
|
||||
Long orgId = req.getOrganizationId();
|
||||
|
||||
List<TriageQueueItem> existing = triageQueueItemService.list(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED));
|
||||
|
||||
int maxOrder = existing.stream().map(TriageQueueItem::getQueueOrder).filter(Objects::nonNull).max(Integer::compareTo).orElse(0);
|
||||
|
||||
int added = 0;
|
||||
for (TriageQueueEncounterItem it : req.getItems()) {
|
||||
if (it == null || it.getEncounterId() == null) continue;
|
||||
boolean exists = existing.stream().anyMatch(e -> Objects.equals(e.getEncounterId(), it.getEncounterId()));
|
||||
if (exists) continue;
|
||||
|
||||
TriageQueueItem qi = new TriageQueueItem()
|
||||
.setTenantId(tenantId)
|
||||
.setQueueDate(qd)
|
||||
.setOrganizationId(orgId)
|
||||
.setOrganizationName(req.getOrganizationName())
|
||||
.setEncounterId(it.getEncounterId())
|
||||
.setPatientId(it.getPatientId())
|
||||
.setPatientName(it.getPatientName())
|
||||
.setHealthcareName(it.getHealthcareName())
|
||||
.setPractitionerName(it.getPractitionerName())
|
||||
.setStatus(STATUS_WAITING)
|
||||
.setQueueOrder(++maxOrder)
|
||||
.setDeleteFlag("0")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
triageQueueItemService.save(qi);
|
||||
|
||||
// 记录到候选池排除列表(避免刷新后重新出现在候选池)
|
||||
TriageCandidateExclusion exclusion = triageCandidateExclusionService.getOne(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, qd)
|
||||
.eq(TriageCandidateExclusion::getEncounterId, it.getEncounterId())
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
if (exclusion == null) {
|
||||
exclusion = new TriageCandidateExclusion()
|
||||
.setTenantId(tenantId)
|
||||
.setExclusionDate(qd)
|
||||
.setEncounterId(it.getEncounterId())
|
||||
.setPatientId(it.getPatientId())
|
||||
.setPatientName(it.getPatientName())
|
||||
.setOrganizationId(orgId)
|
||||
.setOrganizationName(req.getOrganizationName())
|
||||
.setReason("ADDED_TO_QUEUE")
|
||||
.setDeleteFlag("0")
|
||||
.setCreateTime(LocalDateTime.now())
|
||||
.setUpdateTime(LocalDateTime.now());
|
||||
triageCandidateExclusionService.save(exclusion);
|
||||
}
|
||||
|
||||
added++;
|
||||
}
|
||||
|
||||
return R.ok(added);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> remove(Long id) {
|
||||
if (id == null) return R.fail("id 不能为空");
|
||||
TriageQueueItem item = triageQueueItemService.getById(id);
|
||||
if (item == null) return R.fail("队列项不存在");
|
||||
|
||||
// 逻辑删除队列项
|
||||
item.setDeleteFlag("1").setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(item);
|
||||
|
||||
// 从排除列表中删除记录,使患者重新出现在候选池中
|
||||
Integer tenantId = item.getTenantId();
|
||||
LocalDate exclusionDate = item.getQueueDate();
|
||||
Long encounterId = item.getEncounterId();
|
||||
|
||||
if (tenantId != null && exclusionDate != null && encounterId != null) {
|
||||
TriageCandidateExclusion exclusion = triageCandidateExclusionService.getOne(
|
||||
new LambdaQueryWrapper<TriageCandidateExclusion>()
|
||||
.eq(TriageCandidateExclusion::getTenantId, tenantId)
|
||||
.eq(TriageCandidateExclusion::getExclusionDate, exclusionDate)
|
||||
.eq(TriageCandidateExclusion::getEncounterId, encounterId)
|
||||
.eq(TriageCandidateExclusion::getDeleteFlag, "0")
|
||||
);
|
||||
|
||||
if (exclusion != null) {
|
||||
// 逻辑删除排除记录
|
||||
exclusion.setDeleteFlag("1").setUpdateTime(LocalDateTime.now());
|
||||
triageCandidateExclusionService.updateById(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
recalcOrders(item.getOrganizationId(), item.getQueueDate());
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> adjust(TriageQueueAdjustReq req) {
|
||||
if (req == null || req.getId() == null) return R.fail("id 不能为空");
|
||||
if (!"up".equalsIgnoreCase(req.getDirection()) && !"down".equalsIgnoreCase(req.getDirection())) {
|
||||
return R.fail("direction 只能是 up/down");
|
||||
}
|
||||
TriageQueueItem cur = triageQueueItemService.getById(req.getId());
|
||||
if (cur == null) return R.fail("队列项不存在");
|
||||
|
||||
List<TriageQueueItem> list = listInternal(cur.getOrganizationId(), cur.getQueueDate());
|
||||
list.sort(Comparator.comparing(TriageQueueItem::getQueueOrder).thenComparing(TriageQueueItem::getId));
|
||||
int idx = -1;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (Objects.equals(list.get(i).getId(), cur.getId())) { idx = i; break; }
|
||||
}
|
||||
if (idx == -1) return R.fail("队列项不在当前队列");
|
||||
|
||||
int targetIdx = "up".equalsIgnoreCase(req.getDirection()) ? idx - 1 : idx + 1;
|
||||
if (targetIdx < 0 || targetIdx >= list.size()) return R.ok(false);
|
||||
|
||||
TriageQueueItem other = list.get(targetIdx);
|
||||
Integer tmp = cur.getQueueOrder();
|
||||
cur.setQueueOrder(other.getQueueOrder()).setUpdateTime(LocalDateTime.now());
|
||||
other.setQueueOrder(tmp).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(cur);
|
||||
triageQueueItemService.updateById(other);
|
||||
|
||||
recalcOrders(cur.getOrganizationId(), cur.getQueueDate());
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> call(TriageQueueActionReq req) {
|
||||
if (req == null || req.getId() == null) return R.fail("id 不能为空");
|
||||
TriageQueueItem selected = triageQueueItemService.getById(req.getId());
|
||||
if (selected == null) return R.fail("队列项不存在");
|
||||
|
||||
// 只将"等待"状态的患者转为"叫号中",允许有多个"叫号中"的患者
|
||||
if (STATUS_WAITING.equals(selected.getStatus())) {
|
||||
selected.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(selected);
|
||||
return R.ok(true);
|
||||
} else if (STATUS_CALLING.equals(selected.getStatus())) {
|
||||
// 如果已经是"叫号中"状态,直接返回成功(不做任何操作)
|
||||
return R.ok(true);
|
||||
} else {
|
||||
// 其他状态(如 SKIPPED、COMPLETED)不能选呼
|
||||
return R.fail("只能选呼\"等待\"状态的患者,当前患者状态为:" + selected.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> complete(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
// 关键改进:如果提供了 id,直接通过ID获取(像 call 方法一样),不依赖查询条件
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能完成\"叫号中\"状态的患者,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
System.out.println(">>> [TriageQueue] complete() 开始执行(不限制日期,通过查询条件), tenantId=" + tenantId + ", orgId=" + orgId);
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
|
||||
System.out.println(">>> [TriageQueue] complete() 查询叫号中患者(不限制日期): orgId=" + orgId + ", 结果=" + (calling != null ? calling.getPatientName() + "(status=" + calling.getStatus() + ", queueDate=" + calling.getQueueDate() + ")" : "null"));
|
||||
|
||||
if (calling == null) {
|
||||
return R.fail("当前没有叫号中的患者");
|
||||
}
|
||||
}
|
||||
|
||||
// 使用实际找到的科室ID
|
||||
Long actualOrgId = calling.getOrganizationId();
|
||||
|
||||
// 1) 叫号中 -> 完成(移出列表)
|
||||
calling.setStatus(STATUS_COMPLETED).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
|
||||
// 2) 自动推进下一个等待为叫号中(同一科室,包含跳过状态,不限制日期)
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
// 如果指定了科室ID,则按科室过滤;否则查询所有科室(全科模式)
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
System.out.println(">>> [TriageQueue] complete() 查询等待患者(不限制日期): actualOrgId=" + actualOrgId + ", 结果=" + (next != null ? next.getPatientName() + "(status=" + next.getStatus() + ")" : "null"));
|
||||
|
||||
if (next != null) {
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
}
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> requeue(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能对\"叫号中\"状态的患者进行过号重排,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
|
||||
if (calling == null) return R.fail("当前没有叫号中的患者");
|
||||
}
|
||||
|
||||
// 使用实际找到的科室ID
|
||||
Long actualOrgId = calling.getOrganizationId();
|
||||
|
||||
// 关键改进:在执行"跳过"操作之前,先检查是否有等待中的患者(判断队列状态)
|
||||
// 如果没有等待中的患者,就不应该执行"过号重排"操作
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
// 如果指定了科室ID,则按科室过滤;否则查询所有科室(全科模式)
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
// 调试日志:检查查询结果
|
||||
System.out.println(">>> [TriageQueue] requeue() 查询等待中的患者:");
|
||||
System.out.println(">>> - 科室ID: " + actualOrgId);
|
||||
System.out.println(">>> - 找到的等待患者: " + (next != null ? next.getPatientName() + " (状态: " + next.getStatus() + ")" : "null"));
|
||||
|
||||
// 如果找不到等待中的患者,直接返回失败(不执行跳过操作)
|
||||
if (next == null) {
|
||||
System.out.println(">>> [TriageQueue] requeue() 失败:没有等待中的患者");
|
||||
return R.fail("当前没有等待中的患者");
|
||||
}
|
||||
|
||||
// 找末尾序号(同一科室,不限制日期)
|
||||
Integer maxOrder = triageQueueItemService.list(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, actualOrgId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED))
|
||||
.stream()
|
||||
.map(TriageQueueItem::getQueueOrder)
|
||||
.filter(Objects::nonNull)
|
||||
.max(Integer::compareTo)
|
||||
.orElse(0);
|
||||
|
||||
// 1) 叫号中 -> 跳过,并移到末尾
|
||||
calling.setStatus(STATUS_SKIPPED).setQueueOrder(maxOrder + 1).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
|
||||
// 2) 自动推进下一个等待为叫号中
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
|
||||
recalcOrders(actualOrgId, null);
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> skip(TriageQueueActionReq req) {
|
||||
// 当前业务“跳过”按“过号重排”处理:叫号中 -> 跳过并移到末尾,自动推进下一等待
|
||||
return requeue(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> next(TriageQueueActionReq req) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
TriageQueueItem calling = null;
|
||||
|
||||
System.out.println(">>> [TriageQueue] next() 开始执行(不限制日期), tenantId=" + tenantId);
|
||||
|
||||
// 关键改进:如果提供了 id,优先使用 id 直接查找(像 call 方法一样)
|
||||
if (req != null && req.getId() != null) {
|
||||
calling = triageQueueItemService.getById(req.getId());
|
||||
if (calling == null) {
|
||||
return R.fail("队列项不存在");
|
||||
}
|
||||
// 验证状态:必须是"叫号中"状态
|
||||
if (!STATUS_CALLING.equals(calling.getStatus())) {
|
||||
return R.fail("只能对\"叫号中\"状态的患者执行\"下一患者\"操作,当前患者状态为:" + calling.getStatus());
|
||||
}
|
||||
} else {
|
||||
// 如果没有提供 id,通过查询条件查找(兼容旧逻辑)
|
||||
Long orgId = req != null && req.getOrganizationId() != null ? req.getOrganizationId() : null;
|
||||
|
||||
LambdaQueryWrapper<TriageQueueItem> callingWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (orgId != null) {
|
||||
callingWrapper.eq(TriageQueueItem::getOrganizationId, orgId);
|
||||
}
|
||||
|
||||
calling = triageQueueItemService.getOne(callingWrapper, false);
|
||||
}
|
||||
|
||||
Long actualOrgId = null;
|
||||
|
||||
// 当前叫号中 -> 完成(如果不存在,就当作从头找第一位等待)
|
||||
if (calling != null) {
|
||||
calling.setStatus(STATUS_COMPLETED).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(calling);
|
||||
actualOrgId = calling.getOrganizationId(); // 使用叫号中患者所在的科室
|
||||
} else {
|
||||
// 如果没有叫号中的患者,使用请求中的 organizationId(如果有)
|
||||
if (req != null && req.getOrganizationId() != null) {
|
||||
actualOrgId = req.getOrganizationId();
|
||||
}
|
||||
}
|
||||
|
||||
// 下一位等待 -> 叫号中(如果之前有叫号中的,就在同一科室找;否则在全科找)
|
||||
// 注意:也包含"跳过"状态的患者,因为跳过后的患者也可以重新叫号(不限制日期)
|
||||
LambdaQueryWrapper<TriageQueueItem> nextWrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.and(w -> w.eq(TriageQueueItem::getStatus, STATUS_WAITING)
|
||||
.or()
|
||||
.eq(TriageQueueItem::getStatus, STATUS_SKIPPED))
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder)
|
||||
.last("LIMIT 1");
|
||||
|
||||
if (actualOrgId != null) {
|
||||
nextWrapper.eq(TriageQueueItem::getOrganizationId, actualOrgId);
|
||||
}
|
||||
|
||||
TriageQueueItem next = triageQueueItemService.getOne(nextWrapper, false);
|
||||
|
||||
// 调试日志:打印查询条件和结果
|
||||
System.out.println(">>> [TriageQueue] next() 查询条件(不限制日期): tenantId=" + tenantId
|
||||
+ ", actualOrgId=" + actualOrgId
|
||||
+ ", deleteFlag=0"
|
||||
+ ", status IN (WAITING, SKIPPED)");
|
||||
System.out.println(">>> [TriageQueue] next() 查询结果: calling=" + (calling != null ? calling.getPatientName() + "(status=" + calling.getStatus() + ", queueDate=" + calling.getQueueDate() + ")" : "null")
|
||||
+ ", next=" + (next != null ? next.getPatientName() + "(status=" + next.getStatus() + ", queueDate=" + next.getQueueDate() + ")" : "null"));
|
||||
|
||||
if (next == null) {
|
||||
if (actualOrgId != null) {
|
||||
recalcOrders(actualOrgId, null);
|
||||
}
|
||||
return R.fail("当前没有等待的患者");
|
||||
}
|
||||
|
||||
next.setStatus(STATUS_CALLING).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(next);
|
||||
|
||||
if (next.getOrganizationId() != null) {
|
||||
recalcOrders(next.getOrganizationId(), null);
|
||||
}
|
||||
return R.ok(true);
|
||||
}
|
||||
|
||||
private List<TriageQueueItem> listInternal(Long orgId, LocalDate qd) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
LambdaQueryWrapper<TriageQueueItem> wrapper = new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, STATUS_COMPLETED);
|
||||
// 如果 qd 不为 null,才添加日期限制
|
||||
if (qd != null) {
|
||||
wrapper.eq(TriageQueueItem::getQueueDate, qd);
|
||||
}
|
||||
return triageQueueItemService.list(wrapper);
|
||||
}
|
||||
|
||||
private TriageQueueItem findCalling(Long orgId, LocalDate qd) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
return triageQueueItemService.getOne(new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getOrganizationId, orgId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.eq(TriageQueueItem::getStatus, STATUS_CALLING)
|
||||
.last("LIMIT 1"), false);
|
||||
}
|
||||
|
||||
private void recalcOrders(Long orgId, LocalDate qd) {
|
||||
List<TriageQueueItem> list = listInternal(orgId, qd);
|
||||
list.sort(Comparator.comparing(TriageQueueItem::getQueueOrder).thenComparing(TriageQueueItem::getId));
|
||||
int i = 1;
|
||||
for (TriageQueueItem it : list) {
|
||||
if (!Objects.equals(it.getQueueOrder(), i)) {
|
||||
it.setQueueOrder(i).setUpdateTime(LocalDateTime.now());
|
||||
triageQueueItemService.updateById(it);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.openhis.web.triageandqueuemanage.controller;
|
||||
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.web.triageandqueuemanage.appservice.TriageQueueAppService;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueActionReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAddReq;
|
||||
import com.openhis.web.triageandqueuemanage.dto.TriageQueueAdjustReq;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
@RequestMapping("/triage/queue")
|
||||
public class TriageQueueController {
|
||||
|
||||
@Resource
|
||||
private TriageQueueAppService triageQueueAppService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public R<?> list(@RequestParam(value = "organizationId", required = false) Long organizationId,
|
||||
@RequestParam(value = "date", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
|
||||
return triageQueueAppService.list(organizationId, date);
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
public R<?> add(@RequestBody TriageQueueAddReq req) {
|
||||
return triageQueueAppService.add(req);
|
||||
}
|
||||
|
||||
@DeleteMapping("/remove/{id}")
|
||||
public R<?> remove(@PathVariable("id") Long id) {
|
||||
return triageQueueAppService.remove(id);
|
||||
}
|
||||
|
||||
@PutMapping("/adjust")
|
||||
public R<?> adjust(@RequestBody TriageQueueAdjustReq req) {
|
||||
return triageQueueAppService.adjust(req);
|
||||
}
|
||||
|
||||
@PostMapping("/call")
|
||||
public R<?> call(@RequestBody TriageQueueActionReq req) {
|
||||
return triageQueueAppService.call(req);
|
||||
}
|
||||
|
||||
@PostMapping("/complete")
|
||||
public R<?> complete(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.complete(req);
|
||||
}
|
||||
|
||||
@PostMapping("/requeue")
|
||||
public R<?> requeue(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.requeue(req);
|
||||
}
|
||||
|
||||
@PostMapping("/skip")
|
||||
public R<?> skip(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.skip(req);
|
||||
}
|
||||
|
||||
@PostMapping("/next")
|
||||
public R<?> next(@RequestBody(required = false) TriageQueueActionReq req) {
|
||||
return triageQueueAppService.next(req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueActionReq {
|
||||
/** 目标队列项ID(例如:选呼时选中的患者) */
|
||||
private Long id;
|
||||
/** 科室ID(可选:不传则用当前登录人orgId) */
|
||||
private Long organizationId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TriageQueueAddReq {
|
||||
/** 科室ID(就诊科室) */
|
||||
private Long organizationId;
|
||||
/** 科室名称(冗余存储,便于展示) */
|
||||
private String organizationName;
|
||||
/** 要加入队列的就诊记录 */
|
||||
private List<TriageQueueEncounterItem> items;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueAdjustReq {
|
||||
private Long id;
|
||||
/** up / down */
|
||||
private String direction;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.openhis.web.triageandqueuemanage.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TriageQueueEncounterItem {
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private String healthcareName;
|
||||
private String practitionerName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.openhis.triageandqueuemanage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "hisdev.triage_candidate_exclusion")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TriageCandidateExclusion {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer tenantId;
|
||||
private LocalDate exclusionDate;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private Long organizationId;
|
||||
private String organizationName;
|
||||
|
||||
/** 排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等 */
|
||||
private String reason;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private String deleteFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.openhis.triageandqueuemanage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "hisdev.triage_queue_item")
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TriageQueueItem {
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer tenantId;
|
||||
private LocalDate queueDate;
|
||||
private Long organizationId;
|
||||
private String organizationName;
|
||||
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
|
||||
private String healthcareName;
|
||||
private String practitionerName;
|
||||
|
||||
/** WAITING / CALLING / SKIPPED / COMPLETED */
|
||||
private String status;
|
||||
private Integer queueOrder;
|
||||
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
private String deleteFlag;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.openhis.triageandqueuemanage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface TriageCandidateExclusionMapper extends BaseMapper<TriageCandidateExclusion> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.openhis.triageandqueuemanage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface TriageQueueItemMapper extends BaseMapper<TriageQueueItem> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.openhis.triageandqueuemanage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
|
||||
public interface TriageCandidateExclusionService extends IService<TriageCandidateExclusion> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.openhis.triageandqueuemanage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
|
||||
public interface TriageQueueItemService extends IService<TriageQueueItem> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.openhis.triageandqueuemanage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageCandidateExclusion;
|
||||
import com.openhis.triageandqueuemanage.mapper.TriageCandidateExclusionMapper;
|
||||
import com.openhis.triageandqueuemanage.service.TriageCandidateExclusionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TriageCandidateExclusionServiceImpl extends ServiceImpl<TriageCandidateExclusionMapper, TriageCandidateExclusion> implements TriageCandidateExclusionService {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.openhis.triageandqueuemanage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.openhis.triageandqueuemanage.domain.TriageQueueItem;
|
||||
import com.openhis.triageandqueuemanage.mapper.TriageQueueItemMapper;
|
||||
import com.openhis.triageandqueuemanage.service.TriageQueueItemService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TriageQueueItemServiceImpl extends ServiceImpl<TriageQueueItemMapper, TriageQueueItem> implements TriageQueueItemService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
-- 智能分诊排队:候选池排除记录表
|
||||
-- 用途:记录已从智能候选池中移除的患者(加入队列后不再出现在候选池)
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev环境是 hisdev,test环境是 histest,prd环境是 hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hisdev.triage_candidate_exclusion (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id INTEGER NOT NULL,
|
||||
exclusion_date DATE NOT NULL,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(255),
|
||||
organization_id BIGINT,
|
||||
organization_name VARCHAR(255),
|
||||
reason VARCHAR(100) DEFAULT 'ADDED_TO_QUEUE', -- 排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
delete_flag CHAR(1) NOT NULL DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 常用查询索引:按租户/日期/就诊记录查询
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_candidate_exclusion_list
|
||||
ON hisdev.triage_candidate_exclusion (tenant_id, exclusion_date, delete_flag, encounter_id);
|
||||
|
||||
-- 防重复:同一天同租户同就诊记录只能有一条排除记录(未删除)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_triage_candidate_exclusion_encounter
|
||||
ON hisdev.triage_candidate_exclusion (tenant_id, exclusion_date, encounter_id, delete_flag)
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 就诊记录ID索引(用于关联查询)
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_candidate_exclusion_encounter_id
|
||||
ON hisdev.triage_candidate_exclusion (encounter_id);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE hisdev.triage_candidate_exclusion IS '智能分诊排队候选池排除记录表';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.exclusion_date IS '排除日期(通常为当天)';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.encounter_id IS '就诊记录ID(唯一标识)';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.patient_id IS '患者ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.patient_name IS '患者姓名';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.organization_id IS '科室ID';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.organization_name IS '科室名称';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.reason IS '排除原因:ADDED_TO_QUEUE(加入队列)、MANUAL_REMOVE(手动移除)等';
|
||||
COMMENT ON COLUMN hisdev.triage_candidate_exclusion.delete_flag IS '删除标志:0-未删除,1-已删除';
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
-- 智能分诊排队:队列持久化表(与 TriageQueueItem.java 字段一致)
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev环境是 hisdev,test环境是 histest,prd环境是 hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS triage_queue_item (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id INTEGER NOT NULL,
|
||||
queue_date DATE NOT NULL,
|
||||
organization_id BIGINT NOT NULL,
|
||||
organization_name VARCHAR(255),
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(255),
|
||||
healthcare_name VARCHAR(255),
|
||||
practitioner_name VARCHAR(255),
|
||||
status VARCHAR(50) NOT NULL, -- WAITING/CALLING/SKIPPED/COMPLETED
|
||||
queue_order INTEGER NOT NULL,
|
||||
create_time TIMESTAMP,
|
||||
update_time TIMESTAMP,
|
||||
delete_flag CHAR(1) NOT NULL DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 常用查询索引:按租户/科室/日期/状态/顺序取队列
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_queue_item_list
|
||||
ON triage_queue_item (tenant_id, queue_date, organization_id, delete_flag, status, queue_order);
|
||||
|
||||
-- 防重复:同一天同租户同科室同就诊记录只能在队列里一条(未删除)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_triage_queue_item_encounter
|
||||
ON triage_queue_item (tenant_id, queue_date, organization_id, encounter_id, delete_flag)
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 就诊记录ID索引(用于关联查询)
|
||||
CREATE INDEX IF NOT EXISTS idx_triage_queue_item_encounter_id
|
||||
ON triage_queue_item (encounter_id);
|
||||
|
||||
108
openhis-server-new/sql/debug_queue_date_issue.sql
Normal file
108
openhis-server-new/sql/debug_queue_date_issue.sql
Normal file
@@ -0,0 +1,108 @@
|
||||
-- ============================================
|
||||
-- 调试队列日期问题
|
||||
-- 用途:检查数据库中的 queue_date 字段值,找出为什么查询不到数据
|
||||
-- ============================================
|
||||
|
||||
-- 1. 查看所有队列记录的日期分布
|
||||
SELECT
|
||||
queue_date,
|
||||
status,
|
||||
COUNT(*) AS count,
|
||||
STRING_AGG(patient_name, ', ') AS patients
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
GROUP BY queue_date, status
|
||||
ORDER BY queue_date DESC, status;
|
||||
|
||||
-- 2. 查看指定科室的所有记录(不限制日期)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
organization_name,
|
||||
queue_order,
|
||||
create_time
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_date DESC, queue_order ASC;
|
||||
|
||||
-- 3. 查看今天的记录(使用 CURRENT_DATE)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 4. 查看 2026-01-16 的记录
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = '2026-01-16'::DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 5. 查看 2025-01-16 的记录(可能是昨天的数据)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date = '2025-01-16'::DATE
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_order ASC;
|
||||
|
||||
-- 6. 查看最近的记录(最近7天)
|
||||
SELECT
|
||||
queue_date,
|
||||
status,
|
||||
COUNT(*) AS count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND queue_date >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY queue_date, status
|
||||
ORDER BY queue_date DESC, status;
|
||||
|
||||
-- 7. 检查系统当前日期(PostgreSQL)
|
||||
SELECT CURRENT_DATE AS current_date_in_db;
|
||||
|
||||
-- 8. 查看所有状态为 WAITING 或 SKIPPED 的记录(不限制日期)
|
||||
SELECT
|
||||
id,
|
||||
queue_date,
|
||||
status,
|
||||
patient_name,
|
||||
organization_id,
|
||||
queue_order
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE tenant_id = 1
|
||||
AND delete_flag = '0'
|
||||
AND status IN ('WAITING', 'SKIPPED')
|
||||
AND organization_id = 1995302136614969300
|
||||
ORDER BY queue_date DESC, queue_order ASC;
|
||||
|
||||
313
openhis-server-new/sql/query_smart_candidate_pool.sql
Normal file
313
openhis-server-new/sql/query_smart_candidate_pool.sql
Normal file
@@ -0,0 +1,313 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(当日已挂号患者)
|
||||
-- 对应接口:GET /charge-manage/register/current-day-encounter
|
||||
-- 用途:查询当日已挂号但未入队的患者,用于智能分诊排队管理
|
||||
-- ============================================
|
||||
|
||||
-- 完整SQL查询(包含所有关联表)
|
||||
SELECT T9.tenant_id AS tenantId,
|
||||
T9.encounter_id AS encounterId,
|
||||
T9.display_order AS displayOrder,
|
||||
T9.organization_id AS organizationId,
|
||||
T9.organization_name AS organizationName,
|
||||
T9.healthcare_name AS healthcareName,
|
||||
T9.practitioner_user_id AS practitionerUserId,
|
||||
T9.practitioner_name AS practitionerName,
|
||||
T9.contract_name AS contractName,
|
||||
T9.patient_id AS patientId,
|
||||
T9.patient_name AS patientName,
|
||||
T9.phone,
|
||||
T9.gender_enum AS genderEnum,
|
||||
T9.id_card AS idCard,
|
||||
T9.status_enum AS statusEnum,
|
||||
T9.register_time AS registerTime,
|
||||
T9.total_price AS totalPrice,
|
||||
T9.account_name AS accountName,
|
||||
T9.enterer_name AS entererName,
|
||||
T9.charge_item_ids AS chargeItemIds,
|
||||
T9.payment_id AS paymentId,
|
||||
T9.picture_url AS pictureUrl,
|
||||
T9.birth_date AS birthDate,
|
||||
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo
|
||||
FROM (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
T1.display_order AS display_order,
|
||||
T1.organization_id AS organization_id,
|
||||
T2.NAME AS organization_name,
|
||||
T3.NAME AS healthcare_name,
|
||||
T5.user_id AS practitioner_user_id,
|
||||
T5.NAME AS practitioner_name,
|
||||
T7.contract_name AS contract_name,
|
||||
T8.id AS patient_id,
|
||||
T8.NAME AS patient_name,
|
||||
T8.phone AS phone,
|
||||
T8.gender_enum AS gender_enum,
|
||||
T8.id_card AS id_card,
|
||||
T1.status_enum AS status_enum,
|
||||
T1.create_time AS register_time,
|
||||
T10.total_price,
|
||||
T11."name" AS account_name,
|
||||
T12."name" AS enterer_name,
|
||||
T13.charge_item_ids,
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
T8.bus_no AS patient_bus_no,
|
||||
T18.identifier_no AS identifier_no
|
||||
FROM adm_encounter AS T1
|
||||
-- 关联科室表
|
||||
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 (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
-- 如果就诊状态为"进行中",查找"接诊医生"(admitter)
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
OR
|
||||
-- 如果就诊状态不是"进行中",查找"挂号医生"(registration_doctor)
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
)
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T4 ON T1.ID = T4.encounter_id
|
||||
-- 关联医生表
|
||||
LEFT JOIN adm_practitioner AS T5
|
||||
ON T5.ID = T4.practitioner_id
|
||||
AND T5.delete_flag = '0'
|
||||
-- 关联账户表
|
||||
LEFT JOIN adm_account AS T6
|
||||
ON T1.ID = T6.encounter_id
|
||||
AND T6.delete_flag = '0'
|
||||
AND T6.encounter_flag = '1'
|
||||
-- 关联合同表
|
||||
LEFT JOIN fin_contract AS T7
|
||||
ON T6.contract_no = T7.bus_no
|
||||
AND T7.delete_flag = '0'
|
||||
-- 关联患者表
|
||||
LEFT JOIN adm_patient AS T8
|
||||
ON T1.patient_id = T8.ID
|
||||
AND T8.delete_flag = '0'
|
||||
-- 关联患者标识表(身份证号等)
|
||||
LEFT JOIN (
|
||||
SELECT patient_id,
|
||||
identifier_no
|
||||
FROM (
|
||||
SELECT patient_id,
|
||||
identifier_no,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_patient_identifier
|
||||
WHERE delete_flag = '0'
|
||||
AND identifier_no IS NOT NULL
|
||||
AND identifier_no != ''
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T18 ON T8.id = T18.patient_id
|
||||
-- 关联收费项目表(挂号费)
|
||||
LEFT JOIN adm_charge_item AS T10
|
||||
ON T1.id = T10.encounter_id
|
||||
AND T10.delete_flag = '0'
|
||||
AND T10.context_enum = 1 -- REGISTER(挂号)
|
||||
-- 关联账户表(收费账户)
|
||||
LEFT JOIN adm_account AS T11
|
||||
ON T10.account_id = T11.id
|
||||
AND T11.delete_flag = '0'
|
||||
-- 关联医生表(收费医生)
|
||||
LEFT JOIN adm_practitioner AS T12
|
||||
ON T12.ID = T10.enterer_id
|
||||
AND T12.delete_flag = '0'
|
||||
-- 关联支付对账表(已支付)
|
||||
LEFT JOIN fin_payment_reconciliation T13
|
||||
ON T10.id::TEXT = ANY(string_to_array(T13.charge_item_ids,','))
|
||||
AND T13.delete_flag = '0'
|
||||
AND T13.status_enum = 1 -- SUCCESS(支付成功)
|
||||
-- 关联退号记录(当状态为退号时,通过relation_id关联原支付记录)
|
||||
LEFT JOIN fin_payment_reconciliation T14
|
||||
ON T13.id = T14.relation_id
|
||||
AND T14.delete_flag = '0'
|
||||
AND T14.status_enum = 3 -- REFUND(退款)
|
||||
AND T14.payment_enum = 1
|
||||
-- 关联退号医生
|
||||
LEFT JOIN adm_practitioner AS T15
|
||||
ON T15.ID = T14.enterer_id
|
||||
AND T15.delete_flag = '0'
|
||||
-- 关联系统用户表
|
||||
LEFT JOIN sys_user AS T17
|
||||
ON T17.user_id = T15.user_id
|
||||
AND T17.delete_flag = '0'
|
||||
-- 关联退号支付详情,获取退款方式(聚合多个支付方式)
|
||||
LEFT JOIN (
|
||||
SELECT reconciliation_id,
|
||||
STRING_AGG(
|
||||
CASE pay_enum
|
||||
WHEN 220400 THEN '现金'
|
||||
WHEN 220100 THEN '微信'
|
||||
WHEN 220200 THEN '支付宝'
|
||||
WHEN 220300 THEN '银联'
|
||||
END,
|
||||
','
|
||||
ORDER BY pay_enum
|
||||
) AS refund_method
|
||||
FROM fin_payment_rec_detail
|
||||
WHERE delete_flag = '0'
|
||||
AND amount < 0
|
||||
AND pay_enum IN (220400, 220100, 220200, 220300)
|
||||
GROUP BY reconciliation_id
|
||||
) AS T16 ON T14.id = T16.reconciliation_id
|
||||
-- 关联发票表
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id
|
||||
AND ai.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = 1 -- 门诊(AMB)
|
||||
AND T10.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- 动态条件(由前端传入,通过 ${ew.customSqlSegment} 注入)
|
||||
-- 例如:AND T1.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 例如:AND T1.status_enum != 6 -- 排除退号(statusEnum = -1时)
|
||||
-- 例如:AND (T8.name LIKE '%关键词%' OR T2.name LIKE '%关键词%' ...) -- 模糊搜索
|
||||
) AS T9
|
||||
-- 动态查询条件(由 MyBatis-Plus QueryWrapper 生成)
|
||||
-- ${ew.customSqlSegment}
|
||||
ORDER BY T9.register_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 简化版查询(仅查询核心字段,用于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.birth_date AS birthDate,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime,
|
||||
enc.status_enum AS statusEnum
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org
|
||||
ON enc.organization_id = org.id
|
||||
AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs
|
||||
ON enc.service_type_id = hcs.id
|
||||
AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr
|
||||
ON ep.practitioner_id = pr.id
|
||||
AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci
|
||||
ON enc.id = ci.encounter_id
|
||||
AND ci.delete_flag = '0'
|
||||
AND ci.context_enum = 1 -- 挂号
|
||||
INNER JOIN fin_payment_reconciliation prc
|
||||
ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0'
|
||||
AND prc.status_enum = 1 -- 支付成功
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊
|
||||
AND enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
AND enc.status_enum != 6 -- 排除退号
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号(由前端传入 date 参数)
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(当 statusEnum = -1 时)
|
||||
-- - enc.status_enum = 2 -- 进行中(IN_PROGRESS,用于判断医生类型)
|
||||
--
|
||||
-- 4. 模糊搜索(由前端传入 searchKey 参数):
|
||||
-- - patient_name(患者姓名)
|
||||
-- - organization_name(科室名称)
|
||||
-- - practitioner_name(医生姓名)
|
||||
-- - healthcare_name(号别名称)
|
||||
-- - identifier_no(身份证号)
|
||||
--
|
||||
-- 5. 租户过滤(由后端自动添加):
|
||||
-- - tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 6. 分页(由 MyBatis-Plus Page 对象处理):
|
||||
-- - LIMIT ? OFFSET ?
|
||||
|
||||
-- ============================================
|
||||
-- 常用查询示例
|
||||
-- ============================================
|
||||
|
||||
-- 示例1:查询今天所有已挂号患者(排除退号)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- 示例2:查询指定科室的已挂号患者
|
||||
-- 在示例1的基础上添加:
|
||||
-- AND enc.organization_id = ? -- 科室ID
|
||||
|
||||
-- 示例3:按患者姓名模糊搜索
|
||||
-- 在示例1的基础上添加:
|
||||
-- AND pt.name LIKE '%关键词%'
|
||||
|
||||
-- 示例4:查询已加入队列的患者(用于去重)
|
||||
SELECT DISTINCT encounter_id
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
268
openhis-server-new/sql/query_smart_candidate_pool_page.sql
Normal file
268
openhis-server-new/sql/query_smart_candidate_pool_page.sql
Normal file
@@ -0,0 +1,268 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(智能分诊排队管理页面专用)
|
||||
-- 页面:智能分诊排队管理 - 心内科
|
||||
-- 用途:查询"已签到未入队"的患者(当日已挂号,但未加入队列)
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 方案一:完整查询(包含所有字段,排除已在队列中的患者)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
pt.birth_date AS birthDate,
|
||||
-- 计算年龄(前端会调用 parseAge 函数)
|
||||
CASE
|
||||
WHEN pt.birth_date IS NOT NULL THEN
|
||||
EXTRACT(YEAR FROM AGE(pt.birth_date))::TEXT || '岁'
|
||||
ELSE ''
|
||||
END AS age,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime,
|
||||
enc.status_enum AS statusEnum,
|
||||
-- 其他字段(如果需要)
|
||||
pt.phone,
|
||||
pt.gender_enum AS genderEnum,
|
||||
pt.id_card AS idCard
|
||||
FROM adm_encounter enc
|
||||
-- 关联患者表
|
||||
INNER JOIN adm_patient pt
|
||||
ON enc.patient_id = pt.id
|
||||
AND pt.delete_flag = '0'
|
||||
-- 关联科室表
|
||||
LEFT JOIN adm_organization org
|
||||
ON enc.organization_id = org.id
|
||||
AND org.delete_flag = '0'
|
||||
-- 关联医疗服务类型表(号别)
|
||||
LEFT JOIN adm_healthcare_service hcs
|
||||
ON enc.service_type_id = hcs.id
|
||||
AND hcs.delete_flag = '0'
|
||||
-- 关联就诊参与者表(医生信息)
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
-- 如果就诊状态为"进行中",查找"接诊医生"(admitter)
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS e
|
||||
WHERE e.status_enum = 2 -- IN_PROGRESS
|
||||
AND e.id = encounter_id))
|
||||
OR
|
||||
-- 如果就诊状态不是"进行中",查找"挂号医生"(registration_doctor)
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS e
|
||||
WHERE e.status_enum = 2 -- IN_PROGRESS
|
||||
AND e.id = encounter_id))
|
||||
)
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
-- 关联医生表
|
||||
LEFT JOIN adm_practitioner pr
|
||||
ON ep.practitioner_id = pr.id
|
||||
AND pr.delete_flag = '0'
|
||||
-- 关联收费项目表(挂号费)
|
||||
INNER JOIN adm_charge_item ci
|
||||
ON enc.id = ci.encounter_id
|
||||
AND ci.delete_flag = '0'
|
||||
AND ci.context_enum = 1 -- REGISTER(挂号)
|
||||
-- 关联支付对账表(已支付)
|
||||
INNER JOIN fin_payment_reconciliation prc
|
||||
ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0'
|
||||
AND prc.status_enum = 1 -- SUCCESS(支付成功)
|
||||
-- 排除已在队列中的患者(关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED' -- 排除已完成的状态
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1 -- 门诊(AMB)
|
||||
AND enc.create_time::DATE = CURRENT_DATE -- 当日挂号(前端会过滤 registerTime)
|
||||
AND enc.status_enum != 6 -- 排除退号(statusEnum = -1 时)
|
||||
AND tqi.id IS NULL -- 关键:只查询不在队列中的患者
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案二:简化查询(仅核心字段,便于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
-- 排除已在队列中的患者
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
AND tqi.id IS NULL -- 不在队列中
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案三:分步查询(便于调试和理解)
|
||||
-- ============================================
|
||||
|
||||
-- 步骤1:查询当日已挂号的患者(不排除队列)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 步骤2:查询已在队列中的患者
|
||||
SELECT DISTINCT encounter_id
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 步骤3:合并查询(使用 NOT EXISTS 或 NOT IN)
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
pt.name AS patientName,
|
||||
org.name AS organizationName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
-- 排除已在队列中的患者
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.encounter_id = enc.id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
)
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 注意:前端还会基于 registerTime 字段再次过滤今天的数据
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(对应前端 statusEnum = -1)
|
||||
--
|
||||
-- 4. 队列去重:
|
||||
-- - LEFT JOIN hisdev.triage_queue_item ... WHERE tqi.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_queue_item ...)
|
||||
-- 排除已在队列中且状态不是 COMPLETED 的患者
|
||||
--
|
||||
-- 5. 租户过滤(如果需要):
|
||||
-- - enc.tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 6. 模糊搜索(如果需要):
|
||||
-- - pt.name LIKE '%关键词%' -- 患者姓名
|
||||
-- - org.name LIKE '%关键词%' -- 科室名称
|
||||
-- - pr.name LIKE '%关键词%' -- 医生姓名
|
||||
-- - hcs.name LIKE '%关键词%' -- 号别名称
|
||||
|
||||
-- ============================================
|
||||
-- 调试查询(检查数据是否正确)
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查当日已挂号患者总数
|
||||
SELECT COUNT(*) AS total_registered
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 2. 检查已在队列中的患者数量
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_queue_count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 3. 检查候选池应该显示的患者数量(已挂号 - 已在队列)
|
||||
SELECT
|
||||
(SELECT COUNT(*)
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6)
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED')
|
||||
AS candidate_pool_count;
|
||||
|
||||
-- 4. 查看队列中的患者详情(用于对比)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name,
|
||||
tqi.status,
|
||||
tqi.queue_order
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
ORDER BY tqi.queue_order;
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
-- ============================================
|
||||
-- 智能候选池查询SQL(完整版 - 包含排除列表)
|
||||
-- 页面:智能分诊排队管理 - 心内科
|
||||
-- 用途:查询"已签到未入队"的患者(当日已挂号,但未加入队列,且未在排除列表中)
|
||||
-- 更新日期:2025-01-15
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- 方案一:完整查询(包含所有字段,排除已在队列和排除列表中的患者)
|
||||
-- ============================================
|
||||
SELECT
|
||||
T9.tenant_id AS tenantId,
|
||||
T9.encounter_id AS encounterId,
|
||||
T9.display_order AS displayOrder,
|
||||
T9.organization_id AS organizationId,
|
||||
T9.organization_name AS organizationName,
|
||||
T9.healthcare_name AS healthcareName,
|
||||
T9.practitioner_user_id AS practitionerUserId,
|
||||
T9.practitioner_name AS practitionerName,
|
||||
T9.contract_name AS contractName,
|
||||
T9.patient_id AS patientId,
|
||||
T9.patient_name AS patientName,
|
||||
T9.phone,
|
||||
T9.gender_enum AS genderEnum,
|
||||
T9.id_card AS idCard,
|
||||
T9.status_enum AS statusEnum,
|
||||
T9.register_time AS registerTime,
|
||||
T9.total_price AS totalPrice,
|
||||
T9.account_name AS accountName,
|
||||
T9.enterer_name AS entererName,
|
||||
T9.charge_item_ids AS chargeItemIds,
|
||||
T9.payment_id AS paymentId,
|
||||
T9.picture_url AS pictureUrl,
|
||||
T9.birth_date AS birthDate,
|
||||
COALESCE(T9.identifier_no, T9.patient_bus_no, '') AS identifierNo,
|
||||
-- 计算年龄(前端会调用 parseAge 函数)
|
||||
CASE
|
||||
WHEN T9.birth_date IS NOT NULL THEN
|
||||
EXTRACT(YEAR FROM AGE(T9.birth_date))::TEXT || '岁'
|
||||
ELSE ''
|
||||
END AS age
|
||||
FROM (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
T1.display_order AS display_order,
|
||||
T1.organization_id AS organization_id,
|
||||
T2.NAME AS organization_name,
|
||||
T3.NAME AS healthcare_name,
|
||||
T5.user_id AS practitioner_user_id,
|
||||
T5.NAME AS practitioner_name,
|
||||
T7.contract_name AS contract_name,
|
||||
T8.id AS patient_id,
|
||||
T8.NAME AS patient_name,
|
||||
T8.phone AS phone,
|
||||
T8.gender_enum AS gender_enum,
|
||||
T8.id_card AS id_card,
|
||||
T1.status_enum AS status_enum,
|
||||
T1.create_time AS register_time,
|
||||
T10.total_price,
|
||||
T11."name" AS account_name,
|
||||
T12."name" AS enterer_name,
|
||||
T13.charge_item_ids,
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
T8.bus_no AS patient_bus_no,
|
||||
T18.identifier_no AS identifier_no
|
||||
FROM adm_encounter AS T1
|
||||
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 (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT *,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0'
|
||||
AND (
|
||||
(type_code = 'admitter' AND EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
OR
|
||||
(type_code = 'registration_doctor' AND NOT EXISTS(SELECT 1
|
||||
FROM adm_encounter AS T1
|
||||
WHERE T1.status_enum = 2 -- IN_PROGRESS
|
||||
AND T1.ID = encounter_id))
|
||||
)
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T4 ON T1.ID = T4.encounter_id
|
||||
LEFT JOIN adm_practitioner AS T5 ON T5.ID = T4.practitioner_id AND T5.delete_flag = '0'
|
||||
LEFT JOIN adm_account AS T6
|
||||
ON T1.ID = T6.encounter_id AND T6.delete_flag = '0' AND T6.encounter_flag = '1'
|
||||
LEFT JOIN fin_contract AS T7 ON T6.contract_no = T7.bus_no AND T7.delete_flag = '0'
|
||||
LEFT JOIN adm_patient AS T8 ON T1.patient_id = T8.ID AND T8.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT patient_id,
|
||||
identifier_no
|
||||
FROM (
|
||||
SELECT patient_id,
|
||||
identifier_no,
|
||||
ROW_NUMBER() OVER (PARTITION BY patient_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_patient_identifier
|
||||
WHERE delete_flag = '0'
|
||||
AND identifier_no IS NOT NULL
|
||||
AND identifier_no != ''
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) AS T18 ON T8.id = T18.patient_id
|
||||
LEFT JOIN adm_charge_item AS T10 ON T1.id = T10.encounter_id AND T10.delete_flag = '0'
|
||||
LEFT JOIN adm_account AS T11 ON T10.account_id = T11.id AND T11.delete_flag = '0'
|
||||
LEFT JOIN adm_practitioner AS T12 ON T12.ID = T10.enterer_id AND T12.delete_flag = '0'
|
||||
LEFT JOIN fin_payment_reconciliation T13
|
||||
ON T10.id::TEXT = ANY(string_to_array(T13.charge_item_ids,','))
|
||||
AND T13.delete_flag = '0'
|
||||
AND T13.status_enum = 1 -- SUCCESS(支付成功)
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id AND ai.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = 1 -- 门诊(AMB)
|
||||
AND T1.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
AND T1.status_enum != 6 -- 排除退号
|
||||
AND T10.context_enum = 1 -- REGISTER(挂号)
|
||||
) AS T9
|
||||
-- 排除已在队列中的患者(关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON T9.encounter_id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED' -- 排除已完成的状态
|
||||
-- 排除已在排除列表中的患者(新增:关键:LEFT JOIN + WHERE IS NULL)
|
||||
LEFT JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON T9.encounter_id = tce.encounter_id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = T9.tenant_id -- 按租户过滤
|
||||
WHERE tqi.id IS NULL -- 关键:只查询不在队列中的患者
|
||||
AND tce.id IS NULL -- 关键:只查询不在排除列表中的患者
|
||||
ORDER BY T9.register_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案二:简化查询(仅核心字段,便于快速测试)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
-- 排除已在队列中的患者
|
||||
LEFT JOIN hisdev.triage_queue_item tqi
|
||||
ON enc.id = tqi.encounter_id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
-- 排除已在排除列表中的患者(新增)
|
||||
LEFT JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON enc.id = tce.encounter_id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = enc.tenant_id
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
AND tqi.id IS NULL -- 不在队列中
|
||||
AND tce.id IS NULL -- 不在排除列表中
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 方案三:使用 NOT EXISTS 子查询(性能可能更好)
|
||||
-- ============================================
|
||||
SELECT
|
||||
enc.id AS encounterId,
|
||||
enc.patient_id AS patientId,
|
||||
pt.name AS patientName,
|
||||
org.id AS organizationId,
|
||||
org.name AS organizationName,
|
||||
hcs.name AS healthcareName,
|
||||
pr.name AS practitionerName,
|
||||
enc.create_time AS registerTime
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_patient pt ON enc.patient_id = pt.id AND pt.delete_flag = '0'
|
||||
LEFT JOIN adm_organization org ON enc.organization_id = org.id AND org.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service hcs ON enc.service_type_id = hcs.id AND hcs.delete_flag = '0'
|
||||
LEFT JOIN (
|
||||
SELECT encounter_id, practitioner_id,
|
||||
ROW_NUMBER() OVER (PARTITION BY encounter_id ORDER BY create_time ASC) AS rn
|
||||
FROM adm_encounter_participant
|
||||
WHERE delete_flag = '0' AND type_code IN ('admitter', 'registration_doctor')
|
||||
) ep ON enc.id = ep.encounter_id AND ep.rn = 1
|
||||
LEFT JOIN adm_practitioner pr ON ep.practitioner_id = pr.id AND pr.delete_flag = '0'
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6
|
||||
-- 排除已在队列中的患者
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.encounter_id = enc.id
|
||||
AND tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
)
|
||||
-- 排除已在排除列表中的患者(新增)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM hisdev.triage_candidate_exclusion tce
|
||||
WHERE tce.encounter_id = enc.id
|
||||
AND tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
AND tce.tenant_id = enc.tenant_id
|
||||
)
|
||||
ORDER BY enc.create_time DESC;
|
||||
|
||||
-- ============================================
|
||||
-- 查询条件说明
|
||||
-- ============================================
|
||||
-- 1. 基础条件:
|
||||
-- - enc.delete_flag = '0' -- 未删除
|
||||
-- - enc.class_enum = 1 -- 门诊(AMB)
|
||||
-- - ci.context_enum = 1 -- 挂号(REGISTER)
|
||||
-- - prc.status_enum = 1 -- 支付成功(SUCCESS)
|
||||
--
|
||||
-- 2. 日期过滤:
|
||||
-- - enc.create_time::DATE = CURRENT_DATE -- 当日挂号
|
||||
-- 注意:前端还会基于 registerTime 字段再次过滤今天的数据
|
||||
--
|
||||
-- 3. 状态过滤:
|
||||
-- - enc.status_enum != 6 -- 排除退号(对应前端 statusEnum = -1)
|
||||
--
|
||||
-- 4. 队列去重:
|
||||
-- - LEFT JOIN hisdev.triage_queue_item ... WHERE tqi.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_queue_item ...)
|
||||
-- 排除已在队列中且状态不是 COMPLETED 的患者
|
||||
--
|
||||
-- 5. 排除列表去重(新增):
|
||||
-- - LEFT JOIN hisdev.triage_candidate_exclusion ... WHERE tce.id IS NULL
|
||||
-- 或者
|
||||
-- - NOT EXISTS (SELECT 1 FROM hisdev.triage_candidate_exclusion ...)
|
||||
-- 排除已在排除列表中的患者(已加入队列后被记录)
|
||||
--
|
||||
-- 6. 租户过滤(如果需要):
|
||||
-- - enc.tenant_id = ? -- 当前登录用户的租户ID
|
||||
--
|
||||
-- 7. 模糊搜索(如果需要):
|
||||
-- - pt.name LIKE '%关键词%' -- 患者姓名
|
||||
-- - org.name LIKE '%关键词%' -- 科室名称
|
||||
-- - pr.name LIKE '%关键词%' -- 医生姓名
|
||||
-- - hcs.name LIKE '%关键词%' -- 号别名称
|
||||
|
||||
-- ============================================
|
||||
-- 调试查询(检查数据是否正确)
|
||||
-- ============================================
|
||||
|
||||
-- 1. 检查当日已挂号患者总数
|
||||
SELECT COUNT(*) AS total_registered
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6;
|
||||
|
||||
-- 2. 检查已在队列中的患者数量
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_queue_count
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED';
|
||||
|
||||
-- 3. 检查已在排除列表中的患者数量(新增)
|
||||
SELECT COUNT(DISTINCT encounter_id) AS in_exclusion_count
|
||||
FROM hisdev.triage_candidate_exclusion
|
||||
WHERE delete_flag = '0'
|
||||
AND exclusion_date = CURRENT_DATE;
|
||||
|
||||
-- 4. 检查候选池应该显示的患者数量(已挂号 - 已在队列 - 已在排除列表)
|
||||
SELECT
|
||||
(SELECT COUNT(*)
|
||||
FROM adm_encounter enc
|
||||
INNER JOIN adm_charge_item ci ON enc.id = ci.encounter_id AND ci.delete_flag = '0' AND ci.context_enum = 1
|
||||
INNER JOIN fin_payment_reconciliation prc ON ci.id::TEXT = ANY(string_to_array(prc.charge_item_ids,','))
|
||||
AND prc.delete_flag = '0' AND prc.status_enum = 1
|
||||
WHERE enc.delete_flag = '0'
|
||||
AND enc.class_enum = 1
|
||||
AND enc.create_time::DATE = CURRENT_DATE
|
||||
AND enc.status_enum != 6)
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_queue_item
|
||||
WHERE delete_flag = '0'
|
||||
AND queue_date = CURRENT_DATE
|
||||
AND status != 'COMPLETED')
|
||||
-
|
||||
(SELECT COUNT(DISTINCT encounter_id)
|
||||
FROM hisdev.triage_candidate_exclusion
|
||||
WHERE delete_flag = '0'
|
||||
AND exclusion_date = CURRENT_DATE)
|
||||
AS candidate_pool_count;
|
||||
|
||||
-- 5. 查看队列中的患者详情(用于对比)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name,
|
||||
tqi.status,
|
||||
tqi.queue_order
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
AND tqi.status != 'COMPLETED'
|
||||
ORDER BY tqi.queue_order;
|
||||
|
||||
-- 6. 查看排除列表中的患者详情(新增)
|
||||
SELECT
|
||||
tce.encounter_id,
|
||||
tce.patient_name,
|
||||
tce.reason,
|
||||
tce.create_time
|
||||
FROM hisdev.triage_candidate_exclusion tce
|
||||
WHERE tce.delete_flag = '0'
|
||||
AND tce.exclusion_date = CURRENT_DATE
|
||||
ORDER BY tce.create_time DESC;
|
||||
|
||||
-- 7. 查看重叠的患者(既在队列中,又在排除列表中)
|
||||
SELECT
|
||||
tqi.encounter_id,
|
||||
tqi.patient_name AS queue_patient_name,
|
||||
tce.patient_name AS exclusion_patient_name,
|
||||
tqi.status,
|
||||
tce.reason
|
||||
FROM hisdev.triage_queue_item tqi
|
||||
INNER JOIN hisdev.triage_candidate_exclusion tce
|
||||
ON tqi.encounter_id = tce.encounter_id
|
||||
AND tqi.queue_date = tce.exclusion_date
|
||||
AND tqi.tenant_id = tce.tenant_id
|
||||
WHERE tqi.delete_flag = '0'
|
||||
AND tce.delete_flag = '0'
|
||||
AND tqi.queue_date = CURRENT_DATE
|
||||
ORDER BY tqi.encounter_id;
|
||||
|
||||
Reference in New Issue
Block a user