Przeglądaj źródła

新增
1、床位变更导出
2、护理变更导出
3、价格变更导出
4、餐饮变更导出
5、轮候导出
6、新增出院小结模块
修改
1、长者血糖记录重构
2、退住、入住计算费用时对于按次计费的费用项目进行特殊处理

liangwenxuan 4 tygodni temu
rodzic
commit
af33b2d314
50 zmienionych plików z 2233 dodań i 73 usunięć
  1. 30 0
      sql/mysql/alter_elderly_blood_sugar_record_add_time_period.sql
  2. 3 0
      sql/mysql/alter_elderly_discharge_abstract_add_discharge_date.sql
  3. 47 0
      sql/mysql/elderly_discharge_abstract.sql
  4. 81 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
  5. 59 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/BedChangeRecordExcelVO.java
  6. 66 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/CateringChangeRecordExcelVO.java
  7. 75 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/CheckInWaitRecordExcelVO.java
  8. 59 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/NurseChangeRecordExcelVO.java
  9. 42 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/PriceChangeRecordExcelVO.java
  10. 50 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/BpmCheckInWaitStatusConvert.java
  11. 56 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/BpmProcessInstanceStatusConvert.java
  12. 49 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/LocalDateConvert.java
  13. 49 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/LocalDateTimeConvert.java
  14. 45 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/YesNoConvert.java
  15. 4 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
  16. 2 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/bpm/BpmElderlyChangeApiImpl.java
  17. 12 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/ElderlyBloodSugarRecordController.java
  18. 36 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/elderlybloodsugarrecord/ElderlyBloodSugarRecordExportExcelVO.java
  19. 36 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/elderlybloodsugarrecord/ElderlyBloodSugarRecordRespVO.java
  20. 37 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/elderlybloodsugarrecord/ElderlyBloodSugarRecordSaveReqVO.java
  21. 112 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/ElderlyDischargeAbstractController.java
  22. 29 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeAbstractPageReqVO.java
  23. 110 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeAbstractRespVO.java
  24. 77 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeAbstractSaveReqVO.java
  25. 33 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeUseMedicineRespVO.java
  26. 33 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeUseMedicineSaveReqVO.java
  27. 8 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/doctororder/vo/DoctorOrderExecutionPageReqVO.java
  28. 8 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/doctororder/vo/LongDoctorOrderPageReqVO.java
  29. 8 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/doctororder/vo/ShortDoctorOrderPageReqVO.java
  30. 2 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/pay/CcbWxUnifiedOrderController.java
  31. 1 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/pay/vo/CcbCloseOrderRequestVO.java
  32. 36 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/biz/ElderlyBloodSugarRecordDO.java
  33. 82 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/biz/ElderlyDischargeAbstractDO.java
  34. 52 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/biz/ElderlyDischargeUseMedicineDO.java
  35. 10 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/DoctorOrderExecutionMapper.java
  36. 34 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/ElderlyDischargeAbstractMapper.java
  37. 26 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/ElderlyDischargeUseMedicineMapper.java
  38. 27 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/LongDoctorOrderMapper.java
  39. 27 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/ShortDoctorOrderMapper.java
  40. 38 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ElderlyBloodSugarRecordServiceImpl.java
  41. 56 49
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ElderlyVitalSignsServiceImpl.java
  42. 22 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ExpenseOrderServiceImpl.java
  43. 27 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ExpenseServiceImpl.java
  44. 125 7
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/RefundSettlementOrderServiceImpl.java
  45. 50 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeAbstractService.java
  46. 214 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeAbstractServiceImpl.java
  47. 53 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeUseMedicineService.java
  48. 84 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeUseMedicineServiceImpl.java
  49. 3 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/pay/CcbPayUtils.java
  50. 8 1
      yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/ElderlyBloodSugarRecordMapper.xml

+ 30 - 0
sql/mysql/alter_elderly_blood_sugar_record_add_time_period.sql

@@ -0,0 +1,30 @@
+-- ============================================================
+-- 长者血糖记录改造:新增各时段血糖值及护士签名字段
+-- 原 blood_sugar 保留,作为「自定义时间点」的血糖记录值
+-- ============================================================
+ALTER TABLE `elderly_blood_sugar_record`
+  -- 1、空腹
+  ADD COLUMN `fasting_blood_sugar` decimal(6, 2) NULL DEFAULT NULL COMMENT '空腹血糖值(mmol/L)' AFTER `record_date`,
+  ADD COLUMN `fasting_nurse_sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '空腹护士签名' AFTER `fasting_blood_sugar`,
+
+  -- 2、早餐后
+  ADD COLUMN `after_breakfast_blood_sugar` decimal(6, 2) NULL DEFAULT NULL COMMENT '早餐后血糖值(mmol/L)' AFTER `fasting_nurse_sign`,
+  ADD COLUMN `after_breakfast_nurse_sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '早餐后护士签名' AFTER `after_breakfast_blood_sugar`,
+
+  -- 3、晚餐前
+  ADD COLUMN `before_dinner_blood_sugar` decimal(6, 2) NULL DEFAULT NULL COMMENT '晚餐前血糖值(mmol/L)' AFTER `after_breakfast_nurse_sign`,
+  ADD COLUMN `before_dinner_nurse_sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '晚餐前护士签名' AFTER `before_dinner_blood_sugar`,
+
+  -- 4、晚餐后
+  ADD COLUMN `after_dinner_blood_sugar` decimal(6, 2) NULL DEFAULT NULL COMMENT '晚餐后血糖值(mmol/L)' AFTER `before_dinner_nurse_sign`,
+  ADD COLUMN `after_dinner_nurse_sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '晚餐后护士签名' AFTER `after_dinner_blood_sugar`,
+
+  -- 5、睡前
+  ADD COLUMN `before_sleep_blood_sugar` decimal(6, 2) NULL DEFAULT NULL COMMENT '睡前血糖值(mmol/L)' AFTER `after_dinner_nurse_sign`,
+  ADD COLUMN `before_sleep_nurse_sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '睡前护士签名' AFTER `before_sleep_blood_sugar`,
+
+  -- 6、自定义时间点(原 blood_sugar 作为该时间点的血糖记录值)
+  ADD COLUMN `record_time` varchar(5) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '记录时间(HH:mm)' AFTER `before_sleep_nurse_sign`,
+  ADD COLUMN `nurse_sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '护士签名' AFTER `blood_sugar`,
+
+  ADD INDEX `idx_elder_date` (`elder_id`, `record_date`);

+ 3 - 0
sql/mysql/alter_elderly_discharge_abstract_add_discharge_date.sql

@@ -0,0 +1,3 @@
+-- 出院小结:新增出院日期字段
+ALTER TABLE `elderly_discharge_abstract`
+    ADD COLUMN `discharge_date` date NULL DEFAULT NULL COMMENT '出院日期' AFTER `elder_id`;

+ 47 - 0
sql/mysql/elderly_discharge_abstract.sql

@@ -0,0 +1,47 @@
+-- ============================================================
+-- 出院小结(主表:elderly_discharge_abstract / 子表:elderly_discharge_use_medicine)
+-- ============================================================
+
+DROP TABLE IF EXISTS `elderly_discharge_abstract`;
+CREATE TABLE `elderly_discharge_abstract`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
+  `elder_id` bigint(20) NULL DEFAULT NULL COMMENT '长者id',
+  `discharge_date` date NULL DEFAULT NULL COMMENT '出院日期',
+  `anamnesis` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '既往病史',
+  `recent_health_issues` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '近期健康问题',
+  `physical_activity_ability` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '身体活动能力',
+  `cognition_mental_state` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '认知与精神状态',
+  `disease_treatment_control` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '疾病治疗与控制',
+  `recent_medical_treatment_situation` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '近期就医情况',
+  `reason_discharge` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '出院原因',
+  `vital_signs` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生命体征',
+  `physical_functions` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '身体功能',
+  `key_issues` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '重点关注问题',
+  `safety_protection` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '安全防护',
+  `diet_medical_treatment` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '饮食与就医',
+  `doctor_sign` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '医生签名',
+  `sign_date` date NULL DEFAULT NULL COMMENT '签名日期',
+  `tenant_id` bigint(20) NULL DEFAULT NULL COMMENT '机构id',
+  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
+  `updater` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '更新人',
+  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
+  `creator` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '出院小结' ROW_FORMAT = Dynamic;
+
+DROP TABLE IF EXISTS `elderly_discharge_use_medicine`;
+CREATE TABLE `elderly_discharge_use_medicine`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
+  `elder_id` int(11) NULL DEFAULT NULL COMMENT '长者id',
+  `discharge_abstract_id` int(11) NULL DEFAULT NULL COMMENT '出院小结id',
+  `medicine_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '药品',
+  `dose` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '剂量',
+  `usage` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用法',
+  `frequency` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '频次',
+  `tenant_id` bigint(20) NULL DEFAULT NULL COMMENT '机构id',
+  `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
+  `updater` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '更新人',
+  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
+  `creator` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '出院小结用药' ROW_FORMAT = Dynamic;

+ 81 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java

@@ -5,9 +5,11 @@ import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
@@ -17,6 +19,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.elderly.BpmElderlyIn
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.elderly.CheckInPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.elderly.CheckInWaitPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.elderly.ElderlyRetreatRecordPageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.excel.*;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskPageReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
@@ -49,11 +52,14 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -271,6 +277,21 @@ public class BpmProcessInstanceController {
         return success(BeanUtils.toBean(pageResult, BedChangeRecordRespVO.class));
     }
 
+    @GetMapping("/bed-change-export-excel")
+    @Operation(summary = "导出床位变更流程实例 Excel", description = "内容为床位变更分页的全量数据")
+    @ApiAccessLog(operateType = EXPORT)
+    @TenantIgnore
+    public void exportProcessInstanceBedChangeExcel(@Valid BedChangePageReqVO pageReqVO,
+                                                    HttpServletResponse response) throws IOException {
+        pageReqVO.setTenantIds(Objects.isNull(pageReqVO.getTenantIds()) ? new Long[]{TenantContextHolder.getTenantId()} : pageReqVO.getTenantIds());
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<BedChangeRecordRespVO> list = processInstanceService.getProcessInstanceBedChangePage(
+                getLoginUserId(), pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "床位变更.xls", "数据", BedChangeRecordExcelVO.class,
+                BeanUtils.toBean(list, BedChangeRecordExcelVO.class));
+    }
+
     @GetMapping("/nurse-change-page")
     @Operation(summary = "获取护理变更流程实例", description = "在【我的流程】菜单中,进行调用")
 //    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@@ -282,6 +303,21 @@ public class BpmProcessInstanceController {
         return success(pageResult);
     }
 
+    @GetMapping("/nurse-change-export-excel")
+    @Operation(summary = "导出护理变更流程实例 Excel", description = "内容为护理变更分页的全量数据")
+    @ApiAccessLog(operateType = EXPORT)
+    @TenantIgnore
+    public void exportProcessInstanceNurseChangeExcel(@Valid BedChangePageReqVO pageReqVO,
+                                                      HttpServletResponse response) throws IOException {
+        pageReqVO.setTenantIds(Objects.isNull(pageReqVO.getTenantIds()) ? new Long[]{TenantContextHolder.getTenantId()} : pageReqVO.getTenantIds());
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<NurseChangeRecordRespVO> list = processInstanceService.getProcessInstanceNurseChangePage(
+                getLoginUserId(), pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "护理变更.xls", "数据", NurseChangeRecordExcelVO.class,
+                BeanUtils.toBean(list, NurseChangeRecordExcelVO.class));
+    }
+
     @GetMapping("/catering-change-page")
     @Operation(summary = "获取餐饮变更流程实例", description = "在【我的流程】菜单中,进行调用")
 //    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@@ -293,6 +329,21 @@ public class BpmProcessInstanceController {
         return success(pageResult);
     }
 
+    @GetMapping("/catering-change-export-excel")
+    @Operation(summary = "导出餐饮变更流程实例 Excel", description = "内容为餐饮变更分页的全量数据")
+    @ApiAccessLog(operateType = EXPORT)
+    @TenantIgnore
+    public void exportProcessInstanceCateringChangeExcel(@Valid BedChangePageReqVO pageReqVO,
+                                                         HttpServletResponse response) throws IOException {
+        pageReqVO.setTenantIds(Objects.isNull(pageReqVO.getTenantIds()) ? new Long[]{TenantContextHolder.getTenantId()} : pageReqVO.getTenantIds());
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<CateringChangeRecordRespVO> list = processInstanceService.getProcessInstanceCateringChangePage(
+                getLoginUserId(), pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "餐饮变更.xls", "数据", CateringChangeRecordExcelVO.class,
+                BeanUtils.toBean(list, CateringChangeRecordExcelVO.class));
+    }
+
     @GetMapping("/price-change-page")
     @Operation(summary = "获取价格变更流程实例", description = "在【我的流程】菜单中,进行调用")
 //    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@@ -304,6 +355,21 @@ public class BpmProcessInstanceController {
         return success(pageResult);
     }
 
+    @GetMapping("/price-change-export-excel")
+    @Operation(summary = "导出价格变更流程实例 Excel", description = "内容为价格变更分页的全量数据")
+    @ApiAccessLog(operateType = EXPORT)
+    @TenantIgnore
+    public void exportProcessInstancePriceChangeExcel(@Valid BedChangePageReqVO pageReqVO,
+                                                      HttpServletResponse response) throws IOException {
+        pageReqVO.setTenantIds(Objects.isNull(pageReqVO.getTenantIds()) ? new Long[]{TenantContextHolder.getTenantId()} : pageReqVO.getTenantIds());
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<PriceChangeRecordRespVO> list = processInstanceService.getProcessInstancePriceChangePage(
+                getLoginUserId(), pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "价格变更.xls", "数据", PriceChangeRecordExcelVO.class,
+                BeanUtils.toBean(list, PriceChangeRecordExcelVO.class));
+    }
+
     @GetMapping("/transfer-change-page")
     @Operation(summary = "获取转院变更流程实例", description = "在【我的流程】菜单中,进行调用")
 //    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@@ -389,6 +455,21 @@ public class BpmProcessInstanceController {
         return success(pageResult);
     }
 
