docs(release-notes): 添加住院护士站划价功能说明和发版记录

- 新增住院护士站划价服务流程说明文档,详细描述了从参数预处理到结果响应的五大阶段流程
- 包含耗材类医嘱和诊疗活动类医嘱的差异化处理逻辑
- 添加完整的发版内容记录,涵盖新增菜单功能和各模块优化点
- 记录了住院相关功能的新增和门诊业务流程的修复
```
This commit is contained in:
2025-12-25 14:13:14 +08:00
parent 85fcb7c2e2
commit abc0674531
920 changed files with 107068 additions and 14495 deletions

View File

@@ -17,7 +17,6 @@ public interface IDocDefinitionAppService {
*/
R<?> addDefinition(DocDefinitionDto definitionDto);
/**
* 修改文书定义
*
@@ -39,4 +38,12 @@ public interface IDocDefinitionAppService {
R<?> getDefinitionDetailById(Long id);
DocDefinitionDto getDefinitionById(Long id);
/**
* 获取文书ID
*
* @param menuEnum 来源类型
* @return 文书列表
*/
R<?> getDefinitionId(Integer menuEnum);
}

View File

@@ -1,26 +1,26 @@
package com.openhis.web.document.appservice;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import com.core.common.core.domain.R;
import com.openhis.web.document.dto.DocRecordDto;
import com.openhis.web.document.dto.DocRecordPatientQueryParam;
import com.openhis.web.document.dto.DocRecordQueryParam;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
/**
* 文书记录 应用Service
*/
public interface IDocRecordAppService {
// /**
// * 新增记录
// *
// * @param docRecordDto 文书记录信息
// * @return
// */
// R<?> createOrEditRecord(DocRecordDto docRecordDto);
// /**
// * 新增记录
// *
// * @param docRecordDto 文书记录信息
// * @return
// */
// R<?> createOrEditRecord(DocRecordDto docRecordDto);
R<?> addRecord(DocRecordDto docRecordDto);
R<?> updateRecord(DocRecordDto docRecordDto);
@@ -28,9 +28,11 @@ public interface IDocRecordAppService {
/**
* 根据患者ID或就诊ID获取文书记录列表,只针对不需返回患者具体信息的列表,体温单除外,单独处理
*/
R<?> getRecordByEncounterIdList(DocRecordQueryParam docRecordQueryParam, Integer isPage, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request);
R<?> getRecordByEncounterIdList(DocRecordQueryParam docRecordQueryParam, Integer isPage, Integer pageNo,
Integer pageSize, String searchKey, HttpServletRequest request);
R<?> getRecordPageList(DocRecordPatientQueryParam docRecordPatientQueryParam, List<Integer> primaryMenuEnumList, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request);
R<?> getRecordPageList(DocRecordPatientQueryParam docRecordPatientQueryParam, List<Integer> primaryMenuEnumList,
Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request);
/**
* 根据文书记录ID获取文书记录信息
@@ -50,4 +52,47 @@ public interface IDocRecordAppService {
* @return
*/
R<?> deleteRecord(List<Long> ids);
/**
* 根据患者ID或就诊ID以及时间获取文书记录列表,只针对不需返回患者具体信息的列表,体温单除外,单独处理 需返回患者具体信息的列表请使用getPatientRecordList
*
* @param docRecordQueryParam 文书记录查询参数
* @param recordTime 时间
* @return R
*/
R<?> getRecordByEncounterIdAndTimeList(DocRecordQueryParam docRecordQueryParam, String recordTime);
/**
* 根据文书记录ID打印文书记录
*
* @param recordId 文书记录ID
* @return R
*/
R<?> recordPrint(Long recordId);
/**
* 新增或编辑记录
*
* @param docRecordDto 文书记录信息
* @return R
*/
R<?> saveOrUpdateRecord(DocRecordDto docRecordDto);
/**
* 根据病历ID、病人ID和温度单ID获取温度单信息
*
* @param docRecordQueryParam 文书记录查询参数
* @return 温度单信息
*/
R<?> temperatureChart(DocRecordQueryParam docRecordQueryParam);
/**
* 护理记录总结
*
* @param docRecordQueryParam 文书记录查询参数
* @param startTime 开始时间
* @param endTime 结束时间
* @return 护理记录总结
*/
R<?> summaryNursingRecords(DocRecordQueryParam docRecordQueryParam, String startTime, String endTime);
}

View File

@@ -1,20 +1,18 @@
package com.openhis.web.document.appservice;
import com.core.common.core.domain.R;
import com.openhis.document.domain.DocStatistics;
import com.openhis.web.document.dto.DocRecordQueryParam;
import com.openhis.web.document.dto.DocStatisticsDto;
import com.openhis.web.document.dto.DocStatisticsQueryParam;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import com.core.common.core.domain.R;
import com.openhis.web.document.dto.DocStatisticsDto;
import com.openhis.web.document.dto.DocStatisticsQueryParam;
/**
* 文档模板 业务接口
*/
public interface IDocStatisticsAppService {
public R<?> createOrUpdte(List<DocStatisticsDto> docStatisticsList);
public R<?> delete(List<Long> ids);
@@ -23,6 +21,23 @@ public interface IDocStatisticsAppService {
public R<?> queryByEncounterId(Long encounterId);
R<?> getStatisticsList(DocStatisticsQueryParam queryParam, Integer isPage, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request);
R<?> getStatisticsList(DocStatisticsQueryParam queryParam, Integer isPage, Integer pageNo, Integer pageSize,
String searchKey, HttpServletRequest request);
/**
* 根据病人ID和病例ID获取温度单信息
*
* @param encounterId 病例ID
* @param patientId 病人ID
* @param tempId 体温单ID
* @return 体温单信息
*/
List<DocStatisticsDto> getStatisticsListByEncounterIdAndPatientId(Long encounterId, Long patientId, Long tempId);
/**
* 保存/更新入院体征
*
* @param docStatisticsDtoList 入院体征list
*/
void saveOrUpdateAdmissionSigns(List<DocStatisticsDto> docStatisticsDtoList);
}

View File

@@ -1,5 +1,13 @@
package com.openhis.web.document.appservice.impl;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.core.common.core.domain.R;
import com.core.common.utils.AssignSeqUtil;
@@ -14,18 +22,13 @@ import com.openhis.document.service.IDocDefinitionService;
import com.openhis.web.document.appservice.IDocDefinitionAppService;
import com.openhis.web.document.appservice.IDocDefinitionOrganizationAppService;
import com.openhis.web.document.dto.DirectoryNode;
import com.openhis.web.document.dto.DocDefinitionOrganizationDto;
import com.openhis.web.document.dto.DocDefinitionDto;
import com.openhis.web.document.dto.DocDefinitionOrganizationDto;
import com.openhis.web.document.dto.DocDefinitonParam;
import com.openhis.web.document.mapper.DocDefinitionAppMapper;
import com.openhis.web.document.util.DocumentDirectoryProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@@ -39,7 +42,6 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
@Resource
private DocDefinitionAppMapper docDefinitionAppMapper;
@Autowired(required = false)
private AssignSeqUtil assignSeqUtil;
@@ -58,18 +60,18 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
if (definitionDto.getVueRouter() == null || definitionDto.getVueRouter().trim().isEmpty()) {
return R.fail("新增文书定义失败:文书路由不能为空(或不能为空白字符)");
}
if (definitionDto.getUseRangeEnum() != null && DocUseRangeEnum.DEPT_USE.getValue().equals(definitionDto.getUseRangeEnum()) && definitionDto.getOrganizationIds() == null) {
if (definitionDto.getUseRangeEnum() != null
&& DocUseRangeEnum.DEPT_USE.getValue().equals(definitionDto.getUseRangeEnum())
&& definitionDto.getOrganizationIds() == null) {
return R.fail("新增文书定义失败:科室分配时,科室不能为空");
}
// 2. 校验"名称+版本号"唯一性(避免重复创建)
long duplicateCount = docDefinitionService.lambdaQuery()
.eq(DocDefinition::getName, definitionDto.getName().trim())
.eq(DocDefinition::getVersion, definitionDto.getVersion().trim())
.count();
long duplicateCount =
docDefinitionService.lambdaQuery().eq(DocDefinition::getName, definitionDto.getName().trim())
.eq(DocDefinition::getVersion, definitionDto.getVersion().trim()).count();
if (duplicateCount > 0) {
log.warn("新增文书定义失败:名称={}、版本号={}的文书已存在",
definitionDto.getName(), definitionDto.getVersion());
log.warn("新增文书定义失败:名称={}、版本号={}的文书已存在", definitionDto.getName(), definitionDto.getVersion());
return R.fail("新增文书定义失败:相同名称和版本号的文书已存在,请修改后重试");
}
@@ -116,15 +118,14 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
// 5. 保存文书定义
boolean saveSuccess = docDefinitionService.save(docDefinition);
if (!saveSuccess) {
log.error("新增文书定义失败:数据库保存异常(名称={}, 版本号={}, 业务编号={}",
definitionDto.getName(), definitionDto.getVersion(), busNo);
log.error("新增文书定义失败:数据库保存异常(名称={}, 版本号={}, 业务编号={}", definitionDto.getName(), definitionDto.getVersion(),
busNo);
return R.fail("新增文书定义失败:数据库保存操作异常");
}
// 6. 科室分配(仅当使用范围为"科室级"时执行)
if (DocUseRangeEnum.DEPT_USE.getValue().equals(definitionDto.getUseRangeEnum())
&& definitionDto.getOrganizationIds() != null
&& !definitionDto.getOrganizationIds().isEmpty()) {
&& definitionDto.getOrganizationIds() != null && !definitionDto.getOrganizationIds().isEmpty()) {
DocDefinitionOrganizationDto orgDto = new DocDefinitionOrganizationDto();
orgDto.setBusNo(busNo);
orgDto.setDefinitionId(docDefinition.getId());
@@ -136,7 +137,6 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
return R.ok("新增文书定义成功");
}
/**
* 修改文书定义(版本号冲突时自动新增,含科室分配更新)
*/
@@ -158,7 +158,9 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
if (definitionDto.getBusNo() == null || definitionDto.getBusNo().trim().isEmpty()) {
return R.fail("修改文书定义失败:文书业务编号不能为空(或不能为空白字符)");
}
if (definitionDto.getUseRangeEnum() != null && DocUseRangeEnum.DEPT_USE.getValue().equals(definitionDto.getUseRangeEnum()) && definitionDto.getOrganizationIds() == null) {
if (definitionDto.getUseRangeEnum() != null
&& DocUseRangeEnum.DEPT_USE.getValue().equals(definitionDto.getUseRangeEnum())
&& definitionDto.getOrganizationIds() == null) {
return R.fail("修改文书定义失败:科室分配时,科室不能为空");
}
// 2. 校验文书是否存在
@@ -170,8 +172,7 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
// 3. 版本号冲突处理(版本号不一致时,转为新增逻辑)
if (!existingDoc.getVersion().trim().equals(definitionDto.getVersion().trim())) {
log.info("修改文书定义:版本号冲突(原版本={}, 新版本={}),自动执行新增逻辑",
existingDoc.getVersion(), definitionDto.getVersion());
log.info("修改文书定义:版本号冲突(原版本={}, 新版本={}),自动执行新增逻辑", existingDoc.getVersion(), definitionDto.getVersion());
// 新增前清空ID避免覆盖原有记录
definitionDto.setId(null);
return addDefinition(definitionDto);
@@ -211,8 +212,7 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
// 6. 执行修改
boolean updateSuccess = docDefinitionService.updateById(existingDoc);
if (!updateSuccess) {
log.error("修改文书定义失败数据库更新异常ID={}, 业务编号={}",
definitionDto.getId(), definitionDto.getBusNo());
log.error("修改文书定义失败数据库更新异常ID={}, 业务编号={}", definitionDto.getId(), definitionDto.getBusNo());
return R.fail("修改文书定义失败:数据库更新操作异常");
} else {
// 7. 更新科室分配(仅当使用范围为"科室级"时执行)
@@ -255,20 +255,14 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
}
// 2. 数据库查询文书定义列表
List<DocDefinitionDto> docList = docDefinitionAppMapper.getDefinationList(
param.getUseRanges(),
param.getOrganizationId(),
hospitalId,
param.getName(),
param.getPrimaryMenuEnum()
);
List<DocDefinitionDto> docList = docDefinitionAppMapper.getDefinationList(param.getUseRanges(),
param.getOrganizationId(), hospitalId, param.getName(), param.getPrimaryMenuEnum());
// 3. 构建树形结构(空列表时返回空树,避免空指针)
List<DirectoryNode> treeNodes = new ArrayList<>();
if (docList != null && !docList.isEmpty()) {
treeNodes = DocumentDirectoryProcessor.buildDocumentDirectory(docList);
log.info("获取文书定义树形列表成功医院ID={}, 文书数量={}, 树形节点数量={}",
hospitalId, docList.size(), treeNodes.size());
log.info("获取文书定义树形列表成功医院ID={}, 文书数量={}, 树形节点数量={}", hospitalId, docList.size(), treeNodes.size());
} else {
log.info("获取文书定义树形列表成功医院ID={}, 暂无匹配的文书定义", hospitalId);
}
@@ -293,8 +287,7 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
return R.fail("获取文书定义详情失败:未查询到对应的文书定义信息");
}
log.info("获取文书定义详情成功ID={}, 名称={}, 业务编号={}",
id, docDto.getName(), docDto.getBusNo());
log.info("获取文书定义详情成功ID={}, 名称={}, 业务编号={}", id, docDto.getName(), docDto.getBusNo());
return R.ok(docDto, "获取文书定义详情成功");
}
@@ -311,8 +304,8 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
// 2. 查询该文书已分配的科室ID列表
LambdaQueryWrapper<DocDefinitionOrganization> orgWrapper = new LambdaQueryWrapper<>();
orgWrapper.eq(DocDefinitionOrganization::getDefinitionId, id)
.eq(DocDefinitionOrganization::getBusNo, doc.getBusNo());
orgWrapper.eq(DocDefinitionOrganization::getDefinitionId, id).eq(DocDefinitionOrganization::getBusNo,
doc.getBusNo());
List<DocDefinitionOrganization> orgList = docDefinitionOrganizationService.list(orgWrapper);
// 3. 提取科室ID并封装DTO
@@ -327,4 +320,29 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
return docDto;
}
/**
* 获取文书ID
*
* @param menuEnum 来源类型DocDefinitionEnum
* @return 文书列表
*/
@Override
public R<?> getDefinitionId(Integer menuEnum) {
// 1. 获取当前登录用户的医院ID避免跨医院查询
Long hospitalId = SecurityUtils.getLoginUser().getHospitalId();
if (hospitalId == null) {
log.warn("获取文书定义树形列表失败当前登录用户未关联医院ID");
return R.fail("获取文书定义树形列表失败:当前用户未关联医院,请重新登录");
}
if (menuEnum == null) {
return R.fail("来源类型不能为空");
}
// 2. 数据库查询文书定义列表
LambdaQueryWrapper<DocDefinition> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DocDefinition::getHospitalId, hospitalId);
queryWrapper.eq(DocDefinition::getPrimaryMenuEnum, menuEnum);
List<DocDefinition> docList = docDefinitionService.list(queryWrapper);
return R.ok(docList);
}
}

View File

@@ -1,7 +1,27 @@
package com.openhis.web.document.appservice.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -10,16 +30,15 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.utils.AgeCalculatorUtil;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils;
import com.core.common.utils.bean.BeanUtils;
import com.openhis.common.enums.AdministrativeGender;
import com.openhis.common.enums.DocStatusEnum;
import com.openhis.common.enums.DocTypeEnum;
import com.openhis.common.enums.EncounterClass;
import com.openhis.common.utils.HisPageUtils;
import com.openhis.common.enums.*;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.document.domain.DocRecord;
import com.openhis.document.domain.DocStatistics;
import com.openhis.document.mapper.DocRecordMapper;
import com.openhis.document.service.IDocRecordService;
import com.openhis.document.service.IDocStatisticsService;
import com.openhis.web.document.appservice.IDocDefinitionAppService;
import com.openhis.web.document.appservice.IDocRecordAppService;
import com.openhis.web.document.appservice.IDocStatisticsAppService;
@@ -27,23 +46,10 @@ import com.openhis.web.document.appservice.IDocStatisticsDefinitionAppService;
import com.openhis.web.document.dto.*;
import com.openhis.web.document.mapper.DocRecordAppMapper;
import com.openhis.web.document.util.ConvertToDocStatistics;
import com.openhis.web.document.util.OperationDayCalculator;
import com.openhis.web.document.util.PermissionProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@@ -62,11 +68,12 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
@Resource
private IDocStatisticsAppService docStatisticsAppService;
@Resource
private IDocStatisticsService docStatisticsService;
@Resource
ConvertToDocStatistics convertToDocStatistics;
@Autowired
private JdbcTemplate jdbcTemplate; // 使用 JdbcTemplate 执行 SQL
/**
* 插入单条文档操作日志
*
@@ -80,29 +87,19 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
}
try {
String sql = "INSERT INTO doc_record_log(" +
"record_id, definition_id, definition_bus_no, content_json, " +
"status_enum, organization_id, encounter_id, patient_id, record_time, " +
"create_by, create_time, update_by, update_time, delete_flag, tenant_id" +
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
String sql = "INSERT INTO doc_record_log(" + "record_id, definition_id, definition_bus_no, content_json, "
+ "status_enum, organization_id, encounter_id, patient_id, record_time, "
+ "create_by, create_time, update_by, update_time, delete_flag, tenant_id"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
Object[] params = {
docRecord.getId(),
docRecord.getDefinitionId(),
docRecord.getDefinitionBusNo(),
docRecord.getContentJson(),
docRecord.getStatusEnum(),
docRecord.getOrganizationId(),
docRecord.getEncounterId(),
docRecord.getPatientId(),
new Timestamp(docRecord.getRecordTime().getTime()),
SecurityUtils.getUsername(),
new Timestamp(new Date().getTime()),
null, // update_by
null, // update_time
0, // delete_flag未删除
docRecord.getTenantId()
};
Object[] params = {docRecord.getId(), docRecord.getDefinitionId(), docRecord.getDefinitionBusNo(),
docRecord.getContentJson(), docRecord.getStatusEnum(), docRecord.getOrganizationId(),
docRecord.getEncounterId(), docRecord.getPatientId(),
new Timestamp(docRecord.getRecordTime().getTime()), SecurityUtils.getUsername(),
new Timestamp(new Date().getTime()), null, // update_by
null, // update_time
0, // delete_flag未删除
docRecord.getTenantId()};
int rowsAffected = jdbcTemplate.update(sql, params);
return rowsAffected > 0;
@@ -125,11 +122,10 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
}
try {
String sql = "INSERT INTO doc_record_log(" +
"record_id, definition_id, definition_bus_no, content_json, " +
"status_enum, organization_id, encounter_id, patient_id, record_time, " +
"create_by, create_time, update_by, update_time, delete_flag, tenant_id" +
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
String sql = "INSERT INTO doc_record_log(" + "record_id, definition_id, definition_bus_no, content_json, "
+ "status_enum, organization_id, encounter_id, patient_id, record_time, "
+ "create_by, create_time, update_by, update_time, delete_flag, tenant_id"
+ ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
int[] results = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
@@ -210,7 +206,6 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
return "";
}
/**
* 文书统计 - 新增、编辑记录
*
@@ -246,7 +241,8 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
docRecord.setDefinitionId(docRecordDto.getDefinitionId());
docRecord.setDefinitionBusNo(docRecordDto.getDefinitionBusNo());
docRecord.setContentJson(docRecordDto.getContentJson());
docRecord.setStatusEnum(docRecordDto.getStatusEnum() == null ? DocStatusEnum.DRAFT.getValue() : docRecordDto.getStatusEnum());
docRecord.setStatusEnum(
docRecordDto.getStatusEnum() == null ? DocStatusEnum.DRAFT.getValue() : docRecordDto.getStatusEnum());
docRecord.setOrganizationId(SecurityUtils.getLoginUser().getOrgId());
docRecord.setEncounterId(docRecordDto.getEncounterId());
docRecord.setPatientId(docRecordDto.getPatientId());
@@ -269,9 +265,16 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
* @return 返回结果 R对象
*/
public R<?> updateRecord(DocRecordDto docRecordDto) {
String checkMsg = initRecordCheck(docRecordDto);
if (!checkMsg.isEmpty()) {
return R.fail(checkMsg);
Integer printCount = docRecordDto.getPrintCount();
if (printCount > 0) {
return R.fail("已打印的文书记录,禁止修改");
}
List<String> sourceCodes = Arrays.stream(DocDefinitionEnum.values()).map(DocDefinitionEnum::getValue).toList();
if (!sourceCodes.contains(StringUtils.isBlank(docRecordDto.getSource()) ? "" : docRecordDto.getSource())) {
String checkMsg = initRecordCheck(docRecordDto);
if (!checkMsg.isEmpty()) {
return R.fail(checkMsg);
}
}
// 1. 查询原文书记录
DocRecord docRecord = docRecordService.getById(docRecordDto.getId());
@@ -312,7 +315,8 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
* 根据患者ID/就诊ID获取文书记录列表支持分页
*/
@Override
public R<?> getRecordByEncounterIdList(DocRecordQueryParam docRecordQueryParam, Integer isPage, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request) {
public R<?> getRecordByEncounterIdList(DocRecordQueryParam docRecordQueryParam, Integer isPage, Integer pageNo,
Integer pageSize, String searchKey, HttpServletRequest request) {
// 1. 参数校验
if (docRecordQueryParam == null) {
return R.fail("患者文书记录:查询参数不能为空");
@@ -326,10 +330,10 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
// 2. 分页查询逻辑
if (isPage != null && isPage == 1) {
QueryWrapper<DocRecordDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
docRecordQueryParam, null, null, request);
IPage<DocRecordDto> pageResult = docRecordAppMapper.getRecordPageListByEncounterId(
new Page<>(pageNo, pageSize), queryWrapper);
QueryWrapper<DocRecordDto> queryWrapper =
HisQueryUtils.buildQueryWrapper(docRecordQueryParam, null, null, request);
IPage<DocRecordDto> pageResult =
docRecordAppMapper.getRecordPageListByEncounterId(new Page<>(pageNo, pageSize), queryWrapper);
return R.ok(pageResult, "获取患者文书记录列表成功");
}
// 3. 不分页查询逻辑
@@ -350,15 +354,16 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
queryWrapper.eq(DocRecord::getOrganizationId, docRecordQueryParam.getOrganizationId());
}
DocDefinitionDto docDefinitionDto = docDefinitionAppService.getDefinitionById(docRecordQueryParam.getDefinitionId());
DocDefinitionDto docDefinitionDto =
docDefinitionAppService.getDefinitionById(docRecordQueryParam.getDefinitionId());
// 实体转DTO
List<DocRecordDto> dtoList = new ArrayList<>();
List<DocRecord> recordList = docRecordService.list(queryWrapper);
for (DocRecord record : recordList) {
DocRecordDto dto = new DocRecordDto();
BeanUtils.copyProperties(record, dto);
dto.setName(docDefinitionDto!=null?docDefinitionDto.getName():"");
dto.setVersion(docDefinitionDto!=null?docDefinitionDto.getVersion():"");
dto.setName(docDefinitionDto != null ? docDefinitionDto.getName() : "");
dto.setVersion(docDefinitionDto != null ? docDefinitionDto.getVersion() : "");
dtoList.add(dto);
}
return R.ok(dtoList, "获取患者文书记录列表成功");
@@ -369,18 +374,20 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
* 查询患者文书记录列表(携带患者信息,支持分页)
*/
@Override
public R<?> getRecordPageList(DocRecordPatientQueryParam docRecordPatientQueryParam, List<Integer> primaryMenuEnumList, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request) {
public R<?> getRecordPageList(DocRecordPatientQueryParam docRecordPatientQueryParam,
List<Integer> primaryMenuEnumList, Integer pageNo, Integer pageSize, String searchKey,
HttpServletRequest request) {
if (docRecordPatientQueryParam == null) {
return R.fail("文书记录:查询参数不能为空");
}
// 构建查询条件(支持患者姓名搜索)
QueryWrapper<DocRecordPatientDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
docRecordPatientQueryParam, searchKey, new HashSet<>(Arrays.asList("patient_name", "bus_no")), request);
QueryWrapper<DocRecordPatientDto> queryWrapper = HisQueryUtils.buildQueryWrapper(docRecordPatientQueryParam,
searchKey, new HashSet<>(Arrays.asList("patient_name", "bus_no")), request);
// 分页查询
IPage<DocRecordPatientDto> pageResult = docRecordAppMapper.getRecordPageList(
new Page<>(pageNo, pageSize), primaryMenuEnumList, queryWrapper);
IPage<DocRecordPatientDto> pageResult =
docRecordAppMapper.getRecordPageList(new Page<>(pageNo, pageSize), primaryMenuEnumList, queryWrapper);
// 处理返回结果:枚举值转中文描述、计算患者年龄
if (pageResult != null && !pageResult.getRecords().isEmpty()) {
@@ -477,6 +484,7 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
// 4. 执行删除
boolean deleteSuccess = docRecordService.removeByIds(ids);
if (deleteSuccess) {
docStatisticsAppService.delete(ids);
// 5. 批量插入删除日志
boolean logSuccess = insertBatchLog(logRecordList);
if (!logSuccess) {
@@ -487,4 +495,538 @@ public class DocRecordAppServiceImpl implements IDocRecordAppService {
return R.fail("删除患者文书记录失败");
}
}
/**
* 根据患者ID或就诊ID以及时间获取文书记录列表,只针对不需返回患者具体信息的列表,体温单除外,单独处理 需返回患者具体信息的列表请使用getPatientRecordList
*
* @param docRecordQueryParam 文书记录查询参数
* @param recordTime 时间
* @return R
*/
@Override
public R<?> getRecordByEncounterIdAndTimeList(DocRecordQueryParam docRecordQueryParam, String recordTime) {
// 参数校验
if (docRecordQueryParam == null) {
return R.fail("患者文书记录:查询参数不能为空");
}
if (docRecordQueryParam.getPatientId() == null && docRecordQueryParam.getEncounterId() == null) {
return R.fail("患者文书记录患者ID和就诊ID不能同时为空");
}
if (docRecordQueryParam.getDefinitionId() == null) {
return R.fail("患者文书记录文书类型定义ID不能为空");
}
// 未传时间,按照当前时间
Date recordDate = StringUtils.isBlank(recordTime) ? new Date()
: Date.from(LocalDate.parse(recordTime, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay()
.atZone(ZoneId.systemDefault()).toInstant());
// 患者ID查询条件
LambdaQueryWrapper<DocRecord> queryWrapper = new LambdaQueryWrapper<>();
if (docRecordQueryParam.getPatientId() != null) {
queryWrapper.eq(DocRecord::getPatientId, docRecordQueryParam.getPatientId())
.eq(DocRecord::getEncounterId, docRecordQueryParam.getEncounterId())
.ge(DocRecord::getRecordTime, recordDate).lt(DocRecord::getRecordTime,
Date.from(recordDate.toInstant().atZone(ZoneId.systemDefault()).plusDays(1).toInstant()));
}
// 文书类型ID必传
queryWrapper.eq(DocRecord::getDefinitionId, docRecordQueryParam.getDefinitionId());
// 就诊ID查询条件修复原逻辑原代码字段匹配错误用EncounterId匹配DefinitionBusNo
// if (docRecordQueryParam.getEncounterId() != null) {
// queryWrapper.eq(DocRecord::getEncounterId, docRecordQueryParam.getEncounterId());
// }
// 科室ID查询条件修复原逻辑原代码字段匹配错误用OrganizationId匹配EncounterId
if (docRecordQueryParam.getOrganizationId() != null) {
queryWrapper.eq(DocRecord::getOrganizationId, docRecordQueryParam.getOrganizationId());
}
DocDefinitionDto docDefinitionDto =
docDefinitionAppService.getDefinitionById(docRecordQueryParam.getDefinitionId());
// 实体转DTO
List<DocRecordDto> dtoList = new ArrayList<>();
List<DocRecord> recordList = docRecordService.list(queryWrapper);
for (DocRecord record : recordList) {
DocRecordDto dto = new DocRecordDto();
BeanUtils.copyProperties(record, dto);
dto.setName(docDefinitionDto != null ? docDefinitionDto.getName() : "");
dto.setVersion(docDefinitionDto != null ? docDefinitionDto.getVersion() : "");
dtoList.add(dto);
}
return R.ok(dtoList, "获取患者文书记录列表成功");
}
/**
* 根据文书记录ID打印文书记录
*
* @param recordId 文书记录ID
* @return R
*/
@Override
public R<?> recordPrint(Long recordId) {
LambdaQueryWrapper<DocRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DocRecord::getId, recordId);
DocRecord record = docRecordService.getOne(wrapper);
if (record != null) {
record.setPrintCount(record.getPrintCount() + 1);
docRecordService.updateById(record);
}
return R.ok(record);
}
/**
* 新增或编辑记录根据id是否为空判断新增或编辑
*
* @param docRecordDto 文书记录信息
* @return R
*/
@Override
public R<?> saveOrUpdateRecord(DocRecordDto docRecordDto) {
Long id = docRecordDto.getId();
if (id == null) {
return addRecord(docRecordDto);
} else {
return updateRecord(docRecordDto);
}
}
/**
* 根据病历ID、病人ID和体温单ID获取温度单信息
*
* @param docRecordQueryParam 文书记录查询参数
* @return 体温单信息
*/
@Override
public R<?> temperatureChart(DocRecordQueryParam docRecordQueryParam) {
// 参数校验
if (docRecordQueryParam == null) {
return R.fail("患者文书记录:查询参数不能为空");
}
Long encounterId = docRecordQueryParam.getEncounterId();
Long patientId = docRecordQueryParam.getPatientId();
Long tempId = docRecordQueryParam.getDefinitionId();
if (encounterId == null) {
return R.fail("患者文书记录就诊ID不能为空");
}
if (patientId == null) {
return R.fail("患者文书记录患者ID不能为空");
}
if (tempId == null) {
return R.fail("患者文书记录文书类型定义ID不能为空");
}
// 根据病历ID和患者ID获取住院、出院时间
Map<String, Date> dateMap = getDateByEncounterId(encounterId, patientId);
Date hospDate = dateMap.get("hospDate");
Date outDate = dateMap.get("outDate");
// 所有的chartsSmalls
List<TrendChartsSmall> allTrendChartsSmalls = new ArrayList<>();
// 获取体温单信息
List<DocStatisticsDto> statisticsList =
docStatisticsAppService.getStatisticsListByEncounterIdAndPatientId(encounterId, patientId, tempId);
// 过滤出血压
List<DocStatisticsDto> bloodPressureList = statisticsList.stream().filter(item -> {
String code = item.getStatisticDefinitionCode();
if (code != null) {
return TemperatureChartEnum.BLOOD_PRESSURE.getCode().equals(code);
}
return false;
}).toList();
// 移除血压
statisticsList.removeAll(bloodPressureList);
// 获取每一天 上下午 各最新一条的数据
bloodPressureList = getLatestRecordsPerPeriod(bloodPressureList);
// 添加血压list
statisticsList.addAll(bloodPressureList);
// 先排序 按时间分组,确保同一组的数据应该在一组
statisticsList.sort(Comparator.comparing(DocStatisticsDto::getRecordTime));
// 转换为TrendChartsSmall集合
for (DocStatisticsDto docStatisticsDto : statisticsList) {
// typeCode
String typeCode = docStatisticsDto.getStatisticDefinitionCode();
// typeValue
String typeValue = docStatisticsDto.getValue();
// 如果typeValue为空数据不添加进list
if (StringUtils.isBlank(typeValue)) {
continue;
}
// date
Date date = docStatisticsDto.getRecordTime();
// times
String times = formatDate(date, "HH:mm:ss");
// weekNo
Integer weekNo = calculateWeek(hospDate, date);
// orderByDateTimes 暂时不用
String orderByDateTimes = null;
// id 暂时不用
String id = null;
// collectionMode 体温(003)、脉搏(002)、呼吸(001)、疼痛等级(016)为 1其余为null
Integer collectionMode = null;
List<String> temperatureChartCodes =
Arrays.asList(TemperatureChartEnum.TEMPERATURE.getCode(), TemperatureChartEnum.PULSE.getCode(),
TemperatureChartEnum.BREATH.getCode(), TemperatureChartEnum.PAIN_SCORE.getCode());
if (temperatureChartCodes.contains(typeCode)) {
collectionMode = 1;
if (isChineseWithSpace(typeValue)) {
typeCode = TemperatureChartEnum.TEMPERATURE_CHINESE.getCode();
}
}
TrendChartsSmall trendChartsSmall =
new TrendChartsSmall(collectionMode, date, times, typeCode, typeValue, weekNo, orderByDateTimes, id);
allTrendChartsSmalls.add(trendChartsSmall);
}
// 体温单信息
TrendChartsOutput trendChartsOutput = returnTrendChartsOutput(allTrendChartsSmalls, encounterId, hospDate);
trendChartsOutput.setOutDate(outDate);
// 术后天数
calculatePostopDay(trendChartsOutput, getPostopDate(encounterId, patientId));
return R.ok(trendChartsOutput);
}
/**
* 护理记录总结
*
* @param docRecordQueryParam 文书记录查询参数
* @param startTime 开始时间
* @param endTime 结束时间
* @return 护理记录总结
*/
@Override
public R<?> summaryNursingRecords(DocRecordQueryParam docRecordQueryParam, String startTime, String endTime) {
// 参数校验
if (docRecordQueryParam == null) {
return R.fail("患者文书记录:查询参数不能为空");
}
Long encounterId = docRecordQueryParam.getEncounterId();
Long patientId = docRecordQueryParam.getPatientId();
Long definitionId = docRecordQueryParam.getDefinitionId();
if (encounterId == null) {
return R.fail("患者文书记录就诊ID不能为空");
}
if (patientId == null) {
return R.fail("患者文书记录患者ID不能为空");
}
if (definitionId == null) {
return R.fail("患者文书记录文书类型定义ID不能为空");
}
List<DocRecordDto> docRecordList = new ArrayList<>();
// 查询所有
LambdaQueryWrapper<DocRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DocRecord::getEncounterId, encounterId);
wrapper.eq(DocRecord::getPatientId, patientId);
wrapper.eq(DocRecord::getDefinitionId, definitionId);
// 有时间就按时间条件查询
if (StringUtils.isNotBlank(startTime)) {
wrapper.ge(DocRecord::getRecordTime, LocalDate.parse(startTime));
}
if (StringUtils.isNotBlank(endTime)) {
wrapper.lt(StringUtils.isNotBlank(endTime), DocRecord::getRecordTime, LocalDate.parse(endTime).plusDays(1));
}
// 时间最新数据在前面
wrapper.orderByDesc(DocRecord::getRecordTime);
List<DocRecord> recordList = docRecordService.list(wrapper);
recordList.forEach(record -> {
DocRecordDto dto = new DocRecordDto();
BeanUtils.copyProperties(record, dto);
docRecordList.add(dto);
});
// 有时间则会总结出入量
List<Long> recordIds = docRecordList.stream().map(DocRecordDto::getId).toList();
if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime) && !recordIds.isEmpty()) {
// 查询出入量
LambdaQueryWrapper<DocStatistics> statisticsWrapper = new LambdaQueryWrapper<>();
if (docRecordQueryParam.getDefinitionId() != null) {
statisticsWrapper.eq(DocStatistics::getDefinitionId, docRecordQueryParam.getDefinitionId());
}
statisticsWrapper.eq(DocStatistics::getSource, DocDefinitionEnum.NURSING_RECORD.getValue());
statisticsWrapper.in(DocStatistics::getRecordId, recordIds);
statisticsWrapper.in(DocStatistics::getStatisticDefinitionCode,
Arrays.asList(TemperatureChartEnum.OUTPUT.getTypeCode(), TemperatureChartEnum.INPUT.getTypeCode()));
// 获取出、入量数据
List<DocStatistics> nursingRecordList = docStatisticsService.list(statisticsWrapper);
if (!nursingRecordList.isEmpty()) {
// 总出量
BigDecimal totalOutput = BigDecimal.ZERO;
// 总入量
BigDecimal totalInput = BigDecimal.ZERO;
for (DocStatistics statistics : nursingRecordList) {
BigDecimal value = new BigDecimal(statistics.getValue());
if (statistics.getStatisticDefinitionCode().equals(TemperatureChartEnum.OUTPUT.getTypeCode())) {
// 计算总出量
totalOutput = totalOutput.add(value);
} else {
// 计算总入量
totalInput = totalInput.add(value);
}
}
// 将总出、入量添加要返回的数据中
DocRecordDto dto = new DocRecordDto();
HashMap<String, String> map = new HashMap<>();
map.put(TemperatureChartEnum.OUTPUT.getTypeCode(), totalOutput.toString());
map.put(TemperatureChartEnum.INPUT.getTypeCode(), totalInput.toString());
dto.setContentJson(JSONObject.toJSONString(map));
docRecordList.add(dto);
}
}
return R.ok(docRecordList);
}
/**
* 返回封装好的体温单
*
* @param allTrendChartsSmalls 所有的chartsSmalls
* @return 封装好的体温单
*/
private TrendChartsOutput returnTrendChartsOutput(List<TrendChartsSmall> allTrendChartsSmalls, Long encounterId,
Date hospDate) {
allTrendChartsSmalls.sort(Comparator.comparing(TrendChartsSmall::getDate));
// temperaturePulses 集合
List<TrendChartsBig> temperaturePulses = new ArrayList<>();
// temperaturePulses中的chartsSmalls集合
List<TrendChartsSmall> bigSmalls = null;
// 获取others数据
List<TrendChartsSmall> others = new ArrayList<>(allTrendChartsSmalls.stream()
.filter(item -> (item.getCollectionMode() == null || item.getCollectionMode() != 1)).toList());
// 尿量、大便次数、小便次数、入量、其他 记录的是前一天的,时间要减一天
// isOneDayInAdvance(others);
// 移除others数据
allTrendChartsSmalls.removeAll(others);
// 根据病历ID获取入科时间
Date admissionDate = getDateByEncounterId(encounterId);
// 为入科 TrendChartsSmall 准备数据
int weekNo = calculateWeek(hospDate, admissionDate);
// 入科 TrendChartsSmall
TrendChartsSmall admissionSmall = new TrendChartsSmall(null, admissionDate,
formatDate(admissionDate, "HH:mm:ss"), TemperatureChartEnum.WARD_ADMISSION.getCode(),
TemperatureChartEnum.WARD_ADMISSION.getDescription() + "," + formatDate(admissionDate, "HH:mm"), weekNo,
formatDate(admissionDate, "yyyy-MM-dd HH:mm:ss"), "");
ArrayList<TrendChartsSmall> admissionBig = new ArrayList<>();
admissionBig.add(admissionSmall);
// 先添加入科
temperaturePulses.add(new TrendChartsBig(admissionBig, weekNo));
// 根据记录时间分组
long compareTime = 0L;
for (TrendChartsSmall trendChartsSmall : allTrendChartsSmalls) {
// 获取当前项的时间[date]
long smallTime = trendChartsSmall.getDate().getTime();
// 时间相同的分一组
if (compareTime == smallTime && bigSmalls != null) {
bigSmalls.add(trendChartsSmall);
} else {
// 当时间与上一条的时间不同时创建新集合并添加进temperaturePulses
// 1.创建TrendChartsSmall集合
bigSmalls = new ArrayList<>();
// 1.1 将trendChartsSmall添加进TrendChartsSmall集合
bigSmalls.add(trendChartsSmall);
// 2.创建新的TrendChartsBig 并传入 chartsSmalls、weekNo
TrendChartsBig trendChartsBig = new TrendChartsBig(bigSmalls, trendChartsSmall.getWeekNo());
// 2.2 temperaturePulses 添加 TrendChartsBig
temperaturePulses.add(trendChartsBig);
// 将新的时间赋值给进行比较的时间
compareTime = smallTime;
}
}
return new TrendChartsOutput(hospDate, null, null, temperaturePulses, others);
}
/**
* 校验是否为汉字
*
* @param content 待校验字符串
* @return 校验结果
*/
public static boolean isChineseWithSpace(String content) {
Pattern regex = Pattern.compile("^[\\u4E00-\\u9FA5\\s]+$");
if (StringUtils.isBlank(content)) {
return false;
} else {
return regex.matcher(content).matches();
}
}
/**
* 提前一天
*
* @param others others集合
*/
private void isOneDayInAdvance(List<TrendChartsSmall> others) {
List<String> tiqian = Arrays.asList(TemperatureChartEnum.INPUT.getCode(),
TemperatureChartEnum.BOWEL_MOVEMENT_FREQUENCY.getCode(), TemperatureChartEnum.URINATION_FREQUENCY.getCode(),
TemperatureChartEnum.URINE_VOLUME.getCode(), TemperatureChartEnum.OTHERS.getCode());
others.forEach(item -> {
if (StringUtils.isBlank(item.getTypeCode()) && tiqian.contains(item.getTypeCode())) {
LocalDate date = item.getDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
date = date.plusDays(-1);
item.setDate(Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()));
}
});
}
/**
* 每一天的血压取上下午各最新的一条
*
* @param list 血压集合
* @return 每天上下午各最新一条的血压数据的集合
*/
private List<DocStatisticsDto> getLatestRecordsPerPeriod(List<DocStatisticsDto> list) {
// 安全校验列表为空或null时直接返回空列表
if (list == null || list.isEmpty()) {
return Collections.emptyList();
}
// 按上午(AM)/下午(PM)分组,获取每组符合规则的最新数据
Map<String, Optional<DocStatisticsDto>> latestByAmPm =
list.stream().filter(item -> item.getValue() != null && !"/".equals(item.getValue().trim())) // 过滤无效值
.collect(Collectors.groupingBy(
// 按时间判断上下午12点前为上午12点及以后为下午
dto -> {
LocalTime time = dto.getRecordTime().toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
return time.isBefore(LocalTime.NOON) ? "AM" : "PM";
}, Collectors.collectingAndThen(Collectors.toList(), groupList -> {
// 第一步:筛选出数值型数据(非汉字)
List<DocStatisticsDto> numericData = groupList.stream()
.filter(dto -> !isChineseWithSpace(dto.getValue())).collect(Collectors.toList());
// 第二步:如果有数值型数据,取最新的;否则取分组内最新的汉字数据
List<DocStatisticsDto> targetData = numericData.isEmpty() ? groupList : numericData;
return targetData.stream().max(Comparator.comparing(DocStatisticsDto::getRecordTime));
})));
// 提取有效数据并按时间排序
List<DocStatisticsDto> validData = latestByAmPm.values().stream().filter(Optional::isPresent).map(Optional::get)
.sorted(Comparator.comparing(DocStatisticsDto::getRecordTime)).toList();
// 拼接上下午数据(上午值 + 逗号 + 下午值)
List<DocStatisticsDto> result = new ArrayList<>();
if (validData.size() == 1) {
// 只有单条数据(上午/下午),直接保留
result.add(validData.get(0));
} else if (validData.size() >= 2) {
// 取前两条(上午+下午)拼接,上午作为主数据
DocStatisticsDto amDto = validData.get(0);
DocStatisticsDto pmDto = validData.get(1);
amDto.setValue(amDto.getValue() + "," + pmDto.getValue());
result.add(amDto);
}
return result;
}
/**
* 计算weekNo 例开始日期2025-11-08 当前日期2025-11-17 -> 2
*
* @param startDate 开始日期
* @param endDate 结束日期
* @return endDate是距startDate的第几周
*/
private int calculateWeek(Date startDate, Date endDate) {
LocalDate start = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
LocalDate end = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
if (start.isAfter(end)) {
throw new IllegalArgumentException("开始日期不能晚于结束日期");
}
// 计算出的相差天数要加1
long days = ChronoUnit.DAYS.between(start, end) + 1;
return (int)Math.ceil(days / 7.0);
}
/**
* 计算术后天数 [斜线(/)前是最新的术后天数]
*
* @param trendChartsOutput 体温单
* @param postopDateList 手术时间list
*
*/
private void calculatePostopDay(TrendChartsOutput trendChartsOutput, List<LocalDateTime> postopDateList) {
if (!postopDateList.isEmpty()) {
// 统计术后天数的截至日期
LocalDate endDate;
if (trendChartsOutput.getOutDate() == null) {
endDate = LocalDate.now();
} else {
endDate = trendChartsOutput.getOutDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
// 添加手术信息
for (LocalDateTime dateTime : postopDateList) {
// 手术时间
Date postopDate = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
String times = formatDate(postopDate, "HH:mm:ss");
String typeValue =
TemperatureChartEnum.OPERATION.getDescription() + "," + formatDate(postopDate, "HH:mm");
// 封装手术信息
TrendChartsSmall postopSmall =
new TrendChartsSmall(null, postopDate, times, TemperatureChartEnum.OPERATION.getCode(), typeValue,
calculateWeek(trendChartsOutput.getHospDate(), postopDate), null, null);
// 将收入信息添加到有入科信息的集合中
trendChartsOutput.getTemperaturePulses().get(0).getChartsSmalls().add(postopSmall);
}
// 计算术后天数
for (LocalDate currentDate = postopDateList.get(0).toLocalDate(); !currentDate.isAfter(endDate);
currentDate = currentDate.plusDays(1)) {
// 获取术后天数
String typeValue = OperationDayCalculator.calculateOperationDay(
postopDateList.stream().map(LocalDateTime::toLocalDate).toList(), currentDate);
// 封装[术后天数]信息
Date date = Date.from(currentDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
TrendChartsSmall postopSmall =
new TrendChartsSmall(null, date, null, TemperatureChartEnum.POSTOP_DAYS.getCode(), typeValue,
calculateWeek(trendChartsOutput.getHospDate(), date), null, null);
trendChartsOutput.getOthers().add(postopSmall);
}
}
}
/**
* 获取住院、出院时间
*
* @param encounterId 病历ID
* @param patientId 患者ID
* @return 住院时间
*/
private Map<String, Date> getDateByEncounterId(Long encounterId, Long patientId) {
String sql =
"SELECT start_time,end_time FROM adm_encounter WHERE id = ? AND patient_id = ? AND status_enum = ? AND class_enum = ?";
Object[] params = {encounterId, patientId, EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue(),
EncounterClass.IMP.getValue()};
Map<String, Object> result = jdbcTemplate.queryForMap(sql, params);
HashMap<String, Date> map = new HashMap<>();
map.put("hospDate", (Timestamp)result.get("start_time"));
map.put("outTime", (Timestamp)result.get("end_time"));
return map;
}
/**
* 获取入科时间
*
* @param encounterId 病历ID
* @return 入科时间
*/
private Date getDateByEncounterId(Long encounterId) {
String sql =
"SELECT ael.start_time FROM adm_encounter ae INNER JOIN adm_encounter_location ael ON ae.ID=ael.encounter_id AND ael.form_enum=? AND ael.status_enum=? AND ael.delete_flag='0' AND ael.tenant_id=1 LEFT JOIN adm_location al ON ael.location_id=al.ID AND al.delete_flag='0' AND al.tenant_id=1 WHERE ae.ID=? AND ae.delete_flag='0' AND ae.tenant_id=1";
Object[] params = {LocationForm.BED.getValue(), EncounterActivityStatus.ACTIVE.getValue(), encounterId};
Timestamp timestamp = jdbcTemplate.queryForObject(sql, params, Timestamp.class);
return Date.from(timestamp.toInstant());
}
/**
* 获取手术时间
*
* @param encounterId 病历ID
* @param patientId 患者ID
* @return 手术时间列表
*/
private List<LocalDateTime> getPostopDate(Long encounterId, Long patientId) {
String sql =
"SELECT cpp.start_time FROM cli_procedure_performer cpp LEFT JOIN cli_procedure cp ON cpp.procedure_id=cp.ID LEFT JOIN adm_encounter ae ON ae.patient_id=cp.patient_id AND ae.ID=cp.encounter_id AND ae.status_enum=? AND ae.class_enum=? WHERE cp.patient_id=? AND cp.status_enum=? AND cp.encounter_id=? AND cpp.start_time> ae.start_time AND cpp.end_time <= ae.end_time ORDER BY cpp.start_time";
Object[] params = {EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue(), EncounterClass.IMP.getValue(),
patientId, 5, encounterId};
List<Timestamp> dates = jdbcTemplate.queryForList(sql, Timestamp.class, params);
return dates.stream().map(date -> date.toLocalDateTime()).toList();
}
/**
* Date转字符串
*
* @param date 时间
* @param pattern 解析格式
* @return 转后后的时间字符串
*/
private String formatDate(Date date, String pattern) {
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return localDateTime.format(formatter);
}
}

View File

@@ -1,11 +1,21 @@
package com.openhis.web.document.appservice.impl;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.utils.SecurityUtils;
import com.core.common.utils.bean.BeanUtils;
import com.openhis.common.enums.DocDefinitionEnum;
import com.openhis.common.utils.HisPageUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.document.domain.DocStatistics;
@@ -14,13 +24,8 @@ import com.openhis.document.service.IDocStatisticsService;
import com.openhis.web.document.appservice.IDocStatisticsAppService;
import com.openhis.web.document.dto.DocStatisticsDto;
import com.openhis.web.document.dto.DocStatisticsQueryParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
/**
* 文档模板 业务实现类
@@ -34,23 +39,23 @@ public class DocStatisticsAppServiceImpl implements IDocStatisticsAppService {
@Resource
private DocStatisticsMapper docStatisticsMapper;
@Override
public R<?> createOrUpdte(List<DocStatisticsDto> docStatisticsList) {
if (docStatisticsList.size() == 0) {
return R.ok();
}
//根据recordId和statisticDefinitionId判断是否存在存在则更新不存在则新增
// 根据recordId和statisticDefinitionId判断是否存在存在则更新不存在则新增
LambdaQueryWrapper<DocStatistics> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DocStatistics::getRecordId, docStatisticsList.get(0).getRecordId());
// queryWrapper.eq(DocStatistics::getStatisticDefinitionId, docStatisticsList.get(0).getStatisticDefinitionId());
// queryWrapper.eq(DocStatistics::getStatisticDefinitionId,
// docStatisticsList.get(0).getStatisticDefinitionId());
List<DocStatistics> docStatisticsOldList = docStatisticsService.list(queryWrapper);
if (docStatisticsOldList.size() > 0) {
//批量删除
// 批量删除
docStatisticsService.removeByIds(docStatisticsOldList.stream().map(DocStatistics::getId).toList());
}
List<DocStatistics> docStatisticsInsertList = new ArrayList<>();
//批量新增
// 批量新增
for (DocStatisticsDto docStatisticsDto : docStatisticsList) {
DocStatistics docStatistics = new DocStatistics();
docStatistics.setRecordId(docStatisticsDto.getRecordId());
@@ -62,6 +67,7 @@ public class DocStatisticsAppServiceImpl implements IDocStatisticsAppService {
docStatistics.setValue(docStatisticsDto.getValue());
docStatistics.setRecordTime(docStatisticsDto.getRecordTime());
docStatistics.setSource(docStatisticsDto.getSource());
docStatistics.setDefinitionId(docStatisticsDto.getDefinitionId());
docStatisticsInsertList.add(docStatistics);
}
boolean result = docStatisticsService.saveBatch(docStatisticsInsertList);
@@ -74,7 +80,15 @@ public class DocStatisticsAppServiceImpl implements IDocStatisticsAppService {
@Override
public R<?> delete(List<Long> ids) {
return null;
LambdaUpdateWrapper<DocStatistics> wrapper = new LambdaUpdateWrapper<>();
wrapper.in(DocStatistics::getRecordId, ids);
wrapper.set(DocStatistics::getDeleteFlag, "1");
boolean update = docStatisticsService.update(wrapper);
if (update) {
return R.ok();
} else {
return R.fail("删除失败");
}
}
@Override
@@ -106,19 +120,19 @@ public class DocStatisticsAppServiceImpl implements IDocStatisticsAppService {
}
@Override
public R<?> getStatisticsList(DocStatisticsQueryParam queryParam, Integer isPage, Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request) {
public R<?> getStatisticsList(DocStatisticsQueryParam queryParam, Integer isPage, Integer pageNo, Integer pageSize,
String searchKey, HttpServletRequest request) {
// 1. 分页查询逻辑
if (isPage != null && isPage == 1) {
QueryWrapper<DocStatistics> queryWrapper = HisQueryUtils.buildQueryWrapper(
queryParam, null, null, request);
QueryWrapper<DocStatistics> queryWrapper = HisQueryUtils.buildQueryWrapper(queryParam, null, null, request);
// 按记录时间倒序(最新记录在前)
queryWrapper.lambda().orderByDesc(DocStatistics::getRecordTime);
Page<DocStatisticsDto> recordPage = HisPageUtils.selectPage(
docStatisticsMapper, queryWrapper, pageNo, pageSize, DocStatisticsDto.class);
Page<DocStatisticsDto> recordPage =
HisPageUtils.selectPage(docStatisticsMapper, queryWrapper, pageNo, pageSize, DocStatisticsDto.class);
return R.ok(recordPage, "获取患者文书统计数据成功");
}
//2. 不分页查询逻辑
// 2. 不分页查询逻辑
else {
LambdaQueryWrapper<DocStatistics> queryWrapper = new LambdaQueryWrapper<>();
// 患者ID查询条件
@@ -148,4 +162,48 @@ public class DocStatisticsAppServiceImpl implements IDocStatisticsAppService {
return R.ok(dtoList, "获取患者文书统计数据成功");
}
}
/**
* 根据病历ID、病人ID和体温单ID获取温度单信息
*
* @param encounterId 病历ID
* @param patientId 病人ID
* @param tempId 体温单ID
* @return 体温单信息
*/
@Override
public List<DocStatisticsDto> getStatisticsListByEncounterIdAndPatientId(Long encounterId, Long patientId,
Long tempId) {
List<DocStatistics> statisticsList =
docStatisticsMapper.getTempList(encounterId, patientId, tempId, DocDefinitionEnum.TEMPERATURE.getValue());
// 实体转DTO
List<DocStatisticsDto> dtoList = new ArrayList<>();
for (DocStatistics statistics : statisticsList) {
DocStatisticsDto dto = new DocStatisticsDto();
BeanUtils.copyProperties(statistics, dto);
dtoList.add(dto);
}
return dtoList;
}
/**
* 保存/更新入院体征
*
* @param docStatisticsDtoList 入院体征list
*/
@Override
public void saveOrUpdateAdmissionSigns(List<DocStatisticsDto> docStatisticsDtoList) {
// 实体类 转换
List<DocStatistics> docStatisticsList = docStatisticsDtoList.stream().map(item -> {
DocStatistics statistics = new DocStatistics();
BeanUtils.copyProperties(item, statistics);
return statistics;
}).toList();
// 需要删除的
List<DocStatistics> removeList = docStatisticsList.stream().filter(item -> item.getId() != null).toList();
// 需要新增的
List<DocStatistics> addList = docStatisticsList.stream().filter(item -> item.getId() == null).toList();
docStatisticsService.removeBatchByIds(removeList.stream().map(DocStatistics::getId).toList());
docStatisticsService.saveBatch(addList);
}
}

View File

@@ -1,5 +1,14 @@
package com.openhis.web.document.appservice.impl;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -17,14 +26,8 @@ import com.openhis.web.document.appservice.IDocStatisticsDefinitionAppService;
import com.openhis.web.document.dto.DocStatisticsDefinitionDto;
import com.openhis.web.document.dto.OptionDto;
import com.openhis.web.document.mapper.DocStatisticsDefinitionAppMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
/**
* 文档模板 业务实现类
@@ -41,6 +44,7 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
private DocStatisticsDefinitionAppMapper docStatisticsDefinitionAppMapper;
@Autowired
private RedisCache redisCache;
@Override
public R<?> createOrEdit(DocStatisticsDefinitionDto docStatisticsDefinitionDto) {
if (docStatisticsDefinitionDto == null) {
@@ -67,7 +71,8 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
}
}
// 区分新增/编辑逻辑
return docStatisticsDefinitionDto.getId() == null ? add(docStatisticsDefinitionDto) : update(docStatisticsDefinitionDto);
return docStatisticsDefinitionDto.getId() == null ? add(docStatisticsDefinitionDto)
: update(docStatisticsDefinitionDto);
}
@Override
@@ -97,6 +102,8 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
docStatisticsDefinition.setUnit(docStatisticsDefinitionDto.getUnit());
docStatisticsDefinition.setDictType(docStatisticsDefinitionDto.getDictType());
docStatisticsDefinition.setDictName(docStatisticsDefinitionDto.getDictName());
// 新增字段:类型代码 例003-体温
docStatisticsDefinition.setTypeCode(docStatisticsDefinitionDto.getTypeCode());
boolean saveResult = iDocStatisticsDefinitionService.save(docStatisticsDefinition);
if (saveResult) {
return R.ok("文档统计定义新增成功");
@@ -113,7 +120,8 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
return R.fail(checkResult);
}
// 校验待更新数据是否存在
DocStatisticsDefinition existingData = iDocStatisticsDefinitionService.getById(docStatisticsDefinitionDto.getId());
DocStatisticsDefinition existingData =
iDocStatisticsDefinitionService.getById(docStatisticsDefinitionDto.getId());
if (existingData == null) {
return R.fail("更新文档统计定义失败,目标数据不存在");
}
@@ -128,6 +136,7 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
existingData.setUnit(docStatisticsDefinitionDto.getUnit());
existingData.setDictType(docStatisticsDefinitionDto.getDictType());
existingData.setDictName(docStatisticsDefinitionDto.getDictName());
existingData.setTypeCode(docStatisticsDefinitionDto.getTypeCode());
boolean updateResult = iDocStatisticsDefinitionService.updateById(existingData);
if (updateResult) {
// 同步更新选项列表
@@ -157,22 +166,22 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
/**
* 分页查询列表-不包含options分页
*
* @param pageNo 页码
* @param pageSize 每页条数
* @param pageNo 页码
* @param pageSize 每页条数
* @param searchKey 搜索关键词
* @param request 请求对象
* @param request 请求对象
* @return 分页结果
*/
@Override
public R<?> getPageList(Integer pageNo, Integer pageSize, String searchKey, HttpServletRequest request) {
// Map<String, Object> keyMap = redisCache.getAllDictDataWithKeys(CacheConstants.SYS_DICT_KEY);
// Map<String, Object> keyMap = redisCache.getAllDictDataWithKeys(CacheConstants.SYS_DICT_KEY);
// 构建查询条件(支持多字段搜索)
QueryWrapper<DocStatisticsDefinition> queryWrapper = HisQueryUtils.buildQueryWrapper(
null, searchKey, new HashSet<>(Arrays.asList("name", "code")), request);
QueryWrapper<DocStatisticsDefinition> queryWrapper =
HisQueryUtils.buildQueryWrapper(null, searchKey, new HashSet<>(Arrays.asList("name", "code")), request);
// 按记录时间倒序(最新记录在前)
queryWrapper.lambda().orderByDesc(DocStatisticsDefinition::getDisplayOrder);
Page<DocStatisticsDefinitionDto> recordPage = HisPageUtils.selectPage(
docStatisticsDefinitionMapper, queryWrapper, pageNo, pageSize, DocStatisticsDefinitionDto.class);
Page<DocStatisticsDefinitionDto> recordPage = HisPageUtils.selectPage(docStatisticsDefinitionMapper,
queryWrapper, pageNo, pageSize, DocStatisticsDefinitionDto.class);
// 转换为分页结果
return R.ok(recordPage, "文档统计定义列表获取成功");
}
@@ -185,7 +194,7 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
*/
@Override
public List<DocStatisticsDefinitionDto> getList(Integer isStatistics) {
Map<String, Object> keyMap = redisCache.getAllDictDataWithKeys(CacheConstants.SYS_DICT_KEY);
Map<String, Object> keyMap = redisCache.getAllDictDataWithKeys(CacheConstants.SYS_DICT_KEY);
LambdaQueryWrapper<DocStatisticsDefinition> queryWrapper = new LambdaQueryWrapper<>();
if (isStatistics != null) {
queryWrapper.eq(DocStatisticsDefinition::getIsStatistics, isStatistics);
@@ -199,10 +208,11 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
}
return resultList;
}
@Override
public List<DocStatisticsDefinitionDto> getListWithOptionList(Integer isStatistics) {
//从redis中获取所有字典数据
Map<String, Object> keyMap = redisCache.getAllDictDataWithKeys(CacheConstants.SYS_DICT_KEY);
// 从redis中获取所有字典数据
Map<String, Object> keyMap = redisCache.getAllDictDataWithKeys(CacheConstants.SYS_DICT_KEY);
LambdaQueryWrapper<DocStatisticsDefinition> queryWrapper = new LambdaQueryWrapper<>();
if (isStatistics != null) {
queryWrapper.eq(DocStatisticsDefinition::getIsStatistics, isStatistics);
@@ -212,15 +222,16 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
for (DocStatisticsDefinition data : dataList) {
DocStatisticsDefinitionDto dto = new DocStatisticsDefinitionDto();
BeanUtils.copyProperties(data, dto);
if(keyMap.containsKey(CacheConstants.SYS_DICT_KEY+data.getDictType()))
{
List<SysDictData> dictData = (List<SysDictData>) keyMap.get(CacheConstants.SYS_DICT_KEY+data.getDictType());
if (keyMap.containsKey(CacheConstants.SYS_DICT_KEY + data.getDictType())) {
List<SysDictData> dictData =
(List<SysDictData>)keyMap.get(CacheConstants.SYS_DICT_KEY + data.getDictType());
dto.setOptionList(dictData);
}
resultList.add(dto);
}
return resultList;
}
/**
* 获取文档统计定义选项列表并按指定格式返回
*
@@ -237,8 +248,8 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
}
// 3. 按code字段分组
Map<String, List<OptionDto>> groupCodeMap = optionDtoList.stream()
.collect(Collectors.groupingBy(OptionDto::getCode));
Map<String, List<OptionDto>> groupCodeMap =
optionDtoList.stream().collect(Collectors.groupingBy(OptionDto::getCode));
// 4. 准备最终返回的结果列表
List<Map<String, Object>> resultList = new ArrayList<>();
@@ -260,8 +271,7 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
String listKey = code + "List";
// 检查分组中是否存在任何optionId为null的元素
boolean hasNullOptionId = optionDtos.stream()
.anyMatch(dto -> dto.getOptionId() == null);
boolean hasNullOptionId = optionDtos.stream().anyMatch(dto -> dto.getOptionId() == null);
// 如果存在null的optionId返回空集合否则正常处理
List<Map<String, Object>> optionList;
@@ -269,14 +279,12 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
optionList = new ArrayList<>(); // 返回空集合
} else {
// 正常转换有效元素
optionList = optionDtos.stream()
.map(dto -> {
Map<String, Object> optionMap = new HashMap<>();
optionMap.put("id", dto.getOptionId().toString());
optionMap.put("option", dto.getOption());
return optionMap;
})
.collect(Collectors.toList());
optionList = optionDtos.stream().map(dto -> {
Map<String, Object> optionMap = new HashMap<>();
optionMap.put("id", dto.getOptionId().toString());
optionMap.put("option", dto.getOption());
return optionMap;
}).collect(Collectors.toList());
}
// 添加列表到结果中(即使是空集合)
@@ -287,7 +295,6 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
return R.ok(resultList, "文档统计定义选项列表获取成功");
}
@Override
public R<?> deleteDocStatisticsDefinition(Long id) {
if (id == null) {
@@ -301,6 +308,4 @@ public class DocStatisticsDefinitionAppServiceImpl implements IDocStatisticsDefi
}
}
}

View File

@@ -1,5 +1,13 @@
package com.openhis.web.document.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;
import com.core.common.core.domain.R;
import com.openhis.common.enums.DocStatusEnum;
import com.openhis.common.enums.DocTypeEnum;
@@ -8,17 +16,12 @@ import com.openhis.web.document.dto.DocRecordDto;
import com.openhis.web.document.dto.DocRecordPatientQueryParam;
import com.openhis.web.document.dto.DocRecordQueryParam;
import com.openhis.web.document.util.EnumUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文书记录 controller
* 文书记录 controller
*
* @author wanghaiming
*/
@@ -65,6 +68,17 @@ public class DocRecordController {
return docRecordAppService.updateRecord(docRecordDto);
}
/**
* 新增或编辑记录
*
* @param docRecordDto 文书记录信息
* @return R
*/
@PostMapping("/saveOrUpdateRecord")
public R<?> saveOrUpdateRecord(@RequestBody DocRecordDto docRecordDto) {
return docRecordAppService.saveOrUpdateRecord(docRecordDto);
}
/**
* 设置文书为编辑状态
*
@@ -77,44 +91,56 @@ public class DocRecordController {
}
/**
* 根据患者ID或就诊ID获取文书记录列表,只针对不需返回患者具体信息的列表,体温单除外,单独处理
* 需返回患者具体信息的列表请使用getPatientRecordList
* 根据患者ID或就诊ID获取文书记录列表,只针对不需返回患者具体信息的列表,体温单除外,单独处理 需返回患者具体信息的列表请使用getPatientRecordList
*
* @param docRecordQueryParam 文书记录查询参数
* @param pageNo 页码
* @param pageSize 页大小
* @param searchKey 搜索关键字
* @param request 请求
* 1.IsPage 是否分页 0:不分页 1:分页
* @param pageNo 页码
* @param pageSize 页大小
* @param searchKey 搜索关键字
* @param request 请求 1.IsPage 是否分页 0:不分页 1:分页
* @return R
*/
@GetMapping("/getRecordByEncounterIdList")
R<?> getRecordByEncounterIdList(DocRecordQueryParam docRecordQueryParam,
@RequestParam(value = "isPage", defaultValue = "1") Integer isPage,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(name = "searchKey", required = false) String searchKey, HttpServletRequest request) {
return docRecordAppService.getRecordByEncounterIdList(docRecordQueryParam, isPage, pageNo, pageSize, searchKey, request);
@RequestParam(value = "isPage", defaultValue = "1") Integer isPage,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(name = "searchKey", required = false) String searchKey, HttpServletRequest request) {
return docRecordAppService.getRecordByEncounterIdList(docRecordQueryParam, isPage, pageNo, pageSize, searchKey,
request);
}
/**
* 根据患者ID或就诊ID以及时间获取文书记录列表,只针对不需返回患者具体信息的列表,体温单除外,单独处理 需返回患者具体信息的列表请使用getPatientRecordList
*
* @param docRecordQueryParam 文书记录查询参数
* @param recordTime 时间
* @return R
*/
@GetMapping("/getRecordByEncounterIdAndTimeList")
R<?> getRecordByEncounterIdAndTimeList(DocRecordQueryParam docRecordQueryParam,
@RequestParam(value = "recordTime", required = false) String recordTime) {
return docRecordAppService.getRecordByEncounterIdAndTimeList(docRecordQueryParam, recordTime);
}
/**
* 文书记录列表
*
* @param docRecordPatientQueryParam 文书记录查询参数
* @param pageNo 页码
* @param pageSize 页大小
* @param searchKey 搜索关键字
* @param request 请求
* @param pageNo 页码
* @param pageSize 页大小
* @param searchKey 搜索关键字
* @param request 请求
* @return R
*/
@GetMapping("/getRecordPageList")
R<?> getRecordPageList(DocRecordPatientQueryParam docRecordPatientQueryParam,
@RequestParam(value = "primaryMenuEnumList", required = false) List<Integer> primaryMenuEnumList,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(name = "searchKey", required = false) String searchKey,
HttpServletRequest request) {
return docRecordAppService.getRecordPageList(docRecordPatientQueryParam, primaryMenuEnumList, pageNo, pageSize, searchKey, request);
@RequestParam(value = "primaryMenuEnumList", required = false) List<Integer> primaryMenuEnumList,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(name = "searchKey", required = false) String searchKey, HttpServletRequest request) {
return docRecordAppService.getRecordPageList(docRecordPatientQueryParam, primaryMenuEnumList, pageNo, pageSize,
searchKey, request);
}
/**
@@ -137,4 +163,39 @@ public class DocRecordController {
return docRecordAppService.deleteRecord(ids);
}
/**
* 打印
*
* @param recordId 文书分配信息ID列表
*/
@GetMapping("/record-print")
R<?> print(@RequestParam Long recordId) {
return docRecordAppService.recordPrint(recordId);
}
/**
* 根据病历ID、病人ID和温度单ID获取体温单信息
*
* @param docRecordQueryParam 文书记录查询参数
* @return 体温单信息
*/
@GetMapping("/temperature-chart")
R<?> temperatureChart(DocRecordQueryParam docRecordQueryParam) {
return docRecordAppService.temperatureChart(docRecordQueryParam);
}
/**
* 护理记录总结
*
* @param docRecordQueryParam 文书记录查询参数
* @param startTime 开始时间
* @param endTime 结束时间
* @return 护理记录总结
*/
@GetMapping("/summary")
R<?> summaryNursingRecords(DocRecordQueryParam docRecordQueryParam,
@RequestParam(value = "startTime", required = false, defaultValue = "") String startTime,
@RequestParam(value = "endTime", required = false, defaultValue = "") String endTime) {
return docRecordAppService.summaryNursingRecords(docRecordQueryParam, startTime, endTime);
}
}

View File

@@ -59,4 +59,8 @@ public class DocRecordDto {
*/
private String name;
private String version;
/**
* 打印次数
*/
private Integer printCount = 0;
}

View File

@@ -61,5 +61,9 @@ public class DocStatisticsDefinitionDto {
/** 字典类型 */
private String dictType;
/**
* 类型代码 例 003:体温
*/
private String typeCode;
}

View File

@@ -52,5 +52,9 @@ public class DocStatisticsDto {
* 记录来源
*/
private String source;
/**
* 文书ID
*/
private Long definitionId;
}

View File

@@ -0,0 +1,31 @@
package com.openhis.web.document.dto;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 温度、脉搏等生命体征信息
*
* @author swb
* @date 2025-11-19
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class TrendChartsBig {
/**
* 图表信息
* 体温,脉搏:有数据 其他:空
*/
private List<TrendChartsSmall> chartsSmalls = new ArrayList<TrendChartsSmall>();
/**
* 当前数据日期-住院日期 算出第几周(画面筛选上一周下一周用)
*/
private Integer weekNo;
}

View File

@@ -0,0 +1,53 @@
package com.openhis.web.document.dto;
import java.util.Date;
import java.util.List;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 体温单
*
* @author swb
* @date 2025-11-19
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class TrendChartsOutput {
/**
* 住院日期 例2024-09-04
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date hospDate;
/**
* 手术日期 例2024-09-04
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date operaDate;
/**
* 出院日 例2024-09-04
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date outDate;
/**
* 体温003、脉搏002
*/
private List<TrendChartsBig> temperaturePulses;
/**
* typeCode: '004':尿量 typeCode: '005':大便次数 typeCode: '006':摄入液量 typeCode: '007':排除液量 typeCode: '008':血压 typeCode:
* '009':体重 typeCode: '010':术后天数
*/
private List<TrendChartsSmall> others;
}

View File

@@ -0,0 +1,61 @@
package com.openhis.web.document.dto;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 图表信息
*
* @author swb
* @date 2025-11-19
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class TrendChartsSmall {
/**
* 体温脉搏1固定 其他null
*/
private Integer collectionMode;
/**
* 查询的日期 例2024-09-06
*/
// 日期格式
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date date;
/**
* 每隔n小时测量的时间点 例10:00:00
*/
private String times;
/**
* typeCode: '002':脉搏 typeCode: '003':体温 typeCode: '004':尿量 typeCode: '005':大便次数 typeCode: '006':摄入液量 typeCode:
* '007':排除液量 typeCode: '008':血压 typeCode: '009':体重 typeCode: '010':术后天数
*/
private String typeCode;
/**
* 测量出的数据
*/
private String typeValue;
/**
* 当前数据日期-住院日期 算出第几周
*/
private Integer weekNo;
/**
* 其他项录入排序用,不参与任何计算与画面显示
*/
private String orderByDateTimes;
/**
* ID
*/
private String id;
}

View File

@@ -1,22 +1,26 @@
package com.openhis.web.document.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.openhis.web.document.appservice.IDocStatisticsDefinitionAppService;
import com.openhis.web.document.dto.DocRecordDto;
import com.openhis.web.document.dto.DocStatisticsDefinitionDto;
import com.openhis.web.document.dto.DocStatisticsDto;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Component;
import static com.core.framework.datasource.DynamicDataSourceContextHolder.log;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import static com.core.framework.datasource.DynamicDataSourceContextHolder.log;
import javax.annotation.Resource;
import com.core.common.utils.StringUtils;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.openhis.common.enums.TemperatureChartEnum;
import com.openhis.web.document.appservice.IDocStatisticsDefinitionAppService;
import com.openhis.web.document.dto.DocRecordDto;
import com.openhis.web.document.dto.DocStatisticsDefinitionDto;
import com.openhis.web.document.dto.DocStatisticsDto;
@Component
public class ConvertToDocStatistics {
@@ -24,12 +28,58 @@ public class ConvertToDocStatistics {
IDocStatisticsDefinitionAppService docStatisticsDefinitionAppService;
/**
* 将文档记录转换为统计DTO列表的工具方法
* 支持两种JSON结构
* 1. 简单键值对:{"目标code": 值, "recordTime": "yyyy-MM-dd HH:mm:ss"}
* 2. 嵌套Item列表{"目标code+Item": [{"目标code": 值1}, {"目标code": 值2}],"recordTime": "yyyy-MM-dd HH:mm:ss"}
* 3. 记录 Time 字段:{"目标code+Item": [{"目标code": 值1, "recordTime": "yyyy-MM-dd HH:mm:ss"}, {"目标code": 值2, "recordTime": "yyyy-MM-dd HH:mm:ss"}]}
* 4. 记录 Time 字段:{"目标code": 值, "recordTime": "yyyy-MM-dd HH:mm:ss", "目标code+Item": [{"目标code": 值1, "recordTime": "yyyy-MM-dd HH:mm:ss"}, {"目标code": 值2, "recordTime": "yyyy-MM-dd HH:mm:ss"}]}
* 解析jsonObject中的数组(一层)
* 若要多层解析会存在key相同覆盖数据
*
* @param jsonObject jsonObject
* @return JSONObject列表
*/
public List<JSONObject> convertToJSONObjList(JSONObject jsonObject) {
List<JSONObject> jsonObjectList = new ArrayList<>();
List<String> keys = new ArrayList<>();
// 取出所有key
Set<String> keySet = jsonObject.keySet();
for (String key : keySet) {
// 取出key对应的数据
Object obj = jsonObject.get(key);
// 如果obj是数组并且是JSONObject数组数组遍历添加到List
if (obj instanceof JSONArray) {
if ((!((JSONArray)obj).isEmpty()) && (((JSONArray)obj).get(0) instanceof JSONObject)) {
List<JSONObject> javaList = ((JSONArray)obj).toJavaList(JSONObject.class);
jsonObjectList.addAll(javaList);
keys.add(key);
}
}
// 如果是对象添加到jsonObjectList
if (obj instanceof JSONObject) {
jsonObjectList.add((JSONObject)obj);
keys.add(key);
}
}
// 移除 对象和数组
keys.forEach(jsonObject::remove);
// 将原本JSONObject添加到集合
jsonObjectList.add(jsonObject);
// 格式化时间点
for (JSONObject object : jsonObjectList) {
if (object.containsKey(TemperatureChartEnum.TIME_POINT.getTypeCode())) {
// 前端传来的时间点格式 0200转换为02:00:00
String timePointValue = (String)object.get(TemperatureChartEnum.TIME_POINT.getTypeCode());
if (timePointValue != null && timePointValue.matches("\\d{4}")) {
object.put(TemperatureChartEnum.TIME_POINT.getTypeCode(),
timePointValue.substring(0, 2) + ":00:00");
}
}
}
return jsonObjectList;
}
/**
* 将文档记录转换为统计DTO列表的工具方法 支持两种JSON结构 1. 简单键值对:{"目标code": 值, "recordTime": "yyyy-MM-dd HH:mm:ss"} 2.
* 嵌套Item列表{"目标code+Item": [{"目标code": 值1}, {"目标code": 值2}],"recordTime": "yyyy-MM-dd HH:mm:ss"} 3.
* 记录Time字段{"目标code+Item": [{"目标code": 值1, "recordTime": "yyyy-MM-dd HH:mm:ss"}, {"目标code": 值2, "recordTime":
* "yyyy-MM-dd HH:mm:ss"}]} 4. 记录Time字段{"目标code": 值, "recordTime": "yyyy-MM-dd HH:mm:ss", "目标code+Item":
* [{"目标code": 值1, "recordTime": "yyyy-MM-dd HH:mm:ss"}, {"目标code": 值2, "recordTime": "yyyy-MM-dd HH:mm:ss"}]}
* 日期类型使用java.util.Date
*/
public List<DocStatisticsDto> convertToStatisticsDtoList(DocRecordDto docRecordDto) {
@@ -53,66 +103,79 @@ public class ConvertToDocStatistics {
// 解析JSON字符串为JSONObject方便操作嵌套结构
JSONObject contentJsonObj = parseJson(contentJson);
// 如果JSON解析失败返回null直接返回空列表
if (contentJsonObj == null) {
return statisticsDtoList;
}
// 遍历每个统计项定义,解析对应的值
for (DocStatisticsDefinitionDto definition : definitionList) {
// 当前需要解析的目标字段code如"BQ"、"DrugCode"
String targetCode = definition.getCode();
// 场景1先尝试解析顶级键值对结构如{"BQ": 123}
if (contentJsonObj.containsKey(targetCode)) {
// 提取值并转换为字符串
String value = String.valueOf(contentJsonObj.get(targetCode));
Date recordTime = docRecordDto.getRecordTime();
// 创建DTO并添加到结果列表
addSingleDto(statisticsDtoList, definition, docRecordDto, value,
recordTime);
// 处理完当前统计项,继续下一个
continue;
for (JSONObject jsonObject : convertToJSONObjList(contentJsonObj)) {
contentJsonObj = jsonObject;
// 如果JSON解析失败返回null直接返回空列表
if (contentJsonObj == null) {
return statisticsDtoList;
}
// 遍历每个统计项定义,解析对应的值
for (DocStatisticsDefinitionDto definition : definitionList) {
// 当前需要解析的目标字段code如"BQ"、"DrugCode"
String targetCode = definition.getCode();
// 场景2如果顶级键不存在尝试解析嵌套的Item列表
// 列表字段名规则目标code + "Item"如code=BQ → 列表字段名=BQItem
String itemListKey = targetCode + "Item";
// 从JSON中获取对应的列表
JSONArray itemArray = contentJsonObj.getJSONArray(itemListKey);
// 判断列表是否存在且不为空
if (itemArray != null && !itemArray.isEmpty()) {
// 遍历列表中的每个元素每个元素是一个JSONObject
for (int i = 0; i < itemArray.size(); i++) {
JSONObject itemObj = itemArray.getJSONObject(i);
// 检查元素是否包含目标code
if (itemObj != null && itemObj.containsKey(targetCode)) {
// 提取当前元素的目标值
String value = String.valueOf(itemObj.get(targetCode));
Date recordTime = new Date();
if (itemObj.containsKey("recordTime")) {
recordTime = itemObj.getDate("recordTime");
} else if (contentJsonObj.containsKey("recordTime")) {
recordTime = contentJsonObj.getDate("recordTime");
} else {
recordTime = docRecordDto.getRecordTime();
// 场景1先尝试解析顶级键值对结构如{"BQ": 123}
if (contentJsonObj.containsKey(targetCode)) {
// 提取值并转换为字符串
String value = String.valueOf(contentJsonObj.get(targetCode));
Date recordTime = docRecordDto.getRecordTime();
// 判断jsonObject中是否有timePoint、recordTime字段以及值有则取出与拼接
try {
boolean timePoint = contentJsonObj.containsKey("timePoint");
boolean date = contentJsonObj.containsKey("recordTime");
String dateValue = (String)contentJsonObj.get("recordTime");
String timePointValue = (String)contentJsonObj.get("timePoint");
if (timePoint && date && !dateValue.isEmpty() && !timePointValue.isEmpty()) {
// 格式化 例2025-11-20 13:34:56
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
recordTime = format.parse(dateValue + " " + timePointValue);
}
// 创建DTO并添加到结果列表
addSingleDto(statisticsDtoList, definition, docRecordDto, value,
recordTime);
} catch (Exception e) {
e.printStackTrace();
}
// 创建DTO并添加到结果列表
addSingleDto(statisticsDtoList, definition, docRecordDto, value, recordTime);
// 处理完当前统计项,继续下一个
continue;
}
// 日志提示如果列表存在但未找到匹配的code
if (statisticsDtoList.isEmpty()) {
log.warn("列表{}中未找到包含{}的元素,可能数据格式不匹配", itemListKey, targetCode);
// 场景2如果顶级键不存在尝试解析嵌套的Item列表
// 列表字段名规则目标code + "Item"如code=BQ → 列表字段名=BQItem
String itemListKey = targetCode + "Item";
// 从JSON中获取对应的列表
JSONArray itemArray = contentJsonObj.getJSONArray(itemListKey);
// 判断列表是否存在且不为空
if (itemArray != null && !itemArray.isEmpty()) {
// 遍历列表中的每个元素每个元素是一个JSONObject
for (int i = 0; i < itemArray.size(); i++) {
JSONObject itemObj = itemArray.getJSONObject(i);
// 检查元素是否包含目标code
if (itemObj != null && itemObj.containsKey(targetCode)) {
// 提取当前元素的目标值
String value = String.valueOf(itemObj.get(targetCode));
Date recordTime = new Date();
if (itemObj.containsKey("recordTime")) {
recordTime = itemObj.getDate("recordTime");
} else if (contentJsonObj.containsKey("recordTime")) {
recordTime = contentJsonObj.getDate("recordTime");
} else {
recordTime = docRecordDto.getRecordTime();
}
// 创建DTO并添加到结果列表
addSingleDto(statisticsDtoList, definition, docRecordDto, value, recordTime);
}
}
// 日志提示如果列表存在但未找到匹配的code
if (statisticsDtoList.isEmpty()) {
log.warn("列表{}中未找到包含{}的元素,可能数据格式不匹配", itemListKey, targetCode);
}
} else {
// 日志提示未找到对应的Item列表或列表为空
log.error("未找到列表{}或列表为空,无法解析统计项: {}", itemListKey, targetCode);
}
} else {
// 日志提示未找到对应的Item列表或列表为空
log.error("未找到列表{}或列表为空,无法解析统计项: {}", itemListKey, targetCode);
}
}
return statisticsDtoList;
}
@@ -140,32 +203,33 @@ public class ConvertToDocStatistics {
/**
* 工具方法创建单个统计DTO并添加到列表
*
* @param dtoList 目标DTO列表
* @param definition 统计项定义
* @param dtoList 目标DTO列表
* @param definition 统计项定义
* @param docRecordDto 文档记录DTO
* @param value 解析出的统计值
* @param recordTime 记录时间
* @param value 解析出的统计值
* @param recordTime 记录时间
*/
private void addSingleDto(List<DocStatisticsDto> dtoList,
DocStatisticsDefinitionDto definition,
DocRecordDto docRecordDto,
String value,
Date recordTime) {
// 创建统计DTO对象
DocStatisticsDto dto = new DocStatisticsDto();
// 设置统计值和关联的统计项定义信息
dto.setValue(value);
dto.setStatisticDefinitionCode(definition.getCode());
dto.setStatisticDefinitionId(definition.getId());
// 设置关联的文档记录信息
dto.setRecordId(docRecordDto.getId());
dto.setEncounterId(docRecordDto.getEncounterId());
dto.setPatientId(docRecordDto.getPatientId());
dto.setOrganizationId(docRecordDto.getOrganizationId());
dto.setRecordTime(recordTime);
dto.setSource(docRecordDto.getSource());
// 将创建好的DTO添加到结果列表
dtoList.add(dto);
private void addSingleDto(List<DocStatisticsDto> dtoList, DocStatisticsDefinitionDto definition,
DocRecordDto docRecordDto, String value, Date recordTime) {
if (StringUtils.isNotBlank(value)) {
// 创建统计DTO对象
DocStatisticsDto dto = new DocStatisticsDto();
// 设置统计值和关联的统计项定义信息
dto.setValue(value);
dto.setStatisticDefinitionCode(definition.getCode());
dto.setStatisticDefinitionId(definition.getId());
// 设置关联的文档记录信息
dto.setRecordId(docRecordDto.getId());
dto.setEncounterId(docRecordDto.getEncounterId());
dto.setPatientId(docRecordDto.getPatientId());
dto.setOrganizationId(docRecordDto.getOrganizationId());
dto.setRecordTime(recordTime);
dto.setSource(docRecordDto.getSource());
// 文书ID
dto.setDefinitionId(docRecordDto.getDefinitionId());
// 将创建好的DTO添加到结果列表
dtoList.add(dto);
}
}
}

View File

@@ -0,0 +1,99 @@
package com.openhis.web.document.util;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 体温单手术日计算工具类 依据国家最新护理文件书写规范
*
* @author swb
* @date 2025-11-22
*/
public class OperationDayCalculator {
/**
* 计算指定日期对应的手术日记录
*
* @param operationDates 手术日期列表(按时间顺序排列)
* @param targetDate 要计算的目标日期
* @return 手术日记录字符串,如 "0", "3", "14+", "2/3"
*/
public static String calculateOperationDay(List<LocalDate> operationDates, LocalDate targetDate) {
if (operationDates == null || operationDates.isEmpty()) {
// 无手术记录
return "";
}
// 过滤出在目标日期之前或当天的手术
List<LocalDate> validOperations =
operationDates.stream().filter(date -> !date.isAfter(targetDate)).collect(Collectors.toList());
if (validOperations.isEmpty()) {
// 目标日期前无手术
return "";
}
// 多次手术情况14天内进行第二次及以上手术
if (validOperations.size() >= 2) {
LocalDate lastOperation = validOperations.get(validOperations.size() - 1);
LocalDate prevOperation = validOperations.get(validOperations.size() - 2);
// 检查最后两次手术是否在14天内
long daysBetween = ChronoUnit.DAYS.between(prevOperation, lastOperation);
if (daysBetween <= 14) {
return calculateMultipleOperations(validOperations, targetDate);
}
}
// 单次手术情况
LocalDate operationDate = validOperations.get(validOperations.size() - 1);
return calculateSingleOperation(operationDate, targetDate);
}
/**
* 计算单次手术的日期
*/
private static String calculateSingleOperation(LocalDate operationDate, LocalDate targetDate) {
long days = ChronoUnit.DAYS.between(operationDate, targetDate);
if (days < 0) {
// 手术前
return "";
} else if (days == 0) {
// 手术当天
return "0";
} else if (days <= 14) {
// 术后1-14天
return String.valueOf(days);
} else {
// 术后超过14天
return "14+";
}
}
/**
* 计算多次手术的日期
*/
private static String calculateMultipleOperations(List<LocalDate> operationDates, LocalDate targetDate) {
List<String> dayParts = new ArrayList<>();
for (LocalDate opDate : operationDates) {
long days = ChronoUnit.DAYS.between(opDate, targetDate);
if (days >= 0) {
dayParts.add(String.valueOf(days));
}
}
// 按规则:最近的手术天数作为分子,依次排列
// 反转列表,使最新的手术天数在最前面
List<String> reversedParts = new ArrayList<>();
for (int i = dayParts.size() - 1; i >= 0; i--) {
reversedParts.add(dayParts.get(i));
}
return String.join("/", reversedParts);
}
}