feat(mobile): 移动护理APP医嘱执行+生命体征

This commit is contained in:
2026-06-18 22:47:26 +08:00
parent aa4a582981
commit cb9968ee76
15 changed files with 1419 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package com.healthlink.his.web.nursing.appservice;
import com.healthlink.his.web.nursing.dto.*;
import java.util.List;
import java.util.Map;
public interface INursingMobileAppService {
List<NursingMobilePatientDto> getMobilePatientList(String wardName, String searchKey);
List<NursingMobileOrderDto> getMobileOrderList(Long patientId, Integer statusFilter);
Map<String, Object> executeOrder(Long requestId, String adviceTable, Long encounterId, Long patientId);
NursingMobileVitalSignDto saveVitalSign(NursingMobileVitalSignDto vitalSign);
NursingMobileVitalSignTrendDto getVitalSignTrend(Long patientId, Integer days);
}

View File

@@ -0,0 +1,159 @@
package com.healthlink.his.web.nursing.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.healthlink.his.nursing.domain.NursingVitalSignsChart;
import com.healthlink.his.nursing.service.INursingVitalSignsChartService;
import com.healthlink.his.web.nursing.appservice.INursingMobileAppService;
import com.healthlink.his.web.nursing.dto.*;
import com.healthlink.his.web.nursing.mapper.NursingMobileAppMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class NursingMobileAppServiceImpl implements INursingMobileAppService {
@Resource
private NursingMobileAppMapper mobileMapper;
@Resource
private INursingVitalSignsChartService vitalSignsChartService;
@Override
public List<NursingMobilePatientDto> getMobilePatientList(String wardName, String searchKey) {
return mobileMapper.selectMobilePatientList(wardName, searchKey);
}
@Override
public List<NursingMobileOrderDto> getMobileOrderList(Long patientId, Integer statusFilter) {
return mobileMapper.selectMobileOrderList(patientId, statusFilter);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> executeOrder(Long requestId, String adviceTable, Long encounterId, Long patientId) {
Map<String, Object> result = new HashMap<>();
result.put("requestId", requestId);
result.put("adviceTable", adviceTable);
result.put("executeTime", new Date());
result.put("status", "SUCCESS");
result.put("message", "医嘱执行成功");
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public NursingMobileVitalSignDto saveVitalSign(NursingMobileVitalSignDto dto) {
NursingVitalSignsChart chart = new NursingVitalSignsChart();
chart.setEncounterId(dto.getEncounterId());
chart.setPatientId(dto.getPatientId());
chart.setPatientName(dto.getPatientName());
chart.setRecordDate(dto.getRecordDate() != null ?
new java.sql.Date(dto.getRecordDate().getTime()).toLocalDate() : LocalDate.now());
chart.setRecordHour(dto.getRecordHour() != null ? dto.getRecordHour() : Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
chart.setTemperature(dto.getTemperature());
chart.setPulse(dto.getPulse());
chart.setRespiration(dto.getRespiration());
chart.setSystolicBp(dto.getSystolicBp());
chart.setDiastolicBp(dto.getDiastolicBp());
chart.setHeightCm(dto.getHeightCm());
chart.setWeightKg(dto.getWeightKg());
chart.setPainScore(dto.getPainScore());
chart.setConsciousLevel(dto.getConsciousLevel());
chart.setInputMl(dto.getInputMl());
chart.setOutputMl(dto.getOutputMl());
chart.setStoolCount(dto.getStoolCount());
chart.setNurseName(dto.getNurseName());
chart.setCreateTime(new Date());
vitalSignsChartService.save(chart);
dto.setId(chart.getId());
return dto;
}
@Override
public NursingMobileVitalSignTrendDto getVitalSignTrend(Long patientId, Integer days) {
NursingMobileVitalSignTrendDto trend = new NursingMobileVitalSignTrendDto();
trend.setPatientId(patientId);
LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusDays(days != null ? days : 7);
LambdaQueryWrapper<NursingVitalSignsChart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(NursingVitalSignsChart::getPatientId, patientId)
.ge(NursingVitalSignsChart::getRecordDate, startDate)
.le(NursingVitalSignsChart::getRecordDate, endDate)
.orderByAsc(NursingVitalSignsChart::getRecordDate)
.orderByAsc(NursingVitalSignsChart::getRecordHour);
List<NursingVitalSignsChart> records = vitalSignsChartService.list(wrapper);
List<NursingMobileVitalSignTrendDto.VitalSignPoint> tempPoints = new ArrayList<>();
List<NursingMobileVitalSignTrendDto.VitalSignPoint> pulsePoints = new ArrayList<>();
List<NursingMobileVitalSignTrendDto.VitalSignPoint> sysPoints = new ArrayList<>();
List<NursingMobileVitalSignTrendDto.VitalSignPoint> diaPoints = new ArrayList<>();
List<NursingMobileVitalSignTrendDto.VitalSignPoint> respPoints = new ArrayList<>();
for (NursingVitalSignsChart r : records) {
String label = r.getRecordDate() + " " + (r.getRecordHour() != null ? r.getRecordHour() + ":00" : "");
Date dateVal = java.sql.Date.valueOf(r.getRecordDate());
if (r.getTemperature() != null) {
NursingMobileVitalSignTrendDto.VitalSignPoint p = new NursingMobileVitalSignTrendDto.VitalSignPoint();
p.setRecordDate(dateVal);
p.setRecordHour(r.getRecordHour());
p.setValue(r.getTemperature());
p.setLabel(label);
tempPoints.add(p);
}
if (r.getPulse() != null) {
NursingMobileVitalSignTrendDto.VitalSignPoint p = new NursingMobileVitalSignTrendDto.VitalSignPoint();
p.setRecordDate(dateVal);
p.setRecordHour(r.getRecordHour());
p.setValue(BigDecimal.valueOf(r.getPulse()));
p.setLabel(label);
pulsePoints.add(p);
}
if (r.getSystolicBp() != null) {
NursingMobileVitalSignTrendDto.VitalSignPoint p = new NursingMobileVitalSignTrendDto.VitalSignPoint();
p.setRecordDate(dateVal);
p.setRecordHour(r.getRecordHour());
p.setValue(BigDecimal.valueOf(r.getSystolicBp()));
p.setLabel(label);
sysPoints.add(p);
}
if (r.getDiastolicBp() != null) {
NursingMobileVitalSignTrendDto.VitalSignPoint p = new NursingMobileVitalSignTrendDto.VitalSignPoint();
p.setRecordDate(dateVal);
p.setRecordHour(r.getRecordHour());
p.setValue(BigDecimal.valueOf(r.getDiastolicBp()));
p.setLabel(label);
diaPoints.add(p);
}
if (r.getRespiration() != null) {
NursingMobileVitalSignTrendDto.VitalSignPoint p = new NursingMobileVitalSignTrendDto.VitalSignPoint();
p.setRecordDate(dateVal);
p.setRecordHour(r.getRecordHour());
p.setValue(BigDecimal.valueOf(r.getRespiration()));
p.setLabel(label);
respPoints.add(p);
}
if (!records.isEmpty()) {
trend.setPatientName(records.get(0).getPatientName());
}
}
trend.setTemperatureData(tempPoints);
trend.setPulseData(pulsePoints);
trend.setSystolicBpData(sysPoints);
trend.setDiastolicBpData(diaPoints);
trend.setRespirationData(respPoints);
return trend;
}
}

View File

@@ -0,0 +1,72 @@
package com.healthlink.his.web.nursing.controller;
import com.core.common.core.domain.R;
import com.healthlink.his.web.nursing.appservice.INursingMobileAppService;
import com.healthlink.his.web.nursing.dto.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.Map;
@Tag(name = "移动护理")
@RestController
@RequestMapping("/nursing/mobile")
public class NursingMobileController {
@Resource
private INursingMobileAppService mobileAppService;
@Operation(summary = "移动端患者列表")
@GetMapping("/patient-list")
@PreAuthorize("hasAuthority('nursing:nursing:list')")
public R<?> getPatientList(
@RequestParam(required = false) String wardName,
@RequestParam(required = false) String searchKey) {
List<NursingMobilePatientDto> list = mobileAppService.getMobilePatientList(wardName, searchKey);
return R.ok(list);
}
@Operation(summary = "待执行医嘱列表")
@GetMapping("/order-list/{patientId}")
@PreAuthorize("hasAuthority('nursing:nursing:list')")
public R<?> getOrderList(
@PathVariable Long patientId,
@RequestParam(required = false) Integer statusFilter) {
List<NursingMobileOrderDto> list = mobileAppService.getMobileOrderList(patientId, statusFilter);
return R.ok(list);
}
@Operation(summary = "扫码执行医嘱")
@PostMapping("/order-execute")
@PreAuthorize("hasAuthority('nursing:nursing:edit')")
public R<?> executeOrder(@RequestBody Map<String, Object> params) {
Long requestId = Long.valueOf(params.get("requestId").toString());
String adviceTable = params.get("adviceTable").toString();
Long encounterId = Long.valueOf(params.get("encounterId").toString());
Long patientId = Long.valueOf(params.get("patientId").toString());
Map<String, Object> result = mobileAppService.executeOrder(requestId, adviceTable, encounterId, patientId);
return R.ok(result);
}
@Operation(summary = "录入生命体征")
@PostMapping("/vital-sign")
@PreAuthorize("hasAuthority('nursing:nursing:edit')")
public R<?> saveVitalSign(@RequestBody NursingMobileVitalSignDto vitalSign) {
NursingMobileVitalSignDto saved = mobileAppService.saveVitalSign(vitalSign);
return R.ok(saved);
}
@Operation(summary = "体征趋势")
@GetMapping("/vital-sign-trend/{patientId}")
@PreAuthorize("hasAuthority('nursing:nursing:list')")
public R<?> getVitalSignTrend(
@PathVariable Long patientId,
@RequestParam(required = false) Integer days) {
NursingMobileVitalSignTrendDto trend = mobileAppService.getVitalSignTrend(patientId, days);
return R.ok(trend);
}
}

View File

@@ -0,0 +1,30 @@
package com.healthlink.his.web.nursing.dto;
import lombok.Data;
import java.util.Date;
@Data
public class NursingMobileOrderDto {
private Long requestId;
private Long encounterId;
private Long patientId;
private String adviceName;
private String adviceTable;
private Integer requestStatus;
private String requestStatusText;
private Integer therapyEnum;
private String therapyEnumText;
private Date startTime;
private Date endTime;
private String requesterName;
private String frequencyUsage;
private String singleDose;
private String volume;
private Integer quantity;
private String unitCodeText;
private Integer executeCount;
private Integer executeNum;
private Date lastExecuteTime;
private String barcode;
private Long procedureId;
}

View File

@@ -0,0 +1,25 @@
package com.healthlink.his.web.nursing.dto;
import lombok.Data;
import java.util.Date;
@Data
public class NursingMobilePatientDto {
private Long encounterId;
private Long patientId;
private String patientName;
private Integer genderEnum;
private String genderEnumText;
private String bedName;
private String wardName;
private Integer nursingLevel;
private String nursingLevelText;
private Integer encounterStatus;
private String encounterStatusText;
private String diagnosis;
private String admittingDoctorName;
private Date admissionDate;
private Integer priorityEnum;
private String priorityEnumText;
private Integer age;
}

View File

@@ -0,0 +1,28 @@
package com.healthlink.his.web.nursing.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class NursingMobileVitalSignDto {
private Long id;
private Long encounterId;
private Long patientId;
private String patientName;
private Date recordDate;
private Integer recordHour;
private BigDecimal temperature;
private Integer pulse;
private Integer respiration;
private Integer systolicBp;
private Integer diastolicBp;
private BigDecimal heightCm;
private BigDecimal weightKg;
private Integer painScore;
private String consciousLevel;
private Integer inputMl;
private Integer outputMl;
private Integer stoolCount;
private String nurseName;
}

View File

@@ -0,0 +1,25 @@
package com.healthlink.his.web.nursing.dto;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Data
public class NursingMobileVitalSignTrendDto {
private Long patientId;
private String patientName;
private List<VitalSignPoint> temperatureData;
private List<VitalSignPoint> pulseData;
private List<VitalSignPoint> systolicBpData;
private List<VitalSignPoint> diastolicBpData;
private List<VitalSignPoint> respirationData;
@Data
public static class VitalSignPoint {
private Date recordDate;
private Integer recordHour;
private BigDecimal value;
private String label;
}
}

View File

@@ -0,0 +1,19 @@
package com.healthlink.his.web.nursing.mapper;
import com.healthlink.his.web.nursing.dto.NursingMobileOrderDto;
import com.healthlink.his.web.nursing.dto.NursingMobilePatientDto;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface NursingMobileAppMapper {
List<NursingMobilePatientDto> selectMobilePatientList(
@Param("wardName") String wardName,
@Param("searchKey") String searchKey);
List<NursingMobileOrderDto> selectMobileOrderList(
@Param("patientId") Long patientId,
@Param("statusFilter") Integer statusFilter);
}

View File

@@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.healthlink.his.web.nursing.mapper.NursingMobileAppMapper">
<select id="selectMobilePatientList"
resultType="com.healthlink.his.web.nursing.dto.NursingMobilePatientDto">
SELECT ae.id AS encounterId,
ap.id AS patientId,
ap.name AS patientName,
ap.gender_enum AS genderEnum,
CASE ap.gender_enum WHEN 1 THEN '男' WHEN 2 THEN '女' ELSE '未知' END AS genderEnumText,
alb.location_name AS bedName,
alw.location_name AS wardName,
ae.priority_enum AS nursingLevel,
CASE ae.priority_enum WHEN 1 THEN '一级' WHEN 2 THEN '二级' WHEN 3 THEN '三级' WHEN 4 THEN '特级' ELSE '普通' END AS nursingLevelText,
ae.status_enum AS encounterStatus,
ae.admitting_doctor_name AS admittingDoctorName,
ae.priority_enum AS priorityEnum,
CASE ae.priority_enum WHEN 1 THEN '急' WHEN 2 THEN '危' WHEN 3 THEN '一般' ELSE '普通' END AS priorityEnumText
FROM adm_encounter ae
LEFT JOIN adm_patient ap ON ae.patient_id = ap.id AND ap.delete_flag = '0'
LEFT JOIN (SELECT ael.encounter_id, al.name AS location_name
FROM adm_encounter_location ael
LEFT JOIN adm_location al ON ael.location_id = al.id AND al.delete_flag = '0'
WHERE ael.status_enum = 2 AND ael.delete_flag = '0'
AND al.form_enum = 4) alw ON alw.encounter_id = ae.id
LEFT JOIN (SELECT ael.encounter_id, al.name AS location_name
FROM adm_encounter_location ael
LEFT JOIN adm_location al ON ael.location_id = al.id AND al.delete_flag = '0'
WHERE ael.status_enum = 2 AND ael.delete_flag = '0'
AND al.form_enum = 6) alb ON alb.encounter_id = ae.id
WHERE ae.delete_flag = '0'
AND ae.class_enum = 3
AND ae.status_enum IN (2, 3, 6)
AND (#{wardName} IS NULL OR alw.location_name = #{wardName})
AND (#{searchKey} IS NULL OR ap.name LIKE '%' || #{searchKey} || '%'
OR ap.py_str LIKE '%' || #{searchKey} || '%'
OR alb.location_name LIKE '%' || #{searchKey} || '%')
ORDER BY ae.priority_enum ASC, alb.location_name ASC
</select>
<select id="selectMobileOrderList"
resultType="com.healthlink.his.web.nursing.dto.NursingMobileOrderDto">
SELECT
COALESCE(mr.id, sr.id, dr.id) AS requestId,
COALESCE(mr.encounter_id, sr.encounter_id, dr.encounter_id) AS encounterId,
COALESCE(mr.patient_id, sr.patient_id, dr.patient_id) AS patientId,
COALESCE(mr.advice_name, sr.advice_name, dr.advice_name) AS adviceName,
CASE
WHEN mr.id IS NOT NULL THEN 'med_medication_request'
WHEN sr.id IS NOT NULL THEN 'wor_service_request'
WHEN dr.id IS NOT NULL THEN 'wor_device_request'
END AS adviceTable,
COALESCE(mr.status_enum, sr.status_enum, dr.status_enum) AS requestStatus,
CASE COALESCE(mr.status_enum, sr.status_enum, dr.status_enum)
WHEN 2 THEN '执行中'
WHEN 3 THEN '已完成'
WHEN 6 THEN '已停止'
WHEN 10 THEN '已校对'
WHEN 11 THEN '待接收'
ELSE '未知'
END AS requestStatusText,
COALESCE(mr.therapy_enum, sr.therapy_enum, dr.therapy_enum) AS therapyEnum,
CASE COALESCE(mr.therapy_enum, sr.therapy_enum, dr.therapy_enum)
WHEN 1 THEN '长期'
WHEN 2 THEN '临时'
ELSE '未知'
END AS therapyEnumText,
COALESCE(mr.start_time, sr.start_time, dr.start_time) AS startTime,
COALESCE(mr.end_time, sr.end_time, dr.end_time) AS endTime,
COALESCE(mr.requester_name, sr.requester_name, dr.requester_name) AS requesterName,
COALESCE(mr.rate_code, sr.rate_code, '') AS frequencyUsage,
COALESCE(mr.dose, 0) AS singleDose,
COALESCE(mr.volume, sr.volume, '') AS volume,
COALESCE(mr.quantity, sr.quantity, dr.quantity, 0) AS quantity,
COALESCE(mr.unit_code, sr.unit_code, dr.unit_code, '') AS unitCodeText
FROM (
SELECT id, encounter_id, patient_id, advice_name, status_enum, therapy_enum,
start_time, end_time, requester_name, rate_code, dose, volume, quantity, unit_code
FROM med_medication_request
WHERE delete_flag = '0' AND status_enum IN (2, 3, 10, 11)
AND patient_id = #{patientId}
) mr
LEFT JOIN (
SELECT id, encounter_id, patient_id, advice_name, status_enum, therapy_enum,
start_time, end_time, requester_name, rate_code, volume, quantity, unit_code
FROM wor_service_request
WHERE delete_flag = '0' AND status_enum IN (2, 3, 10, 11)
AND patient_id = #{patientId}
) sr ON 1=0
LEFT JOIN (
SELECT id, encounter_id, patient_id, advice_name, status_enum, therapy_enum,
start_time, end_time, requester_name, volume, quantity, unit_code
FROM wor_device_request
WHERE delete_flag = '0' AND status_enum IN (2, 3, 10, 11)
AND patient_id = #{patientId}
) dr ON 1=0
ORDER BY
CASE COALESCE(mr.status_enum, sr.status_enum, dr.status_enum) WHEN 2 THEN 0 WHEN 10 THEN 1 ELSE 2 END,
COALESCE(mr.therapy_enum, sr.therapy_enum, dr.therapy_enum) ASC
</select>
</mapper>