+    @GetMapping("/check-in-wait-export-excel")
+    @Operation(summary = "导出轮候入住流程实例 Excel", description = "内容为轮候入住分页的全量数据")
+    @ApiAccessLog(operateType = EXPORT)
+    @TenantIgnore
+    public void exportProcessInstanceCheckInWaitExcel(@Valid CheckInWaitPageReqVO pageReqVO,
+                                                      HttpServletResponse response) throws IOException {
+        pageReqVO.setTenantIds(pageReqVO.getTenantIds() == null ? new Long[]{TenantContextHolder.getTenantId()} : pageReqVO.getTenantIds());
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<CheckInWaitRecordRespVO> list = processInstanceService.getProcessInstanceCheckInWaitPage(
+                getLoginUserId(), pageReqVO).getList();
+        // 导出 Excel
+        ExcelUtils.write(response, "轮候入住.xls", "数据", CheckInWaitRecordExcelVO.class,
+                BeanUtils.toBean(list, CheckInWaitRecordExcelVO.class));
+    }
+
     @GetMapping("/deleteAllHistoricProcessInstance")
     public CommonResult<Boolean> deleteAllHistoricProcessInstance() {
         processInstanceService.deleteAllHistoricProcessInstance();

+ 59 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/BedChangeRecordExcelVO.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.excel;
+
+import cn.iocoder.yudao.module.bpm.convert.excel.BpmProcessInstanceStatusConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.LocalDateConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.YesNoConvert;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+/**
+ * 管理后台 - 床位变更导出 Excel VO
+ *
+ * @author 养老项目
+ */
+@Schema(description = "管理后台 - 床位变更导出 Excel VO")
+@Data
+@ExcelIgnoreUnannotated
+public class BedChangeRecordExcelVO {
+
+    @Schema(description = "长者姓名")
+    @ExcelProperty("长者姓名")
+    private String elderlyName;
+
+    @Schema(description = "原床位")
+    @ExcelProperty("原床位")
+    private String originalName;
+
+    @Schema(description = "原收费项目")
+    @ExcelProperty("原收费项目")
+    private String originalItemName;
+
+    @Schema(description = "变更后床位")
+    @ExcelProperty("变更后床位")
+    private String expectName;
+
+    @Schema(description = "变更后收费项目")
+    @ExcelProperty("变更后收费项目")
+    private String expectItemName;
+
+    @Schema(description = "变更日期")
+    @ExcelProperty(value = "变更日期", converter = LocalDateConvert.class)
+    private LocalDate changeDate;
+
+    @Schema(description = "是否包房")
+    @ExcelProperty(value = "是否包房", converter = YesNoConvert.class)
+    private Integer isPrivateRoom;
+
+    @Schema(description = "审批状态")
+    @ExcelProperty(value = "审批状态", converter = BpmProcessInstanceStatusConvert.class)
+    private Integer status;
+
+    @Schema(description = "所属机构")
+    @ExcelProperty("所属机构")
+    private String tenantName;
+
+}

+ 66 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/CateringChangeRecordExcelVO.java

@@ -0,0 +1,66 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.excel;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.BpmProcessInstanceStatusConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.LocalDateConvert;
+import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+/**
+ * 管理后台 - 餐饮变更导出 Excel VO
+ *
+ * @author 养老项目
+ */
+@Schema(description = "管理后台 - 餐饮变更导出 Excel VO")
+@Data
+@ExcelIgnoreUnannotated
+public class CateringChangeRecordExcelVO {
+
+    @Schema(description = "长者姓名")
+    @ExcelProperty("长者姓名")
+    private String elderlyName;
+
+    @Schema(description = "长者性别")
+    @ExcelProperty(value = "长者性别", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.USER_SEX)
+    private Integer elderSex;
+
+    @Schema(description = "原套餐")
+    @ExcelProperty("原套餐")
+    private String originalName;
+
+    @Schema(description = "变更后套餐")
+    @ExcelProperty("变更后套餐")
+    private String expectName;
+
+    @Schema(description = "餐别")
+    @ExcelProperty("餐别")
+    private String categoryName;
+
+    @Schema(description = "收费项目")
+    @ExcelProperty("收费项目")
+    private String chargeName;
+
+    @Schema(description = "收费项目名称")
+    @ExcelProperty("收费项目名称")
+    private String originalItemName;
+
+    @Schema(description = "变更日期")
+    @ExcelProperty(value = "变更日期", converter = LocalDateConvert.class)
+    private LocalDate changeDate;
+
+    @Schema(description = "审批状态")
+    @ExcelProperty(value = "审批状态", converter = BpmProcessInstanceStatusConvert.class)
+    private Integer status;
+
+    @Schema(description = "所属机构")
+    @ExcelProperty("所属机构")
+    private String tenantName;
+
+}

+ 75 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/CheckInWaitRecordExcelVO.java

@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.excel;
+
+import cn.iocoder.yudao.module.bpm.convert.excel.BpmCheckInWaitStatusConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.BpmProcessInstanceStatusConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.LocalDateTimeConvert;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 管理后台 - 轮候入住导出 Excel VO
+ *
+ * @author 养老项目
+ */
+@Schema(description = "管理后台 - 轮候入住导出 Excel VO")
+@Data
+@ExcelIgnoreUnannotated
+public class CheckInWaitRecordExcelVO {
+
+    @Schema(description = "长者姓名")
+    @ExcelProperty("长者姓名")
+    private String elderName;
+
+    @Schema(description = "身份证号")
+    @ExcelProperty("身份证号")
+    private String idCard;
+
+    @Schema(description = "最近居住情况")
+    @ExcelProperty("最近居住情况")
+    private String recentLivingSituation;
+
+    @Schema(description = "近六个月病历")
+    @ExcelProperty("近六个月病历")
+    private String medicalRecordsWithinSixMonth;
+
+    @Schema(description = "近期出院小结")
+    @ExcelProperty("近期出院小结")
+    private String recentDischargeSummary;
+
+    @Schema(description = "体检报告")
+    @ExcelProperty("体检报告")
+    private String physicalExaminationReport;
+
+    @Schema(description = "第一审批人")
+    @ExcelProperty("第一审批人")
+    private String firstAuditName;
+
+    @Schema(description = "第二审批人")
+    @ExcelProperty("第二审批人")
+    private String auditName;
+
+    @Schema(description = "备注")
+    @ExcelProperty("备注")
+    private String remark;
+
+    @Schema(description = "审批状态")
+    @ExcelProperty(value = "审批状态", converter = BpmProcessInstanceStatusConvert.class)
+    private Integer bpmStatus;
+
+    @Schema(description = "业务状态")
+    @ExcelProperty(value = "业务状态", converter = BpmCheckInWaitStatusConvert.class)
+    private Integer status;
+
+    @Schema(description = "创建时间")
+    @ExcelProperty(value = "创建时间", converter = LocalDateTimeConvert.class)
+    private LocalDateTime createdTime;
+
+    @Schema(description = "所属机构")
+    @ExcelProperty("所属机构")
+    private String tenantName;
+
+}

+ 59 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/NurseChangeRecordExcelVO.java

@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.excel;
+
+import cn.iocoder.yudao.module.bpm.convert.excel.BpmProcessInstanceStatusConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.LocalDateConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.YesNoConvert;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+/**
+ * 管理后台 - 护理变更导出 Excel VO
+ *
+ * @author 养老项目
+ */
+@Schema(description = "管理后台 - 护理变更导出 Excel VO")
+@Data
+@ExcelIgnoreUnannotated
+public class NurseChangeRecordExcelVO {
+
+    @Schema(description = "长者姓名")
+    @ExcelProperty("长者姓名")
+    private String elderlyName;
+
+    @Schema(description = "原护理等级")
+    @ExcelProperty("原护理等级")
+    private String originalName;
+
+    @Schema(description = "变更后护理等级")
+    @ExcelProperty("变更后护理等级")
+    private String expectName;
+
+    @Schema(description = "收费项目")
+    @ExcelProperty("收费项目")
+    private String chargeName;
+
+    @Schema(description = "收费项目名称")
+    @ExcelProperty("收费项目名称")
+    private String originalItemName;
+
+    @Schema(description = "变更日期")
+    @ExcelProperty(value = "变更日期", converter = LocalDateConvert.class)
+    private LocalDate changeDate;
+
+    @Schema(description = "是否包房")
+    @ExcelProperty(value = "是否包房", converter = YesNoConvert.class)
+    private Integer isPrivateRoom;
+
+    @Schema(description = "审批状态")
+    @ExcelProperty(value = "审批状态", converter = BpmProcessInstanceStatusConvert.class)
+    private Integer status;
+
+    @Schema(description = "所属机构")
+    @ExcelProperty("所属机构")
+    private String tenantName;
+
+}

+ 42 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/excel/PriceChangeRecordExcelVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.excel;
+
+import cn.iocoder.yudao.module.bpm.convert.excel.BpmProcessInstanceStatusConvert;
+import cn.iocoder.yudao.module.bpm.convert.excel.LocalDateConvert;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+/**
+ * 管理后台 - 价格变更导出 Excel VO
+ *
+ * @author 养老项目
+ */
+@Schema(description = "管理后台 - 价格变更导出 Excel VO")
+@Data
+@ExcelIgnoreUnannotated
+public class PriceChangeRecordExcelVO {
+
+    @Schema(description = "长者姓名")
+    @ExcelProperty("长者姓名")
+    private String elderlyName;
+
+    @Schema(description = "收费类别")
+    @ExcelProperty("收费类别")
+    private String categoryName;
+
+    @Schema(description = "变更日期")
+    @ExcelProperty(value = "变更日期", converter = LocalDateConvert.class)
+    private LocalDate changeDate;
+
+    @Schema(description = "审批状态")
+    @ExcelProperty(value = "审批状态", converter = BpmProcessInstanceStatusConvert.class)
+    private Integer status;
+
+    @Schema(description = "所属机构")
+    @ExcelProperty("所属机构")
+    private String tenantName;
+
+}

+ 50 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/BpmCheckInWaitStatusConvert.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.bpm.convert.excel;
+
+import cn.iocoder.yudao.module.bpm.enums.BpmCheckInWaitStatusEnum;
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Excel 轮候入住业务状态转换器
+ *
+ * @author 养老项目
+ */
+public class BpmCheckInWaitStatusConvert implements Converter<Integer> {
+
+    @Override
+    public Class<?> supportJavaTypeKey() {
+        return Integer.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
+                                     GlobalConfiguration globalConfiguration) {
+        // 导出使用,暂不支持导入
+        return null;
+    }
+
+    @Override
+    public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
+                                               GlobalConfiguration globalConfiguration) {
+        WriteCellData<String> cellData = new WriteCellData<>();
+        String cellValue = "";
+        if (value != null) {
+            BpmCheckInWaitStatusEnum statusEnum = BpmCheckInWaitStatusEnum.valueOfStatus(value);
+            cellValue = statusEnum == null ? String.valueOf(value) : statusEnum.getDesc();
+        }
+        cellData.setType(CellDataTypeEnum.STRING);
+        cellData.setStringValue(cellValue);
+        cellData.setData(cellValue);
+        return cellData;
+    }
+
+}

+ 56 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/BpmProcessInstanceStatusConvert.java

@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.module.bpm.convert.excel;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Excel 审批状态转换器:1-审批中,2-审批通过,3-审批不通过,4-已取消,5-系统审批
+ *
+ * @author 养老项目
+ */
+public class BpmProcessInstanceStatusConvert implements Converter<Integer> {
+
+    @Override
+    public Class<?> supportJavaTypeKey() {
+        return Integer.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
+                                     GlobalConfiguration globalConfiguration) {
+        // 导出使用,暂不支持导入
+        return null;
+    }
+
+    @Override
+    public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
+                                               GlobalConfiguration globalConfiguration) {
+        WriteCellData<String> cellData = new WriteCellData<>();
+        String cellValue = "";
+        if (value != null) {
+            if (value == 5) {
+                cellValue = "系统审批";
+            } else {
+                BpmProcessInstanceStatusEnum statusEnum = ArrayUtil.firstMatch(
+                        item -> item.getStatus().equals(value), BpmProcessInstanceStatusEnum.values());
+                cellValue = statusEnum == null ? String.valueOf(value) : statusEnum.getDesc();
+            }
+        }
+        cellData.setType(CellDataTypeEnum.STRING);
+        cellData.setStringValue(cellValue);
+        cellData.setData(cellValue);
+        return cellData;
+    }
+
+}

+ 49 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/LocalDateConvert.java

@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.bpm.convert.excel;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Excel {@link LocalDate} 转换器,格式化为 yyyy-MM-dd
+ *
+ * @author 养老项目
+ */
+public class LocalDateConvert implements Converter<LocalDate> {
+
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+    @Override
+    public Class<?> supportJavaTypeKey() {
+        return LocalDate.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public LocalDate convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
+                                       GlobalConfiguration globalConfiguration) {
+        return LocalDate.parse(cellData.getStringValue(), FORMATTER);
+    }
+
+    @Override
+    public WriteCellData<?> convertToExcelData(LocalDate value, ExcelContentProperty contentProperty,
+                                               GlobalConfiguration globalConfiguration) {
+        WriteCellData<String> cellData = new WriteCellData<>();
+        String cellValue = value == null ? "" : FORMATTER.format(value);
+        cellData.setType(CellDataTypeEnum.STRING);
+        cellData.setStringValue(cellValue);
+        cellData.setData(cellValue);
+        return cellData;
+    }
+
+}

+ 49 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/LocalDateTimeConvert.java

@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.bpm.convert.excel;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Excel {@link LocalDateTime} 转换器,格式化为 yyyy-MM-dd HH:mm:ss
+ *
+ * @author 养老项目
+ */
+public class LocalDateTimeConvert implements Converter<LocalDateTime> {
+
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    @Override
+    public Class<?> supportJavaTypeKey() {
+        return LocalDateTime.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public LocalDateTime convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
+                                           GlobalConfiguration globalConfiguration) {
+        return LocalDateTime.parse(cellData.getStringValue(), FORMATTER);
+    }
+
+    @Override
+    public WriteCellData<?> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty,
+                                               GlobalConfiguration globalConfiguration) {
+        WriteCellData<String> cellData = new WriteCellData<>();
+        String cellValue = value == null ? "" : FORMATTER.format(value);
+        cellData.setType(CellDataTypeEnum.STRING);
+        cellData.setStringValue(cellValue);
+        cellData.setData(cellValue);
+        return cellData;
+    }
+
+}

+ 45 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/excel/YesNoConvert.java

