预约管理-》门诊预约挂号:号源信息的序号未进行取值
316门诊医生站-》医嘱TAB页面:会诊医嘱状态从“已签发”变成“草稿”
317【门诊医生站】已签发会诊医嘱未同步至门诊收费系统生成待收费项目
344
门诊预约挂号:未过滤过期号源,允许预约已过时的时间段
347 医生门诊工作已就诊的病人提示未就诊
This commit is contained in:
Ranyunqiao
2026-04-07 15:36:27 +08:00
parent 616c2d21a6
commit c3f1b105e9
3 changed files with 189 additions and 33 deletions

View File

@@ -421,6 +421,20 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
// 新增:更新门诊医嘱表状态为已提交 // 新增:更新门诊医嘱表状态为已提交
updateServiceRequestStatus(entity.getOrderId(), RequestStatus.ACTIVE.getValue()); updateServiceRequestStatus(entity.getOrderId(), RequestStatus.ACTIVE.getValue());
// 🎯 更新会诊关联费用项状态为"待收费",提交后即可在收费界面看到
if (entity.getOrderId() != null) {
LambdaQueryWrapper<ChargeItem> chargeItemWrapper = new LambdaQueryWrapper<>();
chargeItemWrapper.eq(ChargeItem::getServiceId, entity.getOrderId())
.eq(ChargeItem::getServiceTable, "wor_service_request");
List<ChargeItem> chargeItems = iChargeItemService.list(chargeItemWrapper);
for (ChargeItem chargeItem : chargeItems) {
chargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue());
iChargeItemService.updateById(chargeItem);
}
log.info("会诊提交,更新关联费用项状态为待收费,更新数量: {}", chargeItems.size());
}
return true; return true;
} catch (Exception e) { } catch (Exception e) {
log.error("提交会诊申请失败", e); log.error("提交会诊申请失败", e);
@@ -464,6 +478,18 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
// 更新门诊医嘱表状态为新开 // 更新门诊医嘱表状态为新开
updateServiceRequestStatus(entity.getOrderId(), RequestStatus.DRAFT.getValue()); updateServiceRequestStatus(entity.getOrderId(), RequestStatus.DRAFT.getValue());
// 更新关联费用项状态为草稿
if (entity.getOrderId() != null) {
LambdaQueryWrapper<ChargeItem> chargeItemWrapper = new LambdaQueryWrapper<>();
chargeItemWrapper.eq(ChargeItem::getServiceId, entity.getOrderId())
.eq(ChargeItem::getServiceTable, "wor_service_request");
List<ChargeItem> chargeItems = iChargeItemService.list(chargeItemWrapper);
for (ChargeItem chargeItem : chargeItems) {
chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue());
iChargeItemService.updateById(chargeItem);
}
}
} else { } else {
// 作废:状态校验 - 已确认(20)、已签名(30)、已完成(40) 状态禁止作废 // 作废:状态校验 - 已确认(20)、已签名(30)、已完成(40) 状态禁止作废
ConsultationStatusEnum currentStatus = ConsultationStatusEnum.getByCode(entity.getConsultationStatus()); ConsultationStatusEnum currentStatus = ConsultationStatusEnum.getByCode(entity.getConsultationStatus());
@@ -480,6 +506,18 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
// 更新门诊医嘱表状态为已作废 // 更新门诊医嘱表状态为已作废
updateServiceRequestStatus(entity.getOrderId(), RequestStatus.CANCELLED.getValue()); updateServiceRequestStatus(entity.getOrderId(), RequestStatus.CANCELLED.getValue());
// 更新关联费用项状态为终止
if (entity.getOrderId() != null) {
LambdaQueryWrapper<ChargeItem> chargeItemWrapper = new LambdaQueryWrapper<>();
chargeItemWrapper.eq(ChargeItem::getServiceId, entity.getOrderId())
.eq(ChargeItem::getServiceTable, "wor_service_request");
List<ChargeItem> chargeItems = iChargeItemService.list(chargeItemWrapper);
for (ChargeItem chargeItem : chargeItems) {
chargeItem.setStatusEnum(ChargeItemStatus.ABORTED.getValue());
iChargeItemService.updateById(chargeItem);
}
}
} }
return true; return true;
@@ -668,12 +706,14 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
@Override @Override
public List<ConsultationRequestDto> getMyInvitations() { public List<ConsultationRequestDto> getMyInvitations() {
try { try {
// 获取当前登录医生ID // 获取当前登录医生ID和租户ID
Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId();
Long tenantId = SecurityUtils.getLoginUser().getTenantId().longValue();
// 查询邀请我的会诊申请 // 查询邀请我的会诊申请
LambdaQueryWrapper<ConsultationInvited> invitedWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ConsultationInvited> invitedWrapper = new LambdaQueryWrapper<>();
invitedWrapper.eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) invitedWrapper.eq(ConsultationInvited::getTenantId, tenantId)
.eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId)
.orderByDesc(ConsultationInvited::getCreateTime); .orderByDesc(ConsultationInvited::getCreateTime);
List<ConsultationInvited> invitedList = consultationInvitedMapper.selectList(invitedWrapper); List<ConsultationInvited> invitedList = consultationInvitedMapper.selectList(invitedWrapper);
@@ -1201,15 +1241,17 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
@Override @Override
public List<ConsultationConfirmationDto> getPendingConfirmationList() { public List<ConsultationConfirmationDto> getPendingConfirmationList() {
try { try {
// 获取当前登录医生ID // 获取当前登录医生ID和租户ID
Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId();
Long tenantId = SecurityUtils.getLoginUser().getTenantId().longValue();
log.info("获取待确认会诊列表当前医生ID: {}", currentPhysicianId); log.info("获取待确认会诊列表当前医生ID: {}", currentPhysicianId);
// 🎯 关键修改:查询当前医生个人状态为"待确认"、"已确认"或"已签名"的邀请记录 // 🎯 关键修改:查询当前医生个人状态为"待确认"、"已确认"或"已签名"的邀请记录
// 10=已提交待确认、20=已确认待签名、30=已签名排除40=已完成 // 10=已提交待确认、20=已确认待签名、30=已签名排除40=已完成
LambdaQueryWrapper<ConsultationInvited> invitedWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ConsultationInvited> invitedWrapper = new LambdaQueryWrapper<>();
invitedWrapper.eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) invitedWrapper.eq(ConsultationInvited::getTenantId, tenantId)
.in(ConsultationInvited::getInvitedStatus, .eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId)
.in(ConsultationInvited::getInvitedStatus,
ConsultationStatusEnum.SUBMITTED.getCode(), // 10-待确认 ConsultationStatusEnum.SUBMITTED.getCode(), // 10-待确认
ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认(待签名) ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认(待签名)
ConsultationStatusEnum.SIGNED.getCode()) // 30-已签名 ConsultationStatusEnum.SIGNED.getCode()) // 30-已签名
@@ -1233,7 +1275,8 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
// 🎯 查询会诊申请详情(白名单:只查询正在进行中的会诊,明确业务范围) // 🎯 查询会诊申请详情(白名单:只查询正在进行中的会诊,明确业务范围)
// 查询已提交、已确认、已签名状态的会诊排除已完成40 // 查询已提交、已确认、已签名状态的会诊排除已完成40
LambdaQueryWrapper<ConsultationRequest> requestWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ConsultationRequest> requestWrapper = new LambdaQueryWrapper<>();
requestWrapper.in(ConsultationRequest::getId, requestIds) requestWrapper.eq(ConsultationRequest::getTenantId, tenantId)
.in(ConsultationRequest::getId, requestIds)
.in(ConsultationRequest::getConsultationStatus, .in(ConsultationRequest::getConsultationStatus,
ConsultationStatusEnum.SUBMITTED.getCode(), // 10-已提交 ConsultationStatusEnum.SUBMITTED.getCode(), // 10-已提交
ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认 ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认
@@ -1633,7 +1676,20 @@ public class ConsultationAppServiceImpl implements IConsultationAppService {
// 更新医嘱状态为"已完成" // 更新医嘱状态为"已完成"
updateServiceRequestStatus(request.getOrderId(), RequestStatus.COMPLETED.getValue()); updateServiceRequestStatus(request.getOrderId(), RequestStatus.COMPLETED.getValue());
// 🎯 更新会诊关联费用项状态为"待收费",这样收费界面就能看到了
if (request.getOrderId() != null) {
LambdaQueryWrapper<ChargeItem> chargeItemWrapper = new LambdaQueryWrapper<>();
chargeItemWrapper.eq(ChargeItem::getServiceId, request.getOrderId())
.eq(ChargeItem::getServiceTable, "wor_service_request");
List<ChargeItem> chargeItems = iChargeItemService.list(chargeItemWrapper);
for (ChargeItem chargeItem : chargeItems) {
chargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue());
iChargeItemService.updateById(chargeItem);
}
log.info("会诊完成,更新关联费用项状态为待收费,更新数量: {}", chargeItems.size());
}
log.info("所有医生都已签名,会诊申请状态更新为:已签名(30)"); log.info("所有医生都已签名,会诊申请状态更新为:已签名(30)");
} else { } else {
// 🎯 关键修改部分医生签名整体状态不变保持为10或20 // 🎯 关键修改部分医生签名整体状态不变保持为10或20

View File

@@ -573,12 +573,13 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 1. 校验就诊状态:必须是已接诊状态 // 1. 校验就诊状态:必须是已接诊状态
Encounter encounterCheck = iEncounterService.getById(adviceSaveDto.getEncounterId()); Encounter encounterCheck = iEncounterService.getById(adviceSaveDto.getEncounterId());
if (encounterCheck != null) { if (encounterCheck != null) {
// 就诊状态1001=挂号1002=已接诊1003=已收费1004=已完成 // 就诊状态1=待诊(PLANNED),允许保存的状态 = 2(IN_PROGRESS在诊)、3(ON_HOLD暂离)、4(DISCHARGED诊毕)、5(COMPLETED完成)
if (encounterCheck.getStatusEnum() != null && if (encounterCheck.getStatusEnum() != null &&
encounterCheck.getStatusEnum() != 1002 && encounterCheck.getStatusEnum() != EncounterStatus.IN_PROGRESS.getValue() &&
encounterCheck.getStatusEnum() != 1003 && encounterCheck.getStatusEnum() != EncounterStatus.ON_HOLD.getValue() &&
encounterCheck.getStatusEnum() != 1004) { encounterCheck.getStatusEnum() != EncounterStatus.DISCHARGED.getValue() &&
log.error("BugFix#338: 患者未接诊,禁止划价/保存医嘱encounterId={}, status={}", encounterCheck.getStatusEnum() != EncounterStatus.COMPLETED.getValue()) {
log.error("BugFix#338: 患者未接诊,禁止划价/保存医嘱encounterId={}, status={}",
adviceSaveDto.getEncounterId(), encounterCheck.getStatusEnum()); adviceSaveDto.getEncounterId(), encounterCheck.getStatusEnum());
return R.fail(null, "患者尚未接诊,无法保存医嘱。请先完成接诊操作!"); return R.fail(null, "患者尚未接诊,无法保存医嘱。请先完成接诊操作!");
} }
@@ -967,13 +968,20 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 处理耗材发放 // 处理耗材发放
Long dispenseId = iDeviceDispenseService.handleDeviceDispense(deviceRequest, DbOpType.INSERT.getCode()); Long dispenseId = iDeviceDispenseService.handleDeviceDispense(deviceRequest, DbOpType.INSERT.getCode());
// 查询耗材定价信息 // 查询耗材定价信息 - 直接使用mapper查询避免递归调用getAdviceBaseInfo导致栈溢出
AdviceBaseDto deviceAdviceDto = new AdviceBaseDto(); IPage<AdviceBaseDto> devicePage = doctorStationAdviceAppMapper.getAdviceBaseInfo(
deviceAdviceDto.setAdviceDefinitionId(boundDevice.getDevActId()); new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(1, 1),
deviceAdviceDto.setAdviceTableName(CommonConstants.TableName.ADM_DEVICE_DEFINITION); PublicationStatus.ACTIVE.getValue(),
IPage<AdviceBaseDto> devicePage = getAdviceBaseInfo(deviceAdviceDto, null, null, null, adviceSaveDto.getFounderOrgId(),
adviceSaveDto.getFounderOrgId(), 1, 1, Whether.NO.getValue(), null,
List.of(ItemType.DEVICE.getValue()), null, null); CommonConstants.TableName.ADM_DEVICE_DEFINITION,
null,
null,
List.of(boundDevice.getDevActId()),
null,
null,
null,
null);
if (devicePage == null || devicePage.getRecords().isEmpty()) { if (devicePage == null || devicePage.getRecords().isEmpty()) {
log.warn("无法找到耗材定价信息: deviceDefId={}", boundDevice.getDevActId()); log.warn("无法找到耗材定价信息: deviceDefId={}", boundDevice.getDevActId());
@@ -981,12 +989,19 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
} }
AdviceBaseDto deviceBaseInfo = devicePage.getRecords().get(0); AdviceBaseDto deviceBaseInfo = devicePage.getRecords().get(0);
if (deviceBaseInfo.getPriceList() == null || deviceBaseInfo.getPriceList().isEmpty()) {
// 查询价格信息 - 直接查询定价主表
List<AdvicePriceDto> mainCharge = doctorStationAdviceAppMapper.getMainCharge(
List.of(deviceBaseInfo.getChargeItemDefinitionId()), PublicationStatus.ACTIVE.getValue());
if (mainCharge == null || mainCharge.isEmpty()) {
log.warn("耗材没有定价信息: deviceDefId={}", boundDevice.getDevActId()); log.warn("耗材没有定价信息: deviceDefId={}", boundDevice.getDevActId());
continue; continue;
} }
AdvicePriceDto devicePrice = deviceBaseInfo.getPriceList().get(0); AdvicePriceDto devicePrice = mainCharge.get(0);
devicePrice.setDefinitionId(deviceBaseInfo.getChargeItemDefinitionId());
// 如果需要定价子表ID可以从mainCharge中获取
// 创建耗材费用项 // 创建耗材费用项
ChargeItem deviceChargeItem = new ChargeItem(); ChargeItem deviceChargeItem = new ChargeItem();
@@ -1554,6 +1569,15 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// } // }
// log.error(e.getMessage(), e); // log.error(e.getMessage(), e);
// } // }
// 签发时将收费项目状态从草稿改为待收费
Long chargeItemId = adviceSaveDto.getChargeItemId();
if (chargeItemId != null) {
ChargeItem existingChargeItem = iChargeItemService.getById(chargeItemId);
if (existingChargeItem != null) {
existingChargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue());
iChargeItemService.updateById(existingChargeItem);
}
}
} }
} }
} }

View File

@@ -315,25 +315,105 @@ export default {
computed: { computed: {
filteredDoctors() { filteredDoctors() {
let filtered = [...this.doctors]; let filtered = [...this.doctors];
// 根据号源类型过滤医生列表 // 根据号源类型过滤医生列表
if (this.selectedType === 'general') { if (this.selectedType === 'general') {
filtered = filtered.filter(doctor => doctor.type === 'general'); filtered = filtered.filter(doctor => doctor.type === 'general');
} else if (this.selectedType === 'expert') { } else if (this.selectedType === 'expert') {
filtered = filtered.filter(doctor => doctor.type === 'expert'); filtered = filtered.filter(doctor => doctor.type === 'expert');
} }
// 根据搜索关键词过滤 // 根据搜索关键词过滤
if (this.searchQuery) { if (this.searchQuery) {
filtered = filtered.filter(doctor => filtered = filtered.filter(doctor =>
doctor.name.includes(this.searchQuery) doctor.name.includes(this.searchQuery)
); );
} }
// 🎯 实时更新余号数量:统计该医生当前筛选条件下剩余可预约(未预约 + 未过期)号源数量
const availableCountMap = {};
this.filteredAndSortedTickets.forEach(ticket => {
const doctorId = String(ticket.doctorId || ticket.doctor_id);
if (!availableCountMap[doctorId]) {
availableCountMap[doctorId] = 0;
}
// 只有未预约的号源才算作可预约余号
if (ticket.status === '未预约') {
availableCountMap[doctorId]++;
}
});
// 更新每个医生的余号数量
filtered = filtered.map(doctor => {
const actualAvailable = availableCountMap[String(doctor.id)] || 0;
return {
...doctor,
available: actualAvailable
};
});
return filtered; return filtered;
}, },
// 过滤并排序后的完整号源列表
filteredAndSortedTickets() {
let filtered = [...this.tickets];
// 🎯 精确过滤:过滤掉早于当前时间的号源
// - 日期早于今天 → 过滤
// - 日期是今天但号源时段已经开始(开始时间早于当前时间) → 过滤
const now = new Date();
filtered = filtered.filter(ticket => {
// dateTime 格式示例:"2024-01-01 08:00-09:00"
const parts = (ticket.dateTime || '').split(' ');
if (parts.length < 2) return true; // 如果格式不正确,保留显示
const dateStr = parts[0];
const timeRangeStr = parts[1];
if (!dateStr || !timeRangeStr) return true;
// 提取开始时间
const startTimeStr = timeRangeStr.split('-')[0]; // "08:00"
if (!startTimeStr) return true;
// 构建号源开始时间的完整 Date 对象
const ticketStartStr = `${dateStr} ${startTimeStr}`;
const ticketStart = new Date(ticketStartStr);
// 只显示开始时间晚于当前时间的号源
return ticketStart > now;
});
// 🎯 按开始时间升序排序 → 较早的号源排在前面
filtered.sort((a, b) => {
const getStartTime = (ticket) => {
const parts = (ticket.dateTime || '').split(' ');
if (parts.length < 2) return new Date(0).getTime();
const dateStr = parts[0];
const timeRangeStr = parts[1];
const startTimeStr = (timeRangeStr || '').split('-')[0];
if (!startTimeStr) return new Date(0).getTime();
const ticketStartStr = `${dateStr} ${startTimeStr}`;
return new Date(ticketStartStr).getTime();
};
const timeA = getStartTime(a);
const timeB = getStartTime(b);
return timeA - timeB;
});
return filtered;
},
// 🎯 分页:按照用户选择的每页条数分页,返回当前页的数据
filteredTickets() { filteredTickets() {
return [...this.tickets]; const filtered = this.filteredAndSortedTickets;
const startIndex = (this.currentPage - 1) * this.pageSize;
const endIndex = startIndex + this.pageSize;
return filtered.slice(startIndex, endIndex);
},
// 更新总条数为过滤后的实际条数,分页自动处理
totalTickets() {
return this.filteredAndSortedTickets.length;
}, },
hasSearchCriteria() { hasSearchCriteria() {
return !!this.patientKeyword?.trim(); return !!this.patientKeyword?.trim();
@@ -733,12 +813,8 @@ export default {
const total = Number(payload.total); const total = Number(payload.total);
this.tickets = [...filteredRecords]; this.tickets = [...filteredRecords];
this.allTickets = [...filteredRecords]; this.allTickets = [...filteredRecords];
// 当按状态筛选时,优先使用前端过滤后的数量,避免后端状态未生效导致“显示全部” // 后端已经分页总条数由后端返回始终使用后端返回的total
if (this.selectedStatus && this.selectedStatus !== 'all') { this.totalTickets = Number.isFinite(total) ? total : this.tickets.length;
this.totalTickets = this.tickets.length;
} else {
this.totalTickets = Number.isFinite(total) ? total : this.tickets.length;
}
}, },
applyStatusFilter(records = []) { applyStatusFilter(records = []) {
if (!Array.isArray(records) || records.length === 0) { if (!Array.isArray(records) || records.length === 0) {