diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/IAppointmentConfigAppService.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/IAppointmentConfigAppService.java new file mode 100644 index 00000000..8680e9c6 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/IAppointmentConfigAppService.java @@ -0,0 +1,28 @@ +package com.openhis.web.appointmentmanage.appservice; + +import com.core.common.core.domain.R; +import com.openhis.appointmentmanage.domain.AppointmentConfig; + +/** + * 预约配置AppService接口 + * + * @author openhis + * @date 2026-03-23 + */ +public interface IAppointmentConfigAppService { + + /** + * 获取当前机构的预约配置 + * + * @return 预约配置 + */ + R getAppointmentConfig(); + + /** + * 保存预约配置 + * + * @param appointmentConfig 预约配置 + * @return 结果 + */ + R saveAppointmentConfig(AppointmentConfig appointmentConfig); +} \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/AppointmentConfigAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/AppointmentConfigAppServiceImpl.java new file mode 100644 index 00000000..dc5a4c99 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/appservice/impl/AppointmentConfigAppServiceImpl.java @@ -0,0 +1,62 @@ +package com.openhis.web.appointmentmanage.appservice.impl; + +import com.core.common.core.domain.R; +import com.core.common.utils.SecurityUtils; +import com.openhis.appointmentmanage.domain.AppointmentConfig; +import com.openhis.appointmentmanage.service.IAppointmentConfigService; +import com.openhis.web.appointmentmanage.appservice.IAppointmentConfigAppService; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 预约配置AppService实现类 + * + * @author openhis + * @date 2026-03-23 + */ +@Service +public class AppointmentConfigAppServiceImpl implements IAppointmentConfigAppService { + + @Resource + private IAppointmentConfigService appointmentConfigService; + + @Override + public R getAppointmentConfig() { + // 获取当前登录用户的机构ID + Integer tenantId = SecurityUtils.getLoginUser().getTenantId(); + if (tenantId == null) { + return R.fail("获取机构信息失败"); + } + + AppointmentConfig config = appointmentConfigService.getConfigByTenantId(tenantId); + return R.ok(config); + } + + @Override + public R saveAppointmentConfig(AppointmentConfig appointmentConfig) { + // 获取当前登录用户的机构ID + Integer tenantId = SecurityUtils.getLoginUser().getTenantId(); + if (tenantId == null) { + return R.fail("获取机构信息失败"); + } + + // 查询是否已存在配置 + AppointmentConfig existingConfig = appointmentConfigService.getConfigByTenantId(tenantId); + + if (existingConfig != null) { + // 更新现有配置 + existingConfig.setCancelAppointmentType(appointmentConfig.getCancelAppointmentType()); + existingConfig.setCancelAppointmentCount(appointmentConfig.getCancelAppointmentCount()); + existingConfig.setValidFlag(appointmentConfig.getValidFlag()); + appointmentConfigService.saveOrUpdate(existingConfig); + return R.ok(existingConfig); + } else { + // 新增配置 + appointmentConfig.setTenantId(tenantId); + appointmentConfig.setValidFlag(1); + appointmentConfigService.saveOrUpdateConfig(appointmentConfig); + return R.ok(appointmentConfig); + } + } +} \ No newline at end of file diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DeptController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DeptController.java index fe907e61..9f37d04e 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DeptController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/appointmentmanage/controller/DeptController.java @@ -1,11 +1,10 @@ package com.openhis.web.appointmentmanage.controller; import com.core.common.core.domain.R; +import com.openhis.appointmentmanage.domain.AppointmentConfig; +import com.openhis.web.appointmentmanage.appservice.IAppointmentConfigAppService; import com.openhis.web.appointmentmanage.appservice.IDeptAppService; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @@ -16,6 +15,9 @@ public class DeptController { @Resource private IDeptAppService deptAppService; + @Resource + private IAppointmentConfigAppService appointmentConfigAppService; + /* * 获取科室列表 * @@ -38,4 +40,22 @@ public class DeptController { ){ return R.ok(deptAppService.searchDept(pageNo,pageSize,orgName,deptName)); } + + /* + * 获取预约配置 + * + * */ + @GetMapping("/config") + public R getAppointmentConfig(){ + return appointmentConfigAppService.getAppointmentConfig(); + } + + /* + * 保存预约配置 + * + * */ + @PostMapping("/config") + public R saveAppointmentConfig(@RequestBody AppointmentConfig appointmentConfig){ + return appointmentConfigAppService.saveAppointmentConfig(appointmentConfig); + } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/appservice/impl/CheckPackageAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/appservice/impl/CheckPackageAppServiceImpl.java index da96306c..d81edbce 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/appservice/impl/CheckPackageAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/appservice/impl/CheckPackageAppServiceImpl.java @@ -128,7 +128,10 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService { // 保存套餐明细 if (checkPackageDto.getItems() != null && !checkPackageDto.getItems().isEmpty()) { List details = convertToDetails(checkPackageDto.getItems(), checkPackage.getId(), 1); - checkPackageDetailService.saveBatch(details); + boolean detailSaveResult = checkPackageDetailService.saveBatch(details); + if (!detailSaveResult) { + throw new RuntimeException("保存套餐明细失败"); + } } return R.ok(checkPackage.getId(), "保存成功"); diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/dto/CheckPackageDetailDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/dto/CheckPackageDetailDto.java index f3d1e442..1047b643 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/dto/CheckPackageDetailDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/check/dto/CheckPackageDetailDto.java @@ -49,6 +49,9 @@ public class CheckPackageDetailDto { @NotNull(message = "数量不能为空") private Integer quantity; + /** 单位 */ + private String unit; + /** 单价 */ @NotNull(message = "单价不能为空") private BigDecimal unitPrice; diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/appservice/impl/SurgicalScheduleAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/appservice/impl/SurgicalScheduleAppServiceImpl.java index d84ddf09..757b823f 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/appservice/impl/SurgicalScheduleAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/appservice/impl/SurgicalScheduleAppServiceImpl.java @@ -413,10 +413,10 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi /** * 格式化安排时间 */ - private String formatScheduleDate(LocalDate scheduleDate) { + private String formatScheduleDate(LocalDateTime scheduleDate) { if (scheduleDate == null) return ""; - // 格式化为 yyyy-MM-dd - return scheduleDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")); + // 格式化为 yyyy-MM-dd HH:mm:ss + return scheduleDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/dto/OpCreateScheduleDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/dto/OpCreateScheduleDto.java index 5dfc1cba..57eadb8b 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/dto/OpCreateScheduleDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/dto/OpCreateScheduleDto.java @@ -45,9 +45,10 @@ public class OpCreateScheduleDto { private String postoperativeDiagnosis; /** - * 手术安排日期 + * 手术安排日期时间 */ - private LocalDate scheduleDate; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime scheduleDate; /** * 手术台次序号 @@ -82,11 +83,13 @@ public class OpCreateScheduleDto { /** * 入院时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime admissionTime; /** * 入手术室时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime entryTime; /** @@ -167,21 +170,25 @@ public class OpCreateScheduleDto { /** * 手术开始时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime startTime; /** * 手术结束时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime endTime; /** * 麻醉开始时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime anesStart; /** * 麻醉结束时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime anesEnd; /** diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/datadictionary/ActivityDefinitionManageMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/datadictionary/ActivityDefinitionManageMapper.xml index 91b7b594..53bb90d0 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/datadictionary/ActivityDefinitionManageMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/datadictionary/ActivityDefinitionManageMapper.xml @@ -84,6 +84,7 @@ T1.id, T1.bus_no, T1.name, + T1.permitted_unit_code, T2.price as retail_price FROM wor_activity_definition T1 INNER JOIN adm_charge_item_definition T2 diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/domain/AppointmentConfig.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/domain/AppointmentConfig.java new file mode 100644 index 00000000..b7c874f8 --- /dev/null +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/domain/AppointmentConfig.java @@ -0,0 +1,45 @@ +package com.openhis.appointmentmanage.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 预约配置Entity + * + * @date 2026-03-23 + */ +@Data +@TableName(value = "appointment_config") +@Accessors(chain = true) +public class AppointmentConfig { + + /** 主键ID */ + @TableId(type = IdType.AUTO) + private Long id; + + /** 机构ID(关联 sys_tenant) */ + private Integer tenantId; + + /** 取消预约时间类型:YEAR/MONTH/DAY */ + private String cancelAppointmentType; + + /** 取消预约次数限制 */ + private Integer cancelAppointmentCount; + + /** 有效标志:1=有效,0=无效 */ + private Integer validFlag; + + /** 创建人 */ + private String createBy; + + /** 创建时间 */ + private LocalDateTime createTime; + + /** 更新时间 */ + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/AppointmentConfigMapper.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/AppointmentConfigMapper.java new file mode 100644 index 00000000..02e78dfc --- /dev/null +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/mapper/AppointmentConfigMapper.java @@ -0,0 +1,15 @@ +package com.openhis.appointmentmanage.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.openhis.appointmentmanage.domain.AppointmentConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * 预约配置Mapper接口 + * + * @author openhis + * @date 2026-03-23 + */ +@Mapper +public interface AppointmentConfigMapper extends BaseMapper { +} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/IAppointmentConfigService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/IAppointmentConfigService.java new file mode 100644 index 00000000..62d3086a --- /dev/null +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/IAppointmentConfigService.java @@ -0,0 +1,29 @@ +package com.openhis.appointmentmanage.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.openhis.appointmentmanage.domain.AppointmentConfig; + +/** + * 预约配置Service接口 + * + * @author openhis + * @date 2026-03-23 + */ +public interface IAppointmentConfigService extends IService { + + /** + * 根据机构ID获取预约配置 + * + * @param tenantId 机构ID + * @return 预约配置 + */ + AppointmentConfig getConfigByTenantId(Integer tenantId); + + /** + * 保存或更新预约配置 + * + * @param appointmentConfig 预约配置 + * @return 结果 + */ + int saveOrUpdateConfig(AppointmentConfig appointmentConfig); +} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/impl/AppointmentConfigServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/impl/AppointmentConfigServiceImpl.java new file mode 100644 index 00000000..03dd63ca --- /dev/null +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/appointmentmanage/service/impl/AppointmentConfigServiceImpl.java @@ -0,0 +1,44 @@ +package com.openhis.appointmentmanage.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.openhis.appointmentmanage.domain.AppointmentConfig; +import com.openhis.appointmentmanage.mapper.AppointmentConfigMapper; +import com.openhis.appointmentmanage.service.IAppointmentConfigService; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +/** + * 预约配置Service实现类 + * + * @author openhis + * @date 2026-03-23 + */ +@Service +public class AppointmentConfigServiceImpl + extends ServiceImpl + implements IAppointmentConfigService { + + @Override + public AppointmentConfig getConfigByTenantId(Integer tenantId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AppointmentConfig::getTenantId, tenantId) + .eq(AppointmentConfig::getValidFlag, 1); + return this.getOne(wrapper); + } + + @Override + public int saveOrUpdateConfig(AppointmentConfig appointmentConfig) { + if (appointmentConfig.getId() == null) { + // 新增 + appointmentConfig.setCreateTime(LocalDateTime.now()); + appointmentConfig.setUpdateTime(LocalDateTime.now()); + return this.baseMapper.insert(appointmentConfig) > 0 ? 1 : 0; + } else { + // 更新 + appointmentConfig.setUpdateTime(LocalDateTime.now()); + return this.baseMapper.updateById(appointmentConfig) > 0 ? 1 : 0; + } + } +} \ No newline at end of file diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/check/domain/CheckPackageDetail.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/check/domain/CheckPackageDetail.java index 98569618..8cef7578 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/check/domain/CheckPackageDetail.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/check/domain/CheckPackageDetail.java @@ -42,6 +42,7 @@ public class CheckPackageDetail { private String dose; /** 途径 */ + @TableField("\"method\"") private String method; /** 频次 */ @@ -53,6 +54,10 @@ public class CheckPackageDetail { /** 数量 */ private Integer quantity; + /** 单位 */ + @TableField("\"unit\"") + private String unit; + /** 单价 */ private BigDecimal unitPrice; diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IOrderService.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IOrderService.java index 87c3e66c..cf2aa2ba 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IOrderService.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/IOrderService.java @@ -33,4 +33,14 @@ public interface IOrderService extends IService { Order createAppointmentOrder(Map params); int cancelAppointmentOrder(Long orderId, String cancelReason); + + /** + * 统计患者在指定机构、指定起始时间后的取消预约次数 + * + * @param patientId 患者ID + * @param tenantId 机构ID + * @param startTime 起始时间 + * @return 取消次数 + */ + long countPatientCancellations(Long patientId, Integer tenantId, java.time.LocalDateTime startTime); } diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/OrderServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/OrderServiceImpl.java index 0543aff3..27c699b1 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/OrderServiceImpl.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/OrderServiceImpl.java @@ -178,4 +178,16 @@ public class OrderServiceImpl extends ServiceImpl implements Date cancelTime = new Date(); return orderMapper.updateOrderCancelInfoById(orderId, cancelTime, cancelReason); } + + @Override + public long countPatientCancellations(Long patientId, Integer tenantId, java.time.LocalDateTime startTime) { + if (patientId == null || tenantId == null || startTime == null) { + return 0; + } + return this.count(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(Order::getPatientId, patientId) + .eq(Order::getTenantId, tenantId) + .ge(Order::getCancelTime, startTime) + .eq(Order::getStatus, AppointmentOrderStatus.CANCELLED)); + } } diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java index e9bbb715..01fb025e 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/clinical/service/impl/TicketServiceImpl.java @@ -2,6 +2,8 @@ package com.openhis.clinical.service.impl; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.openhis.appointmentmanage.domain.AppointmentConfig; +import com.openhis.appointmentmanage.service.IAppointmentConfigService; import com.openhis.appointmentmanage.domain.TicketSlotDTO; import com.openhis.appointmentmanage.mapper.SchedulePoolMapper; import com.openhis.appointmentmanage.mapper.ScheduleSlotMapper; @@ -23,6 +25,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; +import java.time.temporal.TemporalAdjusters; import java.util.Date; import java.util.List; import java.util.Map; @@ -49,6 +52,9 @@ public class TicketServiceImpl extends ServiceImpl impleme @Resource private SchedulePoolMapper schedulePoolMapper; + @Resource + private IAppointmentConfigService appointmentConfigService; + /** * 查询号源列表 * @@ -235,6 +241,27 @@ public class TicketServiceImpl extends ServiceImpl impleme if (orders == null || orders.isEmpty()) { throw new RuntimeException("当前号源没有可取消的预约订单"); } + + // 核心逻辑:获取订单信息并检查机构取消限制 + Order latestOrder = orders.get(0); + Integer tenantId = latestOrder.getTenantId(); + Long patientId = latestOrder.getPatientId(); + + if (tenantId != null && patientId != null) { + AppointmentConfig config = appointmentConfigService.getConfigByTenantId(tenantId); + if (config != null && config.getCancelAppointmentCount() != null + && config.getCancelAppointmentCount() > 0) { + // 计算当前周期的起始时间 + LocalDateTime startTime = calculatePeriodStartTime(config.getCancelAppointmentType()); + // 统计已取消次数 + long cancelledCount = orderService.countPatientCancellations(patientId, tenantId, startTime); + if (cancelledCount >= config.getCancelAppointmentCount()) { + String periodName = getPeriodName(config.getCancelAppointmentType()); + throw new RuntimeException("您在" + periodName + "内已达到该机构取消预约次数上限(" + config.getCancelAppointmentCount() + "次),禁止取消"); + } + } + } + for (Order order : orders) { orderService.cancelAppointmentOrder(order.getId(), "患者取消预约"); } @@ -309,4 +336,35 @@ public class TicketServiceImpl extends ServiceImpl impleme throw new RuntimeException("挂号费格式错误: " + fee); } } + + /** + * 根据类型获取周期名称 + */ + private String getPeriodName(String type) { + if ("YEAR".equalsIgnoreCase(type)) { + return "年度"; + } else if ("MONTH".equalsIgnoreCase(type)) { + return "月度"; + } else { + return "当日"; + } + } + + /** + * 根据取消预约时间类型计算本周期的起始时间 + * + * @param type YEAR/MONTH/DAY + * @return 起始时间 + */ + private LocalDateTime calculatePeriodStartTime(String type) { + LocalDateTime now = LocalDateTime.now(); + if ("YEAR".equalsIgnoreCase(type)) { + return now.with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); + } else if ("MONTH".equalsIgnoreCase(type)) { + return now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN); + } else { + // 默认为 DAY + return now.with(LocalTime.MIN); + } + } } diff --git a/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java b/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java index 84c2a9ce..54599d7b 100644 --- a/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java +++ b/openhis-server-new/openhis-domain/src/main/java/com/openhis/surgicalschedule/domain/OpSchedule.java @@ -1,7 +1,6 @@ package com.openhis.surgicalschedule.domain; import java.math.BigDecimal; -import java.time.LocalDate; import java.time.LocalDateTime; import com.baomidou.mybatisplus.annotation.*; @@ -52,9 +51,10 @@ public class OpSchedule extends HisBaseEntity { @TableField(value = "postoperative_diagnosis", insertStrategy = FieldStrategy.IGNORED) private String postoperativeDiagnosis; - /** 手术安排日期 */ - @DateTimeFormat(pattern = "yyyy-MM-dd") - private LocalDate scheduleDate; + /** 手术安排日期时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime scheduleDate; /** 手术台次序号 */ private Integer sequenceNo; diff --git a/openhis-server-new/openhis-domain/src/main/resources/mapper/clinical/OrderMapper.xml b/openhis-server-new/openhis-domain/src/main/resources/mapper/clinical/OrderMapper.xml index 4ade0b2d..593d1165 100644 --- a/openhis-server-new/openhis-domain/src/main/resources/mapper/clinical/OrderMapper.xml +++ b/openhis-server-new/openhis-domain/src/main/resources/mapper/clinical/OrderMapper.xml @@ -239,32 +239,32 @@ diff --git a/openhis-ui-vue3/src/api/appoinmentmanage/appointmentConfig.js b/openhis-ui-vue3/src/api/appoinmentmanage/appointmentConfig.js new file mode 100644 index 00000000..8be83457 --- /dev/null +++ b/openhis-ui-vue3/src/api/appoinmentmanage/appointmentConfig.js @@ -0,0 +1,25 @@ +import request from '@/utils/request' + +/** + * 获取预约配置 + * @returns + */ +export function getAppointmentConfig() { + return request({ + url: '/dept/config', + method: 'get' + }) +} + +/** + * 保存预约配置 + * @param {Object} data - 预约配置数据 + * @returns + */ +export function saveAppointmentConfig(data) { + return request({ + url: '/dept/config', + method: 'post', + data: data + }) +} \ No newline at end of file diff --git a/openhis-ui-vue3/src/views/appoinmentmanage/deptManage/index.vue b/openhis-ui-vue3/src/views/appoinmentmanage/deptManage/index.vue index 535e10a0..4d36e36b 100644 --- a/openhis-ui-vue3/src/views/appoinmentmanage/deptManage/index.vue +++ b/openhis-ui-vue3/src/views/appoinmentmanage/deptManage/index.vue @@ -100,9 +100,9 @@ - - - + + + @@ -390,6 +390,7 @@ import {useRouter} from 'vue-router' import {ElDialog, ElForm, ElFormItem, ElInput, ElMessage, ElMessageBox, ElOption, ElSelect} from 'element-plus' import {DocumentRemove, EditPen, View, Delete} from '@element-plus/icons-vue' import {listDept, searchDept} from '@/api/appoinmentmanage/dept' +import {getAppointmentConfig, saveAppointmentConfig} from '@/api/appoinmentmanage/appointmentConfig' import {getLocationTree, getPractitionerMetadata, getHealthcareMetadata} from '@/views/charge/outpatientregistration/components/outpatientregistration' import {addDoctorSchedule, addDoctorScheduleWithDate, updateDoctorSchedule, deleteDoctorSchedule, getRegisterOrganizations, getDoctorScheduleListByDeptId, getDoctorScheduleListByDeptIdAndDateRange} from './api' import {getClinicRoomList} from '@/api/appoinmentmanage/clinicRoom' @@ -922,16 +923,51 @@ const handleReset = async () => { } // 预约设置弹窗显示 -const handleAppointmentSetting = () => { +const handleAppointmentSetting = async () => { + // 获取当前机构的预约配置 + try { + const res = await getAppointmentConfig() + if (res.code === 200 && res.data) { + // 回显已有配置 + appointmentSettingForm.value = { + cancelAppointmentType: res.data.cancelAppointmentType || 'YEAR', + cancelAppointmentCount: res.data.cancelAppointmentCount || 0 + } + } else { + // 无配置时使用默认值 + appointmentSettingForm.value = { + cancelAppointmentType: 'YEAR', + cancelAppointmentCount: 0 + } + } + } catch (error) { + console.error('获取预约配置失败:', error) + appointmentSettingForm.value = { + cancelAppointmentType: 'YEAR', + cancelAppointmentCount: 0 + } + } appointmentSettingDialog.value = true } // 预约设置确定 -const handleAppointmentSettingConfirm = () => { - // 这里可以添加表单验证和提交逻辑 - console.log('预约设置提交:', appointmentSettingForm.value) - ElMessage.success('预约设置保存成功') - appointmentSettingDialog.value = false +const handleAppointmentSettingConfirm = async () => { + try { + const res = await saveAppointmentConfig({ + cancelAppointmentType: appointmentSettingForm.value.cancelAppointmentType, + cancelAppointmentCount: appointmentSettingForm.value.cancelAppointmentCount, + validFlag: 1 + }) + if (res.code === 200) { + ElMessage.success('预约设置保存成功') + appointmentSettingDialog.value = false + } else { + ElMessage.error(res.msg || '保存失败') + } + } catch (error) { + console.error('保存预约配置失败:', error) + ElMessage.error('保存失败') + } } // 预约设置取消 diff --git a/openhis-ui-vue3/src/views/appoinmentmanage/deptappthoursManage/index.vue b/openhis-ui-vue3/src/views/appoinmentmanage/deptappthoursManage/index.vue index 0cbf46dc..46d9a814 100644 --- a/openhis-ui-vue3/src/views/appoinmentmanage/deptappthoursManage/index.vue +++ b/openhis-ui-vue3/src/views/appoinmentmanage/deptappthoursManage/index.vue @@ -11,7 +11,7 @@ { - const errorMsg = error.message || '取消预约失败,请稍后重试'; - ElMessage.error(`取消预约失败:${errorMsg}`); + console.error('取消预约失败:', error); this.closeContextMenu(); }); }, @@ -590,12 +589,10 @@ export default { ElMessage.success('预约成功,号源已锁定。患者到院签到时需缴费取号。'); }).catch(error => { - // 显示具体的错误信息 - const errorMessage = error.response?.data?.msg || error.response?.data?.message || '预约失败,请稍后重试。'; - ElMessage.error(errorMessage); + console.error('预约失败:', error); }); } catch (error) { - ElMessage.error('预约信息格式错误,请重新操作。'); + console.error('操作异常:', error); } }, // 切换侧边栏显示/隐藏 diff --git a/openhis-ui-vue3/src/views/doctorstation/components/diagnosis/diagnosis.vue b/openhis-ui-vue3/src/views/doctorstation/components/diagnosis/diagnosis.vue index aa7f424b..e45c8343 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/diagnosis/diagnosis.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/diagnosis/diagnosis.vue @@ -628,11 +628,11 @@ async function handleSaveDiagnosis() { // 开始加载状态,防止重复提交 saveLoading.value = true; - // 保存前按排序号排序,并转换日期格式 + // 保存前按排序号排序,并转换日期格式为ISO字符串 const diagnosisChildList = form.value.diagnosisList.map(item => ({ ...item, - onsetDate: item.onsetDate ? new Date(item.onsetDate) : null, - diagnosisTime: item.diagnosisTime ? new Date(item.diagnosisTime) : null + onsetDate: item.onsetDate ? new Date(item.onsetDate).toISOString() : null, + diagnosisTime: item.diagnosisTime ? new Date(item.diagnosisTime).toISOString() : null })); // 调用保存诊断接口 diff --git a/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue b/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue index 17a28741..4f2df7bc 100644 --- a/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue +++ b/openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue @@ -194,10 +194,13 @@ import {ElMessage} from 'element-plus'; import {getLocationTree} from '@/views/charge/outpatientregistration/components/outpatientregistration'; import {listInspectionPackage, delInspectionPackage} from '@/api/system/inspectionPackage'; import { getTenantPage } from '@/api/system/tenant'; +import useTagsViewStore from '@/store/modules/tagsView'; // 创建路由实例 const router = useRouter(); +const tagsViewStore = useTagsViewStore(); + // 侧边栏状态 const sidebarActive = ref(false); @@ -207,7 +210,6 @@ const departments = ref([]); // 获取科室数据 - 与门诊挂号页面保持一致 function getDepartmentList() { - console.log('调用getLocationTree API...'); getLocationTree().then((response) => { @@ -430,13 +432,17 @@ const fetchTenantList = async () => { // 初始化数据 // 整合后的 onMounted 钩子 onMounted(async () => { - // 1. 加载科室数据 + // 1. 加载科室数据(不需要等待) getDepartmentList(); - // 2. 加载机构列表 (包含默认选中逻辑) - await fetchTenantList(); + // 2. 并行发起:机构列表 + 表格数据,减少串行等待 + const tenantPromise = fetchTenantList(); - // 3. 等待 DOM 更新 + // 先用默认参数(不带 tenantId)立刻加载表格,让用户尽快看到数据 + loadData(); + + // 3. 等待机构列表加载完成 + await tenantPromise; await nextTick(); // 4. 防御性检查 @@ -444,9 +450,10 @@ onMounted(async () => { selectedTenantId.value = tenantOptions.value[0].value; } - // 5. 加载表格数据 - loadData(); - + // 5. 如果选中了机构,用 tenantId 重新过滤一次 + if (selectedTenantId.value) { + loadData(); + } }); // 过滤后的数据 - 现在直接从API获取,这里保留前端过滤作为补充 @@ -583,51 +590,27 @@ const pageButtons = computed(() => { // 处理新增 function handleAdd() { - router.push({ - path: '/maintainSystem/Inspection', - query: { - tab: '2', - mode: 'add' - } - }); + window.location.href = '/maintainSystem/Inspection?tab=2&mode=add'; } // 处理编辑 function handleEdit(item) { - // 跳转到套餐设置主界面,并传递套餐ID用于加载数据 - // 后端接口使用 basicInformationId 作为路径参数 const packageId = item.basicInformationId || item.id; if (!packageId) { ElMessage.error('无法获取套餐ID,请刷新页面后重试'); return; } - router.push({ - path: '/maintainSystem/Inspection', - query: { - tab: '2', - packageId: packageId, - mode: 'edit' - } - }); + window.location.href = `/maintainSystem/Inspection?tab=2&mode=edit&packageId=${packageId}`; } // 处理查看 function handleView(item) { - // 跳转到套餐设置主界面,并传递套餐ID用于加载数据 - // 后端接口使用 basicInformationId 作为路径参数 const packageId = item.basicInformationId || item.id; if (!packageId) { ElMessage.error('无法获取套餐ID,请刷新页面后重试'); return; } - router.push({ - path: '/maintainSystem/Inspection', - query: { - tab: '2', - packageId: packageId, - mode: 'view' - } - }); + window.location.href = `/maintainSystem/Inspection?tab=2&mode=view&packageId=${packageId}`; } // 处理删除 diff --git a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue index f1c310ef..255934a3 100644 --- a/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue +++ b/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue @@ -813,7 +813,8 @@ import { updateInspectionPackage, getInspectionPackage, listInspectionPackageDetails, - saveInspectionPackageDetails + saveInspectionPackageDetails, + batchSaveInspectionPackageDetails } from '@/api/system/inspectionPackage'; import { getTenantPage } from '@/api/system/tenant'; import {deptTreeSelect} from '@/api/system/user'; @@ -1470,6 +1471,8 @@ const packageItems = ref([]); const packageMode = ref('add'); // 当前编辑的套餐ID(用于编辑和查看模式) const currentPackageId = ref(null); +// 正在从后端加载套餐数据的标志,加载期间跳过 calculateAmounts 防止覆盖后端金额 +const isLoadingPackage = ref(false); // 是否为查看模式(只读) const isViewMode = computed(() => packageMode.value === 'view'); @@ -2364,6 +2367,10 @@ const savePackageData = async (basicInfo, detailData) => { if (isEditMode) { // 编辑模式:调用更新API basicResponse = await updateInspectionPackage(basicInfo); + if (basicResponse.code !== 200) { + loading.close(); + throw new Error(basicResponse.msg || '更新基本信息失败'); + } packageId = currentPackageId.value; } else { // 新增模式:调用新增API @@ -2390,29 +2397,41 @@ const savePackageData = async (basicInfo, detailData) => { // 验证套餐ID是否存在 if (!packageId) { loading.close(); - console.error('无法从响应中获取套餐ID,完整响应:', basicResponse); throw new Error('保存成功但未返回套餐ID。请检查后端接口是否正确返回了packageId或id字段'); } } - // 2. 分别保存每个明细数据到明细表,并将后端返回的 id 回填到 packageItems - for (let i = 0; i < detailData.length; i++) { - const detailItem = { - ...detailData[i], - packageId: packageId + // 2. 保存明细数据:编辑模式用批量接口(先删旧后插新),新增模式逐条新增 + if (isEditMode) { + // 编辑模式:调用批量保存接口,后端会先删除旧明细再插入新明细 + const batchPayload = { + basicInformationId: packageId, + details: detailData.map(item => ({ ...item, packageId: packageId })) }; - - const detailResponse = await saveInspectionPackageDetails(detailItem); - - if (detailResponse.code !== 200) { + const batchResponse = await batchSaveInspectionPackageDetails(batchPayload); + if (batchResponse.code !== 200) { loading.close(); - throw new Error(`保存第 ${i + 1} 个明细项失败: ${detailResponse.msg || '未知错误'}`); + throw new Error(batchResponse.msg || '保存明细数据失败'); } + } else { + // 新增模式:逐条保存并回填后端生成的明细 id + for (let i = 0; i < detailData.length; i++) { + const detailItem = { + ...detailData[i], + packageId: packageId + }; - // 回填后端生成的明细 id,防止取消编辑时被误判为新增行 - // 后端返回字段名是 detailId(见 InspectionPackageDetail.java) - if (detailResponse.data && (detailResponse.data.detailId || detailResponse.data.id)) { - packageItems.value[i].id = detailResponse.data.detailId || detailResponse.data.id; + const detailResponse = await saveInspectionPackageDetails(detailItem); + + if (detailResponse.code !== 200) { + loading.close(); + throw new Error(`保存第 ${i + 1} 个明细项失败: ${detailResponse.msg || '未知错误'}`); + } + + // 回填后端生成的明细 id,防止取消编辑时被误判为新增行 + if (detailResponse.data && (detailResponse.data.detailId || detailResponse.data.id)) { + packageItems.value[i].id = detailResponse.data.detailId || detailResponse.data.id; + } } } @@ -2421,8 +2440,14 @@ const savePackageData = async (basicInfo, detailData) => { ElMessage.success('保存成功'); - // 保存成功后重置表单 - doResetForm(); + if (isEditMode) { + // 编辑模式:重新加载最新数据,保持在编辑页面 + loadInspectionPackage(String(packageId)); + } else { + // 新增模式:保存成功后重置表单 + doResetForm(); + } + } catch (error) { // 确保在错误时也关闭loading @@ -2495,7 +2520,7 @@ const doResetForm = () => { // 清空明细数据 packageItems.value = []; - // 重置模式为新增 + // 重置为新增模式(仅在外部未指定 mode 时,由调用方负责设置 packageMode) packageMode.value = 'add'; currentPackageId.value = null; @@ -2527,6 +2552,9 @@ const loadInspectionPackage = async (packageId) => { const basicData = basicResponse.data; const detailData = detailResponse.data || []; + // 【关键】暂停 packageItems 的 watch,防止赋值时触发 calculateAmounts 覆盖后端返回的金额 + isLoadingPackage.value = true; + // 填充基本信息 packageLevel.value = basicData.packageLevel; packageName.value = basicData.packageName; @@ -2536,8 +2564,8 @@ const loadInspectionPackage = async (packageId) => { showPackageName.value = basicData.showPackageName !== false; generateServiceFee.value = basicData.generateServiceFee !== false; enablePackagePrice.value = basicData.enablePackagePrice !== false; - packageAmount.value = basicData.packageAmount || 0.00; - serviceFee.value = basicData.serviceFee || 0.00; + packageAmount.value = parseFloat(basicData.packageAmount || 0); + serviceFee.value = parseFloat(basicData.serviceFee || 0); selectedLisGroup.value = basicData.lisGroup || ''; bloodVolume.value = basicData.bloodVolume || ''; remarks.value = basicData.remarks || ''; @@ -2559,11 +2587,15 @@ const loadInspectionPackage = async (packageId) => { origin: item.origin || '' })); + // 恢复监听 + isLoadingPackage.value = false; + loading.close(); ElMessage.success('套餐数据加载成功'); } catch (error) { - console.error('加载套餐数据失败:', error); + isLoadingPackage.value = false; + ElMessage.error(error.message || '加载套餐数据失败'); } }; @@ -2597,47 +2629,43 @@ watch(activity_category_code, (newVal) => { }, { immediate: true }); /** - * 关键修复: - * 从“套餐管理”跳转到这里时,通常只是改变 query(packageId/mode/tab),组件不会重新挂载, - * 所以仅靠 onMounted 读取一次 query 会导致“查看/修改”出现空白,刷新才正常。 - * 这里监听 query 变化,自动切换 tab 并加载套餐数据。 + * 统一监听 query 变化,处理 tab 切换和套餐数据加载。 + * 使用 watch(route) 深度监听,确保在 keep-alive 场景下同路由 query 变化也能响应。 */ -watch( - () => route.query.tab, - (tab) => { - if (tab === '0' || tab === '1' || tab === '2') { - activeNav.value = parseInt(String(tab)); - } - }, - { immediate: true } -); - const applyRouteForPackage = async (query) => { + const tab = query?.tab; const packageId = query?.packageId; const mode = query?.mode || 'add'; - - // 设置当前模式和套餐ID - packageMode.value = mode; - currentPackageId.value = packageId ? String(packageId) : null; - - // 如果是新增模式,重置表单 + + // 先切换 tab + if (tab === '0' || tab === '1' || tab === '2') { + activeNav.value = parseInt(tab); + }; + + // 只有 tab=2(套餐设置)才处理套餐数据 + if (activeNav.value !== 2) return; + + // 新增模式:重置表单,然后明确设置 mode if (mode === 'add' || !packageId) { - activeNav.value = 2; - doResetForm(); + doResetForm(); // 内部会设 packageMode='add',符合预期 return; } - - activeNav.value = 2; + + // 编辑/查看模式:先设置 mode 和 ID,再加载数据 + packageMode.value = mode; + currentPackageId.value = String(packageId); + await nextTick(); loadInspectionPackage(String(packageId)); }; +// 监听整个 route.query,确保任何 query 变化(tab/packageId/mode)都能触发 watch( - () => route.query.packageId, - async () => { - await applyRouteForPackage(route.query); + () => route.query, + (newQuery) => { + applyRouteForPackage(newQuery); }, - { immediate: true, flush: 'post' } + { immediate: true, deep: true } ); // 兜底:如果该页面被 keep-alive 缓存,从别的页面返回时不会触发 onMounted @@ -2652,11 +2680,14 @@ onBeforeRouteUpdate((to) => { // 监听生成服务费选项变更 watch(generateServiceFee, (newVal) => { + if (isLoadingPackage.value) return; calculateAmounts(); }); // 监听套餐项目变化 watch(packageItems, (newVal) => { + // 加载期间跳过计算,防止覆盖后端返回的金额 + if (isLoadingPackage.value) return; calculateAmounts(); }, { deep: true }); // 样本类型数据 diff --git a/openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/components/PackageSettings.vue b/openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/components/PackageSettings.vue index 1058c9e4..17769d57 100644 --- a/openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/components/PackageSettings.vue +++ b/openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/components/PackageSettings.vue @@ -152,11 +152,11 @@ @@ -281,6 +281,12 @@ {{ row.quantity }} + + + @@ -539,6 +545,7 @@ function loadPackageData(data) { days: item.days || '', quantity: item.quantity || 1, unitPrice: item.unitPrice || 0, + unit: item.unit || '', amount: item.amount || 0, serviceCharge: item.serviceCharge || 0, total: item.total || 0, @@ -553,6 +560,9 @@ function loadPackageData(data) { console.log('formData 加载后:', formData) console.log('detailData 加载后:', detailData.value) + + // 加载数据后自动计算总服务费 + calculateTotalServiceFee() } onMounted(async () => { @@ -781,10 +791,12 @@ async function loadDiagnosisTreatmentItem(itemId, itemData) { async function loadDiagnosisTreatmentList(forceRefresh = false) { // 如果不是强制刷新且已有数据且未过期,直接返回 if (!forceRefresh && diagnosisTreatmentList.value.length > 0) { - return + // 由于缓存过期时间改为0,始终视为过期,需要重新加载 + // 这里直接跳过,不返回,让它重新加载 } // 从session缓存读取 + let useCache = false try { const cachedData = cache.session.getJSON(DIAGNOSIS_TREATMENT_CACHE_KEY) if (cachedData && cachedData.timestamp) { @@ -812,7 +824,7 @@ async function loadDiagnosisTreatmentList(forceRefresh = false) { if (allItems.length > 0) { diagnosisTreatmentList.value = allItems - + // 保存到缓存 cache.session.setJSON(DIAGNOSIS_TREATMENT_CACHE_KEY, { data: allItems, @@ -877,6 +889,7 @@ function handleAddRow() { frequency: '', days: '', quantity: 1, + unit: '', unitPrice: 0, amount: 0, serviceCharge: 0, @@ -914,6 +927,7 @@ function handleDeleteRow(index) { }).then(() => { detailData.value.splice(index, 1) calculatePackagePrice() + calculateTotalServiceFee() ElMessage.success('删除成功') }).catch(() => {}) } @@ -1027,7 +1041,9 @@ function handleItemSelect(row) { row.itemName = item.name || item.itemName || '' row.code = item.busNo || item.code || item.itemCode || '' row.unitPrice = parseFloat(item.retailPrice || item.unitPrice || item.price || 0) - console.log('设置单价:', row.unitPrice) + // permittedUnitCode_dictText是字典翻译后的值,permittedUnitCode是后端返回的原始值 + row.unit = item.permittedUnitCode_dictText || item.permittedUnitCode || '' + // 缓存选中的项目 loadDiagnosisTreatmentItem(row.itemId, item) @@ -1044,10 +1060,23 @@ function calculateAmount(row) { calculateTotal(row) } +// 处理服务费输入 +function handleServiceChargeInput(val, row) { + row.serviceCharge = val || 0 + calculateTotal(row) +} + // 计算总金额 function calculateTotal(row) { row.total = (row.amount || 0) + (row.serviceCharge || 0) calculatePackagePrice() + calculateTotalServiceFee() +} + +// 计算总服务费(合计所有明细行的服务费) +function calculateTotalServiceFee() { + const totalServiceFee = detailData.value.reduce((sum, item) => sum + (item.serviceCharge || 0), 0) + formData.serviceFee = totalServiceFee } // 计算套餐金额(应用折扣) @@ -1203,6 +1232,7 @@ async function handleSave() { frequency: item.frequency || '', days: item.days || '', quantity: parseInt(item.quantity) || 1, + unit: item.unit || '', unitPrice: parseFloat(item.unitPrice) || 0, amount: parseFloat(item.amount) || 0, serviceCharge: parseFloat(item.serviceCharge) || 0,