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/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/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/emr.js b/healthlink-his-ui/src/api/emr.js index d25656c70..07f667130 100644 --- a/healthlink-his-ui/src/api/emr.js +++ b/healthlink-his-ui/src/api/emr.js @@ -9,3 +9,7 @@ export function getEmrRevisionList(emrId) { return request({ url: "/emr/revision 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 } }) } 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 @@ + + + + +