feat(anesthesia): add preop visit, intraop events, Aldrete scoring, enhance safety check
- AnesthesiaPreopVisit: pre-anesthesia assessment (术前访视) - AnesthesiaIntraopEvent: intraoperative events (插管/拔管/体位) - AnesthesiaAldreteScore: PACU Aldrete recovery scoring - SurgerySafetyCheckController: added @PreAuthorize and phase status/stats endpoints - Flyway V70 migration for new tables
This commit is contained in:
@@ -1,113 +1,130 @@
|
||||
package com.healthlink.his.web.anesthesia.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.healthlink.his.anesthesia.domain.*;
|
||||
import com.healthlink.his.anesthesia.service.*;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaAldreteScore;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaIntraopEvent;
|
||||
import com.healthlink.his.anesthesia.domain.AnesthesiaPreopVisit;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaAldreteScoreService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaIntraopEventService;
|
||||
import com.healthlink.his.anesthesia.service.IAnesthesiaPreopVisitService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/anesthesia-enhanced")
|
||||
@RequestMapping("/api/v1/anesthesia")
|
||||
@Tag(name = "麻醉扩展功能")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class AnesthesiaEnhancedController {
|
||||
|
||||
private final IAnesthesiaSpecimenService specimenService;
|
||||
private final IAnesthesiaPostopFollowupService followupService;
|
||||
private final IAnesthesiaQualityControlService qcService;
|
||||
private final IAnesthesiaPreopVisitService preopVisitService;
|
||||
private final IAnesthesiaIntraopEventService intraopEventService;
|
||||
private final IAnesthesiaAldreteScoreService aldreteScoreService;
|
||||
|
||||
// ==================== 标本管理 ====================
|
||||
@GetMapping("/specimen/page")
|
||||
public R<?> getSpecimenPage(
|
||||
@RequestParam(value = "patientName", required = false) String patientName,
|
||||
@RequestParam(value = "pathologyStatus", required = false) String status,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<AnesthesiaSpecimen> w = new LambdaQueryWrapper<>();
|
||||
w.like(StringUtils.hasText(patientName), AnesthesiaSpecimen::getPatientName, patientName)
|
||||
.eq(StringUtils.hasText(status), AnesthesiaSpecimen::getPathologyStatus, status)
|
||||
.orderByDesc(AnesthesiaSpecimen::getCreateTime);
|
||||
return R.ok(specimenService.page(new Page<>(pageNo, pageSize), w));
|
||||
}
|
||||
|
||||
@PostMapping("/specimen/add")
|
||||
@PostMapping("/preop-visit")
|
||||
@Operation(summary = "保存麻醉前评估(术前访视)")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> addSpecimen(@RequestBody AnesthesiaSpecimen s) {
|
||||
s.setPathologyStatus("PENDING");
|
||||
s.setCreateTime(new Date());
|
||||
specimenService.save(s);
|
||||
return R.ok(s);
|
||||
public R<AnesthesiaPreopVisit> savePreopVisit(@RequestBody AnesthesiaPreopVisit visit) {
|
||||
if (visit.getId() != null) {
|
||||
preopVisitService.updateById(visit);
|
||||
} else {
|
||||
visit.setCreateTime(new Date());
|
||||
preopVisitService.save(visit);
|
||||
}
|
||||
return R.ok(visit);
|
||||
}
|
||||
|
||||
@PostMapping("/specimen/report")
|
||||
@GetMapping("/preop-visit/record/{recordId}")
|
||||
@Operation(summary = "查询麻醉前评估列表")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<AnesthesiaPreopVisit>> getPreopVisits(@PathVariable Long recordId) {
|
||||
return R.ok(preopVisitService.selectByRecordId(recordId));
|
||||
}
|
||||
|
||||
@GetMapping("/preop-visit/encounter/{encounterId}")
|
||||
@Operation(summary = "按就诊查询麻醉前评估")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<AnesthesiaPreopVisit>> getPreopVisitsByEncounter(@PathVariable Long encounterId) {
|
||||
return R.ok(preopVisitService.selectByEncounterId(encounterId));
|
||||
}
|
||||
|
||||
@PostMapping("/intraop-event")
|
||||
@Operation(summary = "记录术中事件(插管/拔管/体位)")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> reportSpecimen(@RequestBody Map<String, Object> params) {
|
||||
Long id = Long.valueOf(params.get("id").toString());
|
||||
AnesthesiaSpecimen s = specimenService.getById(id);
|
||||
if (s == null) return R.fail("标本不存在");
|
||||
s.setPathologyStatus("REPORTED");
|
||||
s.setPathologyResult((String) params.get("pathologyResult"));
|
||||
s.setReportTime(new Date());
|
||||
s.setUpdateTime(new Date());
|
||||
specimenService.updateById(s);
|
||||
return R.ok();
|
||||
public R<AnesthesiaIntraopEvent> saveIntraopEvent(@RequestBody AnesthesiaIntraopEvent event) {
|
||||
if (event.getId() != null) {
|
||||
intraopEventService.updateById(event);
|
||||
} else {
|
||||
event.setCreateTime(new Date());
|
||||
intraopEventService.save(event);
|
||||
}
|
||||
return R.ok(event);
|
||||
}
|
||||
|
||||
// ==================== 术后随访 ====================
|
||||
@GetMapping("/followup/page")
|
||||
public R<?> getFollowupPage(
|
||||
@RequestParam(value = "followupType", required = false) String type,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<AnesthesiaPostopFollowup> w = new LambdaQueryWrapper<>();
|
||||
w.eq(StringUtils.hasText(type), AnesthesiaPostopFollowup::getFollowupType, type)
|
||||
.orderByDesc(AnesthesiaPostopFollowup::getFollowupTime);
|
||||
return R.ok(followupService.page(new Page<>(pageNo, pageSize), w));
|
||||
@GetMapping("/intraop-event/{recordId}")
|
||||
@Operation(summary = "查询术中事件列表")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<AnesthesiaIntraopEvent>> getIntraopEvents(@PathVariable Long recordId) {
|
||||
return R.ok(intraopEventService.selectByRecordId(recordId));
|
||||
}
|
||||
|
||||
@PostMapping("/followup/add")
|
||||
@PostMapping("/aldrete-score")
|
||||
@Operation(summary = "保存麻醉复苏评分(PACU Aldrete)")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> addFollowup(@RequestBody AnesthesiaPostopFollowup f) {
|
||||
f.setStatus(0);
|
||||
f.setCreateTime(new Date());
|
||||
followupService.save(f);
|
||||
return R.ok(f);
|
||||
public R<AnesthesiaAldreteScore> saveAldreteScore(@RequestBody AnesthesiaAldreteScore score) {
|
||||
int total = (score.getActivityScore() != null ? score.getActivityScore() : 0)
|
||||
+ (score.getRespirationScore() != null ? score.getRespirationScore() : 0)
|
||||
+ (score.getCirculationScore() != null ? score.getCirculationScore() : 0)
|
||||
+ (score.getConsciousnessScore() != null ? score.getConsciousnessScore() : 0)
|
||||
+ (score.getSpo2Score() != null ? score.getSpo2Score() : 0);
|
||||
score.setTotalScore(total);
|
||||
if (total >= 9) {
|
||||
score.setRiskLevel("NORMAL");
|
||||
} else if (total >= 7) {
|
||||
score.setRiskLevel("WARNING");
|
||||
} else {
|
||||
score.setRiskLevel("CRITICAL");
|
||||
}
|
||||
if (score.getId() != null) {
|
||||
aldreteScoreService.updateById(score);
|
||||
} else {
|
||||
score.setCreateTime(new Date());
|
||||
aldreteScoreService.save(score);
|
||||
}
|
||||
return R.ok(score);
|
||||
}
|
||||
|
||||
// ==================== 麻醉质控 ====================
|
||||
@GetMapping("/qc/page")
|
||||
public R<?> getQcPage(
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<AnesthesiaQualityControl> w = new LambdaQueryWrapper<>();
|
||||
w.orderByDesc(AnesthesiaQualityControl::getCreateTime);
|
||||
return R.ok(qcService.page(new Page<>(pageNo, pageSize), w));
|
||||
@GetMapping("/aldrete-score/{recordId}")
|
||||
@Operation(summary = "查询复苏评分列表")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<AnesthesiaAldreteScore>> getAldreteScores(@PathVariable Long recordId) {
|
||||
return R.ok(aldreteScoreService.selectByRecordId(recordId));
|
||||
}
|
||||
|
||||
@PostMapping("/qc/add")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> addQc(@RequestBody AnesthesiaQualityControl qc) {
|
||||
qc.setStatus(0);
|
||||
qc.setCreateTime(new Date());
|
||||
qcService.save(qc);
|
||||
return R.ok(qc);
|
||||
}
|
||||
|
||||
@GetMapping("/qc/stats")
|
||||
public R<?> getQcStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("total", qcService.count());
|
||||
LambdaQueryWrapper<AnesthesiaQualityControl> w = new LambdaQueryWrapper<>();
|
||||
w.isNotNull(AnesthesiaQualityControl::getComplications);
|
||||
w.ne(AnesthesiaQualityControl::getComplications, "");
|
||||
stats.put("withComplications", qcService.count(w));
|
||||
return R.ok(stats);
|
||||
@GetMapping("/aldrete-score/summary/{recordId}")
|
||||
@Operation(summary = "复苏评分趋势汇总")
|
||||
@PreAuthorize("@ss.hasPermi('inpatient:anesthesia:list')")
|
||||
public R<List<Map<String, Object>>> getAldreteTrend(@PathVariable Long recordId) {
|
||||
List<AnesthesiaAldreteScore> scores = aldreteScoreService.selectByRecordId(recordId);
|
||||
return R.ok(scores.stream().map(s -> {
|
||||
Map<String, Object> m = new HashMap<>();
|
||||
m.put("assessTime", s.getAssessTime());
|
||||
m.put("totalScore", s.getTotalScore());
|
||||
m.put("riskLevel", s.getRiskLevel());
|
||||
return m;
|
||||
}).toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,19 +5,26 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.healthlink.his.surgicalschedule.domain.SurgerySafetyCheck;
|
||||
import com.healthlink.his.surgicalschedule.service.ISurgerySafetyCheckService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 术前安全核查 Controller (WS/T 313)
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/surgery-safety-check")
|
||||
@Tag(name = "手术安全核查")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class SurgerySafetyCheckController {
|
||||
@@ -25,6 +32,8 @@ public class SurgerySafetyCheckController {
|
||||
private final ISurgerySafetyCheckService safetyCheckService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页查询安全核查")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:list')")
|
||||
public R<?> getPage(
|
||||
@RequestParam(value = "patientName", required = false) String patientName,
|
||||
@RequestParam(value = "encounterId", required = false) Long encounterId,
|
||||
@@ -40,6 +49,8 @@ public class SurgerySafetyCheckController {
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询安全核查列表")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:list')")
|
||||
public R<?> getList(@RequestParam("encounterId") Long encounterId) {
|
||||
LambdaQueryWrapper<SurgerySafetyCheck> w = new LambdaQueryWrapper<>();
|
||||
w.eq(SurgerySafetyCheck::getEncounterId, encounterId)
|
||||
@@ -48,11 +59,15 @@ public class SurgerySafetyCheckController {
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取安全核查详情")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:list')")
|
||||
public R<?> getById(@PathVariable Long id) {
|
||||
return R.ok(safetyCheckService.getById(id));
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增安全核查")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> add(@RequestBody SurgerySafetyCheck check) {
|
||||
check.setCreateTime(new Date());
|
||||
@@ -61,6 +76,8 @@ public class SurgerySafetyCheckController {
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新安全核查")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:edit')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> update(@RequestBody SurgerySafetyCheck check) {
|
||||
safetyCheckService.updateById(check);
|
||||
@@ -68,9 +85,45 @@ public class SurgerySafetyCheckController {
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete/{id}")
|
||||
@Operation(summary = "删除安全核查")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:remove')")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> delete(@PathVariable Long id) {
|
||||
safetyCheckService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/phase-status/{encounterId}")
|
||||
@Operation(summary = "查询各阶段核查完成状态")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:list')")
|
||||
public R<?> getPhaseStatus(@PathVariable Long encounterId) {
|
||||
LambdaQueryWrapper<SurgerySafetyCheck> w = new LambdaQueryWrapper<>();
|
||||
w.eq(SurgerySafetyCheck::getEncounterId, encounterId);
|
||||
List<SurgerySafetyCheck> checks = safetyCheckService.list(w);
|
||||
Map<String, Boolean> phaseMap = new HashMap<>();
|
||||
phaseMap.put("sign_in", false);
|
||||
phaseMap.put("time_out", false);
|
||||
phaseMap.put("sign_out", false);
|
||||
for (SurgerySafetyCheck c : checks) {
|
||||
if (Boolean.TRUE.equals(c.getChecklistCompleted()) && c.getCheckPhase() != null) {
|
||||
phaseMap.put(c.getCheckPhase(), true);
|
||||
}
|
||||
}
|
||||
return R.ok(phaseMap);
|
||||
}
|
||||
|
||||
@GetMapping("/compliance-stats")
|
||||
@Operation(summary = "安全核查合规统计")
|
||||
@PreAuthorize("@ss.hasPermi('surgery:schedule:list')")
|
||||
public R<?> getComplianceStats() {
|
||||
LambdaQueryWrapper<SurgerySafetyCheck> w = new LambdaQueryWrapper<>();
|
||||
long total = safetyCheckService.count(w);
|
||||
w.eq(SurgerySafetyCheck::getChecklistCompleted, true);
|
||||
long completed = safetyCheckService.count(w);
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("total", total);
|
||||
stats.put("completed", completed);
|
||||
stats.put("complianceRate", total > 0 ? (completed * 100.0 / total) : 0);
|
||||
return R.ok(stats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
-- V70: 麻醉扩展功能表(术前访视、术中事件、复苏评分)
|
||||
|
||||
-- 麻醉前评估(术前访视)
|
||||
CREATE TABLE IF NOT EXISTS anes_preop_visit (
|
||||
id BIGINT PRIMARY KEY,
|
||||
record_id BIGINT,
|
||||
encounter_id BIGINT,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(64),
|
||||
visit_doctor_id BIGINT,
|
||||
visit_doctor_name VARCHAR(64),
|
||||
visit_time TIMESTAMP,
|
||||
chief_complaint TEXT,
|
||||
present_illness TEXT,
|
||||
past_history TEXT,
|
||||
allergy_history TEXT,
|
||||
airway_assessment VARCHAR(255),
|
||||
asa_grade VARCHAR(16),
|
||||
cardiovascular_status VARCHAR(255),
|
||||
respiratory_status VARCHAR(255),
|
||||
neurological_status VARCHAR(255),
|
||||
hepatorenal_function VARCHAR(255),
|
||||
coagulation_status VARCHAR(255),
|
||||
fasting_status VARCHAR(32),
|
||||
npo_hours VARCHAR(16),
|
||||
difficult_airway_risk VARCHAR(255),
|
||||
anesthesia_risk VARCHAR(255),
|
||||
risk_factors TEXT,
|
||||
recommendation TEXT,
|
||||
status VARCHAR(16) DEFAULT 'DRAFT',
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
tenant_id INT DEFAULT 0,
|
||||
delete_flag VARCHAR(1) DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 术中事件记录(插管/拔管/体位)
|
||||
CREATE TABLE IF NOT EXISTS anes_intraop_event (
|
||||
id BIGINT PRIMARY KEY,
|
||||
record_id BIGINT,
|
||||
encounter_id BIGINT,
|
||||
patient_id BIGINT,
|
||||
event_type VARCHAR(32),
|
||||
event_detail TEXT,
|
||||
event_time TIMESTAMP,
|
||||
operator_name VARCHAR(64),
|
||||
position VARCHAR(64),
|
||||
intubation_type VARCHAR(64),
|
||||
intubation_result VARCHAR(64),
|
||||
extubation_reason VARCHAR(255),
|
||||
extubation_result VARCHAR(64),
|
||||
complication TEXT,
|
||||
action TEXT,
|
||||
remark TEXT,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
tenant_id INT DEFAULT 0,
|
||||
delete_flag VARCHAR(1) DEFAULT '0'
|
||||
);
|
||||
|
||||
-- 麻醉复苏评分(PACU Aldrete)
|
||||
CREATE TABLE IF NOT EXISTS anes_alldrete_score (
|
||||
id BIGINT PRIMARY KEY,
|
||||
record_id BIGINT,
|
||||
encounter_id BIGINT,
|
||||
patient_id BIGINT,
|
||||
patient_name VARCHAR(64),
|
||||
assess_time TIMESTAMP,
|
||||
activity_score INT DEFAULT 0,
|
||||
respiration_score INT DEFAULT 0,
|
||||
circulation_score INT DEFAULT 0,
|
||||
consciousness_score INT DEFAULT 0,
|
||||
spo2_score INT DEFAULT 0,
|
||||
total_score INT DEFAULT 0,
|
||||
risk_level VARCHAR(16),
|
||||
assessor_id BIGINT,
|
||||
assessor_name VARCHAR(64),
|
||||
remark TEXT,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP,
|
||||
tenant_id INT DEFAULT 0,
|
||||
delete_flag VARCHAR(1) DEFAULT '0'
|
||||
);
|
||||
Reference in New Issue
Block a user