diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrCompletenessAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrCompletenessAppService.java new file mode 100644 index 000000000..d7ea9d242 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrCompletenessAppService.java @@ -0,0 +1,13 @@ +package com.healthlink.his.web.emr.appservice; + +import com.healthlink.his.emr.domain.EmrCompletenessCheck; + +import java.util.List; +import java.util.Map; + +public interface IEmrCompletenessAppService { + + Map checkCompleteness(Long emrId, Long encounterId); + + List getCheckResults(Long emrId); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrRevisionAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrRevisionAppService.java new file mode 100644 index 000000000..e13489d18 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrRevisionAppService.java @@ -0,0 +1,14 @@ +package com.healthlink.his.web.emr.appservice; + +import com.healthlink.his.emr.domain.EmrRevision; + +import java.util.List; + +public interface IEmrRevisionAppService { + + EmrRevision recordRevision(EmrRevision revision); + + List getRevisions(Long emrId); + + EmrRevision getRevisionDetail(Long id); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrTimelinessAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrTimelinessAppService.java new file mode 100644 index 000000000..d7523e216 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrTimelinessAppService.java @@ -0,0 +1,14 @@ +package com.healthlink.his.web.emr.appservice; + +import com.healthlink.his.emr.domain.EmrTimeliness; +import com.healthlink.his.emr.dto.EmrTimelinessStatisticsDto; + +import java.util.List; +import java.util.Map; + +public interface IEmrTimelinessAppService { + + EmrTimelinessStatisticsDto checkTimeliness(Long encounterId); + + Map getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrVersionAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrVersionAppService.java new file mode 100644 index 000000000..94559aace --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/IEmrVersionAppService.java @@ -0,0 +1,15 @@ +package com.healthlink.his.web.emr.appservice; + +import com.healthlink.his.emr.domain.EmrVersion; + +import java.util.List; +import java.util.Map; + +public interface IEmrVersionAppService { + + EmrVersion saveVersion(EmrVersion version); + + List getVersions(Long emrId); + + Map compareVersions(Long versionId1, Long versionId2); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrCompletenessAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrCompletenessAppServiceImpl.java new file mode 100644 index 000000000..914b8d1e2 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrCompletenessAppServiceImpl.java @@ -0,0 +1,112 @@ +package com.healthlink.his.web.emr.appservice.impl; + +import com.healthlink.his.document.domain.Emr; +import com.healthlink.his.document.service.IEmrService; +import com.healthlink.his.emr.domain.EmrCompletenessCheck; +import com.healthlink.his.emr.service.IEmrCompletenessCheckService; +import com.healthlink.his.web.emr.appservice.IEmrCompletenessAppService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +@Service +public class EmrCompletenessAppServiceImpl implements IEmrCompletenessAppService { + + @Resource + private IEmrCompletenessCheckService emrCompletenessCheckService; + + @Resource + private IEmrService emrService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Map checkCompleteness(Long emrId, Long encounterId) { + Emr emr = emrService.getById(emrId); + if (emr == null) { + throw new IllegalArgumentException("病历不存在: " + emrId); + } + + List checks = new ArrayList<>(); + int total = 0; + int requiredPassed = 0; + int requiredTotal = 0; + + String[][] checkDefs = { + {"chief_complaint", "basic", "true", "主诉"}, + {"medical_history", "basic", "true", "现病史"}, + {"past_history", "basic", "false", "既往史"}, + {"physical_exam", "basic", "true", "体格检查"}, + {"auxiliary_exam", "examination", "false", "辅助检查"}, + {"diagnosis", "diagnosis", "true", "诊断"}, + {"treatment_plan", "treatment", "true", "治疗计划"}, + {"signature", "signature", "false", "签名"} + }; + + Map contentMap = parseContent(emr.getContextJson()); + + for (String[] def : checkDefs) { + total++; + boolean isRequired = Boolean.parseBoolean(def[2]); + if (isRequired) requiredTotal++; + + boolean hasValue = contentMap.containsKey(def[0]) + && contentMap.get(def[0]) != null + && !contentMap.get(def[0]).toString().trim().isEmpty(); + + String result = hasValue ? "PASS" : "FAIL"; + String detail = def[3] + (hasValue ? " - 已填写" : " - 未填写"); + if (!isRequired && !hasValue) { + detail = def[3] + " - 未填写(选填项)"; + } + + if (isRequired && hasValue) requiredPassed++; + + EmrCompletenessCheck check = new EmrCompletenessCheck() + .setEmrId(emrId) + .setEncounterId(encounterId) + .setCheckItem(def[0]) + .setCheckCategory(def[1]) + .setIsRequired(isRequired) + .setCheckResult(result) + .setCheckDetail(detail) + .setCheckTime(new Date()); + + emrCompletenessCheckService.save(check); + checks.add(check); + } + + Map result = new LinkedHashMap<>(); + result.put("emrId", emrId); + result.put("encounterId", encounterId); + result.put("totalItems", total); + result.put("requiredTotal", requiredTotal); + result.put("requiredPassed", requiredPassed); + result.put("requiredFailed", requiredTotal - requiredPassed); + result.put("isComplete", requiredPassed == requiredTotal); + result.put("checks", checks); + return result; + } + + @Override + public List getCheckResults(Long emrId) { + return emrCompletenessCheckService.selectByEmrId(emrId); + } + + private Map parseContent(String contextJson) { + Map map = new HashMap<>(); + if (contextJson == null || contextJson.isEmpty()) { + return map; + } + try { + com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); + @SuppressWarnings("unchecked") + Map parsed = mapper.readValue(contextJson, Map.class); + map.putAll(parsed); + } catch (Exception e) { + map.put("raw", contextJson); + } + return map; + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrRevisionAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrRevisionAppServiceImpl.java new file mode 100644 index 000000000..a37c5f195 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrRevisionAppServiceImpl.java @@ -0,0 +1,43 @@ +package com.healthlink.his.web.emr.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.healthlink.his.emr.domain.EmrRevision; +import com.healthlink.his.emr.service.IEmrRevisionService; +import com.healthlink.his.web.emr.appservice.IEmrRevisionAppService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; + +@Service +public class EmrRevisionAppServiceImpl implements IEmrRevisionAppService { + + @Resource + private IEmrRevisionService emrRevisionService; + + @Override + @Transactional(rollbackFor = Exception.class) + public EmrRevision recordRevision(EmrRevision revision) { + EmrRevision latest = emrRevisionService.selectLatest(revision.getEmrId()); + int nextNumber = (latest != null) ? latest.getRevisionNumber() + 1 : 1; + revision.setRevisionNumber(nextNumber); + if (revision.getEncounterId() == null && latest != null) { + revision.setEncounterId(latest.getEncounterId()); + } + revision.setCreateTime(new Date()); + emrRevisionService.save(revision); + return revision; + } + + @Override + public List getRevisions(Long emrId) { + return emrRevisionService.selectByEmrId(emrId); + } + + @Override + public EmrRevision getRevisionDetail(Long id) { + return emrRevisionService.getById(id); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrTimelinessAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrTimelinessAppServiceImpl.java new file mode 100644 index 000000000..b3a871098 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrTimelinessAppServiceImpl.java @@ -0,0 +1,84 @@ +package com.healthlink.his.web.emr.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.healthlink.his.emr.domain.EmrTimeliness; +import com.healthlink.his.emr.dto.EmrTimelinessStatisticsDto; +import com.healthlink.his.emr.service.IEmrTimelinessService; +import com.healthlink.his.web.emr.appservice.IEmrTimelinessAppService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +public class EmrTimelinessAppServiceImpl implements IEmrTimelinessAppService { + + @Resource + private IEmrTimelinessService emrTimelinessService; + + @Override + @Transactional(rollbackFor = Exception.class) + public EmrTimelinessStatisticsDto checkTimeliness(Long encounterId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + if (encounterId != null) { + wrapper.eq(EmrTimeliness::getEncounterId, encounterId); + } + wrapper.eq(EmrTimeliness::getStatus, "PENDING"); + List pendingList = emrTimelinessService.list(wrapper); + + Date now = new Date(); + int overdueCount = 0; + for (EmrTimeliness record : pendingList) { + if (record.getDeadlineTime() != null && now.after(record.getDeadlineTime())) { + record.setStatus("OVERDUE"); + emrTimelinessService.updateById(record); + overdueCount++; + } + } + + EmrTimelinessStatisticsDto stats = new EmrTimelinessStatisticsDto(); + LambdaQueryWrapper countWrapper = new LambdaQueryWrapper<>(); + if (encounterId != null) { + countWrapper.eq(EmrTimeliness::getEncounterId, encounterId); + } + long total = emrTimelinessService.count(countWrapper); + + countWrapper.eq(EmrTimeliness::getStatus, "COMPLETED"); + long completed = emrTimelinessService.count(countWrapper); + + countWrapper.eq(EmrTimeliness::getStatus, "OVERDUE"); + long overdue = emrTimelinessService.count(countWrapper); + + long pending = total - completed - overdue; + double rate = total > 0 ? Math.round(completed * 10000.0 / total) / 100.0 : 0; + + stats.setTotalCount(total); + stats.setCompletedCount(completed); + stats.setOverdueCount(overdue); + stats.setPendingCount(pending); + stats.setCompletionRate(rate); + return stats; + } + + @Override + public Map getTimelinessAlerts(String emrType, String status, String departmentName, int pageNum, int pageSize) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StringUtils.hasText(status), EmrTimeliness::getStatus, status); + wrapper.eq(StringUtils.hasText(emrType), EmrTimeliness::getEmrType, emrType); + wrapper.eq(StringUtils.hasText(departmentName), EmrTimeliness::getDepartmentName, departmentName); + wrapper.orderByAsc(EmrTimeliness::getDeadlineTime); + + Page page = emrTimelinessService.page(new Page<>(pageNum, pageSize), wrapper); + + Map result = new LinkedHashMap<>(); + result.put("total", page.getTotal()); + result.put("rows", page.getRecords()); + return result; + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrVersionAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrVersionAppServiceImpl.java new file mode 100644 index 000000000..91b1967d9 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/appservice/impl/EmrVersionAppServiceImpl.java @@ -0,0 +1,92 @@ +package com.healthlink.his.web.emr.appservice.impl; + +import com.healthlink.his.emr.domain.EmrVersion; +import com.healthlink.his.emr.service.IEmrVersionService; +import com.healthlink.his.web.emr.appservice.IEmrVersionAppService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +public class EmrVersionAppServiceImpl implements IEmrVersionAppService { + + @Resource + private IEmrVersionService emrVersionService; + + @Override + @Transactional(rollbackFor = Exception.class) + public EmrVersion saveVersion(EmrVersion version) { + EmrVersion latest = emrVersionService.selectLatest(version.getEmrId()); + int nextNumber = (latest != null) ? latest.getVersionNumber() + 1 : 1; + version.setVersionNumber(nextNumber); + + if (latest != null) { + version.setContentDiff(computeDiff(latest.getContentSnapshot(), version.getContentSnapshot())); + if (version.getEncounterId() == null) { + version.setEncounterId(latest.getEncounterId()); + } + } + version.setCreateTime(new Date()); + emrVersionService.save(version); + return version; + } + + @Override + public List getVersions(Long emrId) { + return emrVersionService.selectByEmrId(emrId); + } + + @Override + public Map compareVersions(Long versionId1, Long versionId2) { + EmrVersion v1 = emrVersionService.getById(versionId1); + EmrVersion v2 = emrVersionService.getById(versionId2); + if (v1 == null || v2 == null) { + throw new IllegalArgumentException("版本记录不存在"); + } + + String content1 = v1.getContentSnapshot() != null ? v1.getContentSnapshot() : ""; + String content2 = v2.getContentSnapshot() != null ? v2.getContentSnapshot() : ""; + + String diff = computeDiff(content1, content2); + + Map result = new LinkedHashMap<>(); + result.put("version1", v1); + result.put("version2", v2); + result.put("diff", diff); + return result; + } + + private String computeDiff(String oldContent, String newContent) { + if (oldContent == null) oldContent = ""; + if (newContent == null) newContent = ""; + + if (oldContent.equals(newContent)) { + return ""; + } + + StringBuilder diff = new StringBuilder(); + String[] oldLines = oldContent.split("\n"); + String[] newLines = newContent.split("\n"); + int maxLen = Math.max(oldLines.length, newLines.length); + + for (int i = 0; i < maxLen; i++) { + String oldLine = i < oldLines.length ? oldLines[i] : ""; + String newLine = i < newLines.length ? newLines[i] : ""; + + if (!oldLine.equals(newLine)) { + if (!oldLine.isEmpty()) { + diff.append("- ").append(oldLine).append("\n"); + } + if (!newLine.isEmpty()) { + diff.append("+ ").append(newLine).append("\n"); + } + } + } + return diff.toString(); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrCompletenessController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrCompletenessController.java new file mode 100644 index 000000000..322b55279 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrCompletenessController.java @@ -0,0 +1,38 @@ +package com.healthlink.his.web.emr.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.emr.appservice.IEmrCompletenessAppService; +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.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("/emr/completeness") +@Slf4j +@AllArgsConstructor +@Tag(name = "病历完整性检查") +public class EmrCompletenessController { + + private final IEmrCompletenessAppService emrCompletenessAppService; + + @PostMapping("/check") + @PreAuthorize("@ss.hasPermi('inpatient:emr:edit')") + @Operation(summary = "执行病历完整性检查") + public R> checkCompleteness( + @RequestParam("emrId") Long emrId, + @RequestParam("encounterId") Long encounterId) { + return R.ok(emrCompletenessAppService.checkCompleteness(emrId, encounterId)); + } + + @GetMapping("/results/{emrId}") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "获取完整性检查结果") + public R getCheckResults(@PathVariable Long emrId) { + return R.ok(emrCompletenessAppService.getCheckResults(emrId)); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrRevisionController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrRevisionController.java index a72bf7b41..dac0678a4 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrRevisionController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrRevisionController.java @@ -5,26 +5,47 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.core.common.core.domain.R; import com.healthlink.his.emr.domain.EmrRevision; import com.healthlink.his.emr.service.IEmrRevisionService; +import com.healthlink.his.web.emr.appservice.IEmrRevisionAppService; +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.transaction.annotation.Transactional; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import java.util.Date; import java.util.Map; /** * 病历修改留痕 Controller */ @RestController -@RequestMapping("/emr-revision") +@RequestMapping("/emr/revision") @Slf4j @AllArgsConstructor +@Tag(name = "病历修改留痕") public class EmrRevisionController { private final IEmrRevisionService revisionService; + private final IEmrRevisionAppService emrRevisionAppService; + + @PostMapping("/record") + @PreAuthorize("@ss.hasPermi('inpatient:emr:edit')") + @Operation(summary = "记录修改留痕") + public R recordRevision(@RequestBody EmrRevision revision) { + return R.ok(emrRevisionAppService.recordRevision(revision)); + } + + @GetMapping("/list/{emrId}") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "获取修改历史列表") + public R getRevisions(@PathVariable Long emrId) { + return R.ok(emrRevisionAppService.getRevisions(emrId)); + } + @GetMapping("/page") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "分页查询修改留痕") public R getPage( @RequestParam(value = "emrId", required = false) Long emrId, @RequestParam(value = "encounterId", required = false) Long encounterId, @@ -39,35 +60,16 @@ public class EmrRevisionController { return R.ok(revisionService.page(new Page<>(pageNo, pageSize), w)); } - @GetMapping("/list") - public R getList(@RequestParam("emrId") Long emrId) { - LambdaQueryWrapper w = new LambdaQueryWrapper<>(); - w.eq(EmrRevision::getEmrId, emrId) - .orderByAsc(EmrRevision::getRevisionNumber); - return R.ok(revisionService.list(w)); - } - @GetMapping("/{id}") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "获取修订详情") public R getById(@PathVariable Long id) { - return R.ok(revisionService.getById(id)); - } - - @PostMapping("/record") - @Transactional(rollbackFor = Exception.class) - public R recordRevision(@RequestBody EmrRevision revision) { - // 自动计算版本号 - LambdaQueryWrapper w = new LambdaQueryWrapper<>(); - w.eq(EmrRevision::getEmrId, revision.getEmrId()) - .orderByDesc(EmrRevision::getRevisionNumber) - .last("LIMIT 1"); - EmrRevision last = revisionService.getOne(w); - revision.setRevisionNumber(last == null ? 1 : last.getRevisionNumber() + 1); - revision.setCreateTime(new Date()); - revisionService.save(revision); - return R.ok(revision); + return R.ok(emrRevisionAppService.getRevisionDetail(id)); } @GetMapping("/compare") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "对比两个修订版本") public R compareRevisions( @RequestParam("revisionId1") Long id1, @RequestParam("revisionId2") Long id2) { diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrTimelinessController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrTimelinessController.java new file mode 100644 index 000000000..8fba25ac1 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrTimelinessController.java @@ -0,0 +1,44 @@ +package com.healthlink.his.web.emr.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.emr.domain.EmrTimeliness; +import com.healthlink.his.emr.dto.EmrTimelinessStatisticsDto; +import com.healthlink.his.web.emr.appservice.IEmrTimelinessAppService; +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.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("/emr/timeliness") +@Slf4j +@AllArgsConstructor +@Tag(name = "病历时限监控") +public class EmrTimelinessController { + + private final IEmrTimelinessAppService emrTimelinessAppService; + + @PostMapping("/check") + @PreAuthorize("@ss.hasPermi('inpatient:emr:edit')") + @Operation(summary = "执行病历时限检查") + public R checkTimeliness( + @RequestParam(value = "encounterId", required = false) Long encounterId) { + return R.ok(emrTimelinessAppService.checkTimeliness(encounterId)); + } + + @GetMapping("/alerts") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "获取病历时限提醒列表") + public R> getTimelinessAlerts( + @RequestParam(value = "emrType", required = false) String emrType, + @RequestParam(value = "status", required = false) String status, + @RequestParam(value = "departmentName", required = false) String departmentName, + @RequestParam(value = "pageNum", defaultValue = "1") int pageNum, + @RequestParam(value = "pageSize", defaultValue = "20") int pageSize) { + return R.ok(emrTimelinessAppService.getTimelinessAlerts(emrType, status, departmentName, pageNum, pageSize)); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrVersionController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrVersionController.java new file mode 100644 index 000000000..780f420ad --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrVersionController.java @@ -0,0 +1,44 @@ +package com.healthlink.his.web.emr.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.emr.domain.EmrVersion; +import com.healthlink.his.web.emr.appservice.IEmrVersionAppService; +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.web.bind.annotation.*; + +@RestController +@RequestMapping("/emr/version") +@Slf4j +@AllArgsConstructor +@Tag(name = "病历版本管理") +public class EmrVersionController { + + private final IEmrVersionAppService emrVersionAppService; + + @PostMapping("/save") + @PreAuthorize("@ss.hasPermi('inpatient:emr:edit')") + @Operation(summary = "保存病历版本") + public R saveVersion(@RequestBody EmrVersion version) { + return R.ok(emrVersionAppService.saveVersion(version)); + } + + @GetMapping("/list/{emrId}") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "获取病历版本列表") + public R getVersions(@PathVariable Long emrId) { + return R.ok(emrVersionAppService.getVersions(emrId)); + } + + @GetMapping("/compare") + @PreAuthorize("@ss.hasPermi('inpatient:emr:list')") + @Operation(summary = "对比两个版本") + public R compareVersions( + @RequestParam("versionId1") Long versionId1, + @RequestParam("versionId2") Long versionId2) { + return R.ok(emrVersionAppService.compareVersions(versionId1, versionId2)); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V64__emr_version_management.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V64__emr_version_management.sql new file mode 100644 index 000000000..7bda355cf --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V64__emr_version_management.sql @@ -0,0 +1,26 @@ +-- V64: 病历版本管理 + +CREATE TABLE IF NOT EXISTS emr_version ( + id BIGSERIAL PRIMARY KEY, + emr_id BIGINT NOT NULL, + encounter_id BIGINT NOT NULL, + version_number INT NOT NULL DEFAULT 1, + content_snapshot TEXT, + content_diff TEXT, + emr_type VARCHAR(50), + emr_title VARCHAR(200), + operator_id BIGINT, + operator_name VARCHAR(64), + save_reason VARCHAR(200), + tenant_id BIGINT DEFAULT 0, + delete_flag VARCHAR(1) DEFAULT '0', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +COMMENT ON TABLE emr_version IS '病历版本管理(不可删除,三甲评审要求)'; +COMMENT ON COLUMN emr_version.version_number IS '版本号,递增'; +COMMENT ON COLUMN emr_version.content_snapshot IS '完整内容快照'; +COMMENT ON COLUMN emr_version.content_diff IS '与上一版本的差异'; +COMMENT ON COLUMN emr_version.save_reason IS '保存原因'; +CREATE INDEX IF NOT EXISTS idx_ev_emr ON emr_version(emr_id); +CREATE INDEX IF NOT EXISTS idx_ev_encounter ON emr_version(encounter_id); +CREATE INDEX IF NOT EXISTS idx_ev_version ON emr_version(emr_id, version_number); diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/domain/EmrVersion.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/domain/EmrVersion.java new file mode 100644 index 000000000..9bc835389 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/domain/EmrVersion.java @@ -0,0 +1,37 @@ +package com.healthlink.his.emr.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.core.common.core.domain.HisBaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("emr_version") +@Accessors(chain = true) +public class EmrVersion extends HisBaseEntity { + + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + private Long emrId; + + private Long encounterId; + + private Integer versionNumber; + + private String contentSnapshot; + + private String contentDiff; + + private String emrType; + + private String emrTitle; + + private Long operatorId; + + private String operatorName; + + private String saveReason; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/mapper/EmrVersionMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/mapper/EmrVersionMapper.java new file mode 100644 index 000000000..3ec6d3487 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/mapper/EmrVersionMapper.java @@ -0,0 +1,16 @@ +package com.healthlink.his.emr.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.emr.domain.EmrVersion; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface EmrVersionMapper extends BaseMapper { + + List selectByEmrId(@Param("emrId") Long emrId); + + EmrVersion selectLatest(@Param("emrId") Long emrId); +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/IEmrVersionService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/IEmrVersionService.java new file mode 100644 index 000000000..15a35852a --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/IEmrVersionService.java @@ -0,0 +1,13 @@ +package com.healthlink.his.emr.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.emr.domain.EmrVersion; + +import java.util.List; + +public interface IEmrVersionService extends IService { + + List selectByEmrId(Long emrId); + + EmrVersion selectLatest(Long emrId); +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/impl/EmrVersionServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/impl/EmrVersionServiceImpl.java new file mode 100644 index 000000000..80c30ea74 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/impl/EmrVersionServiceImpl.java @@ -0,0 +1,25 @@ +package com.healthlink.his.emr.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.emr.domain.EmrVersion; +import com.healthlink.his.emr.mapper.EmrVersionMapper; +import com.healthlink.his.emr.service.IEmrVersionService; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class EmrVersionServiceImpl + extends ServiceImpl + implements IEmrVersionService { + + @Override + public List selectByEmrId(Long emrId) { + return baseMapper.selectByEmrId(emrId); + } + + @Override + public EmrVersion selectLatest(Long emrId) { + return baseMapper.selectLatest(emrId); + } +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/emr/EmrVersionMapper.xml b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/emr/EmrVersionMapper.xml new file mode 100644 index 000000000..1581a55d8 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/resources/mapper/emr/EmrVersionMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/healthlink-his-ui/src/api/anesthesia.js b/healthlink-his-ui/src/api/anesthesia.js index 73a3aef09..69ac8662c 100644 --- a/healthlink-his-ui/src/api/anesthesia.js +++ b/healthlink-his-ui/src/api/anesthesia.js @@ -93,3 +93,11 @@ export function saveAnesSummary(data) { export function getAnesSummary(recordId) { return request({ url: '/api/v1/anesthesia/summary/' + recordId, method: 'get' }) } + +export function recordPostopFollowup(data) { + return request({ url: '/api/v1/anesthesia/postop-followup', method: 'post', data }) +} + +export function getPostopFollowups(encounterId) { + return request({ url: '/api/v1/anesthesia/postop-followup/' + encounterId, method: 'get' }) +} diff --git a/healthlink-his-ui/src/api/emr.js b/healthlink-his-ui/src/api/emr.js index 9c4c0c511..feb01352e 100644 --- a/healthlink-his-ui/src/api/emr.js +++ b/healthlink-his-ui/src/api/emr.js @@ -2,6 +2,20 @@ import request from "@/utils/request" export function getTimelinessByEncounter(encounterId) { return request({ url: "/emr-revision/timeliness/" + encounterId, method: "get" }) } export function getTimelinessStatistics(params) { return request({ url: "/emr-revision/statistics", method: "get", params }) } export function getPendingEmrCount(params) { return request({ url: "/emr-archive/pending-count", method: "get", params }) } - -// 查询超期病历列表 export function getOverdueList(params) { return request({ url: "/emr-archive/overdue/list", method: "get", params }) } + +export function recordEmrRevision(data) { return request({ url: "/emr/revision/record", method: "post", data }) } +export function getEmrRevisionList(emrId) { return request({ url: "/emr/revision/list/" + emrId, method: "get" }) } +export function getEmrRevisionPage(params) { return request({ url: "/emr/revision/page", method: "get", params }) } +export function getEmrRevisionDetail(id) { return request({ url: "/emr/revision/" + id, method: "get" }) } +export function compareEmrRevisions(id1, id2) { return request({ url: "/emr/revision/compare", method: "get", params: { revisionId1: id1, revisionId2: id2 } }) } + +export function saveEmrVersion(data) { return request({ url: "/emr/version/save", method: "post", data }) } +export function getEmrVersionList(emrId) { return request({ url: "/emr/version/list/" + emrId, method: "get" }) } +export function compareEmrVersions(id1, id2) { return request({ url: "/emr/version/compare", method: "get", params: { versionId1: id1, versionId2: id2 } }) } + +export function checkCompleteness(emrId, encounterId) { return request({ url: "/emr/completeness/check", method: "post", params: { emrId, encounterId } }) } +export function getCompletenessResults(emrId) { return request({ url: "/emr/completeness/results/" + emrId, method: "get" }) } + +export function checkTimeliness(encounterId) { return request({ url: "/emr/timeliness/check", method: "post", params: { encounterId } }) } +export function getTimelinessAlerts(params) { return request({ url: "/emr/timeliness/alerts", method: "get", params }) } diff --git a/healthlink-his-ui/src/api/emr/index.js b/healthlink-his-ui/src/api/emr/index.js index c814665a3..3a7487460 100644 --- a/healthlink-his-ui/src/api/emr/index.js +++ b/healthlink-his-ui/src/api/emr/index.js @@ -1,4 +1,5 @@ import request from '@/utils/request' + export function createRevision(data) { return request({ url: '/api/v1/emr/revision', method: 'post', data }) } export function getRevisionHistory(emrId) { return request({ url: '/api/v1/emr/revision/' + emrId, method: 'get' }) } export function executeCompletenessCheck(emrId) { return request({ url: '/api/v1/emr/completeness-check/' + emrId, method: 'post' }) } @@ -7,3 +8,9 @@ export function getTimelinessByEncounter(encounterId) { return request({ url: '/ export function getOverdueList() { return request({ url: '/api/v1/emr/timeliness/overdue', method: 'get' }) } export function getTimelinessStatistics(params) { return request({ url: '/api/v1/emr/timeliness/statistics', method: 'get', params }) } export function checkTimeliness(data) { return request({ url: '/api/v1/emr/timeliness/check', method: 'post', data }) } + +export function recordEmrRevision(data) { return request({ url: '/emr/revision/record', method: 'post', data }) } +export function getEmrRevisionList(emrId) { return request({ url: '/emr/revision/list/' + emrId, method: 'get' }) } +export function getEmrRevisionPage(params) { return request({ url: '/emr/revision/page', method: 'get', params }) } +export function getEmrRevisionDetail(id) { return request({ url: '/emr/revision/' + id, method: 'get' }) } +export function compareEmrRevisions(id1, id2) { return request({ url: '/emr/revision/compare', method: 'get', params: { revisionId1: id1, revisionId2: id2 } }) } diff --git a/healthlink-his-ui/src/views/emr/completeness-check/index.vue b/healthlink-his-ui/src/views/emr/completeness-check/index.vue new file mode 100644 index 000000000..5ea97470e --- /dev/null +++ b/healthlink-his-ui/src/views/emr/completeness-check/index.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/healthlink-his-ui/src/views/inpatientDoctor/EmrRevisionTrack.vue b/healthlink-his-ui/src/views/inpatientDoctor/EmrRevisionTrack.vue new file mode 100644 index 000000000..e829a99e2 --- /dev/null +++ b/healthlink-his-ui/src/views/inpatientDoctor/EmrRevisionTrack.vue @@ -0,0 +1,277 @@ + + + + + diff --git a/healthlink-his-ui/src/views/inpatientDoctor/EmrTimelinessMonitor.vue b/healthlink-his-ui/src/views/inpatientDoctor/EmrTimelinessMonitor.vue new file mode 100644 index 000000000..138d6284d --- /dev/null +++ b/healthlink-his-ui/src/views/inpatientDoctor/EmrTimelinessMonitor.vue @@ -0,0 +1,254 @@ + + + + + diff --git a/healthlink-his-ui/src/views/inpatientDoctor/EmrVersionCompare.vue b/healthlink-his-ui/src/views/inpatientDoctor/EmrVersionCompare.vue new file mode 100644 index 000000000..98877f34e --- /dev/null +++ b/healthlink-his-ui/src/views/inpatientDoctor/EmrVersionCompare.vue @@ -0,0 +1,251 @@ + + + + +