diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrArchiveController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrArchiveController.java new file mode 100644 index 000000000..5de332ee4 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrArchiveController.java @@ -0,0 +1,102 @@ +package com.healthlink.his.web.emr.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.emr.domain.EmrArchiveRecord; +import com.healthlink.his.emr.service.IEmrArchiveRecordService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +/** + * 病历打印归档 Controller + */ +@RestController +@RequestMapping("/emr-archive") +@Slf4j +@AllArgsConstructor +public class EmrArchiveController { + + private final IEmrArchiveRecordService archiveService; + + @GetMapping("/page") + public R getPage( + @RequestParam(value = "patientName", required = false) String patientName, + @RequestParam(value = "emrType", required = false) String emrType, + @RequestParam(value = "archiveStatus", required = false) String archiveStatus, + @RequestParam(value = "encounterId", required = false) Long encounterId, + @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) { + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + w.like(StringUtils.hasText(patientName), EmrArchiveRecord::getPatientName, patientName) + .eq(StringUtils.hasText(emrType), EmrArchiveRecord::getEmrType, emrType) + .eq(StringUtils.hasText(archiveStatus), EmrArchiveRecord::getArchiveStatus, archiveStatus) + .eq(encounterId != null, EmrArchiveRecord::getEncounterId, encounterId) + .orderByDesc(EmrArchiveRecord::getCreateTime); + return R.ok(archiveService.page(new Page<>(pageNo, pageSize), w)); + } + + @PostMapping("/print") + @Transactional(rollbackFor = Exception.class) + public R recordPrint(@RequestBody EmrArchiveRecord record) { + record.setArchiveType("PRINT"); + record.setArchiveStatus("PRINTED"); + record.setPrintTime(new Date()); + record.setPrintCount(1); + record.setCreateTime(new Date()); + archiveService.save(record); + return R.ok(record); + } + + @PutMapping("/archive/{id}") + @Transactional(rollbackFor = Exception.class) + public R archive(@PathVariable Long id, @RequestParam("archivedBy") String archivedBy) { + EmrArchiveRecord record = archiveService.getById(id); + if (record == null) return R.fail("归档记录不存在"); + record.setArchiveStatus("ARCHIVED"); + record.setArchiveTime(new Date()); + record.setArchivedBy(archivedBy); + archiveService.updateById(record); + return R.ok(record); + } + + @PutMapping("/reprint/{id}") + @Transactional(rollbackFor = Exception.class) + public R reprint(@PathVariable Long id) { + EmrArchiveRecord record = archiveService.getById(id); + if (record == null) return R.fail("归档记录不存在"); + record.setPrintCount(record.getPrintCount() + 1); + archiveService.updateById(record); + return R.ok(record); + } + + @GetMapping("/stats") + public R getArchiveStats(@RequestParam(required = false) Long encounterId) { + Map stats = new HashMap<>(); + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + if (encounterId != null) w.eq(EmrArchiveRecord::getEncounterId, encounterId); + stats.put("total", archiveService.count(w)); + + w.eq(EmrArchiveRecord::getArchiveStatus, "ARCHIVED"); + stats.put("archived", archiveService.count(w)); + + w.eq(EmrArchiveRecord::getArchiveStatus, "PRINTED"); + stats.put("printed", archiveService.count(w)); + + // 24h归档率 + LambdaQueryWrapper w24 = new LambdaQueryWrapper<>(); + if (encounterId != null) w24.eq(EmrArchiveRecord::getEncounterId, encounterId); + w24.ge(EmrArchiveRecord::getCreateTime, new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000)); + long total24h = archiveService.count(w24); + w24.eq(EmrArchiveRecord::getArchiveStatus, "ARCHIVED"); + long archived24h = archiveService.count(w24); + stats.put("archiveRate24h", total24h > 0 ? Math.round(archived24h * 100.0 / total24h) : 100); + + return R.ok(stats); + } +} 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 new file mode 100644 index 000000000..a72bf7b41 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emr/controller/EmrRevisionController.java @@ -0,0 +1,79 @@ +package com.healthlink.his.web.emr.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.emr.domain.EmrRevision; +import com.healthlink.his.emr.service.IEmrRevisionService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.Map; + +/** + * 病历修改留痕 Controller + */ +@RestController +@RequestMapping("/emr-revision") +@Slf4j +@AllArgsConstructor +public class EmrRevisionController { + + private final IEmrRevisionService revisionService; + + @GetMapping("/page") + public R getPage( + @RequestParam(value = "emrId", required = false) Long emrId, + @RequestParam(value = "encounterId", required = false) Long encounterId, + @RequestParam(value = "operatorName", required = false) String operatorName, + @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) { + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + w.eq(emrId != null, EmrRevision::getEmrId, emrId) + .eq(encounterId != null, EmrRevision::getEncounterId, encounterId) + .like(operatorName != null, EmrRevision::getOperatorName, operatorName) + .orderByDesc(EmrRevision::getCreateTime); + 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}") + 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); + } + + @GetMapping("/compare") + public R compareRevisions( + @RequestParam("revisionId1") Long id1, + @RequestParam("revisionId2") Long id2) { + EmrRevision r1 = revisionService.getById(id1); + EmrRevision r2 = revisionService.getById(id2); + if (r1 == null || r2 == null) return R.fail("修订记录不存在"); + return R.ok(Map.of("revision1", r1, "revision2", r2)); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V27__emr_revision_archive.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V27__emr_revision_archive.sql new file mode 100644 index 000000000..120b7513a --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V27__emr_revision_archive.sql @@ -0,0 +1,29 @@ +-- V27: 电子病历增强 — 修改留痕+打印归档 + +-- 病历打印归档记录表 +CREATE TABLE IF NOT EXISTS emr_archive_record ( + id BIGSERIAL PRIMARY KEY, + emr_id BIGINT NOT NULL, + encounter_id BIGINT NOT NULL, + patient_id BIGINT NOT NULL, + patient_name VARCHAR(50), + emr_type VARCHAR(50), + emr_title VARCHAR(200), + archive_type VARCHAR(20) NOT NULL, + print_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + print_by VARCHAR(64), + print_count INT DEFAULT 1, + archive_status VARCHAR(20) DEFAULT 'PRINTED', + archive_time TIMESTAMP, + archived_by VARCHAR(64), + file_path VARCHAR(500), + tenant_id BIGINT DEFAULT 0, + is_deleted INT NOT NULL DEFAULT 0, + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); +COMMENT ON TABLE emr_archive_record IS '病历打印归档记录'; +COMMENT ON COLUMN emr_archive_record.archive_type IS '归档类型(PRINT打印/ARCHIVE归档/REPRINT补打)'; +COMMENT ON COLUMN emr_archive_record.archive_status IS '状态(PRINTED已打印/ARCHIVED已归档/LOST遗失)'; +CREATE INDEX idx_ear_encounter ON emr_archive_record(encounter_id); +CREATE INDEX idx_ear_patient ON emr_archive_record(patient_id); +CREATE INDEX idx_ear_status ON emr_archive_record(archive_status); diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/domain/EmrArchiveRecord.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/domain/EmrArchiveRecord.java new file mode 100644 index 000000000..ec4bf9933 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/domain/EmrArchiveRecord.java @@ -0,0 +1,33 @@ +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 java.util.Date; + +/** + * 病历打印归档记录 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("emr_archive_record") +public class EmrArchiveRecord extends HisBaseEntity { + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + private Long emrId; + private Long encounterId; + private Long patientId; + private String patientName; + private String emrType; + private String emrTitle; + private String archiveType; + private Date printTime; + private String printBy; + private Integer printCount; + private String archiveStatus; + private Date archiveTime; + private String archivedBy; + private String filePath; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/mapper/EmrArchiveRecordMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/mapper/EmrArchiveRecordMapper.java new file mode 100644 index 000000000..47ed492ee --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/mapper/EmrArchiveRecordMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.emr.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.emr.domain.EmrArchiveRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface EmrArchiveRecordMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/IEmrArchiveRecordService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/IEmrArchiveRecordService.java new file mode 100644 index 000000000..9bd91ba8b --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/IEmrArchiveRecordService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.emr.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.emr.domain.EmrArchiveRecord; + +public interface IEmrArchiveRecordService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/impl/EmrArchiveRecordServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/impl/EmrArchiveRecordServiceImpl.java new file mode 100644 index 000000000..1dd5fec3b --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/emr/service/impl/EmrArchiveRecordServiceImpl.java @@ -0,0 +1,13 @@ +package com.healthlink.his.emr.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.emr.domain.EmrArchiveRecord; +import com.healthlink.his.emr.mapper.EmrArchiveRecordMapper; +import com.healthlink.his.emr.service.IEmrArchiveRecordService; +import org.springframework.stereotype.Service; + +@Service +public class EmrArchiveRecordServiceImpl + extends ServiceImpl + implements IEmrArchiveRecordService { +} diff --git a/healthlink-his-ui/src/views/emr/archive/api.js b/healthlink-his-ui/src/views/emr/archive/api.js new file mode 100644 index 000000000..12f501c83 --- /dev/null +++ b/healthlink-his-ui/src/views/emr/archive/api.js @@ -0,0 +1,6 @@ +import request from '@/utils/request' +export function getArchivePage(p){return request({url:'/emr-archive/page',method:'get',params:p})} +export function recordPrint(d){return request({url:'/emr-archive/print',method:'post',data:d})} +export function archive(id,archivedBy){return request({url:'/emr-archive/archive/'+id,method:'put',params:{archivedBy}})} +export function reprint(id){return request({url:'/emr-archive/reprint/'+id,method:'put'})} +export function getArchiveStats(p){return request({url:'/emr-archive/stats',method:'get',params:p})} diff --git a/healthlink-his-ui/src/views/emr/archive/index.vue b/healthlink-his-ui/src/views/emr/archive/index.vue new file mode 100644 index 000000000..fbefbd2cb --- /dev/null +++ b/healthlink-his-ui/src/views/emr/archive/index.vue @@ -0,0 +1,64 @@ + + diff --git a/healthlink-his-ui/src/views/emr/revision-history/api.js b/healthlink-his-ui/src/views/emr/revision-history/api.js new file mode 100644 index 000000000..e3c0dbbc8 --- /dev/null +++ b/healthlink-his-ui/src/views/emr/revision-history/api.js @@ -0,0 +1,5 @@ +import request from '@/utils/request' +export function getRevisionPage(p){return request({url:'/emr-revision/page',method:'get',params:p})} +export function getRevisionList(p){return request({url:'/emr-revision/list',method:'get',params:p})} +export function recordRevision(d){return request({url:'/emr-revision/record',method:'post',data:d})} +export function compareRevisions(id1,id2){return request({url:'/emr-revision/compare',method:'get',params:{revisionId1:id1,revisionId2:id2}})} diff --git a/healthlink-his-ui/src/views/emr/revision-history/index.vue b/healthlink-his-ui/src/views/emr/revision-history/index.vue index fb695eb78..4b5f06ac8 100644 --- a/healthlink-his-ui/src/views/emr/revision-history/index.vue +++ b/healthlink-his-ui/src/views/emr/revision-history/index.vue @@ -1,49 +1,52 @@