@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.bpm.convert.excel;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+/**
+ * Excel 是否转换器:1-是,其它-否
+ *
+ * @author 养老项目
+ */
+public class YesNoConvert implements Converter<Integer> {
+
+    @Override
+    public Class<?> supportJavaTypeKey() {
+        return Integer.class;
+    }
+
+    @Override
+    public CellDataTypeEnum supportExcelTypeKey() {
+        return CellDataTypeEnum.STRING;
+    }
+
+    @Override
+    public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
+                                     GlobalConfiguration globalConfiguration) {
+        // 导出使用,暂不支持导入
+        return null;
+    }
+
+    @Override
+    public WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty,
+                                               GlobalConfiguration globalConfiguration) {
+        WriteCellData<String> cellData = new WriteCellData<>();
+        String cellValue = value == null ? "" : (value == 1 ? "是" : "否");
+        cellData.setType(CellDataTypeEnum.STRING);
+        cellData.setStringValue(cellValue);
+        cellData.setData(cellValue);
+        return cellData;
+    }
+
+}

+ 4 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java

@@ -344,4 +344,8 @@ public interface ErrorCodeConstants {
     ErrorCode ELDERLY_DOCTOR_ORDER_ENTRY_NOT_EXISTS = new ErrorCode(1_008_024_001, "医嘱条目不存在");
     ErrorCode ELDERLY_DOCTOR_ORDER_EXEC_NOT_EXISTS = new ErrorCode(1_008_024_002, "护士执行记录不存在");
     ErrorCode ELDERLY_DOCTOR_ORDER_ELDER_NOT_EXISTS = new ErrorCode(1_008_024_003, "长者信息不存在");
+
+    // ========== 出院小结 1-008-026-000 ==========
+    ErrorCode ELDERLY_DISCHARGE_ABSTRACT_NOT_EXISTS = new ErrorCode(1_008_026_000, "出院小结不存在");
+    ErrorCode ELDERLY_DISCHARGE_USE_MEDICINE_NOT_EXISTS = new ErrorCode(1_008_026_001, "出院小结用药不存在");
 }

+ 2 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/bpm/BpmElderlyChangeApiImpl.java

@@ -582,6 +582,7 @@ public class BpmElderlyChangeApiImpl implements BpmElderlyChangeApi {
                 expenseItemMapper.updateById(udpateExpenseItemDO);
             }
             item.setId(null);
+            item.setCreateTime(new Date());
             item.setChangeStartDate(bedChangeRecordDO.getChangeDate());
             totalAmout.add(item.getActualAmount());
         });
@@ -683,7 +684,6 @@ public class BpmElderlyChangeApiImpl implements BpmElderlyChangeApi {
                 .setCreatedTime(new Date())
                 .setCreatedBy(SecurityFrameworkUtils.getLoginUserNickname()));
         BigDecimal totalAmout = BigDecimal.ZERO;
-        Date date = new Date();
         priceChangeRecordDOS.forEach(priceChange ->{
             categoryList.forEach(category ->{
                 if (category.getOverheadChargeId().equals(priceChange.getOriginalId())){
@@ -861,7 +861,7 @@ public class BpmElderlyChangeApiImpl implements BpmElderlyChangeApi {
                 }
                 item.setId(null);
                 item.setChangeStartDate(changeRecordDO.getChangeDate());
-                item.setCreateTime(date);
+                item.setCreateTime(new Date());
                 totalAmout.add(item.getActualAmount());
             });
         });

+ 12 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/ElderlyBloodSugarRecordController.java

@@ -102,7 +102,19 @@ public class ElderlyBloodSugarRecordController {
             excelVO.setBedName(vo.getBedName());
             excelVO.setElderSexDisplay(vo.getElderSex() == null ? "" : (vo.getElderSex() == 1 ? "男" : "女"));
             excelVO.setRecordDate(vo.getRecordDate());
+            excelVO.setFastingBloodSugar(vo.getFastingBloodSugar());
+            excelVO.setFastingNurseSign(vo.getFastingNurseSign());
+            excelVO.setAfterBreakfastBloodSugar(vo.getAfterBreakfastBloodSugar());
+            excelVO.setAfterBreakfastNurseSign(vo.getAfterBreakfastNurseSign());
+            excelVO.setBeforeDinnerBloodSugar(vo.getBeforeDinnerBloodSugar());
+            excelVO.setBeforeDinnerNurseSign(vo.getBeforeDinnerNurseSign());
+            excelVO.setAfterDinnerBloodSugar(vo.getAfterDinnerBloodSugar());
+            excelVO.setAfterDinnerNurseSign(vo.getAfterDinnerNurseSign());
+            excelVO.setBeforeSleepBloodSugar(vo.getBeforeSleepBloodSugar());
+            excelVO.setBeforeSleepNurseSign(vo.getBeforeSleepNurseSign());
+            excelVO.setRecordTime(vo.getRecordTime());
             excelVO.setBloodSugar(vo.getBloodSugar());
+            excelVO.setNurseSign(vo.getNurseSign());
             excelVO.setCreator(vo.getCreator());
             excelVO.setRemark(vo.getRemark());
             return excelVO;

+ 36 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/elderlybloodsugarrecord/ElderlyBloodSugarRecordExportExcelVO.java

@@ -27,9 +27,45 @@ public class ElderlyBloodSugarRecordExportExcelVO {
     @ExcelProperty("记录日期")
     private LocalDate recordDate;
 
+    @ExcelProperty("空腹血糖值")
+    private java.math.BigDecimal fastingBloodSugar;
+
+    @ExcelProperty("空腹护士签名")
+    private String fastingNurseSign;
+
+    @ExcelProperty("早餐后血糖值")
+    private java.math.BigDecimal afterBreakfastBloodSugar;
+
+    @ExcelProperty("早餐后护士签名")
+    private String afterBreakfastNurseSign;
+
+    @ExcelProperty("晚餐前血糖值")
+    private java.math.BigDecimal beforeDinnerBloodSugar;
+
+    @ExcelProperty("晚餐前护士签名")
+    private String beforeDinnerNurseSign;
+
+    @ExcelProperty("晚餐后血糖值")
+    private java.math.BigDecimal afterDinnerBloodSugar;
+
+    @ExcelProperty("晚餐后护士签名")
+    private String afterDinnerNurseSign;
+
+    @ExcelProperty("睡前血糖值")
+    private java.math.BigDecimal beforeSleepBloodSugar;
+
+    @ExcelProperty("睡前护士签名")
+    private String beforeSleepNurseSign;
+
+    @ExcelProperty("记录时间")
+    private String recordTime;
+
     @ExcelProperty("血糖值")
     private java.math.BigDecimal bloodSugar;
 
+    @ExcelProperty("护士签名")
+    private String nurseSign;
+
     @ExcelProperty("备注")
     private String remark;
 

+ 36 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/elderlybloodsugarrecord/ElderlyBloodSugarRecordRespVO.java

@@ -39,9 +39,45 @@ public class ElderlyBloodSugarRecordRespVO {
     @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT)
     private LocalDate recordDate;
 
+    @Schema(description = "空腹血糖值")
+    private BigDecimal fastingBloodSugar;
+
+    @Schema(description = "空腹护士签名")
+    private String fastingNurseSign;
+
+    @Schema(description = "早餐后血糖值")
+    private BigDecimal afterBreakfastBloodSugar;
+
+    @Schema(description = "早餐后护士签名")
+    private String afterBreakfastNurseSign;
+
+    @Schema(description = "晚餐前血糖值")
+    private BigDecimal beforeDinnerBloodSugar;
+
+    @Schema(description = "晚餐前护士签名")
+    private String beforeDinnerNurseSign;
+
+    @Schema(description = "晚餐后血糖值")
+    private BigDecimal afterDinnerBloodSugar;
+
+    @Schema(description = "晚餐后护士签名")
+    private String afterDinnerNurseSign;
+
+    @Schema(description = "睡前血糖值")
+    private BigDecimal beforeSleepBloodSugar;
+
+    @Schema(description = "睡前护士签名")
+    private String beforeSleepNurseSign;
+
+    @Schema(description = "记录时间(HH:mm)")
+    private String recordTime;
+
     @Schema(description = "血糖值")
     private BigDecimal bloodSugar;
 
+    @Schema(description = "护士签名")
+    private String nurseSign;
+
     @Schema(description = "备注")
     private String remark;
 

+ 37 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/elderlybloodsugarrecord/ElderlyBloodSugarRecordSaveReqVO.java

@@ -29,10 +29,45 @@ public class ElderlyBloodSugarRecordSaveReqVO {
     @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT)
     private LocalDate recordDate;
 
-    @Schema(description = "血糖值", requiredMode = Schema.RequiredMode.REQUIRED, example = "5.60")
-    @NotNull(message = "血糖值不能为空")
+    @Schema(description = "空腹血糖值", example = "5.60")
+    private BigDecimal fastingBloodSugar;
+
+    @Schema(description = "空腹护士签名")
+    private String fastingNurseSign;
+
+    @Schema(description = "早餐后血糖值", example = "5.60")
+    private BigDecimal afterBreakfastBloodSugar;
+
+    @Schema(description = "早餐后护士签名")
+    private String afterBreakfastNurseSign;
+
+    @Schema(description = "晚餐前血糖值", example = "5.60")
+    private BigDecimal beforeDinnerBloodSugar;
+
+    @Schema(description = "晚餐前护士签名")
+    private String beforeDinnerNurseSign;
+
+    @Schema(description = "晚餐后血糖值", example = "5.60")
+    private BigDecimal afterDinnerBloodSugar;
+
+    @Schema(description = "晚餐后护士签名")
+    private String afterDinnerNurseSign;
+
+    @Schema(description = "睡前血糖值", example = "5.60")
+    private BigDecimal beforeSleepBloodSugar;
+
+    @Schema(description = "睡前护士签名")
+    private String beforeSleepNurseSign;
+
+    @Schema(description = "记录时间(HH:mm)", example = "09:30")
+    private String recordTime;
+
+    @Schema(description = "血糖值", example = "5.60")
     private BigDecimal bloodSugar;
 
+    @Schema(description = "护士签名")
+    private String nurseSign;
+
     @Schema(description = "备注")
     private String remark;
 

+ 112 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/ElderlyDischargeAbstractController.java

@@ -0,0 +1,112 @@
+package cn.iocoder.yudao.module.system.controller.admin.discharge;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractPageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractRespVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractSaveReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeUseMedicineRespVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeUseMedicineSaveReqVO;
+import cn.iocoder.yudao.module.system.service.discharge.ElderlyDischargeAbstractService;
+import cn.iocoder.yudao.module.system.service.discharge.ElderlyDischargeUseMedicineService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+/**
+ * 管理后台 - 出院小结
+ */
+@Tag(name = "管理后台 - 出院小结")
+@RestController
+@RequestMapping("/elderly/dischargeAbstract")
+@Validated
+public class ElderlyDischargeAbstractController {
+
+    @Resource
+    private ElderlyDischargeAbstractService dischargeAbstractService;
+    @Resource
+    private ElderlyDischargeUseMedicineService dischargeUseMedicineService;
+
+    @GetMapping("/page")
+    @Operation(summary = "获得出院小结分页")
+    public CommonResult<PageResult<ElderlyDischargeAbstractRespVO>> getDischargeAbstractPage(
+            @Valid ElderlyDischargeAbstractPageReqVO pageReqVO) {
+        return success(dischargeAbstractService.getDischargeAbstractPage(pageReqVO));
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得出院小结")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    public CommonResult<ElderlyDischargeAbstractRespVO> getDischargeAbstract(@RequestParam("id") Long id) {
+        return success(dischargeAbstractService.getDischargeAbstract(id));
+    }
+
+    @PostMapping("/create")
+    @Operation(summary = "创建出院小结")
+    public CommonResult<Long> createDischargeAbstract(
+            @Valid @RequestBody ElderlyDischargeAbstractSaveReqVO createReqVO) {
+        return success(dischargeAbstractService.createDischargeAbstract(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "更新出院小结")
+    public CommonResult<Boolean> updateDischargeAbstract(
+            @Valid @RequestBody ElderlyDischargeAbstractSaveReqVO updateReqVO) {
+        dischargeAbstractService.updateDischargeAbstract(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除出院小结")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    public CommonResult<Boolean> deleteDischargeAbstract(@RequestParam("id") Long id) {
+        dischargeAbstractService.deleteDischargeAbstract(id);
+        return success(true);
+    }
+
+    @GetMapping("/medicine/list")
+    @Operation(summary = "获得出院小结用药列表")
+    @Parameter(name = "dischargeAbstractId", description = "出院小结编号", required = true, example = "1024")
+    public CommonResult<List<ElderlyDischargeUseMedicineRespVO>> getMedicineList(
+            @RequestParam("dischargeAbstractId") Long dischargeAbstractId) {
+        return success(dischargeUseMedicineService.getMedicineList(dischargeAbstractId));
+    }
+
+    @PostMapping("/medicine/create")
+    @Operation(summary = "创建出院小结用药")
+    public CommonResult<Long> createMedicine(
+            @Valid @RequestBody ElderlyDischargeUseMedicineSaveReqVO createReqVO) {
+        return success(dischargeUseMedicineService.createMedicine(createReqVO));
+    }
+
+    @PutMapping("/medicine/update")
+    @Operation(summary = "更新出院小结用药")
+    public CommonResult<Boolean> updateMedicine(
+            @Valid @RequestBody ElderlyDischargeUseMedicineSaveReqVO updateReqVO) {
+        dischargeUseMedicineService.updateMedicine(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/medicine/delete")
+    @Operation(summary = "删除出院小结用药")
+    @Parameter(name = "id", description = "编号", required = true, example = "1024")
+    public CommonResult<Boolean> deleteMedicine(@RequestParam("id") Long id) {
+        dischargeUseMedicineService.deleteMedicine(id);
+        return success(true);
+    }
+}

+ 29 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeAbstractPageReqVO.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.system.controller.admin.discharge.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+/**
+ * 管理后台 - 出院小结分页 Request VO
+ */
+@Schema(description = "管理后台 - 出院小结分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class ElderlyDischargeAbstractPageReqVO extends PageParam {
+
+    @Schema(description = "长者姓名(模糊匹配)")
+    private String elderName;
+
+    @Schema(description = "出院日期范围")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate[] dischargeDate;
+}

+ 110 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeAbstractRespVO.java

@@ -0,0 +1,110 @@
+package cn.iocoder.yudao.module.system.controller.admin.discharge.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+ * 管理后台 - 出院小结 Response VO
+ */
+@Schema(description = "管理后台 - 出院小结 Response VO")
+@Data
+public class ElderlyDischargeAbstractRespVO {
+
+    @Schema(description = "编号", example = "1024")
+    private Long id;
+
+    @Schema(description = "长者id", example = "1")
+    private Long elderId;
+
+    @Schema(description = "长者姓名", example = "张三")
+    private String elderName;
+
+    @Schema(description = "性别 1男 2女", example = "1")
+    private Integer elderSex;
+
+    @Schema(description = "性别名称", example = "男")
+    private String elderSexName;
+
+    @Schema(description = "年龄", example = "80")
+    private Integer elderAge;
+
+    @Schema(description = "居住区域", example = "天河区")
+    private String region;
+
+    @Schema(description = "入住日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate checkInDate;
+
+    @Schema(description = "出院日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate dischargeDate;
+
+    @Schema(description = "出院原因")
+    private String reasonDischarge;
+
+    @Schema(description = "医生签名")
+    private String doctorSign;
+
+    @Schema(description = "既往病史")
+    private String anamnesis;
+
+    @Schema(description = "近期健康问题")
+    private String recentHealthIssues;
+
+    @Schema(description = "身体活动能力")
+    private String physicalActivityAbility;
+
+    @Schema(description = "认知与精神状态")
+    private String cognitionMentalState;
+
+    @Schema(description = "疾病治疗与控制")
+    private String diseaseTreatmentControl;
+
+    @Schema(description = "近期就医情况")
+    private String recentMedicalTreatmentSituation;
+
+    @Schema(description = "生命体征")
+    private String vitalSigns;
+
+    @Schema(description = "身体功能")
+    private String physicalFunctions;
+
+    @Schema(description = "重点关注问题")
+    private String keyIssues;
+
+    @Schema(description = "安全防护")
+    private String safetyProtection;
+
+    @Schema(description = "饮食与就医")
+    private String dietMedicalTreatment;
+
+    @Schema(description = "签名日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate signDate;
+
+    @Schema(description = "机构id", example = "1")
+    private Long tenantId;
+
+    @Schema(description = "创建人")
+    private String creator;
+
+    @Schema(description = "创建时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime createTime;
+
+    @Schema(description = "用药列表")
+    private List<ElderlyDischargeUseMedicineRespVO> medicines;
+}

+ 77 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeAbstractSaveReqVO.java

@@ -0,0 +1,77 @@
+package cn.iocoder.yudao.module.system.controller.admin.discharge.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+/**
+ * 管理后台 - 出院小结新增/修改 Request VO
+ */
+@Schema(description = "管理后台 - 出院小结新增/修改 Request VO")
+@Data
+public class ElderlyDischargeAbstractSaveReqVO {
+
+    @Schema(description = "编号,修改时必传", example = "1024")
+    private Long id;
+
+    @Schema(description = "长者id", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Long elderId;
+
+    @Schema(description = "出院日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate dischargeDate;
+
+    @Schema(description = "既往病史")
+    private String anamnesis;
+
+    @Schema(description = "近期健康问题")
+    private String recentHealthIssues;
+
+    @Schema(description = "身体活动能力")
+    private String physicalActivityAbility;
+
+    @Schema(description = "认知与精神状态")
+    private String cognitionMentalState;
+
+    @Schema(description = "疾病治疗与控制")
+    private String diseaseTreatmentControl;
+
+    @Schema(description = "近期就医情况")
+    private String recentMedicalTreatmentSituation;
+
+    @Schema(description = "出院原因")
+    private String reasonDischarge;
+
+    @Schema(description = "生命体征")
+    private String vitalSigns;
+
+    @Schema(description = "身体功能")
+    private String physicalFunctions;
+
+    @Schema(description = "重点关注问题")
+    private String keyIssues;
+
+    @Schema(description = "安全防护")
+    private String safetyProtection;
+
+    @Schema(description = "饮食与就医")
+    private String dietMedicalTreatment;
+
+    @Schema(description = "医生签名")
+    private String doctorSign;
+
+    @Schema(description = "签名日期")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate signDate;
+
+    @Schema(description = "机构id", example = "1")
+    private Long tenantId;
+
+    @Schema(description = "用药列表")
+    private List<ElderlyDischargeUseMedicineSaveReqVO> medicines;
+}

+ 33 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeUseMedicineRespVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.system.controller.admin.discharge.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 管理后台 - 出院小结用药 Response VO
+ */
+@Schema(description = "管理后台 - 出院小结用药 Response VO")
+@Data
+public class ElderlyDischargeUseMedicineRespVO {
+
+    @Schema(description = "编号", example = "1024")
+    private Long id;
+
+    @Schema(description = "长者id", example = "1")
+    private Long elderId;
+
+    @Schema(description = "出院小结id", example = "1024")
+    private Long dischargeAbstractId;
+
+    @Schema(description = "药品", example = "阿莫西林")
+    private String medicineName;
+
+    @Schema(description = "剂量", example = "1片")
+    private String dose;
+
+    @Schema(description = "用法", example = "口服")
+    private String usage;
+
+    @Schema(description = "频次", example = "每日三次")
+    private String frequency;
+}

+ 33 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/discharge/vo/ElderlyDischargeUseMedicineSaveReqVO.java

@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.system.controller.admin.discharge.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 管理后台 - 出院小结用药新增/修改 Request VO
+ */
+@Schema(description = "管理后台 - 出院小结用药新增/修改 Request VO")
+@Data
+public class ElderlyDischargeUseMedicineSaveReqVO {
+
+    @Schema(description = "编号,修改时必传", example = "1024")
+    private Long id;
+
+    @Schema(description = "长者id", example = "1")
+    private Long elderId;
+
+    @Schema(description = "出院小结id", example = "1024")
+    private Long dischargeAbstractId;
+
+    @Schema(description = "药品", example = "阿莫西林")
+    private String medicineName;
+
+    @Schema(description = "剂量", example = "1片")
+    private String dose;
+
+    @Schema(description = "用法", example = "口服")
+    private String usage;
+
+    @Schema(description = "频次", example = "每日三次")
+    private String frequency;
+}

+ 8 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/doctororder/vo/DoctorOrderExecutionPageReqVO.java

@@ -4,8 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
 
-import java.util.List;
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
 
 /**
  * 护士执行记录分页请求 VO
@@ -28,4 +31,8 @@ public class DoctorOrderExecutionPageReqVO extends PageParam {
     @Schema(description = "长者姓名(模糊匹配)")
     private String elderName;
 
+    @Schema(description = "计划执行日期范围(plan_time)")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate[] planTime;
+
 }

+ 8 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/doctororder/vo/LongDoctorOrderPageReqVO.java

@@ -4,8 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
 
-import java.util.List;
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
 
 /**
  * 长期医嘱分页请求 VO
@@ -20,4 +23,8 @@ public class LongDoctorOrderPageReqVO extends PageParam {
     @Schema(description = "长者姓名(模糊匹配)")
     private String elderName;
 
+    @Schema(description = "最近开医嘱日期范围(按条目最大 order_time 过滤)")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate[] latestOrderTime;
+
 }

+ 8 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/doctororder/vo/ShortDoctorOrderPageReqVO.java

@@ -4,8 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
 
-import java.util.List;
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
 
 /**
  * 临时医嘱分页请求 VO
@@ -19,4 +22,8 @@ public class ShortDoctorOrderPageReqVO extends PageParam {
     @Schema(description = "长者姓名(模糊匹配)")
     private String elderName;
 
+    @Schema(description = "最近开医嘱日期范围(按条目最大 order_time 过滤)")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate[] latestOrderTime;
+
 }

+ 2 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/pay/CcbWxUnifiedOrderController.java

@@ -190,6 +190,7 @@ public class CcbWxUnifiedOrderController {
         request.setPOSID(merchantRecordDO.getCounterNumber());
         request.setBRANCHID(CcbPayConstant.BRANCHID);
         request.setORDERID(orderId);
+        request.setPUB(CcbPayConstant.CCB_PUB); // 商户公钥后30位,参与关单MAC摘要
         request.setSUB_APPID(subAppId); // 可选字段,可为null
         // 关单交易码,可通过参数临时覆盖(用于排查交易码是否正确,MAC校验失败可能由交易码不匹配导致)
         request.setTXCODE(txCode == null || txCode.isEmpty() ? CcbPayConstant.CLOSE_TXCODE : txCode);
@@ -314,6 +315,7 @@ public class CcbWxUnifiedOrderController {
             request.setPOSID(merchantRecordDO.getCounterNumber());
             request.setBRANCHID(CcbPayConstant.BRANCHID);
             request.setORDERID(expenseOrder.getBillOrderNumber());
+            request.setPUB(CcbPayConstant.CCB_PUB); // 商户公钥后30位,参与关单MAC摘要
             request.setSUB_APPID(subAppId); // 可选字段,可为null
             request.setTXCODE(CcbPayConstant.CLOSE_TXCODE);
 

+ 1 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/pay/vo/CcbCloseOrderRequestVO.java

@@ -14,5 +14,6 @@ public class CcbCloseOrderRequestVO {
 
     // 可选字段(文档要求N)
     private String SUB_APPID;  // 子商户公众账号ID(微信支付专有,非必传)
+    private String PUB;        // 商户公钥后30位(参与关单MAC摘要,取 CcbPayConstant.CCB_PUB)
     private String MAC;        // MAC校验域(生成后赋值,32位MD5大写)
 }

+ 36 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/biz/ElderlyBloodSugarRecordDO.java

@@ -38,9 +38,45 @@ public class ElderlyBloodSugarRecordDO extends BaseNoDeleteDO {
     @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY, timezone = TIME_ZONE_DEFAULT)
     private LocalDate recordDate;
 
+    @Schema(description = "空腹血糖值")
+    private BigDecimal fastingBloodSugar;
+
+    @Schema(description = "空腹护士签名")
+    private String fastingNurseSign;
+
+    @Schema(description = "早餐后血糖值")
+    private BigDecimal afterBreakfastBloodSugar;
+
+    @Schema(description = "早餐后护士签名")
+    private String afterBreakfastNurseSign;
+
+    @Schema(description = "晚餐前血糖值")
+    private BigDecimal beforeDinnerBloodSugar;
+
+    @Schema(description = "晚餐前护士签名")
+    private String beforeDinnerNurseSign;
+
+    @Schema(description = "晚餐后血糖值")
+    private BigDecimal afterDinnerBloodSugar;
+
+    @Schema(description = "晚餐后护士签名")
+    private String afterDinnerNurseSign;
+
+    @Schema(description = "睡前血糖值")
+    private BigDecimal beforeSleepBloodSugar;
+
+    @Schema(description = "睡前护士签名")
+    private String beforeSleepNurseSign;
+
+    @Schema(description = "记录时间(HH:mm)")
+    private String recordTime;
+
     @Schema(description = "血糖值")
     private BigDecimal bloodSugar;
 
+    @Schema(description = "护士签名")
+    private String nurseSign;
+
     @Schema(description = "备注")
     private String remark;
 

+ 82 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/biz/ElderlyDischargeAbstractDO.java

@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.biz;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseNoDeleteDO;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+
+import java.time.LocalDate;
+
+/**
+ * 出院小结 DO
+ */
+@TableName("elderly_discharge_abstract")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ElderlyDischargeAbstractDO extends BaseNoDeleteDO {
+
+    /** id */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 长者id */
+    private Long elderId;
+
+    /** 出院日期 */
+    private LocalDate dischargeDate;
+
+    /** 既往病史 */
+    private String anamnesis;
+
+    /** 近期健康问题 */
+    private String recentHealthIssues;
+
+    /** 身体活动能力 */
+    private String physicalActivityAbility;
+
+    /** 认知与精神状态 */
+    private String cognitionMentalState;
+
+    /** 疾病治疗与控制 */
+    private String diseaseTreatmentControl;
+
+    /** 近期就医情况 */
+    private String recentMedicalTreatmentSituation;
+
+    /** 出院原因 */
+    private String reasonDischarge;
+
+    /** 生命体征 */
+    private String vitalSigns;
+
+    /** 身体功能 */
+    private String physicalFunctions;
+
+    /** 重点关注问题 */
+    private String keyIssues;
+
+    /** 安全防护 */
+    private String safetyProtection;
+
+    /** 饮食与就医 */
+    private String dietMedicalTreatment;
+
+    /** 医生签名 */
+    private String doctorSign;
+
+    /** 签名日期 */
+    private LocalDate signDate;
+
+    /** 机构id */
+    private Long tenantId;
+}

+ 52 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/biz/ElderlyDischargeUseMedicineDO.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.system.dal.dataobject.biz;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseNoDeleteDO;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+
+/**
+ * 出院小结用药 DO
+ */
+@TableName("elderly_discharge_use_medicine")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ElderlyDischargeUseMedicineDO extends BaseNoDeleteDO {
+
+    /** id */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 长者id */
+    private Long elderId;
+
+    /** 出院小结id */
+    private Long dischargeAbstractId;
+
+    /** 药品 */
+    private String medicineName;
+
+    /** 剂量 */
+    private String dose;
+
+    /** 用法(usage 为数据库关键字,需使用反引号包裹) */
+    @TableField("`usage`")
+    private String usage;
+
+    /** 频次 */
+    private String frequency;
+
+    /** 机构id */
+    private Long tenantId;
+}

+ 10 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/DoctorOrderExecutionMapper.java

@@ -7,6 +7,9 @@ import cn.iocoder.yudao.module.system.controller.admin.doctororder.vo.DoctorOrde
 import cn.iocoder.yudao.module.system.dal.dataobject.biz.DoctorOrderExecutionDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.List;
 
 /**
@@ -28,6 +31,13 @@ public interface DoctorOrderExecutionMapper extends BaseMapperX<DoctorOrderExecu
         if (reqVO.getPlanDate() != null && !reqVO.getPlanDate().isEmpty()) {
             query.apply("DATE(plan_time) = {0}", reqVO.getPlanDate());
         }
+        // 按 plan_time 日期范围匹配
+        LocalDate[] range = reqVO.getPlanTime();
+        if (range != null && range.length > 0) {
+            LocalDateTime begin = range[0] == null ? null : range[0].atStartOfDay();
+            LocalDateTime end = range.length > 1 && range[1] != null ? range[1].atTime(LocalTime.MAX) : null;
+            query.betweenIfPresent(DoctorOrderExecutionDO::getPlanTime, begin, end);
+        }
         return selectPage(reqVO, query);
     }
 

+ 34 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/ElderlyDischargeAbstractMapper.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.system.dal.mysql.biz;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractPageReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyDischargeAbstractDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 出院小结 Mapper
+ */
+@Mapper
+public interface ElderlyDischargeAbstractMapper extends BaseMapperX<ElderlyDischargeAbstractDO> {
+
+    default PageResult<ElderlyDischargeAbstractDO> selectPage(ElderlyDischargeAbstractPageReqVO reqVO,
+                                                              Collection<Long> elderIds) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<ElderlyDischargeAbstractDO>()
+                .betweenIfPresent(ElderlyDischargeAbstractDO::getDischargeDate, reqVO.getDischargeDate())
+                .inIfPresent(ElderlyDischargeAbstractDO::getElderId, elderIds)
+                .inIfPresent(ElderlyDischargeAbstractDO::getTenantId, reqVO.getTenantIds())
+                .orderByDesc(ElderlyDischargeAbstractDO::getCreateTime)
+                .orderByDesc(ElderlyDischargeAbstractDO::getId));
+    }
+
+    default List<ElderlyDischargeAbstractDO> selectListByElderId(Long elderId) {
+        return selectList(new LambdaQueryWrapperX<ElderlyDischargeAbstractDO>()
+                .eq(ElderlyDischargeAbstractDO::getElderId, elderId)
+                .orderByDesc(ElderlyDischargeAbstractDO::getId));
+    }
+}

+ 26 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/ElderlyDischargeUseMedicineMapper.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.system.dal.mysql.biz;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyDischargeUseMedicineDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 出院小结用药 Mapper
+ */
+@Mapper
+public interface ElderlyDischargeUseMedicineMapper extends BaseMapperX<ElderlyDischargeUseMedicineDO> {
+
+    default List<ElderlyDischargeUseMedicineDO> selectListByDischargeAbstractId(Long dischargeAbstractId) {
+        return selectList(new LambdaQueryWrapperX<ElderlyDischargeUseMedicineDO>()
+                .eq(ElderlyDischargeUseMedicineDO::getDischargeAbstractId, dischargeAbstractId)
+                .orderByAsc(ElderlyDischargeUseMedicineDO::getId));
+    }
+
+    default void deleteByDischargeAbstractId(Long dischargeAbstractId) {
+        delete(new LambdaQueryWrapperX<ElderlyDischargeUseMedicineDO>()
+                .eq(ElderlyDischargeUseMedicineDO::getDischargeAbstractId, dischargeAbstractId));
+    }
+}

+ 27 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/LongDoctorOrderMapper.java

@@ -7,6 +7,10 @@ import cn.iocoder.yudao.module.system.controller.admin.doctororder.vo.LongDoctor
 import cn.iocoder.yudao.module.system.dal.dataobject.biz.LongDoctorOrderDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
 /**
  * 长期医嘱主单 Mapper
  *
@@ -16,10 +20,31 @@ import org.apache.ibatis.annotations.Mapper;
 public interface LongDoctorOrderMapper extends BaseMapperX<LongDoctorOrderDO> {
 
     default PageResult<LongDoctorOrderDO> selectPage(LongDoctorOrderPageReqVO reqVO) {
-        return selectPage(reqVO, new LambdaQueryWrapperX<LongDoctorOrderDO>()
+        LambdaQueryWrapperX<LongDoctorOrderDO> query = new LambdaQueryWrapperX<LongDoctorOrderDO>()
                 .likeIfPresent(LongDoctorOrderDO::getElderName, reqVO.getElderName())
                 .inIfPresent(LongDoctorOrderDO::getTenantId, reqVO.getTenantIds())
-                .orderByDesc(LongDoctorOrderDO::getCreateTime));
+                .orderByDesc(LongDoctorOrderDO::getCreateTime);
+        // latestOrderTime = 该单条目 order_time 最大值,按该值的日期范围过滤
+        LocalDate[] range = reqVO.getLatestOrderTime();
+        if (range != null && range.length > 0) {
+            LocalDateTime begin = range[0] == null ? null : range[0].atStartOfDay();
+            LocalDateTime end = range.length > 1 && range[1] != null ? range[1].atTime(LocalTime.MAX) : null;
+            applyLatestOrderTimeRange(query, begin, end);
+        }
+        return selectPage(reqVO, query);
+    }
+
+    static void applyLatestOrderTimeRange(LambdaQueryWrapperX<LongDoctorOrderDO> query,
+                                          LocalDateTime begin, LocalDateTime end) {
+        String prefix = "EXISTS (SELECT 1 FROM elderly_long_doctor_order_entry e WHERE e.long_doctor_order_id = elderly_long_doctor_order.id "
+                + "GROUP BY e.long_doctor_order_id HAVING ";
+        if (begin != null && end != null) {
+            query.apply(prefix + "MAX(e.order_time) >= {0} AND MAX(e.order_time) <= {1})", begin, end);
+        } else if (begin != null) {
+            query.apply(prefix + "MAX(e.order_time) >= {0})", begin);
+        } else if (end != null) {
+            query.apply(prefix + "MAX(e.order_time) <= {0})", end);
+        }
     }
 
 }

+ 27 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/biz/ShortDoctorOrderMapper.java

@@ -7,6 +7,10 @@ import cn.iocoder.yudao.module.system.controller.admin.doctororder.vo.ShortDocto
 import cn.iocoder.yudao.module.system.dal.dataobject.biz.ShortDoctorOrderDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
 /**
  * 临时医嘱主单 Mapper
  *
@@ -16,10 +20,31 @@ import org.apache.ibatis.annotations.Mapper;
 public interface ShortDoctorOrderMapper extends BaseMapperX<ShortDoctorOrderDO> {
 
     default PageResult<ShortDoctorOrderDO> selectPage(ShortDoctorOrderPageReqVO reqVO) {
-        return selectPage(reqVO, new LambdaQueryWrapperX<ShortDoctorOrderDO>()
+        LambdaQueryWrapperX<ShortDoctorOrderDO> query = new LambdaQueryWrapperX<ShortDoctorOrderDO>()
                 .likeIfPresent(ShortDoctorOrderDO::getElderName, reqVO.getElderName())
                 .inIfPresent(ShortDoctorOrderDO::getTenantId, reqVO.getTenantIds())
-                .orderByDesc(ShortDoctorOrderDO::getCreateTime));
+                .orderByDesc(ShortDoctorOrderDO::getCreateTime);
+        // latestOrderTime = 该单条目 order_time 最大值,按该值的日期范围过滤
+        LocalDate[] range = reqVO.getLatestOrderTime();
+        if (range != null && range.length > 0) {
+            LocalDateTime begin = range[0] == null ? null : range[0].atStartOfDay();
+            LocalDateTime end = range.length > 1 && range[1] != null ? range[1].atTime(LocalTime.MAX) : null;
+            applyLatestOrderTimeRange(query, begin, end);
+        }
+        return selectPage(reqVO, query);
+    }
+
+    static void applyLatestOrderTimeRange(LambdaQueryWrapperX<ShortDoctorOrderDO> query,
+                                          LocalDateTime begin, LocalDateTime end) {
+        String prefix = "EXISTS (SELECT 1 FROM elderly_short_doctor_order_entry e WHERE e.short_doctor_order_id = elderly_short_doctor_order.id "
+                + "GROUP BY e.short_doctor_order_id HAVING ";
+        if (begin != null && end != null) {
+            query.apply(prefix + "MAX(e.order_time) >= {0} AND MAX(e.order_time) <= {1})", begin, end);
+        } else if (begin != null) {
+            query.apply(prefix + "MAX(e.order_time) >= {0})", begin);
+        } else if (end != null) {
+            query.apply(prefix + "MAX(e.order_time) <= {0})", end);
+        }
     }
 
 }

+ 38 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ElderlyBloodSugarRecordServiceImpl.java

@@ -11,6 +11,8 @@ import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyInfoDO;
 import cn.iocoder.yudao.module.system.dal.mysql.biz.BuildBedMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyBloodSugarRecordMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyInfoMapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -47,7 +49,42 @@ public class ElderlyBloodSugarRecordServiceImpl implements ElderlyBloodSugarReco
     public void updateElderlyBloodSugarRecord(ElderlyBloodSugarRecordSaveReqVO updateReqVO) {
         validateElderlyBloodSugarRecordExists(updateReqVO.getId());
         ElderlyBloodSugarRecordDO updateObj = BeanUtils.toBean(updateReqVO, ElderlyBloodSugarRecordDO.class);
-        elderlyBloodSugarRecordMapper.updateById(updateObj);
+        // updateById 默认忽略 null 字段,导致无法清空已录入的值;
+        // 这里对本次传 null 的时段字段显式 set null,实现"清空某时段血糖值/护士签名"
+        LambdaUpdateWrapper<ElderlyBloodSugarRecordDO> updateWrapper = new LambdaUpdateWrapper<ElderlyBloodSugarRecordDO>()
+                .eq(ElderlyBloodSugarRecordDO::getId, updateReqVO.getId());
+        // 1、空腹
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getFastingBloodSugar, updateReqVO.getFastingBloodSugar());
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getFastingNurseSign, updateReqVO.getFastingNurseSign());
+        // 2、早餐后
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getAfterBreakfastBloodSugar, updateReqVO.getAfterBreakfastBloodSugar());
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getAfterBreakfastNurseSign, updateReqVO.getAfterBreakfastNurseSign());
+        // 3、晚餐前
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getBeforeDinnerBloodSugar, updateReqVO.getBeforeDinnerBloodSugar());
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getBeforeDinnerNurseSign, updateReqVO.getBeforeDinnerNurseSign());
+        // 4、晚餐后
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getAfterDinnerBloodSugar, updateReqVO.getAfterDinnerBloodSugar());
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getAfterDinnerNurseSign, updateReqVO.getAfterDinnerNurseSign());
+        // 5、睡前
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getBeforeSleepBloodSugar, updateReqVO.getBeforeSleepBloodSugar());
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getBeforeSleepNurseSign, updateReqVO.getBeforeSleepNurseSign());
+        // 6、自定义时间点
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getRecordTime, updateReqVO.getRecordTime());
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getBloodSugar, updateReqVO.getBloodSugar());
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getNurseSign, updateReqVO.getNurseSign());
+        // 备注
+        setNullIfAbsent(updateWrapper, ElderlyBloodSugarRecordDO::getRemark, updateReqVO.getRemark());
+        elderlyBloodSugarRecordMapper.update(updateObj, updateWrapper);
+    }
+
+    /**
+     * 字段值为 null 时,显式将其更新为 NULL(否则 updateById 会跳过该字段)
+     */
+    private <V> void setNullIfAbsent(LambdaUpdateWrapper<ElderlyBloodSugarRecordDO> updateWrapper,
+                                     SFunction<ElderlyBloodSugarRecordDO, V> column, V value) {
+        if (value == null) {
+            updateWrapper.set(column, null);
+        }
     }
 
     @Override

+ 56 - 49
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ElderlyVitalSignsServiceImpl.java

@@ -82,26 +82,28 @@ public class ElderlyVitalSignsServiceImpl implements ElderlyVitalSignsService {
         if (StringUtils.isNotBlank(elderlyVitalSigns.getTemperature())) {
             VitalSignsEnum vitalSignsEnum = VitalSignsEnum.TEMPERATURE;
             double v = Double.parseDouble(elderlyVitalSigns.getTemperature());
-            if (v >= vitalSignsEnum.getMaxValue()) {
-                elderlyVitalSigns.setTemperature(String.format("%s%s", elderlyVitalSigns.getTemperature(), "(偏高)"));
-            } else if (v < vitalSignsEnum.getMinValue()) {
-                elderlyVitalSigns.setTemperature(String.format("%s%s", elderlyVitalSigns.getTemperature(), "(偏低)"));
-            } else {
-                elderlyVitalSigns.setTemperature(String.format("%s%s", elderlyVitalSigns.getTemperature(), "(正常)"));
-            }
+//            if (v >= vitalSignsEnum.getMaxValue()) {
+//                elderlyVitalSigns.setTemperature(String.format("%s%s", elderlyVitalSigns.getTemperature(), "(偏高)"));
+//            } else if (v < vitalSignsEnum.getMinValue()) {
+//                elderlyVitalSigns.setTemperature(String.format("%s%s", elderlyVitalSigns.getTemperature(), "(偏低)"));
+//            } else {
+//                elderlyVitalSigns.setTemperature(String.format("%s%s", elderlyVitalSigns.getTemperature(), "(正常)"));
+//            }
+            elderlyVitalSigns.setTemperature(String.valueOf(v));
         }
 
         if (StringUtils.isNotBlank(elderlyVitalSigns.getPulse())) {
             VitalSignsEnum vitalSignsEnum = VitalSignsEnum.PULSE;
             double v = Double.parseDouble(elderlyVitalSigns.getPulse());
 
-            if (v >= vitalSignsEnum.getMaxValue()) {
-                elderlyVitalSigns.setPulse(String.format("%s%s", elderlyVitalSigns.getPulse(), "(偏高)"));
-            } else if (v < vitalSignsEnum.getMinValue()) {
-                elderlyVitalSigns.setPulse(String.format("%s%s", elderlyVitalSigns.getPulse(), "(偏低)"));
-            } else {
-                elderlyVitalSigns.setPulse(String.format("%s%s", elderlyVitalSigns.getPulse(), "(正常)"));
-            }
+//            if (v >= vitalSignsEnum.getMaxValue()) {
+//                elderlyVitalSigns.setPulse(String.format("%s%s", elderlyVitalSigns.getPulse(), "(偏高)"));
+//            } else if (v < vitalSignsEnum.getMinValue()) {
+//                elderlyVitalSigns.setPulse(String.format("%s%s", elderlyVitalSigns.getPulse(), "(偏低)"));
+//            } else {
+//                elderlyVitalSigns.setPulse(String.format("%s%s", elderlyVitalSigns.getPulse(), "(正常)"));
+//            }
+            elderlyVitalSigns.setPulse(String.valueOf(v));
         }
 
 
@@ -109,26 +111,28 @@ public class ElderlyVitalSignsServiceImpl implements ElderlyVitalSignsService {
             VitalSignsEnum vitalSignsEnum = VitalSignsEnum.RESPIRATORY_RATE;
             double v = Double.parseDouble(elderlyVitalSigns.getRespiratoryRate());
 
-            if (v >= vitalSignsEnum.getMaxValue()) {
-                elderlyVitalSigns.setRespiratoryRate(String.format("%s%s", elderlyVitalSigns.getRespiratoryRate(), "(偏高)"));
-            } else if (v < vitalSignsEnum.getMinValue()) {
-                elderlyVitalSigns.setRespiratoryRate(String.format("%s%s", elderlyVitalSigns.getRespiratoryRate(), "(偏低)"));
-            } else {
-                elderlyVitalSigns.setRespiratoryRate(String.format("%s%s", elderlyVitalSigns.getRespiratoryRate(), "(正常)"));
-            }
+//            if (v >= vitalSignsEnum.getMaxValue()) {
+//                elderlyVitalSigns.setRespiratoryRate(String.format("%s%s", elderlyVitalSigns.getRespiratoryRate(), "(偏高)"));
+//            } else if (v < vitalSignsEnum.getMinValue()) {
+//                elderlyVitalSigns.setRespiratoryRate(String.format("%s%s", elderlyVitalSigns.getRespiratoryRate(), "(偏低)"));
+//            } else {
+//                elderlyVitalSigns.setRespiratoryRate(String.format("%s%s", elderlyVitalSigns.getRespiratoryRate(), "(正常)"));
+//            }
+            elderlyVitalSigns.setRespiratoryRate(String.valueOf(v));
         }
 
         if (StringUtils.isNotBlank(elderlyVitalSigns.getSystolicPressure())) {
             VitalSignsEnum vitalSignsEnum = VitalSignsEnum.SYSTOLIC_PRESSURE;
             double v = Double.parseDouble(elderlyVitalSigns.getSystolicPressure());
 
-            if (v >= vitalSignsEnum.getMaxValue()) {
-                elderlyVitalSigns.setSystolicPressure(String.format("%s%s", elderlyVitalSigns.getSystolicPressure(), "(偏高)"));
-            } else if (v < vitalSignsEnum.getMinValue()) {
-                elderlyVitalSigns.setSystolicPressure(String.format("%s%s", elderlyVitalSigns.getSystolicPressure(), "(偏低)"));
-            } else {
-                elderlyVitalSigns.setSystolicPressure(String.format("%s%s", elderlyVitalSigns.getSystolicPressure(), "(正常)"));
-            }
+//            if (v >= vitalSignsEnum.getMaxValue()) {
+//                elderlyVitalSigns.setSystolicPressure(String.format("%s%s", elderlyVitalSigns.getSystolicPressure(), "(偏高)"));
+//            } else if (v < vitalSignsEnum.getMinValue()) {
+//                elderlyVitalSigns.setSystolicPressure(String.format("%s%s", elderlyVitalSigns.getSystolicPressure(), "(偏低)"));
+//            } else {
+//                elderlyVitalSigns.setSystolicPressure(String.format("%s%s", elderlyVitalSigns.getSystolicPressure(), "(正常)"));
+//            }
+            elderlyVitalSigns.setSystolicPressure(String.valueOf(v));
         }
 
 
@@ -136,13 +140,14 @@ public class ElderlyVitalSignsServiceImpl implements ElderlyVitalSignsService {
             VitalSignsEnum vitalSignsEnum = VitalSignsEnum.DIASTOLIC_PRESSURE;
             double v = Double.parseDouble(elderlyVitalSigns.getDiastolicPressure());
 
-            if (v >= vitalSignsEnum.getMaxValue()) {
-                elderlyVitalSigns.setDiastolicPressure(String.format("%s%s", elderlyVitalSigns.getDiastolicPressure(), "(偏高)"));
-            } else if (v < vitalSignsEnum.getMinValue()) {
-                elderlyVitalSigns.setDiastolicPressure(String.format("%s%s", elderlyVitalSigns.getDiastolicPressure(), "(偏低)"));
-            } else {
-                elderlyVitalSigns.setDiastolicPressure(String.format("%s%s", elderlyVitalSigns.getDiastolicPressure(), "(正常)"));
-            }
+//            if (v >= vitalSignsEnum.getMaxValue()) {
+//                elderlyVitalSigns.setDiastolicPressure(String.format("%s%s", elderlyVitalSigns.getDiastolicPressure(), "(偏高)"));
+//            } else if (v < vitalSignsEnum.getMinValue()) {
+//                elderlyVitalSigns.setDiastolicPressure(String.format("%s%s", elderlyVitalSigns.getDiastolicPressure(), "(偏低)"));
+//            } else {
+//                elderlyVitalSigns.setDiastolicPressure(String.format("%s%s", elderlyVitalSigns.getDiastolicPressure(), "(正常)"));
+//            }
+            elderlyVitalSigns.setDiastolicPressure(String.valueOf(v));
         }
 
 
@@ -150,26 +155,28 @@ public class ElderlyVitalSignsServiceImpl implements ElderlyVitalSignsService {
             VitalSignsEnum vitalSignsEnum = VitalSignsEnum.BLOOD_OXYGEN_SATURATION;
             double v = Double.parseDouble(elderlyVitalSigns.getBloodOxygenSaturation());
 
-            if (v >= vitalSignsEnum.getMaxValue()) {
-                elderlyVitalSigns.setBloodOxygenSaturation(String.format("%s%s", elderlyVitalSigns.getBloodOxygenSaturation(), "(偏高)"));
-            } else if (v < vitalSignsEnum.getMinValue()) {
-                elderlyVitalSigns.setBloodOxygenSaturation(String.format("%s%s", elderlyVitalSigns.getBloodOxygenSaturation(), "(偏低)"));
-            } else {
-                elderlyVitalSigns.setBloodOxygenSaturation(String.format("%s%s", elderlyVitalSigns.getBloodOxygenSaturation(), "(正常)"));
-            }
+//            if (v >= vitalSignsEnum.getMaxValue()) {
+//                elderlyVitalSigns.setBloodOxygenSaturation(String.format("%s%s", elderlyVitalSigns.getBloodOxygenSaturation(), "(偏高)"));
+//            } else if (v < vitalSignsEnum.getMinValue()) {
+//                elderlyVitalSigns.setBloodOxygenSaturation(String.format("%s%s", elderlyVitalSigns.getBloodOxygenSaturation(), "(偏低)"));
+//            } else {
+//                elderlyVitalSigns.setBloodOxygenSaturation(String.format("%s%s", elderlyVitalSigns.getBloodOxygenSaturation(), "(正常)"));
+//            }
+            elderlyVitalSigns.setBloodOxygenSaturation(String.valueOf(v));
         }
 
         if (StringUtils.isNotBlank(elderlyVitalSigns.getBloodSugar())){
             VitalSignsEnum vitalSignsEnum = VitalSignsEnum.BLOOD_SUGAR;
             double v = Double.parseDouble(elderlyVitalSigns.getBloodSugar());
 
-            if (v >= vitalSignsEnum.getMaxValue()) {
-                elderlyVitalSigns.setBloodSugar(String.format("%s%s", elderlyVitalSigns.getBloodSugar(), "(偏高)"));
-            } else if (v < vitalSignsEnum.getMinValue()) {
-                elderlyVitalSigns.setBloodSugar(String.format("%s%s", elderlyVitalSigns.getBloodSugar(), "(偏低)"));
-            } else {
-                elderlyVitalSigns.setBloodSugar(String.format("%s%s", elderlyVitalSigns.getBloodSugar(), "(正常)"));
-            }
+//            if (v >= vitalSignsEnum.getMaxValue()) {
+//                elderlyVitalSigns.setBloodSugar(String.format("%s%s", elderlyVitalSigns.getBloodSugar(), "(偏高)"));
+//            } else if (v < vitalSignsEnum.getMinValue()) {
+//                elderlyVitalSigns.setBloodSugar(String.format("%s%s", elderlyVitalSigns.getBloodSugar(), "(偏低)"));
+//            } else {
+//                elderlyVitalSigns.setBloodSugar(String.format("%s%s", elderlyVitalSigns.getBloodSugar(), "(正常)"));
+//            }
+            elderlyVitalSigns.setBloodSugar(String.valueOf(v));
         }
     }
 

+ 22 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ExpenseOrderServiceImpl.java

@@ -4019,6 +4019,22 @@ public class ExpenseOrderServiceImpl implements ExpenseOrderService {
                     .le(ExpenseItemDO::getFreeStartTime, queryTime.atDay(1))
                     .ge(ExpenseItemDO::getFreeEndTime, queryTime.atEndOfMonth()));
             expenseItems.addAll(freeGiftItemList);
+
+            // 构建 费用项(itemId) -> 计量单位(chargeType) 映射,用于识别按次(inSequence)收费项
+            // inSequence:每月按次收费,金额为固定月金额,不乘天数、也不按实际在住区间比例折算
+            List<Long> itemIds = expenseItems.stream()
+                    .map(ExpenseItemDO::getItemId)
+                    .filter(Objects::nonNull)
+                    .distinct()
+                    .collect(Collectors.toList());
+            Map<Long, String> chargeTypeMap = new HashMap<>();
+            if (CollectionUtil.isNotEmpty(itemIds)) {
+                List<OverheadChargeDO> overheadChargeList = sysOverheadChargeMapper.selectBatchIds(itemIds);
+                if (CollectionUtil.isNotEmpty(overheadChargeList)) {
+                    overheadChargeList.forEach(oc -> chargeTypeMap.put(oc.getId(), oc.getChargeType()));
+                }
+            }
+
             LocalDate finalActualStart = actualStart;
             LocalDate finalActualEnd = actualEnd;
             collect = expenseItems.stream().map(item -> {
@@ -4031,7 +4047,12 @@ public class ExpenseOrderServiceImpl implements ExpenseOrderService {
                 respVO.setExpenseSource(BusinessConstants.EXPENSE_ITEM);
                 respVO.setSourceExpenseItemId(item.getId());
                 long monthDays = ChronoUnit.DAYS.between(monthStart, monthEnd) + 1;
-                if (item.getIsDayCalculate() == 1){
+                // 按次(inSequence)收费且仅限月度费用项:固定月金额,不乘天数、不按实际在住区间比例折算
+                if ("inSequence".equals(chargeTypeMap.get(item.getItemId()))
+                        && item.getIsMonthlyExpense() != null && item.getIsMonthlyExpense() == 1) {
+                    respVO.setPrice(item.getActualAmount());
+                    respVO.setTotalAmount(item.getTotalAmount());
+                } else if (item.getIsDayCalculate() == 1){
                     respVO.setPrice(item.getActualAmount());
                     respVO.setTotalAmount(respVO.getPrice().multiply(new BigDecimal(monthDays)));
                 }else {

+ 27 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ExpenseServiceImpl.java

@@ -121,6 +121,8 @@ public class ExpenseServiceImpl implements ExpenseService {
     @Autowired
     private SysChargeCategoryMapper sysChargeCategoryMapper;
 
+    @Resource
+    private SysOverheadChargeMapper sysOverheadChargeMapper;
 
     @Override
     @Transactional
@@ -174,6 +176,21 @@ public class ExpenseServiceImpl implements ExpenseService {
         //计算月度费用
         BigDecimal amount = BigDecimal.ZERO;  //账单实际金额
 
+        // 构建 费用项(itemId) -> 计量单位(chargeType) 映射,用于识别按次(inSequence)收费项
+        // inSequence:每月按次收费,金额为固定月金额,不乘天数、也不按入住日折算
+        List<Long> monthlyItemIds = monthlyExpenses.stream()
+                .map(ExpenseItemDO::getItemId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        Map<Long, String> monthlyChargeTypeMap = new HashMap<>();
+        if (CollectionUtil.isNotEmpty(monthlyItemIds)) {
+            List<OverheadChargeDO> monthlyOverheadCharges = sysOverheadChargeMapper.selectBatchIds(monthlyItemIds);
+            if (CollectionUtil.isNotEmpty(monthlyOverheadCharges)) {
+                monthlyOverheadCharges.forEach(oc -> monthlyChargeTypeMap.put(oc.getId(), oc.getChargeType()));
+            }
+        }
+
         for (ExpenseItemDO item : monthlyExpenses) {
             item.setIsMonthlyExpense(BooleanEnum.TRUE.getValue());
             item.setItemCategoryName(categoryMap.getOrDefault(item.getItemCategoryId(),new SysChargeCategoryDO()).getName());
@@ -192,8 +209,16 @@ public class ExpenseServiceImpl implements ExpenseService {
             orderItemDO.setItemName(item.getItemName());
 
 
-            //判断是否是月初一号入住的,那就按照一整月计算,不要按照天数算
-            if(item.getIsDayCalculate() != null && item.getIsDayCalculate() == 1){
+            // 按次(inSequence)收费且仅限月度费用项:固定月金额,不乘天数、不按入住日折算
+            if ("inSequence".equals(monthlyChargeTypeMap.get(item.getItemId()))
+                    && item.getIsMonthlyExpense() != null && item.getIsMonthlyExpense() == 1) {
+                orderItemDO.setPrice(item.getAmount());
+                orderItemDO.setActualPrice(item.getActualAmount());
+                orderItemDO.setTotalAmount(item.getTotalAmount());
+                orderItemDO.setRoundAmount(orderItemDO.getTotalAmount().setScale(0, RoundingMode.HALF_UP));
+                orderItemDO.setRoundTwoDecimalAmount(orderItemDO.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
+                amount = amount.add(orderItemDO.getTotalAmount());
+            } else if(item.getIsDayCalculate() != null && item.getIsDayCalculate() == 1){
                 orderItemDO.setPrice(item.getAmount());
                 orderItemDO.setActualPrice(item.getActualAmount());
                 orderItemDO.setTotalAmount(item.getTotalAmount().multiply(new BigDecimal(daysUntilMonthEnd)));

+ 125 - 7
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/RefundSettlementOrderServiceImpl.java

@@ -9,8 +9,10 @@ import cn.iocoder.yudao.module.bpm.enums.message.BpmMessageEnum;
 import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
 import cn.iocoder.yudao.module.system.controller.admin.biz.vo.*;
 import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.discount.DiscountRuleDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
 import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
+import cn.iocoder.yudao.module.system.dal.mysql.discount.DiscountRuleMapper;
 import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
 import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
 import cn.iocoder.yudao.module.system.enums.change.BusinessConstants;
@@ -110,6 +112,12 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
     @Resource
     private BizUtil bizUtil;
 
+    @Resource
+    private SysOverheadChargeMapper sysOverheadChargeMapper;
+
+    @Resource
+    private DiscountRuleMapper discountRuleMapper;
+
     @Override
     public PageResult<RefundSettlementOrderPageRespVO> getRefundSettlementOrderPage(RefundSettlementOrderPageReqVO pageReqVO) {
         Page<RefundSettlementOrderDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
@@ -1096,7 +1104,7 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
                 // 根据长者类型处理费用项
                 if (elderlyInfo.getInStatusType() == 2) {
                     // 试住类型的长者处理逻辑
-                    processProbationElderlyExpenseItem(item, elderlyInfo, costDeadlineDate, itemVO);
+                    processProbationElderlyExpenseItem(item, elderlyInfo, order, costDeadlineDate, itemVO);
                 } else {
                     // 正式入住长者处理逻辑
                     processRegularElderlyExpenseItem(item, elderlyInfo, order, costDeadlineDate, itemVO);
@@ -1278,8 +1286,13 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
      */
     private void processProbationElderlyExpenseItem(ExpenseOrderItemDO item,
                                                     ElderlyInfoDO elderlyInfo,
+                                                    ExpenseOrderDO order,
                                                     LocalDate costDeadlineDate,
                                                     ExpenseOrderItemVO itemVO) {
+        // 优惠抵扣费用项单独处理,已处理则无需继续
+        if (processDiscountDeductionItem(item, order, costDeadlineDate, itemVO)) {
+            return;
+        }
         if (item.getExpenseSource().equals(BusinessConstants.EXPENSE_ITEM)) {
             ExpenseItemDO expenseItem = expenseItemMapper.selectById(item.getSourceExpenseItemId());
             if (expenseItem != null && expenseItem.getIsDeposit() != null
@@ -1352,6 +1365,10 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
                                                   ExpenseOrderDO order,
                                                   LocalDate costDeadlineDate,
                                                   ExpenseOrderItemVO itemVO) {
+        // 优惠抵扣费用项单独处理,已处理则无需继续
+        if (processDiscountDeductionItem(item, order, costDeadlineDate, itemVO)) {
+            return;
+        }
         if (item.getExpenseSource().equals(BusinessConstants.EXPENSE_ITEM)) {
             // 处理费用项
             ExpenseItemDO expenseItem = expenseItemMapper.selectById(item.getSourceExpenseItemId());
@@ -1368,8 +1385,14 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
 //                    // 月度账单
 //                    processMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO);
 //                }
+                // 判断费用项是否为按次(inSequence)收费(固定月金额,不乘天数、不按比例折算)
+                boolean isInSequence = false;
+                if (expenseItem.getItemId() != null) {
+                    OverheadChargeDO overheadCharge = sysOverheadChargeMapper.selectById(expenseItem.getItemId());
+                    isInSequence = overheadCharge != null && "inSequence".equals(overheadCharge.getChargeType());
+                }
                 // 月度账单
-                processMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO);
+                processMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO, isInSequence);
             }
         } else {
             // 处理长护险和其他费用
@@ -1377,6 +1400,88 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
         }
     }
 
+    /**
+     * 处理优惠抵扣费用项(expenseSource = discount_deduction)
+     * 优惠抵扣项的 sourceExpenseItemId 为优惠规则(DiscountRuleDO)的ID,
+     * 按费用截止日期折算长者实际可享受的优惠金额:
+     * 未缴费:仅享受计费开始日期到费用截止日期的优惠(负数,抵扣应收)
+     * 已缴费:收回费用截止日期之后未享受天数对应的优惠(正数,补收)
+     *
+     * @return true 表示已按优惠抵扣处理,无需再走其他费用项处理逻辑
+     */
+    private boolean processDiscountDeductionItem(ExpenseOrderItemDO item,
+                                                 ExpenseOrderDO order,
+                                                 LocalDate costDeadlineDate,
+                                                 ExpenseOrderItemVO itemVO) {
+        if (item == null || !BusinessConstants.DISCOUNT_DEDUCTION.equals(item.getExpenseSource())) {
+            return false;
+        }
+        DiscountRuleDO discountRule = discountRuleMapper.selectById(item.getSourceExpenseItemId());
+        if (discountRule == null) {
+            itemVO.setSkip(true);
+            return true;
+        }
+        // 1. 确定优惠抵扣的计费区间:优先账单明细起止时间,其次优惠规则时间,最后账单月份
+        LocalDate ruleStartDate = discountRule.getStartTime() == null ? null : discountRule.getStartTime().toLocalDate();
+        LocalDate ruleEndDate = discountRule.getEndTime() == null ? null : discountRule.getEndTime().toLocalDate();
+        LocalDate startDate = item.getStartDate() != null ? item.getStartDate() : ruleStartDate;
+        LocalDate endDate = item.getEndDate() != null ? item.getEndDate() : ruleEndDate;
+        if (startDate == null && endDate == null && order != null && StringUtils.isNotBlank(order.getBillingMonth())) {
+            YearMonth billingYearMonth = YearMonth.parse(order.getBillingMonth(), DateTimeFormatter.ofPattern("yyyy-MM"));
+            startDate = billingYearMonth.atDay(1);
+            endDate = billingYearMonth.atEndOfMonth();
+        }
+        if (startDate == null || endDate == null || endDate.isBefore(startDate)) {
+            itemVO.setSkip(true);
+            return true;
+        }
+        // 2. 优惠总金额(负数):优先取账单明细金额,其次取优惠规则的优惠金额取负
+        BigDecimal totalDiscount = item.getTotalAmount() != null && item.getTotalAmount().compareTo(BigDecimal.ZERO) != 0
+                ? item.getTotalAmount()
+                : (discountRule.getDiscountAmount() == null ? BigDecimal.ZERO : discountRule.getDiscountAmount().negate());
+        long totalDays = ChronoUnit.DAYS.between(startDate, endDate) + 1;
+        BigDecimal dayDiscount = totalDiscount.divide(BigDecimal.valueOf(totalDays), 8, RoundingMode.HALF_UP);
+
+        // 3. 按费用截止日期折算长者实际可享受的优惠天数与金额
+        LocalDate calcEndDate = endDate.isAfter(costDeadlineDate) ? costDeadlineDate : endDate;
+        long actualDays = calcEndDate.isBefore(startDate) ? 0 : ChronoUnit.DAYS.between(startDate, calcEndDate) + 1;
+        BigDecimal actualDiscount = dayDiscount.multiply(BigDecimal.valueOf(actualDays)).setScale(2, RoundingMode.HALF_UP);
+
+        boolean paid = item.getPayStatus() != null && item.getPayStatus().equals(BooleanEnum.TRUE.getValue());
+        // 未缴费:抵扣截止日期之前的优惠(负数);已缴费:收回截止日期之后的优惠(正数)
+        BigDecimal amount = paid ? totalDiscount.subtract(actualDiscount).negate().setScale(2, RoundingMode.HALF_UP)
+                : actualDiscount;
+
+        itemVO.setItemName(StringUtils.isNotBlank(item.getItemName()) ? item.getItemName() : discountRule.getRuleName());
+        itemVO.setItemId(discountRule.getId());
+        itemVO.setCount(1);
+        itemVO.setDays((int) actualDays);
+        itemVO.setIsMonthlyExpense(BooleanEnum.FALSE.getValue());
+        itemVO.setPrice(item.getPrice() != null ? item.getPrice().setScale(2, RoundingMode.HALF_UP) : actualDiscount);
+        itemVO.setAmount(amount);
+        // 4. 组装备注
+        StringBuilder remarks = new StringBuilder(StringUtils.isNotBlank(discountRule.getRuleName())
+                ? discountRule.getRuleName() : "优惠抵扣");
+        if (!paid) {
+            remarks.append(":").append(startDate).append("至").append(calcEndDate)
+                    .append("共").append(actualDays).append("天,抵扣")
+                    .append(actualDiscount.abs().setScale(2, RoundingMode.HALF_UP)).append("元");
+        } else if (actualDays >= totalDays) {
+            remarks.append(":").append(startDate).append("至").append(endDate)
+                    .append("优惠已全部享受,无需退补");
+        } else {
+            remarks.append(":").append(startDate).append("至").append(calcEndDate)
+                    .append("共").append(actualDays).append("天享受优惠,收回")
+                    .append(calcEndDate.plusDays(1)).append("至").append(endDate)
+                    .append("共").append(totalDays - actualDays).append("天的优惠")
+                    .append(amount.abs().setScale(2, RoundingMode.HALF_UP)).append("元");
+        }
+        itemVO.setRemarks(remarks.toString());
+        // 金额为0时无需展示
+        itemVO.setSkip(amount.compareTo(BigDecimal.ZERO) == 0);
+        return true;
+    }
+
     /**
      * 处理入院账单
      */
@@ -1408,14 +1513,15 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
                                     ExpenseItemDO expenseItem,
                                     ExpenseOrderDO order,
                                     LocalDate costDeadlineDate,
-                                    ExpenseOrderItemVO itemVO) {
+                                    ExpenseOrderItemVO itemVO,
+                                    boolean isInSequence) {
         itemVO.setPrice(item.getPrice());
         itemVO.setAmount(item.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
         itemVO.setCount(item.getCount());
 
         if (expenseItem.getIsMonthlyExpense().equals(BooleanEnum.TRUE.getValue())) {
             // 月度费用需要计算实际天数(按当月第一天开始计算)
-            processMonthlyExpenseForMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO);
+            processMonthlyExpenseForMonthlyBill(item, expenseItem, order, costDeadlineDate, itemVO, isInSequence);
         } else if (expenseItem.getIsFreeGift() != null && expenseItem.getIsFreeGift().equals(BooleanEnum.TRUE.getValue())) {
             // 赠送费用
             processFreeGiftExpense(expenseItem, item, itemVO);
@@ -1459,7 +1565,8 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
                                                      ExpenseItemDO expenseItem,
                                                      ExpenseOrderDO order,
                                                      LocalDate costDeadlineDate,
-                                                     ExpenseOrderItemVO itemVO) {
+                                                     ExpenseOrderItemVO itemVO,
+                                                     boolean isInSequence) {
         String orderMonth = order.getBillingMonth();
         YearMonth orderYearMonthParsed = YearMonth.from(DateTimeFormatter.ofPattern("yyyy-MM").parse(orderMonth));
         LocalDate firstDayDate = orderYearMonthParsed.atDay(1);
@@ -1476,7 +1583,7 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
         if (costDeadlineDate.getYear() == orderYearMonthParsed.getYear() &&
                 costDeadlineDate.getMonthValue() == orderYearMonthParsed.getMonthValue()) {
             // 当月退住需要计算实际天数
-            processCurrentMonthExpense(item, beginDate,endDayDate, itemVO,costDeadlineDate);
+            processCurrentMonthExpense(item, beginDate,endDayDate, itemVO,costDeadlineDate, isInSequence);
         } else if (costDeadlineDate.isBefore(beginDate)) {
             // 退住日期在账单月之前,不计算费用
             processPreMonthExpense(item, beginDate, itemVO);
@@ -1594,7 +1701,7 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
      * 处理当前月份费用(退住当月)
      */
     private void processCurrentMonthExpense(ExpenseOrderItemDO item, LocalDate firstDayDate,
-                                            LocalDate endDayDate, ExpenseOrderItemVO itemVO,LocalDate costDeadlineDate) {
+                                            LocalDate endDayDate, ExpenseOrderItemVO itemVO,LocalDate costDeadlineDate, boolean isInSequence) {
         LocalDate calEndDate = Collections.min(Arrays.asList(endDayDate, costDeadlineDate));
         // 获取计费天数
         long stayDay = firstDayDate.isAfter(calEndDate) ? 0 : ChronoUnit.DAYS.between(firstDayDate, calEndDate) + 1;
@@ -1606,6 +1713,17 @@ public class RefundSettlementOrderServiceImpl implements RefundSettlementOrderSe
         itemVO.setPrice(dayPrice.setScale(2, RoundingMode.HALF_UP));
         itemVO.setAmount(amount);
         itemVO.setRemarks("收" + firstDayDate + "到" + calEndDate + "共" + stayDay + "天的费用");
+        // 按次(inSequence)收费:固定月金额,不乘天数、不按比例折算
+        if (isInSequence) {
+            itemVO.setPrice(item.getPrice());
+            itemVO.setAmount(item.getTotalAmount().setScale(2, RoundingMode.HALF_UP));
+            // 已缴清则无退费(固定月金额,不按天数折算)
+            if (item.getPayStatus().equals(BooleanEnum.TRUE.getValue())) {
+                itemVO.setAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
+            }
+            itemVO.setRemarks("收" + firstDayDate + "到" + calEndDate + "共" + stayDay + "天的费用");
+            return;
+        }
         // 如果账单已支付,需要计算应退金额
         if (item.getPayStatus().equals(BooleanEnum.TRUE.getValue())) {
             // 修正退费天数计算逻辑:从退住日期的下一天开始计算

+ 50 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeAbstractService.java

@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.system.service.discharge;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractPageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractRespVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractSaveReqVO;
+
+/**
+ * 出院小结 Service
+ */
+public interface ElderlyDischargeAbstractService {
+
+    /**
+     * 获得出院小结分页列表
+     *
+     * @param pageReqVO 分页查询条件
+     * @return 出院小结分页列表
+     */
+    PageResult<ElderlyDischargeAbstractRespVO> getDischargeAbstractPage(ElderlyDischargeAbstractPageReqVO pageReqVO);
+
+    /**
+     * 获得出院小结详情(含用药列表)
+     *
+     * @param id 编号
+     * @return 出院小结
+     */
+    ElderlyDischargeAbstractRespVO getDischargeAbstract(Long id);
+
+    /**
+     * 创建出院小结
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createDischargeAbstract(ElderlyDischargeAbstractSaveReqVO createReqVO);
+
+    /**
+     * 更新出院小结
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateDischargeAbstract(ElderlyDischargeAbstractSaveReqVO updateReqVO);
+
+    /**
+     * 删除出院小结(同时删除用药)
+     *
+     * @param id 编号
+     */
+    void deleteDischargeAbstract(Long id);
+}

+ 214 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeAbstractServiceImpl.java

@@ -0,0 +1,214 @@
+package cn.iocoder.yudao.module.system.service.discharge;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractPageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractRespVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeAbstractSaveReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeUseMedicineRespVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyDischargeAbstractDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyDischargeUseMedicineDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyInfoDO;
+import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyDischargeAbstractMapper;
+import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyDischargeUseMedicineMapper;
+import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyInfoMapper;
+import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 出院小结 Service 实现
+ */
+@Service
+@Validated
+public class ElderlyDischargeAbstractServiceImpl implements ElderlyDischargeAbstractService {
+
+    @Resource
+    private ElderlyDischargeAbstractMapper dischargeAbstractMapper;
+    @Resource
+    private ElderlyDischargeUseMedicineMapper dischargeUseMedicineMapper;
+    @Resource
+    private ElderlyDischargeUseMedicineService dischargeUseMedicineService;
+    @Resource
+    private ElderlyInfoMapper elderlyInfoMapper;
+
+    @Override
+    public PageResult<ElderlyDischargeAbstractRespVO> getDischargeAbstractPage(ElderlyDischargeAbstractPageReqVO pageReqVO) {
+        Collection<Long> elderIds = queryElderIds(pageReqVO);
+        if (elderIds != null && elderIds.isEmpty()) {
+            return PageResult.empty();
+        }
+
+        PageResult<ElderlyDischargeAbstractDO> pageResult = dischargeAbstractMapper.selectPage(pageReqVO, elderIds);
+        List<ElderlyDischargeAbstractRespVO> list = pageResult.getList().stream()
+                .map(this::convertToResp)
+                .collect(Collectors.toList());
+        fillElderInfo(list);
+        fillMedicines(list);
+        return new PageResult<>(list, pageResult.getTotal());
+    }
+
+    @Override
+    public ElderlyDischargeAbstractRespVO getDischargeAbstract(Long id) {
+        ElderlyDischargeAbstractDO dischargeAbstract = dischargeAbstractMapper.selectById(id);
+        if (dischargeAbstract == null) {
+            return null;
+        }
+        ElderlyDischargeAbstractRespVO respVO = convertToResp(dischargeAbstract);
+        fillElderInfo(Collections.singletonList(respVO));
+        respVO.setMedicines(dischargeUseMedicineService.getMedicineList(id));
+        return respVO;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createDischargeAbstract(ElderlyDischargeAbstractSaveReqVO createReqVO) {
+        ElderlyInfoDO elder = elderlyInfoMapper.selectById(createReqVO.getElderId());
+        if (elder == null) {
+            throw new ServiceException(ErrorCodeConstants.ELDERLY_INFO_NOT_EXISTS);
+        }
+
+        ElderlyDischargeAbstractDO dischargeAbstract = BeanUtils.toBean(createReqVO, ElderlyDischargeAbstractDO.class);
+        dischargeAbstract.setId(null);
+        dischargeAbstract.setTenantId(TenantContextHolder.getTenantId());
+        dischargeAbstractMapper.insert(dischargeAbstract);
+
+        dischargeUseMedicineService.saveMedicines(dischargeAbstract.getId(), dischargeAbstract.getElderId(),
+                dischargeAbstract.getTenantId(), createReqVO.getMedicines());
+        return dischargeAbstract.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateDischargeAbstract(ElderlyDischargeAbstractSaveReqVO updateReqVO) {
+        ElderlyDischargeAbstractDO dischargeAbstract = dischargeAbstractMapper.selectById(updateReqVO.getId());
+        if (dischargeAbstract == null) {
+            throw new ServiceException(ErrorCodeConstants.ELDERLY_DISCHARGE_ABSTRACT_NOT_EXISTS);
+        }
+        if (updateReqVO.getElderId() != null && elderlyInfoMapper.selectById(updateReqVO.getElderId()) == null) {
+            throw new ServiceException(ErrorCodeConstants.ELDERLY_INFO_NOT_EXISTS);
+        }
+
+        ElderlyDischargeAbstractDO updateObj = BeanUtils.toBean(updateReqVO, ElderlyDischargeAbstractDO.class);
+        dischargeAbstractMapper.updateById(updateObj);
+
+        dischargeUseMedicineService.saveMedicines(updateObj.getId(), updateObj.getElderId(),
+                dischargeAbstract.getTenantId(), updateReqVO.getMedicines());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteDischargeAbstract(Long id) {
+        if (dischargeAbstractMapper.selectById(id) == null) {
+            throw new ServiceException(ErrorCodeConstants.ELDERLY_DISCHARGE_ABSTRACT_NOT_EXISTS);
+        }
+        dischargeUseMedicineMapper.deleteByDischargeAbstractId(id);
+        dischargeAbstractMapper.deleteById(id);
+    }
+
+    // ======================== 私有方法 ========================
+
+    /**
+     * 根据长者姓名、出院(退住)日期范围,查询符合条件的长者id集合
+     *
+     * @param pageReqVO 查询条件
+     * @return 长者id集合,无需过滤时返回 null
+     */
+    private Collection<Long> queryElderIds(ElderlyDischargeAbstractPageReqVO pageReqVO) {
+        if (StrUtil.isBlank(pageReqVO.getElderName())) {
+            return null;
+        }
+        LambdaQueryWrapperX<ElderlyInfoDO> queryWrapper = new LambdaQueryWrapperX<>();
+        queryWrapper.like(ElderlyInfoDO::getElderName, pageReqVO.getElderName());
+        List<ElderlyInfoDO> elders = elderlyInfoMapper.selectList(queryWrapper);
+        return elders.stream().map(ElderlyInfoDO::getId).filter(Objects::nonNull).collect(Collectors.toList());
+    }
+
+    private ElderlyDischargeAbstractRespVO convertToResp(ElderlyDischargeAbstractDO dischargeAbstract) {
+        return BeanUtils.toBean(dischargeAbstract, ElderlyDischargeAbstractRespVO.class);
+    }
+
+    private void fillElderInfo(List<ElderlyDischargeAbstractRespVO> list) {
+        if (CollUtil.isEmpty(list)) {
+            return;
+        }
+        Set<Long> elderIds = list.stream().map(ElderlyDischargeAbstractRespVO::getElderId)
+                .filter(Objects::nonNull).collect(Collectors.toSet());
+        if (CollUtil.isEmpty(elderIds)) {
+            return;
+        }
+        Map<Long, ElderlyInfoDO> elderMap = elderlyInfoMapper.selectBatchIds(elderIds).stream()
+                .collect(Collectors.toMap(ElderlyInfoDO::getId, Function.identity(), (a, b) -> a));
+        list.forEach(vo -> fillElderInfo(vo, elderMap.get(vo.getElderId())));
+    }
+
+    private void fillElderInfo(ElderlyDischargeAbstractRespVO respVO, ElderlyInfoDO elder) {
+        if (elder == null) {
+            return;
+        }
+        respVO.setElderName(elder.getElderName());
+        respVO.setElderSex(elder.getElderSex());
+        respVO.setElderSexName(elder.getElderSex() != null
+                ? (elder.getElderSex() == 1 ? "男" : elder.getElderSex() == 2 ? "女" : "") : "");
+        respVO.setElderAge(elder.getElderAge() != null ? elder.getElderAge()
+                : (elder.getBirthday() != null ? calcAge(elder.getBirthday()) : null));
+        respVO.setRegion(elder.getRegion());
+        respVO.setCheckInDate(toLocalDate(elder.getCheckInTime()));
+    }
+
+    private void fillMedicines(List<ElderlyDischargeAbstractRespVO> list) {
+        if (CollUtil.isEmpty(list)) {
+            return;
+        }
+        Set<Long> abstractIds = list.stream().map(ElderlyDischargeAbstractRespVO::getId)
+                .filter(Objects::nonNull).collect(Collectors.toSet());
+        if (CollUtil.isEmpty(abstractIds)) {
+            return;
+        }
+        List<ElderlyDischargeUseMedicineDO> medicines = dischargeUseMedicineMapper.selectList(
+                new LambdaQueryWrapperX<ElderlyDischargeUseMedicineDO>()
+                        .in(ElderlyDischargeUseMedicineDO::getDischargeAbstractId, abstractIds)
+                        .orderByAsc(ElderlyDischargeUseMedicineDO::getId));
+        Map<Long, List<ElderlyDischargeUseMedicineDO>> medicineMap = new HashMap<>();
+        for (ElderlyDischargeUseMedicineDO medicine : medicines) {
+            medicineMap.computeIfAbsent(medicine.getDischargeAbstractId(), k -> new ArrayList<>()).add(medicine);
+        }
+        list.forEach(vo -> vo.setMedicines(BeanUtils.toBean(
+                medicineMap.getOrDefault(vo.getId(), Collections.emptyList()),
+                ElderlyDischargeUseMedicineRespVO.class)));
+    }
+
+    private static LocalDate toLocalDate(Date date) {
+        if (date == null) {
+            return null;
+        }
+        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+    }
+
+    private static Integer calcAge(Date birthday) {
+        LocalDate birth = birthday.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+        return Period.between(birth, LocalDate.now()).getYears();
+    }
+}

+ 53 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeUseMedicineService.java

@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.system.service.discharge;
+
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeUseMedicineRespVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeUseMedicineSaveReqVO;
+
+import java.util.List;
+
+/**
+ * 出院小结用药 Service
+ */
+public interface ElderlyDischargeUseMedicineService {
+
+    /**
+     * 获得指定出院小结的用药列表
+     *
+     * @param dischargeAbstractId 出院小结id
+     * @return 用药列表
+     */
+    List<ElderlyDischargeUseMedicineRespVO> getMedicineList(Long dischargeAbstractId);
+
+    /**
+     * 创建用药
+     *
+     * @param createReqVO 创建信息
+     * @return 用药id
+     */
+    Long createMedicine(ElderlyDischargeUseMedicineSaveReqVO createReqVO);
+
+    /**
+     * 更新用药
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateMedicine(ElderlyDischargeUseMedicineSaveReqVO updateReqVO);
+
+    /**
+     * 删除用药
+     *
+     * @param id 用药id
+     */
+    void deleteMedicine(Long id);
+
+    /**
+     * 保存出院小结下的所有用药(先删除旧的,再全量新增)
+     *
+     * @param dischargeAbstractId 出院小结id
+     * @param elderId 长者id
+     * @param tenantId 机构id
+     * @param medicines 用药列表
+     */
+    void saveMedicines(Long dischargeAbstractId, Long elderId, Long tenantId,
+                       List<ElderlyDischargeUseMedicineSaveReqVO> medicines);
+}

+ 84 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/discharge/ElderlyDischargeUseMedicineServiceImpl.java

@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.module.system.service.discharge;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeUseMedicineRespVO;
+import cn.iocoder.yudao.module.system.controller.admin.discharge.vo.ElderlyDischargeUseMedicineSaveReqVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyDischargeUseMedicineDO;
+import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyDischargeUseMedicineMapper;
+import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 出院小结用药 Service 实现
+ */
+@Service
+@Validated
+public class ElderlyDischargeUseMedicineServiceImpl implements ElderlyDischargeUseMedicineService {
+
+    @Resource
+    private ElderlyDischargeUseMedicineMapper dischargeUseMedicineMapper;
+
+    @Override
+    public List<ElderlyDischargeUseMedicineRespVO> getMedicineList(Long dischargeAbstractId) {
+        if (dischargeAbstractId == null) {
+            return Collections.emptyList();
+        }
+        return BeanUtils.toBean(dischargeUseMedicineMapper.selectListByDischargeAbstractId(dischargeAbstractId),
+                ElderlyDischargeUseMedicineRespVO.class);
+    }
+
+    @Override
+    public Long createMedicine(ElderlyDischargeUseMedicineSaveReqVO createReqVO) {
+        ElderlyDischargeUseMedicineDO medicine = BeanUtils.toBean(createReqVO, ElderlyDischargeUseMedicineDO.class);
+        medicine.setId(null);
+        if (medicine.getTenantId() == null) {
+            medicine.setTenantId(TenantContextHolder.getTenantId());
+        }
+        dischargeUseMedicineMapper.insert(medicine);
+        return medicine.getId();
+    }
+
+    @Override
+    public void updateMedicine(ElderlyDischargeUseMedicineSaveReqVO updateReqVO) {
+        if (dischargeUseMedicineMapper.selectById(updateReqVO.getId()) == null) {
+            throw new ServiceException(ErrorCodeConstants.ELDERLY_DISCHARGE_USE_MEDICINE_NOT_EXISTS);
+        }
+        ElderlyDischargeUseMedicineDO medicine = BeanUtils.toBean(updateReqVO, ElderlyDischargeUseMedicineDO.class);
+        dischargeUseMedicineMapper.updateById(medicine);
+    }
+
+    @Override
+    public void deleteMedicine(Long id) {
+        if (dischargeUseMedicineMapper.selectById(id) == null) {
+            throw new ServiceException(ErrorCodeConstants.ELDERLY_DISCHARGE_USE_MEDICINE_NOT_EXISTS);
+        }
+        dischargeUseMedicineMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void saveMedicines(Long dischargeAbstractId, Long elderId, Long tenantId,
+                              List<ElderlyDischargeUseMedicineSaveReqVO> medicines) {
+        dischargeUseMedicineMapper.deleteByDischargeAbstractId(dischargeAbstractId);
+        if (CollUtil.isEmpty(medicines)) {
+            return;
+        }
+        for (ElderlyDischargeUseMedicineSaveReqVO medicineReqVO : medicines) {
+            ElderlyDischargeUseMedicineDO medicine = BeanUtils.toBean(medicineReqVO, ElderlyDischargeUseMedicineDO.class);
+            medicine.setId(null);
+            medicine.setDischargeAbstractId(dischargeAbstractId);
+            medicine.setElderId(elderId);
+            medicine.setTenantId(tenantId);
+            dischargeUseMedicineMapper.insert(medicine);
+        }
+    }
+}

+ 3 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/pay/CcbPayUtils.java

@@ -185,11 +185,13 @@ public class CcbPayUtils {
      * (变量名必须大写,TXCODE、SUB_APPID 等字段不参与摘要)
      */
     public static String generateCloseOrderMac(CcbCloseOrderRequestVO request) {
+        // 关单MAC摘要源串:MERCHANTID&POSID&BRANCHID&ORDERID&PUB(PUB取商户公钥后30位)
         String source = macSource(linkedMap(
                 "MERCHANTID", request.getMERCHANTID(),
                 "POSID", request.getPOSID(),
                 "BRANCHID", request.getBRANCHID(),
-                "ORDERID", request.getORDERID()));
+                "ORDERID", request.getORDERID(),
+                "PUB", request.getPUB()));
         log.info("关闭订单MAC摘要原串:{}", source);
         return DigestUtils.md5Hex(source.getBytes(StandardCharsets.UTF_8));
     }

+ 8 - 1
yudao-module-system/yudao-module-system-biz/src/main/resources/mapper/ElderlyBloodSugarRecordMapper.xml

@@ -32,7 +32,14 @@
     </select>
 
     <select id="selectListWithElderInfo" resultType="cn.iocoder.yudao.module.system.controller.admin.biz.vo.elderlybloodsugarrecord.ElderlyBloodSugarRecordRespVO">
-        select ebsr.id, ebsr.elder_id, ebsr.record_date, ebsr.blood_sugar, ebsr.remark, ebsr.creator,ebsr.tenant_id,
+        select ebsr.id, ebsr.elder_id, ebsr.record_date,
+               ebsr.fasting_blood_sugar, ebsr.fasting_nurse_sign,
+               ebsr.after_breakfast_blood_sugar, ebsr.after_breakfast_nurse_sign,
+               ebsr.before_dinner_blood_sugar, ebsr.before_dinner_nurse_sign,
+               ebsr.after_dinner_blood_sugar, ebsr.after_dinner_nurse_sign,
+               ebsr.before_sleep_blood_sugar, ebsr.before_sleep_nurse_sign,
+               ebsr.record_time, ebsr.blood_sugar, ebsr.nurse_sign,
+               ebsr.remark, ebsr.creator, ebsr.tenant_id,
                ei.elder_name as elderName, ei.elder_sex as elderSex,
                br.room_name as roomName, bb.bed_name as bedName
         from elderly_blood_sugar_record ebsr