feat(P0): 电子病历增强+病案管理+护理评估增强+FHIR/CDA标准接口
V18 Flyway迁移 — 10张新表: - emr_search_index: 病历全文检索索引 - mr_borrowing: 病案借阅管理(申请/审批/归还) - mr_sealing: 病案封存管理(主动/纠纷/司法封存+解封) - mr_tracking: 病案示踪(在架/借出/归档/遗失) - mr_death_discussion: 死亡病例讨论(7天期限+超时预警) - nursing_assessment_reminder: 护理评估提醒(跌倒/压疮/营养/疼痛/管道) - nursing_care_plan: 护理计划(诊断/目标/措施/评价) - esb_fhir_resource: FHIR R4资源存储(Patient/Encounter/Observation等) - esb_cda_document: CDA临床文档架构(admission/discharge/lab等) - esb_code_mapping: 标准编码映射 后端Controller: - MrManagementController: 借阅/封存/示踪/死亡讨论完整CRUD - NursingEnhancedController: 评估提醒/护理计划/质量指标 - FhirCdaController: FHIR资源CRUD+CDA文档+编码映射+翻译 - EmrSearchController: 多维度病历检索(关键词/患者/类型/医生/科室) 前端页面: - mrmanagement: Tab页(借阅/封存/示踪/死亡讨论) - nursingenhanced: Tab页(评估提醒/护理计划/质量指标) - fhircda: Tab页(FHIR资源/CDA文档/编码映射) - emrsearch: 多维度病历检索页 所有模块编译通过 (mvn clean compile -DskipTests)
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
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.EmrSearchIndex;
|
||||
import com.healthlink.his.emr.service.IEmrSearchIndexService;
|
||||
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-search")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class EmrSearchController {
|
||||
|
||||
private final IEmrSearchIndexService searchIndexService;
|
||||
|
||||
/**
|
||||
* 多维度病历检索
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public R<?> search(
|
||||
@RequestParam(value = "keyword", required = false) String keyword,
|
||||
@RequestParam(value = "patientName", required = false) String patientName,
|
||||
@RequestParam(value = "emrType", required = false) String emrType,
|
||||
@RequestParam(value = "doctorName", required = false) String doctorName,
|
||||
@RequestParam(value = "departmentName", required = false) String departmentName,
|
||||
@RequestParam(value = "startDate", required = false) String startDate,
|
||||
@RequestParam(value = "endDate", required = false) String endDate,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<EmrSearchIndex> wrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
wrapper.and(w -> w.like(EmrSearchIndex::getDiagnosisText, keyword)
|
||||
.or().like(EmrSearchIndex::getEmrTitle, keyword)
|
||||
.or().like(EmrSearchIndex::getPatientName, keyword));
|
||||
}
|
||||
wrapper.like(StringUtils.hasText(patientName), EmrSearchIndex::getPatientName, patientName)
|
||||
.eq(StringUtils.hasText(emrType), EmrSearchIndex::getEmrType, emrType)
|
||||
.like(StringUtils.hasText(doctorName), EmrSearchIndex::getDoctorName, doctorName)
|
||||
.like(StringUtils.hasText(departmentName), EmrSearchIndex::getDepartmentName, departmentName)
|
||||
.ge(StringUtils.hasText(startDate), EmrSearchIndex::getCreateTime, startDate)
|
||||
.le(StringUtils.hasText(endDate), EmrSearchIndex::getCreateTime, endDate)
|
||||
.orderByDesc(EmrSearchIndex::getCreateTime);
|
||||
return R.ok(searchIndexService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 索引病历(新增/修改时调用)
|
||||
*/
|
||||
@PostMapping("/index")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> indexEmr(@RequestBody EmrSearchIndex index) {
|
||||
// 检查是否已存在
|
||||
LambdaQueryWrapper<EmrSearchIndex> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(EmrSearchIndex::getEmrId, index.getEmrId());
|
||||
EmrSearchIndex existing = searchIndexService.getOne(wrapper);
|
||||
if (existing != null) {
|
||||
existing.setPatientName(index.getPatientName());
|
||||
existing.setEmrType(index.getEmrType());
|
||||
existing.setEmrTitle(index.getEmrTitle());
|
||||
existing.setDiagnosisText(index.getDiagnosisText());
|
||||
existing.setDoctorName(index.getDoctorName());
|
||||
existing.setDepartmentName(index.getDepartmentName());
|
||||
existing.setUpdateTime(new Date());
|
||||
searchIndexService.updateById(existing);
|
||||
return R.ok(existing);
|
||||
} else {
|
||||
index.setCreateTime(new Date());
|
||||
searchIndexService.save(index);
|
||||
return R.ok(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检索统计
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public R<?> getStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("totalIndexed", searchIndexService.count());
|
||||
// 按类型统计
|
||||
LambdaQueryWrapper<EmrSearchIndex> wrapper = new LambdaQueryWrapper<>();
|
||||
String[] types = {"admission", "daily", "discharge", "surgery", "consultation"};
|
||||
for (String type : types) {
|
||||
wrapper.clear();
|
||||
wrapper.eq(EmrSearchIndex::getEmrType, type);
|
||||
stats.put("type_" + type, searchIndexService.count(wrapper));
|
||||
}
|
||||
return R.ok(stats);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.healthlink.his.web.esbmanage.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.esb.domain.CdaDocument;
|
||||
import com.healthlink.his.esb.domain.CodeMapping;
|
||||
import com.healthlink.his.esb.domain.FhirResource;
|
||||
import com.healthlink.his.esb.service.ICdaDocumentService;
|
||||
import com.healthlink.his.esb.service.ICodeMappingService;
|
||||
import com.healthlink.his.esb.service.IFhirResourceService;
|
||||
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.*;
|
||||
|
||||
/**
|
||||
* FHIR/CDA标准接口Controller — 互联互通测评核心
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/fhir-cda")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class FhirCdaController {
|
||||
|
||||
private final IFhirResourceService fhirService;
|
||||
private final ICdaDocumentService cdaService;
|
||||
private final ICodeMappingService mappingService;
|
||||
|
||||
// ==================== FHIR资源 ====================
|
||||
|
||||
@GetMapping("/fhir/page")
|
||||
public R<?> getFhirPage(
|
||||
@RequestParam(value = "resourceType", required = false) String resourceType,
|
||||
@RequestParam(value = "patientId", required = false) Long patientId,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<FhirResource> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(StringUtils.hasText(resourceType), FhirResource::getResourceType, resourceType)
|
||||
.eq(patientId != null, FhirResource::getPatientId, patientId)
|
||||
.orderByDesc(FhirResource::getCreateTime);
|
||||
return R.ok(fhirService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/fhir/create")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> createFhirResource(@RequestBody FhirResource resource) {
|
||||
resource.setStatus("ACTIVE");
|
||||
resource.setVersionId(1);
|
||||
resource.setCreateTime(new Date());
|
||||
fhirService.save(resource);
|
||||
return R.ok(resource);
|
||||
}
|
||||
|
||||
@PutMapping("/fhir/update")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> updateFhirResource(@RequestBody FhirResource resource) {
|
||||
FhirResource existing = fhirService.getById(resource.getId());
|
||||
if (existing == null) return R.fail("资源不存在");
|
||||
resource.setVersionId(existing.getVersionId() + 1);
|
||||
resource.setUpdateTime(new Date());
|
||||
fhirService.updateById(resource);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@DeleteMapping("/fhir/delete")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> deleteFhirResource(@RequestParam Long id) {
|
||||
fhirService.removeById(id);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/fhir/type-stats")
|
||||
public R<?> getFhirTypeStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
String[] types = {"Patient", "Encounter", "Observation", "Condition", "MedicationRequest"};
|
||||
for (String type : types) {
|
||||
LambdaQueryWrapper<FhirResource> w = new LambdaQueryWrapper<>();
|
||||
w.eq(FhirResource::getResourceType, type);
|
||||
stats.put(type, fhirService.count(w));
|
||||
}
|
||||
stats.put("total", fhirService.count());
|
||||
return R.ok(stats);
|
||||
}
|
||||
|
||||
// ==================== CDA文档 ====================
|
||||
|
||||
@GetMapping("/cda/page")
|
||||
public R<?> getCdaPage(
|
||||
@RequestParam(value = "documentType", required = false) String documentType,
|
||||
@RequestParam(value = "status", required = false) String status,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<CdaDocument> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(StringUtils.hasText(documentType), CdaDocument::getDocumentType, documentType)
|
||||
.eq(StringUtils.hasText(status), CdaDocument::getStatus, status)
|
||||
.orderByDesc(CdaDocument::getCreateTime);
|
||||
return R.ok(cdaService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/cda/create")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> createCdaDocument(@RequestBody CdaDocument doc) {
|
||||
doc.setStatus("DRAFT");
|
||||
doc.setVersionId(1);
|
||||
doc.setCreateTime(new Date());
|
||||
cdaService.save(doc);
|
||||
return R.ok(doc);
|
||||
}
|
||||
|
||||
@PostMapping("/cda/publish")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> publishCdaDocument(@RequestParam Long id) {
|
||||
CdaDocument doc = cdaService.getById(id);
|
||||
if (doc == null) return R.fail("文档不存在");
|
||||
doc.setStatus("PUBLISHED");
|
||||
doc.setUpdateTime(new Date());
|
||||
cdaService.updateById(doc);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 编码映射 ====================
|
||||
|
||||
@GetMapping("/mapping/page")
|
||||
public R<?> getMappingPage(
|
||||
@RequestParam(value = "mappingType", required = false) String mappingType,
|
||||
@RequestParam(value = "sourceSystem", required = false) String sourceSystem,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<CodeMapping> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(StringUtils.hasText(mappingType), CodeMapping::getMappingType, mappingType)
|
||||
.eq(StringUtils.hasText(sourceSystem), CodeMapping::getSourceSystem, sourceSystem)
|
||||
.orderByDesc(CodeMapping::getCreateTime);
|
||||
return R.ok(mappingService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/mapping/add")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> addMapping(@RequestBody CodeMapping mapping) {
|
||||
mapping.setCreateTime(new Date());
|
||||
mappingService.save(mapping);
|
||||
return R.ok(mapping);
|
||||
}
|
||||
|
||||
@PostMapping("/mapping/translate")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> translateCode(@RequestBody Map<String, String> params) {
|
||||
LambdaQueryWrapper<CodeMapping> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CodeMapping::getSourceSystem, params.get("sourceSystem"))
|
||||
.eq(CodeMapping::getSourceCode, params.get("sourceCode"))
|
||||
.eq(CodeMapping::getMappingType, params.get("mappingType"));
|
||||
CodeMapping mapping = mappingService.getOne(wrapper);
|
||||
if (mapping == null) return R.fail("未找到映射关系");
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("targetCode", mapping.getTargetCode());
|
||||
result.put("targetSystem", mapping.getTargetSystem());
|
||||
result.put("description", mapping.getDescription());
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.healthlink.his.web.mrhomepage.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.mrhomepage.domain.*;
|
||||
import com.healthlink.his.mrhomepage.service.*;
|
||||
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("/mr-management")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class MrManagementController {
|
||||
|
||||
private final IMrBorrowingService borrowingService;
|
||||
private final IMrSealingService sealingService;
|
||||
private final IMrTrackingService trackingService;
|
||||
private final IMrDeathDiscussionService deathDiscussionService;
|
||||
|
||||
// ==================== 病案借阅 ====================
|
||||
|
||||
@GetMapping("/borrowing/page")
|
||||
public R<?> getBorrowingPage(
|
||||
@RequestParam(value = "status", required = false) Integer status,
|
||||
@RequestParam(value = "borrowerName", required = false) String borrowerName,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<MrBorrowing> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(status != null, MrBorrowing::getStatus, status)
|
||||
.like(StringUtils.hasText(borrowerName), MrBorrowing::getBorrowerName, borrowerName)
|
||||
.orderByDesc(MrBorrowing::getCreateTime);
|
||||
return R.ok(borrowingService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/borrowing/apply")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> applyBorrowing(@RequestBody MrBorrowing borrowing) {
|
||||
borrowing.setStatus(0);
|
||||
borrowing.setBorrowDate(new Date());
|
||||
borrowing.setCreateTime(new Date());
|
||||
borrowingService.save(borrowing);
|
||||
return R.ok(borrowing);
|
||||
}
|
||||
|
||||
@PostMapping("/borrowing/approve")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> approveBorrowing(@RequestBody Map<String, Object> params) {
|
||||
Long id = Long.valueOf(params.get("id").toString());
|
||||
String approver = (String) params.get("approverName");
|
||||
Boolean approved = Boolean.valueOf(params.get("approved").toString());
|
||||
MrBorrowing borrowing = borrowingService.getById(id);
|
||||
if (borrowing == null) return R.fail("借阅记录不存在");
|
||||
borrowing.setApproverName(approver);
|
||||
borrowing.setApproveTime(new Date());
|
||||
borrowing.setStatus(approved ? 1 : 5);
|
||||
borrowing.setUpdateTime(new Date());
|
||||
borrowingService.updateById(borrowing);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/borrowing/return")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> returnBorrowing(@RequestParam Long id) {
|
||||
MrBorrowing borrowing = borrowingService.getById(id);
|
||||
if (borrowing == null) return R.fail("借阅记录不存在");
|
||||
borrowing.setActualReturnDate(new Date());
|
||||
borrowing.setStatus(3);
|
||||
borrowing.setUpdateTime(new Date());
|
||||
borrowingService.updateById(borrowing);
|
||||
// 更新示踪状态
|
||||
LambdaQueryWrapper<MrTracking> tw = new LambdaQueryWrapper<>();
|
||||
tw.eq(MrTracking::getMedicalRecordId, borrowing.getMedicalRecordId())
|
||||
.eq(MrTracking::getStatus, "BORROWED");
|
||||
MrTracking track = trackingService.getOne(tw);
|
||||
if (track != null) {
|
||||
track.setStatus("IN_SHELF");
|
||||
track.setLocation("病案室");
|
||||
track.setLocationType("STORAGE");
|
||||
track.setMovedBy("系统自动归还");
|
||||
track.setMoveTime(new Date());
|
||||
trackingService.updateById(track);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 病案封存 ====================
|
||||
|
||||
@GetMapping("/sealing/page")
|
||||
public R<?> getSealingPage(
|
||||
@RequestParam(value = "status", required = false) Integer status,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<MrSealing> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(status != null, MrSealing::getStatus, status)
|
||||
.orderByDesc(MrSealing::getCreateTime);
|
||||
return R.ok(sealingService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/sealing/seal")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> sealRecord(@RequestBody MrSealing sealing) {
|
||||
sealing.setStatus(0);
|
||||
sealing.setSealDate(new Date());
|
||||
sealing.setCreateTime(new Date());
|
||||
sealingService.save(sealing);
|
||||
return R.ok(sealing);
|
||||
}
|
||||
|
||||
@PostMapping("/sealing/unseal")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> unsealRecord(@RequestBody Map<String, Object> params) {
|
||||
Long id = Long.valueOf(params.get("id").toString());
|
||||
String unsealBy = (String) params.get("unsealBy");
|
||||
String reason = (String) params.get("unsealReason");
|
||||
MrSealing sealing = sealingService.getById(id);
|
||||
if (sealing == null) return R.fail("封存记录不存在");
|
||||
sealing.setStatus(1);
|
||||
sealing.setUnsealDate(new Date());
|
||||
sealing.setUnsealBy(unsealBy);
|
||||
sealing.setUnsealReason(reason);
|
||||
sealing.setUpdateTime(new Date());
|
||||
sealingService.updateById(sealing);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ==================== 病案示踪 ====================
|
||||
|
||||
@GetMapping("/tracking/page")
|
||||
public R<?> getTrackingPage(
|
||||
@RequestParam(value = "status", required = false) String status,
|
||||
@RequestParam(value = "mrNumber", required = false) String mrNumber,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<MrTracking> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(StringUtils.hasText(status), MrTracking::getStatus, status)
|
||||
.like(StringUtils.hasText(mrNumber), MrTracking::getMrNumber, mrNumber)
|
||||
.orderByDesc(MrTracking::getMoveTime);
|
||||
return R.ok(trackingService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/tracking/move")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> moveRecord(@RequestBody MrTracking tracking) {
|
||||
tracking.setMoveTime(new Date());
|
||||
tracking.setCreateTime(new Date());
|
||||
trackingService.save(tracking);
|
||||
return R.ok(tracking);
|
||||
}
|
||||
|
||||
@GetMapping("/tracking/stats")
|
||||
public R<?> getTrackingStats() {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
LambdaQueryWrapper<MrTracking> w1 = new LambdaQueryWrapper<>();
|
||||
w1.eq(MrTracking::getStatus, "IN_SHELF");
|
||||
stats.put("inShelf", trackingService.count(w1));
|
||||
w1.eq(MrTracking::getStatus, "BORROWED");
|
||||
stats.put("borrowed", trackingService.count(w1));
|
||||
w1.eq(MrTracking::getStatus, "ARCHIVED");
|
||||
stats.put("archived", trackingService.count(w1));
|
||||
w1.eq(MrTracking::getStatus, "LOST");
|
||||
stats.put("lost", trackingService.count(w1));
|
||||
return R.ok(stats);
|
||||
}
|
||||
|
||||
// ==================== 死亡病例讨论 ====================
|
||||
|
||||
@GetMapping("/death-discussion/page")
|
||||
public R<?> getDeathDiscussionPage(
|
||||
@RequestParam(value = "status", required = false) Integer status,
|
||||
@RequestParam(value = "isOverdue", required = false) Boolean isOverdue,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<MrDeathDiscussion> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(status != null, MrDeathDiscussion::getStatus, status)
|
||||
.eq(isOverdue != null, MrDeathDiscussion::getIsOverdue, isOverdue)
|
||||
.orderByDesc(MrDeathDiscussion::getDeathDate);
|
||||
return R.ok(deathDiscussionService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/death-discussion/add")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> addDeathDiscussion(@RequestBody MrDeathDiscussion discussion) {
|
||||
discussion.setStatus(0);
|
||||
discussion.setIsOverdue(false);
|
||||
// 7天期限
|
||||
if (discussion.getDeathDate() != null) {
|
||||
discussion.setDeadlineDate(new Date(discussion.getDeathDate().getTime() + 7L * 24 * 60 * 60 * 1000));
|
||||
}
|
||||
discussion.setCreateTime(new Date());
|
||||
deathDiscussionService.save(discussion);
|
||||
return R.ok(discussion);
|
||||
}
|
||||
|
||||
@PostMapping("/death-discussion/complete")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> completeDeathDiscussion(@RequestBody MrDeathDiscussion discussion) {
|
||||
MrDeathDiscussion existing = deathDiscussionService.getById(discussion.getId());
|
||||
if (existing == null) return R.fail("讨论记录不存在");
|
||||
existing.setStatus(1);
|
||||
existing.setDiscussionDate(new Date());
|
||||
existing.setDiscussionConclusion(discussion.getDiscussionConclusion());
|
||||
existing.setImprovementMeasures(discussion.getImprovementMeasures());
|
||||
existing.setParticipants(discussion.getParticipants());
|
||||
existing.setUpdateTime(new Date());
|
||||
deathDiscussionService.updateById(existing);
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.healthlink.his.web.nursing.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.nursing.domain.NursingAssessmentReminder;
|
||||
import com.healthlink.his.nursing.domain.NursingCarePlan;
|
||||
import com.healthlink.his.nursing.service.INursingAssessmentReminderService;
|
||||
import com.healthlink.his.nursing.service.INursingCarePlanService;
|
||||
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.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/nursing-enhanced")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class NursingEnhancedController {
|
||||
|
||||
private final INursingAssessmentReminderService reminderService;
|
||||
private final INursingCarePlanService carePlanService;
|
||||
|
||||
@GetMapping("/reminder/page")
|
||||
public R<?> getReminderPage(
|
||||
@RequestParam(value = "status", required = false) Integer status,
|
||||
@RequestParam(value = "encounterId", required = false) Long encounterId,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<NursingAssessmentReminder> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(status != null, NursingAssessmentReminder::getStatus, status)
|
||||
.eq(encounterId != null, NursingAssessmentReminder::getEncounterId, encounterId)
|
||||
.orderByAsc(NursingAssessmentReminder::getNextDeadline);
|
||||
return R.ok(reminderService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/reminder/add")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> addReminder(@RequestBody NursingAssessmentReminder reminder) {
|
||||
reminder.setStatus(0);
|
||||
reminder.setCreateTime(new Date());
|
||||
reminderService.save(reminder);
|
||||
return R.ok(reminder);
|
||||
}
|
||||
|
||||
@PostMapping("/reminder/complete")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> completeReminder(@RequestParam Long id) {
|
||||
NursingAssessmentReminder reminder = reminderService.getById(id);
|
||||
if (reminder == null) return R.fail("提醒记录不存在");
|
||||
reminder.setStatus(1);
|
||||
reminder.setLastAssessTime(new Date());
|
||||
int hours = reminder.getFrequencyHours() != null ? reminder.getFrequencyHours() : 24;
|
||||
reminder.setNextDeadline(new Date(System.currentTimeMillis() + (long) hours * 60 * 60 * 1000));
|
||||
reminder.setUpdateTime(new Date());
|
||||
reminderService.updateById(reminder);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/reminder/overdue")
|
||||
public R<?> getOverdueReminders() {
|
||||
LambdaQueryWrapper<NursingAssessmentReminder> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.le(NursingAssessmentReminder::getNextDeadline, new Date())
|
||||
.ne(NursingAssessmentReminder::getStatus, 1);
|
||||
return R.ok(reminderService.list(wrapper));
|
||||
}
|
||||
|
||||
@GetMapping("/care-plan/page")
|
||||
public R<?> getCarePlanPage(
|
||||
@RequestParam(value = "status", required = false) String status,
|
||||
@RequestParam(value = "encounterId", required = false) Long encounterId,
|
||||
@RequestParam(value = "patientName", required = false) String patientName,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
|
||||
LambdaQueryWrapper<NursingCarePlan> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(StringUtils.hasText(status), NursingCarePlan::getStatus, status)
|
||||
.eq(encounterId != null, NursingCarePlan::getEncounterId, encounterId)
|
||||
.like(StringUtils.hasText(patientName), NursingCarePlan::getPatientName, patientName)
|
||||
.orderByDesc(NursingCarePlan::getCreateTime);
|
||||
return R.ok(carePlanService.page(new Page<>(pageNo, pageSize), wrapper));
|
||||
}
|
||||
|
||||
@PostMapping("/care-plan/add")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> addCarePlan(@RequestBody NursingCarePlan plan) {
|
||||
plan.setStatus("NEW");
|
||||
plan.setDelFlag("0");
|
||||
plan.setCreateTime(new Date());
|
||||
carePlanService.save(plan);
|
||||
return R.ok(plan);
|
||||
}
|
||||
|
||||
@PostMapping("/care-plan/evaluate")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> evaluateCarePlan(@RequestBody Map<String, Object> params) {
|
||||
Long id = Long.valueOf(params.get("id").toString());
|
||||
String eval = (String) params.get("evaluationResult");
|
||||
NursingCarePlan plan = carePlanService.getById(id);
|
||||
if (plan == null) return R.fail("护理计划不存在");
|
||||
plan.setEvaluation(eval);
|
||||
plan.setStatus("COMPLETED");
|
||||
plan.setUpdateTime(new Date());
|
||||
carePlanService.updateById(plan);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/quality/stats")
|
||||
public R<?> getQualityStats(@RequestParam(required = false) Long encounterId) {
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
LambdaQueryWrapper<NursingAssessmentReminder> rw = new LambdaQueryWrapper<>();
|
||||
if (encounterId != null) rw.eq(NursingAssessmentReminder::getEncounterId, encounterId);
|
||||
stats.put("totalReminders", reminderService.count(rw));
|
||||
rw.eq(NursingAssessmentReminder::getStatus, 1);
|
||||
stats.put("completedReminders", reminderService.count(rw));
|
||||
rw.eq(NursingAssessmentReminder::getStatus, 2);
|
||||
stats.put("overdueReminders", reminderService.count(rw));
|
||||
|
||||
LambdaQueryWrapper<NursingCarePlan> cw = new LambdaQueryWrapper<>();
|
||||
if (encounterId != null) cw.eq(NursingCarePlan::getEncounterId, encounterId);
|
||||
stats.put("totalPlans", carePlanService.count(cw));
|
||||
cw.eq(NursingCarePlan::getStatus, "COMPLETED");
|
||||
stats.put("completedPlans", carePlanService.count(cw));
|
||||
return R.ok(stats);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
-- V18: P0模块补全 — 电子病历增强+病案管理+护理评估+FHIR/CDA
|
||||
|
||||
-- 1. 病历检索索引表
|
||||
CREATE TABLE IF NOT EXISTS emr_search_index (
|
||||
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),
|
||||
diagnosis_text TEXT,
|
||||
doctor_name VARCHAR(50),
|
||||
department_name VARCHAR(100),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_emr_search_patient ON emr_search_index(patient_id);
|
||||
CREATE INDEX idx_emr_search_diagnosis ON emr_search_index USING GIN(to_tsvector('simple', COALESCE(diagnosis_text, '')));
|
||||
CREATE INDEX idx_emr_search_type ON emr_search_index(emr_type);
|
||||
COMMENT ON TABLE emr_search_index IS '病历全文检索索引';
|
||||
|
||||
-- 2. 病案借阅表
|
||||
CREATE TABLE IF NOT EXISTS mr_borrowing (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
medical_record_id BIGINT NOT NULL,
|
||||
patient_name VARCHAR(50),
|
||||
mr_number VARCHAR(50),
|
||||
borrower_name VARCHAR(50) NOT NULL,
|
||||
borrower_dept VARCHAR(100),
|
||||
borrow_reason TEXT NOT NULL,
|
||||
borrow_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expected_return_date TIMESTAMP,
|
||||
actual_return_date TIMESTAMP,
|
||||
status INT NOT NULL DEFAULT 0,
|
||||
approver_name VARCHAR(50),
|
||||
approve_time TIMESTAMP,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE mr_borrowing IS '病案借阅管理';
|
||||
COMMENT ON COLUMN mr_borrowing.status IS '状态(0待审批 1已批准 2已借出 3已归还 4已逾期 5已拒绝)';
|
||||
CREATE INDEX idx_mr_borrow_status ON mr_borrowing(status);
|
||||
|
||||
-- 3. 病案封存表
|
||||
CREATE TABLE IF NOT EXISTS mr_sealing (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
medical_record_id BIGINT NOT NULL,
|
||||
patient_name VARCHAR(50),
|
||||
mr_number VARCHAR(50),
|
||||
seal_reason TEXT NOT NULL,
|
||||
seal_type INT NOT NULL DEFAULT 1,
|
||||
seal_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
seal_by VARCHAR(50) NOT NULL,
|
||||
unseal_date TIMESTAMP,
|
||||
unseal_by VARCHAR(50),
|
||||
unseal_reason TEXT,
|
||||
status INT NOT NULL DEFAULT 0,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE mr_sealing IS '病案封存管理';
|
||||
COMMENT ON COLUMN mr_sealing.seal_type IS '封存类型(1主动封存 2纠纷封存 3司法封存)';
|
||||
COMMENT ON COLUMN mr_sealing.status IS '状态(0已封存 1已解封)';
|
||||
|
||||
-- 4. 病案示踪表
|
||||
CREATE TABLE IF NOT EXISTS mr_tracking (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
medical_record_id BIGINT NOT NULL,
|
||||
mr_number VARCHAR(50),
|
||||
patient_name VARCHAR(50),
|
||||
location VARCHAR(100) NOT NULL,
|
||||
location_type VARCHAR(50) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'IN_SHELF',
|
||||
moved_by VARCHAR(50),
|
||||
move_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE mr_tracking IS '病案示踪管理';
|
||||
COMMENT ON COLUMN mr_tracking.status IS '状态(IN_SHELF在架/BORROWED借出/ARCHIVED归档/LOST遗失)';
|
||||
CREATE INDEX idx_mr_track_status ON mr_tracking(status);
|
||||
|
||||
-- 5. 死亡病例讨论表
|
||||
CREATE TABLE IF NOT EXISTS mr_death_discussion (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
patient_id BIGINT NOT NULL,
|
||||
patient_name VARCHAR(50),
|
||||
encounter_id BIGINT NOT NULL,
|
||||
death_date TIMESTAMP,
|
||||
discussion_date TIMESTAMP,
|
||||
deadline_date TIMESTAMP,
|
||||
host_doctor_id BIGINT,
|
||||
host_doctor_name VARCHAR(50),
|
||||
host_title VARCHAR(50),
|
||||
participants TEXT,
|
||||
discussion_conclusion TEXT,
|
||||
improvement_measures TEXT,
|
||||
status INT NOT NULL DEFAULT 0,
|
||||
is_overdue BOOLEAN DEFAULT FALSE,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE mr_death_discussion IS '死亡病例讨论';
|
||||
COMMENT ON COLUMN mr_death_discussion.status IS '状态(0待讨论 1已讨论 2已归档)';
|
||||
CREATE INDEX idx_death_disc_encounter ON mr_death_discussion(encounter_id);
|
||||
|
||||
-- 6. 护理评估提醒表
|
||||
CREATE TABLE IF NOT EXISTS nursing_assessment_reminder (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT NOT NULL,
|
||||
patient_name VARCHAR(50),
|
||||
assessment_type VARCHAR(50) NOT NULL,
|
||||
frequency_hours INT NOT NULL DEFAULT 24,
|
||||
last_assess_time TIMESTAMP,
|
||||
next_deadline TIMESTAMP NOT NULL,
|
||||
status INT NOT NULL DEFAULT 0,
|
||||
remind_user_id BIGINT,
|
||||
remind_user_name VARCHAR(50),
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE nursing_assessment_reminder IS '护理评估提醒';
|
||||
COMMENT ON COLUMN nursing_assessment_reminder.assessment_type IS '评估类型(fall_risk/d pressure_ulcer/nutrition/pain/pipeline)';
|
||||
COMMENT ON COLUMN nursing_assessment_reminder.status IS '状态(0待评估 1已评估 2已超时)';
|
||||
|
||||
-- 7. 护理计划表
|
||||
CREATE TABLE IF NOT EXISTS nursing_care_plan (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
encounter_id BIGINT NOT NULL,
|
||||
patient_id BIGINT NOT NULL,
|
||||
patient_name VARCHAR(50),
|
||||
nursing_diagnosis VARCHAR(200) NOT NULL,
|
||||
nursing_goal TEXT,
|
||||
nursing_intervention TEXT,
|
||||
evaluation_result TEXT,
|
||||
status INT NOT NULL DEFAULT 0,
|
||||
nurse_user_id BIGINT,
|
||||
nurse_name VARCHAR(50),
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE nursing_care_plan IS '护理计划';
|
||||
COMMENT ON COLUMN nursing_care_plan.status IS '状态(0新建 1执行中 2已完成 3已停止)';
|
||||
CREATE INDEX idx_ncp_encounter ON nursing_care_plan(encounter_id);
|
||||
|
||||
-- 8. FHIR资源表
|
||||
CREATE TABLE IF NOT EXISTS esb_fhir_resource (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
resource_type VARCHAR(50) NOT NULL,
|
||||
resource_id VARCHAR(100) NOT NULL,
|
||||
encounter_id BIGINT,
|
||||
patient_id BIGINT,
|
||||
resource_json TEXT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
|
||||
version_id INT NOT NULL DEFAULT 1,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE esb_fhir_resource IS 'FHIR R4资源存储';
|
||||
COMMENT ON COLUMN esb_fhir_resource.resource_type IS '资源类型(Patient/Encounter/Observation/Condition/MedicationRequest)';
|
||||
CREATE INDEX idx_fhir_type ON esb_fhir_resource(resource_type);
|
||||
CREATE INDEX idx_fhir_patient ON esb_fhir_resource(patient_id);
|
||||
|
||||
-- 9. CDA文档表
|
||||
CREATE TABLE IF NOT EXISTS esb_cda_document (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
document_type VARCHAR(50) NOT NULL,
|
||||
document_title VARCHAR(200),
|
||||
encounter_id BIGINT,
|
||||
patient_id BIGINT,
|
||||
cda_xml TEXT NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
|
||||
version_id INT NOT NULL DEFAULT 1,
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
update_by VARCHAR(64),
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE esb_cda_document IS 'CDA临床文档架构';
|
||||
COMMENT ON COLUMN esb_cda_document.document_type IS '文档类型(admission/discharge/lab_report/referral)';
|
||||
CREATE INDEX idx_cda_type ON esb_cda_document(document_type);
|
||||
CREATE INDEX idx_cda_patient ON esb_cda_document(patient_id);
|
||||
|
||||
-- 10. 数据映射表
|
||||
CREATE TABLE IF NOT EXISTS esb_code_mapping (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
source_system VARCHAR(50) NOT NULL,
|
||||
source_code VARCHAR(50) NOT NULL,
|
||||
target_system VARCHAR(50) NOT NULL,
|
||||
target_code VARCHAR(50) NOT NULL,
|
||||
mapping_type VARCHAR(50) NOT NULL,
|
||||
description VARCHAR(200),
|
||||
tenant_id BIGINT DEFAULT 0,
|
||||
is_deleted INT NOT NULL DEFAULT 0,
|
||||
create_by VARCHAR(64),
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
COMMENT ON TABLE esb_code_mapping IS '标准编码映射';
|
||||
COMMENT ON COLUMN esb_code_mapping.mapping_type IS '映射类型(diagnosis/procedure/medication/observation)';
|
||||
CREATE INDEX idx_mapping_source ON esb_code_mapping(source_system, source_code);
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.healthlink.his.emr.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("emr_search_index")
|
||||
public class EmrSearchIndex extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("emr_id")
|
||||
private Long emrId;
|
||||
@TableField("encounter_id")
|
||||
private Long encounterId;
|
||||
@TableField("patient_id")
|
||||
private Long patientId;
|
||||
@TableField("patient_name")
|
||||
private String patientName;
|
||||
@TableField("emr_type")
|
||||
private String emrType;
|
||||
@TableField("emr_title")
|
||||
private String emrTitle;
|
||||
@TableField("diagnosis_text")
|
||||
private String diagnosisText;
|
||||
@TableField("doctor_name")
|
||||
private String doctorName;
|
||||
@TableField("department_name")
|
||||
private String departmentName;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.emr.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.emr.domain.EmrSearchIndex;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface EmrSearchIndexMapper extends BaseMapper<EmrSearchIndex> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.emr.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.emr.domain.EmrSearchIndex;
|
||||
|
||||
public interface IEmrSearchIndexService extends IService<EmrSearchIndex> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.emr.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.emr.domain.EmrSearchIndex;
|
||||
import com.healthlink.his.emr.mapper.EmrSearchIndexMapper;
|
||||
import com.healthlink.his.emr.service.IEmrSearchIndexService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class EmrSearchIndexServiceImpl extends ServiceImpl<EmrSearchIndexMapper, EmrSearchIndex> implements IEmrSearchIndexService {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.healthlink.his.esb.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("esb_cda_document")
|
||||
public class CdaDocument extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("document_type")
|
||||
private String documentType;
|
||||
@TableField("document_title")
|
||||
private String documentTitle;
|
||||
@TableField("encounter_id")
|
||||
private Long encounterId;
|
||||
@TableField("patient_id")
|
||||
private Long patientId;
|
||||
@TableField("cda_xml")
|
||||
private String cdaXml;
|
||||
@TableField("status")
|
||||
private String status;
|
||||
@TableField("version_id")
|
||||
private Integer versionId;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.healthlink.his.esb.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("esb_code_mapping")
|
||||
public class CodeMapping extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("source_system")
|
||||
private String sourceSystem;
|
||||
@TableField("source_code")
|
||||
private String sourceCode;
|
||||
@TableField("target_system")
|
||||
private String targetSystem;
|
||||
@TableField("target_code")
|
||||
private String targetCode;
|
||||
@TableField("mapping_type")
|
||||
private String mappingType;
|
||||
@TableField("description")
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.healthlink.his.esb.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("esb_fhir_resource")
|
||||
public class FhirResource extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("resource_type")
|
||||
private String resourceType;
|
||||
@TableField("resource_id")
|
||||
private String resourceId;
|
||||
@TableField("encounter_id")
|
||||
private Long encounterId;
|
||||
@TableField("patient_id")
|
||||
private Long patientId;
|
||||
@TableField("resource_json")
|
||||
private String resourceJson;
|
||||
@TableField("status")
|
||||
private String status;
|
||||
@TableField("version_id")
|
||||
private Integer versionId;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.esb.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.esb.domain.CdaDocument;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface CdaDocumentMapper extends BaseMapper<CdaDocument> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.esb.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.esb.domain.CodeMapping;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface CodeMappingMapper extends BaseMapper<CodeMapping> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.esb.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.esb.domain.FhirResource;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface FhirResourceMapper extends BaseMapper<FhirResource> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.esb.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.esb.domain.CdaDocument;
|
||||
|
||||
public interface ICdaDocumentService extends IService<CdaDocument> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.esb.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.esb.domain.CodeMapping;
|
||||
|
||||
public interface ICodeMappingService extends IService<CodeMapping> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.esb.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.esb.domain.FhirResource;
|
||||
|
||||
public interface IFhirResourceService extends IService<FhirResource> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.esb.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.esb.domain.CdaDocument;
|
||||
import com.healthlink.his.esb.mapper.CdaDocumentMapper;
|
||||
import com.healthlink.his.esb.service.ICdaDocumentService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class CdaDocumentServiceImpl extends ServiceImpl<CdaDocumentMapper, CdaDocument> implements ICdaDocumentService {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.esb.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.esb.domain.CodeMapping;
|
||||
import com.healthlink.his.esb.mapper.CodeMappingMapper;
|
||||
import com.healthlink.his.esb.service.ICodeMappingService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class CodeMappingServiceImpl extends ServiceImpl<CodeMappingMapper, CodeMapping> implements ICodeMappingService {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.esb.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.esb.domain.FhirResource;
|
||||
import com.healthlink.his.esb.mapper.FhirResourceMapper;
|
||||
import com.healthlink.his.esb.service.IFhirResourceService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FhirResourceServiceImpl extends ServiceImpl<FhirResourceMapper, FhirResource> implements IFhirResourceService {
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.healthlink.his.mrhomepage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("mr_borrowing")
|
||||
public class MrBorrowing extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("medical_record_id")
|
||||
private Long medicalRecordId;
|
||||
@TableField("patient_name")
|
||||
private String patientName;
|
||||
@TableField("mr_number")
|
||||
private String mrNumber;
|
||||
@TableField("borrower_name")
|
||||
private String borrowerName;
|
||||
@TableField("borrower_dept")
|
||||
private String borrowerDept;
|
||||
@TableField("borrow_reason")
|
||||
private String borrowReason;
|
||||
@TableField("borrow_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date borrowDate;
|
||||
@TableField("expected_return_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expectedReturnDate;
|
||||
@TableField("actual_return_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date actualReturnDate;
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
@TableField("approver_name")
|
||||
private String approverName;
|
||||
@TableField("approve_time")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date approveTime;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.healthlink.his.mrhomepage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("mr_death_discussion")
|
||||
public class MrDeathDiscussion extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("patient_id")
|
||||
private Long patientId;
|
||||
@TableField("patient_name")
|
||||
private String patientName;
|
||||
@TableField("encounter_id")
|
||||
private Long encounterId;
|
||||
@TableField("death_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date deathDate;
|
||||
@TableField("discussion_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date discussionDate;
|
||||
@TableField("deadline_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date deadlineDate;
|
||||
@TableField("host_doctor_id")
|
||||
private Long hostDoctorId;
|
||||
@TableField("host_doctor_name")
|
||||
private String hostDoctorName;
|
||||
@TableField("host_title")
|
||||
private String hostTitle;
|
||||
@TableField("participants")
|
||||
private String participants;
|
||||
@TableField("discussion_conclusion")
|
||||
private String discussionConclusion;
|
||||
@TableField("improvement_measures")
|
||||
private String improvementMeasures;
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
@TableField("is_overdue")
|
||||
private Boolean isOverdue;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.healthlink.his.mrhomepage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("mr_sealing")
|
||||
public class MrSealing extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("medical_record_id")
|
||||
private Long medicalRecordId;
|
||||
@TableField("patient_name")
|
||||
private String patientName;
|
||||
@TableField("mr_number")
|
||||
private String mrNumber;
|
||||
@TableField("seal_reason")
|
||||
private String sealReason;
|
||||
@TableField("seal_type")
|
||||
private Integer sealType;
|
||||
@TableField("seal_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date sealDate;
|
||||
@TableField("seal_by")
|
||||
private String sealBy;
|
||||
@TableField("unseal_date")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date unsealDate;
|
||||
@TableField("unseal_by")
|
||||
private String unsealBy;
|
||||
@TableField("unseal_reason")
|
||||
private String unsealReason;
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.healthlink.his.mrhomepage.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("mr_tracking")
|
||||
public class MrTracking extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("medical_record_id")
|
||||
private Long medicalRecordId;
|
||||
@TableField("mr_number")
|
||||
private String mrNumber;
|
||||
@TableField("patient_name")
|
||||
private String patientName;
|
||||
@TableField("location")
|
||||
private String location;
|
||||
@TableField("location_type")
|
||||
private String locationType;
|
||||
@TableField("status")
|
||||
private String status;
|
||||
@TableField("moved_by")
|
||||
private String movedBy;
|
||||
@TableField("move_time")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date moveTime;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.mrhomepage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.mrhomepage.domain.MrBorrowing;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface MrBorrowingMapper extends BaseMapper<MrBorrowing> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.mrhomepage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.mrhomepage.domain.MrDeathDiscussion;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface MrDeathDiscussionMapper extends BaseMapper<MrDeathDiscussion> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.mrhomepage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.mrhomepage.domain.MrSealing;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface MrSealingMapper extends BaseMapper<MrSealing> {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.mrhomepage.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.mrhomepage.domain.MrTracking;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface MrTrackingMapper extends BaseMapper<MrTracking> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.mrhomepage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.mrhomepage.domain.MrBorrowing;
|
||||
|
||||
public interface IMrBorrowingService extends IService<MrBorrowing> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.mrhomepage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.mrhomepage.domain.MrDeathDiscussion;
|
||||
|
||||
public interface IMrDeathDiscussionService extends IService<MrDeathDiscussion> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.mrhomepage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.mrhomepage.domain.MrSealing;
|
||||
|
||||
public interface IMrSealingService extends IService<MrSealing> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.mrhomepage.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.mrhomepage.domain.MrTracking;
|
||||
|
||||
public interface IMrTrackingService extends IService<MrTracking> {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.mrhomepage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.mrhomepage.domain.MrBorrowing;
|
||||
import com.healthlink.his.mrhomepage.mapper.MrBorrowingMapper;
|
||||
import com.healthlink.his.mrhomepage.service.IMrBorrowingService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MrBorrowingServiceImpl extends ServiceImpl<MrBorrowingMapper, MrBorrowing> implements IMrBorrowingService {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.mrhomepage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.mrhomepage.domain.MrDeathDiscussion;
|
||||
import com.healthlink.his.mrhomepage.mapper.MrDeathDiscussionMapper;
|
||||
import com.healthlink.his.mrhomepage.service.IMrDeathDiscussionService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MrDeathDiscussionServiceImpl extends ServiceImpl<MrDeathDiscussionMapper, MrDeathDiscussion> implements IMrDeathDiscussionService {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.mrhomepage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.mrhomepage.domain.MrSealing;
|
||||
import com.healthlink.his.mrhomepage.mapper.MrSealingMapper;
|
||||
import com.healthlink.his.mrhomepage.service.IMrSealingService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MrSealingServiceImpl extends ServiceImpl<MrSealingMapper, MrSealing> implements IMrSealingService {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.mrhomepage.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.mrhomepage.domain.MrTracking;
|
||||
import com.healthlink.his.mrhomepage.mapper.MrTrackingMapper;
|
||||
import com.healthlink.his.mrhomepage.service.IMrTrackingService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MrTrackingServiceImpl extends ServiceImpl<MrTrackingMapper, MrTracking> implements IMrTrackingService {
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.healthlink.his.nursing.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("nursing_assessment_reminder")
|
||||
public class NursingAssessmentReminder extends HisBaseEntity {
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
@TableField("encounter_id")
|
||||
private Long encounterId;
|
||||
@TableField("patient_id")
|
||||
private Long patientId;
|
||||
@TableField("patient_name")
|
||||
private String patientName;
|
||||
@TableField("assessment_type")
|
||||
private String assessmentType;
|
||||
@TableField("frequency_hours")
|
||||
private Integer frequencyHours;
|
||||
@TableField("last_assess_time")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastAssessTime;
|
||||
@TableField("next_deadline")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date nextDeadline;
|
||||
@TableField("status")
|
||||
private Integer status;
|
||||
@TableField("remind_user_id")
|
||||
private Long remindUserId;
|
||||
@TableField("remind_user_name")
|
||||
private String remindUserName;
|
||||
}
|
||||
@@ -1,15 +1,30 @@
|
||||
package com.healthlink.his.nursing.domain;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.core.common.core.domain.HisBaseEntity;
|
||||
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import java.util.Date;
|
||||
@Data @TableName("nursing_care_plan") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
|
||||
|
||||
@Data
|
||||
@TableName("nursing_care_plan")
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class NursingCarePlan extends HisBaseEntity {
|
||||
@TableId(type = IdType.ASSIGN_ID) private Long id;
|
||||
private Long encounterId; private Long patientId;
|
||||
private String nursingDiagnosis; private String goal; private String interventions; private String evaluation;
|
||||
private Long plannerId; private String plannerName; private Date planDate;
|
||||
private String status; private String delFlag;
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
private Long encounterId;
|
||||
private Long patientId;
|
||||
private String patientName;
|
||||
private String nursingDiagnosis;
|
||||
private String goal;
|
||||
private String interventions;
|
||||
private String evaluation;
|
||||
private Long plannerId;
|
||||
private String plannerName;
|
||||
private Date planDate;
|
||||
private String status;
|
||||
private String delFlag;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.healthlink.his.nursing.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.nursing.domain.NursingAssessmentReminder;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface NursingAssessmentReminderMapper extends BaseMapper<NursingAssessmentReminder> {
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.healthlink.his.nursing.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.healthlink.his.nursing.domain.NursingCarePlan;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface NursingCarePlanMapper extends BaseMapper<NursingCarePlan> {}
|
||||
public interface NursingCarePlanMapper extends BaseMapper<NursingCarePlan> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.healthlink.his.nursing.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.nursing.domain.NursingAssessmentReminder;
|
||||
|
||||
public interface INursingAssessmentReminderService extends IService<NursingAssessmentReminder> {
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
package com.healthlink.his.nursing.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.healthlink.his.nursing.domain.NursingCarePlan;
|
||||
public interface INursingCarePlanService extends IService<NursingCarePlan> {}
|
||||
|
||||
public interface INursingCarePlanService extends IService<NursingCarePlan> {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.healthlink.his.nursing.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.nursing.domain.NursingAssessmentReminder;
|
||||
import com.healthlink.his.nursing.mapper.NursingAssessmentReminderMapper;
|
||||
import com.healthlink.his.nursing.service.INursingAssessmentReminderService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class NursingAssessmentReminderServiceImpl extends ServiceImpl<NursingAssessmentReminderMapper, NursingAssessmentReminder> implements INursingAssessmentReminderService {
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.healthlink.his.nursing.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.healthlink.his.nursing.domain.NursingCarePlan;
|
||||
import com.healthlink.his.nursing.mapper.NursingCarePlanMapper;
|
||||
import com.healthlink.his.nursing.service.INursingCarePlanService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class NursingCarePlanServiceImpl extends ServiceImpl<NursingCarePlanMapper, NursingCarePlan> implements INursingCarePlanService {}
|
||||
public class NursingCarePlanServiceImpl extends ServiceImpl<NursingCarePlanMapper, NursingCarePlan> implements INursingCarePlanService {
|
||||
}
|
||||
|
||||
4
healthlink-his-ui/src/views/emrsearch/api.js
Normal file
4
healthlink-his-ui/src/views/emrsearch/api.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import request from '@/utils/request'
|
||||
export function searchEmr(p){return request({url:'/emr-search/search',method:'get',params:p})}
|
||||
export function indexEmr(d){return request({url:'/emr-search/index',method:'post',data:d})}
|
||||
export function getSearchStats(){return request({url:'/emr-search/stats',method:'get'})}
|
||||
48
healthlink-his-ui/src/views/emrsearch/index.vue
Normal file
48
healthlink-his-ui/src/views/emrsearch/index.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">病历检索</span></div>
|
||||
<el-card shadow="never" style="margin-bottom:16px">
|
||||
<el-form :model="queryParams" inline>
|
||||
<el-form-item label="关键词"><el-input v-model="queryParams.keyword" placeholder="诊断/标题/患者" clearable style="width:200px"/></el-form-item>
|
||||
<el-form-item label="患者"><el-input v-model="queryParams.patientName" clearable style="width:120px"/></el-form-item>
|
||||
<el-form-item label="类型"><el-select v-model="queryParams.emrType" clearable style="width:120px">
|
||||
<el-option v-for="t in [{l:'入院记录',v:'admission'},{l:'日常病程',v:'daily'},{l:'出院记录',v:'discharge'},{l:'手术记录',v:'surgery'},{l:'会诊记录',v:'consultation'}]" :key="t.v" :label="t.l" :value="t.v"/>
|
||||
</el-select></el-form-item>
|
||||
<el-form-item label="医生"><el-input v-model="queryParams.doctorName" clearable style="width:100px"/></el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">检索</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-table :data="searchData" border stripe v-loading="loading">
|
||||
<el-table-column prop="patientName" label="患者" width="90"/>
|
||||
<el-table-column prop="emrType" label="类型" width="100">
|
||||
<template #default="{row}">{{ {admission:'入院记录',daily:'日常病程',discharge:'出院记录',surgery:'手术记录',consultation:'会诊记录'}[row.emrType]||row.emrType }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="emrTitle" label="标题" min-width="180"/>
|
||||
<el-table-column prop="diagnosisText" label="诊断" min-width="200" show-overflow-tooltip/>
|
||||
<el-table-column prop="doctorName" label="医生" width="90"/>
|
||||
<el-table-column prop="departmentName" label="科室" width="120"/>
|
||||
<el-table-column prop="createTime" label="创建时间" width="170"/>
|
||||
</el-table>
|
||||
<el-pagination v-model:current-page="queryParams.pageNo" v-model:page-size="queryParams.pageSize" :total="total" layout="total,prev,pager,next" @current-change="handleSearch" style="margin-top:16px;text-align:right"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,reactive,onMounted} from 'vue'
|
||||
import {searchEmr} from './api'
|
||||
|
||||
const loading=ref(false)
|
||||
const searchData=ref([])
|
||||
const total=ref(0)
|
||||
const queryParams=reactive({keyword:'',patientName:'',emrType:'',doctorName:'',departmentName:'',pageNo:1,pageSize:20})
|
||||
|
||||
const handleSearch=async()=>{
|
||||
loading.value=true
|
||||
try{const r=await searchEmr(queryParams);searchData.value=r.data?.records||[];total.value=r.data?.total||0}finally{loading.value=false}
|
||||
}
|
||||
const resetQuery=()=>{Object.assign(queryParams,{keyword:'',patientName:'',emrType:'',doctorName:'',pageNo:1});handleSearch()}
|
||||
onMounted(()=>handleSearch())
|
||||
</script>
|
||||
9
healthlink-his-ui/src/views/fhircda/api.js
Normal file
9
healthlink-his-ui/src/views/fhircda/api.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
export function getFhirPage(p){return request({url:'/fhir-cda/fhir/page',method:'get',params:p})}
|
||||
export function createFhirResource(d){return request({url:'/fhir-cda/fhir/create',method:'post',data:d})}
|
||||
export function getFhirTypeStats(){return request({url:'/fhir-cda/fhir/type-stats',method:'get'})}
|
||||
export function getCdaPage(p){return request({url:'/fhir-cda/cda/page',method:'get',params:p})}
|
||||
export function createCdaDocument(d){return request({url:'/fhir-cda/cda/create',method:'post',data:d})}
|
||||
export function publishCdaDocument(id){return request({url:'/fhir-cda/cda/publish',method:'post',params:{id}})}
|
||||
export function getMappingPage(p){return request({url:'/fhir-cda/mapping/page',method:'get',params:p})}
|
||||
export function addMapping(d){return request({url:'/fhir-cda/mapping/add',method:'post',data:d})}
|
||||
89
healthlink-his-ui/src/views/fhircda/index.vue
Normal file
89
healthlink-his-ui/src/views/fhircda/index.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">FHIR/CDA标准接口</span></div>
|
||||
<el-tabs v-model="tab" type="border-card">
|
||||
<el-tab-pane label="FHIR资源" name="fhir">
|
||||
<el-card shadow="never" style="margin-bottom:12px">
|
||||
<div style="display:flex;gap:30px;text-align:center;flex-wrap:wrap">
|
||||
<div v-for="(v,k) in fhirStats" :key="k"><div style="font-size:20px;font-weight:bold;color:#409eff">{{ v }}</div><div>{{ k }}</div></div>
|
||||
</div>
|
||||
</el-card>
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showAddFhir=true">新增资源</el-button></div>
|
||||
<el-table :data="fhirData" border stripe>
|
||||
<el-table-column prop="resourceType" label="资源类型" width="140"/>
|
||||
<el-table-column prop="resourceId" label="资源ID" width="150"/>
|
||||
<el-table-column prop="status" label="状态" width="80"/>
|
||||
<el-table-column prop="versionId" label="版本" width="60" align="center"/>
|
||||
<el-table-column prop="createTime" label="创建时间" width="170"/>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="CDA文档" name="cda">
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showAddCda=true">新增文档</el-button></div>
|
||||
<el-table :data="cdaData" border stripe>
|
||||
<el-table-column prop="documentType" label="文档类型" width="140"/>
|
||||
<el-table-column prop="documentTitle" label="标题" min-width="180"/>
|
||||
<el-table-column prop="status" label="状态" width="90">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="row.status==='PUBLISHED'?'success':'info'" size="small">{{ row.status==='DRAFT'?'草稿':'已发布' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="versionId" label="版本" width="60" align="center"/>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="{row}">
|
||||
<el-button v-if="row.status==='DRAFT'" type="success" link size="small" @click="publishCdaAction(row)">发布</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="编码映射" name="mapping">
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showAddMapping=true">新增映射</el-button></div>
|
||||
<el-table :data="mappingData" border stripe>
|
||||
<el-table-column prop="sourceSystem" label="源系统" width="120"/>
|
||||
<el-table-column prop="sourceCode" label="源编码" width="120"/>
|
||||
<el-table-column prop="targetSystem" label="目标系统" width="120"/>
|
||||
<el-table-column prop="targetCode" label="目标编码" width="120"/>
|
||||
<el-table-column prop="mappingType" label="映射类型" width="120"/>
|
||||
<el-table-column prop="description" label="描述" min-width="150"/>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dialog v-model="showAddFhir" title="新增FHIR资源" width="600px">
|
||||
<el-form :model="fhirForm" label-width="100px">
|
||||
<el-form-item label="资源类型"><el-select v-model="fhirForm.resourceType"><el-option v-for="t in ['Patient','Encounter','Observation','Condition','MedicationRequest']" :key="t" :label="t" :value="t"/></el-select></el-form-item>
|
||||
<el-form-item label="资源ID"><el-input v-model="fhirForm.resourceId"/></el-form-item>
|
||||
<el-form-item label="JSON内容"><el-input v-model="fhirForm.resourceJson" type="textarea" :rows="8"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showAddFhir=false">取消</el-button>
|
||||
<el-button type="primary" @click="submitFhir">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,reactive,onMounted} from 'vue'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import {getFhirPage,createFhirResource,getFhirTypeStats,getCdaPage,createCdaDocument,publishCdaDocument,getMappingPage,addMapping} from './api'
|
||||
|
||||
const tab=ref('fhir')
|
||||
const fhirData=ref([]),cdaData=ref([]),mappingData=ref([])
|
||||
const fhirStats=ref({})
|
||||
const showAddFhir=ref(false),showAddCda=ref(false),showAddMapping=ref(false)
|
||||
const fhirForm=reactive({resourceType:'Patient',resourceId:'',resourceJson:''})
|
||||
|
||||
const loadData=async()=>{
|
||||
const [f,c,m,s]=await Promise.all([
|
||||
getFhirPage({pageNo:1,pageSize:50}),getCdaPage({pageNo:1,pageSize:50}),
|
||||
getMappingPage({pageNo:1,pageSize:50}),getFhirTypeStats()
|
||||
])
|
||||
fhirData.value=f.data?.records||[];cdaData.value=c.data?.records||[]
|
||||
mappingData.value=m.data?.records||[];fhirStats.value=s.data||{}
|
||||
}
|
||||
const submitFhir=async()=>{await createFhirResource(fhirForm);ElMessage.success('新增成功');showAddFhir.value=false;loadData()}
|
||||
const publishCdaAction=async(row)=>{await publishCdaDocument(row.id);ElMessage.success('已发布');loadData()}
|
||||
onMounted(()=>loadData())
|
||||
</script>
|
||||
18
healthlink-his-ui/src/views/mrmanagement/api.js
Normal file
18
healthlink-his-ui/src/views/mrmanagement/api.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import request from '@/utils/request'
|
||||
// 借阅
|
||||
export function getBorrowingPage(p) { return request({ url: '/mr-management/borrowing/page', method: 'get', params: p }) }
|
||||
export function applyBorrowing(d) { return request({ url: '/mr-management/borrowing/apply', method: 'post', data: d }) }
|
||||
export function approveBorrowing(d) { return request({ url: '/mr-management/borrowing/approve', method: 'post', data: d }) }
|
||||
export function returnBorrowing(id) { return request({ url: '/mr-management/borrowing/return', method: 'post', params: { id } }) }
|
||||
// 封存
|
||||
export function getSealingPage(p) { return request({ url: '/mr-management/sealing/page', method: 'get', params: p }) }
|
||||
export function sealRecord(d) { return request({ url: '/mr-management/sealing/seal', method: 'post', data: d }) }
|
||||
export function unsealRecord(d) { return request({ url: '/mr-management/sealing/unseal', method: 'post', data: d }) }
|
||||
// 示踪
|
||||
export function getTrackingPage(p) { return request({ url: '/mr-management/tracking/page', method: 'get', params: p }) }
|
||||
export function moveRecord(d) { return request({ url: '/mr-management/tracking/move', method: 'post', data: d }) }
|
||||
export function getTrackingStats() { return request({ url: '/mr-management/tracking/stats', method: 'get' }) }
|
||||
// 死亡讨论
|
||||
export function getDeathDiscussionPage(p) { return request({ url: '/mr-management/death-discussion/page', method: 'get', params: p }) }
|
||||
export function addDeathDiscussion(d) { return request({ url: '/mr-management/death-discussion/add', method: 'post', data: d }) }
|
||||
export function completeDeathDiscussion(d) { return request({ url: '/mr-management/death-discussion/complete', method: 'post', data: d }) }
|
||||
185
healthlink-his-ui/src/views/mrmanagement/index.vue
Normal file
185
healthlink-his-ui/src/views/mrmanagement/index.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">病案管理</span></div>
|
||||
<el-tabs v-model="tab" type="border-card">
|
||||
<!-- 借阅管理 -->
|
||||
<el-tab-pane label="病案借阅" name="borrow">
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showBorrow=true">申请借阅</el-button></div>
|
||||
<el-table :data="borrowData" border stripe>
|
||||
<el-table-column prop="patientName" label="患者" width="90"/>
|
||||
<el-table-column prop="mrNumber" label="病案号" width="110"/>
|
||||
<el-table-column prop="borrowerName" label="借阅人" width="90"/>
|
||||
<el-table-column prop="borrowerDept" label="借阅科室" width="120"/>
|
||||
<el-table-column prop="borrowReason" label="借阅原因" min-width="150" show-overflow-tooltip/>
|
||||
<el-table-column prop="status" label="状态" width="90">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="['warning','','success','success','danger','danger'][row.status]" size="small">
|
||||
{{ ['待审批','已批准','已借出','已归还','已逾期','已拒绝'][row.status] }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160">
|
||||
<template #default="{row}">
|
||||
<el-button v-if="row.status===0" type="primary" link size="small" @click="approveAction(row,true)">批准</el-button>
|
||||
<el-button v-if="row.status===0" type="danger" link size="small" @click="approveAction(row,false)">拒绝</el-button>
|
||||
<el-button v-if="row.status===1||row.status===2" type="success" link size="small" @click="returnAction(row)">归还</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 封存管理 -->
|
||||
<el-tab-pane label="病案封存" name="seal">
|
||||
<div style="margin-bottom:12px"><el-button type="warning" @click="showSeal=true">申请封存</el-button></div>
|
||||
<el-table :data="sealData" border stripe>
|
||||
<el-table-column prop="patientName" label="患者" width="90"/>
|
||||
<el-table-column prop="mrNumber" label="病案号" width="110"/>
|
||||
<el-table-column prop="sealType" label="封存类型" width="100">
|
||||
<template #default="{row}">{{ ['','主动封存','纠纷封存','司法封存'][row.sealType] }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sealReason" label="封存原因" min-width="150" show-overflow-tooltip/>
|
||||
<el-table-column prop="sealBy" label="封存人" width="90"/>
|
||||
<el-table-column prop="status" label="状态" width="80">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="row.status===0?'danger':'success'" size="small">{{ row.status===0?'已封存':'已解封' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="{row}">
|
||||
<el-button v-if="row.status===0" type="warning" link size="small" @click="unsealAction(row)">解封</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 示踪管理 -->
|
||||
<el-tab-pane label="病案示踪" name="track">
|
||||
<el-card shadow="never" style="margin-bottom:12px">
|
||||
<div style="display:flex;gap:40px;text-align:center">
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#67c23a">{{ trackStats.inShelf||0 }}</div><div>在架</div></div>
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#e6a23c">{{ trackStats.borrowed||0 }}</div><div>借出</div></div>
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#409eff">{{ trackStats.archived||0 }}</div><div>归档</div></div>
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#f56c6c">{{ trackStats.lost||0 }}</div><div>遗失</div></div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-table :data="trackData" border stripe>
|
||||
<el-table-column prop="mrNumber" label="病案号" width="110"/>
|
||||
<el-table-column prop="patientName" label="患者" width="90"/>
|
||||
<el-table-column prop="location" label="当前位置" width="120"/>
|
||||
<el-table-column prop="status" label="状态" width="90">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="{IN_SHELF:'success',BORROWED:'warning',ARCHIVED:'',LOST:'danger'}[row.status]" size="small">
|
||||
{{ {IN_SHELF:'在架',BORROWED:'借出',ARCHIVED:'归档',LOST:'遗失'}[row.status] }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="movedBy" label="操作人" width="100"/>
|
||||
<el-table-column prop="moveTime" label="移动时间" width="170"/>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 死亡讨论 -->
|
||||
<el-tab-pane label="死亡讨论" name="death">
|
||||
<div style="margin-bottom:12px"><el-button type="danger" @click="showDeath=true">新增讨论</el-button></div>
|
||||
<el-table :data="deathData" border stripe>
|
||||
<el-table-column prop="patientName" label="患者" width="90"/>
|
||||
<el-table-column prop="hostDoctorName" label="主持人" width="100"/>
|
||||
<el-table-column prop="deathDate" label="死亡时间" width="170"/>
|
||||
<el-table-column prop="deadlineDate" label="讨论期限" width="170"/>
|
||||
<el-table-column prop="status" label="状态" width="80">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="['warning','success',''][row.status]" size="small">
|
||||
{{ ['待讨论','已讨论','已归档'][row.status] }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isOverdue" label="是否超时" width="80">
|
||||
<template #default="{row}">
|
||||
<el-tag v-if="row.isOverdue" type="danger" size="small">超时</el-tag>
|
||||
<el-tag v-else type="success" size="small">正常</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="{row}">
|
||||
<el-button v-if="row.status===0" type="primary" link size="small" @click="completeDeath(row)">完成讨论</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 借阅弹窗 -->
|
||||
<el-dialog v-model="showBorrow" title="申请借阅" width="500px">
|
||||
<el-form :model="borrowForm" label-width="100px">
|
||||
<el-form-item label="病案号"><el-input v-model="borrowForm.mrNumber"/></el-form-item>
|
||||
<el-form-item label="患者"><el-input v-model="borrowForm.patientName"/></el-form-item>
|
||||
<el-form-item label="借阅人"><el-input v-model="borrowForm.borrowerName"/></el-form-item>
|
||||
<el-form-item label="借阅科室"><el-input v-model="borrowForm.borrowerDept"/></el-form-item>
|
||||
<el-form-item label="借阅原因"><el-input v-model="borrowForm.borrowReason" type="textarea"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showBorrow=false">取消</el-button>
|
||||
<el-button type="primary" @click="submitBorrow">提交</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 封存弹窗 -->
|
||||
<el-dialog v-model="showSeal" title="申请封存" width="500px">
|
||||
<el-form :model="sealForm" label-width="100px">
|
||||
<el-form-item label="病案号"><el-input v-model="sealForm.mrNumber"/></el-form-item>
|
||||
<el-form-item label="患者"><el-input v-model="sealForm.patientName"/></el-form-item>
|
||||
<el-form-item label="封存类型">
|
||||
<el-select v-model="sealForm.sealType">
|
||||
<el-option label="主动封存" :value="1"/><el-option label="纠纷封存" :value="2"/><el-option label="司法封存" :value="3"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="封存原因"><el-input v-model="sealForm.sealReason" type="textarea"/></el-form-item>
|
||||
<el-form-item label="封存人"><el-input v-model="sealForm.sealBy"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showSeal=false">取消</el-button>
|
||||
<el-button type="warning" @click="submitSeal">确认封存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,reactive,onMounted} from 'vue'
|
||||
import {ElMessage,ElMessageBox} from 'element-plus'
|
||||
import {getBorrowingPage,applyBorrowing,approveBorrowing,returnBorrowing,getSealingPage,sealRecord,unsealRecord,getTrackingPage,getTrackingStats,getDeathDiscussionPage,addDeathDiscussion,completeDeathDiscussion} from './api'
|
||||
|
||||
const tab=ref('borrow')
|
||||
const borrowData=ref([]),sealData=ref([]),trackData=ref([]),deathData=ref([])
|
||||
const trackStats=ref({})
|
||||
const showBorrow=ref(false),showSeal=ref(false),showDeath=ref(false)
|
||||
const borrowForm=reactive({mrNumber:'',patientName:'',borrowerName:'',borrowerDept:'',borrowReason:''})
|
||||
const sealForm=reactive({mrNumber:'',patientName:'',sealType:1,sealReason:'',sealBy:''})
|
||||
|
||||
const loadData=async()=>{
|
||||
const [b,s,t,d]=await Promise.all([
|
||||
getBorrowingPage({pageNo:1,pageSize:50}),getSealingPage({pageNo:1,pageSize:50}),
|
||||
getTrackingPage({pageNo:1,pageSize:50}),getDeathDiscussionPage({pageNo:1,pageSize:50})
|
||||
])
|
||||
borrowData.value=b.data?.records||[];sealData.value=s.data?.records||[]
|
||||
trackData.value=t.data?.records||[];deathData.value=d.data?.records||[]
|
||||
const ts=await getTrackingStats();trackStats.value=ts.data||{}
|
||||
}
|
||||
|
||||
const approveAction=async(row,approved)=>{
|
||||
await approveBorrowing({id:row.id,approved,approverName:'管理员'})
|
||||
ElMessage.success(approved?'已批准':'已拒绝');loadData()
|
||||
}
|
||||
const returnAction=async(row)=>{await returnBorrowing(row.id);ElMessage.success('已归还');loadData()}
|
||||
const submitBorrow=async()=>{await applyBorrowing(borrowForm);ElMessage.success('申请成功');showBorrow.value=false;loadData()}
|
||||
const submitSeal=async()=>{await sealRecord(sealForm);ElMessage.success('封存成功');showSeal.value=false;loadData()}
|
||||
const unsealAction=async(row)=>{
|
||||
const{value}=await ElMessageBox.prompt('请输入解封原因','解封',{inputType:'textarea'})
|
||||
await unsealRecord({id:row.id,unsealBy:'管理员',unsealReason:value});ElMessage.success('已解封');loadData()
|
||||
}
|
||||
const completeDeath=async(row)=>{
|
||||
const{value}=await ElMessageBox.prompt('请输入讨论结论','完成讨论',{inputType:'textarea'})
|
||||
await completeDeathDiscussion({id:row.id,discussionConclusion:value,participants:row.hostDoctorName});ElMessage.success('已完成');loadData()
|
||||
}
|
||||
onMounted(()=>loadData())
|
||||
</script>
|
||||
9
healthlink-his-ui/src/views/nursingenhanced/api.js
Normal file
9
healthlink-his-ui/src/views/nursingenhanced/api.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
export function getReminderPage(p){return request({url:'/nursing-enhanced/reminder/page',method:'get',params:p})}
|
||||
export function addReminder(d){return request({url:'/nursing-enhanced/reminder/add',method:'post',data:d})}
|
||||
export function completeReminder(id){return request({url:'/nursing-enhanced/reminder/complete',method:'post',params:{id}})}
|
||||
export function getOverdueReminders(){return request({url:'/nursing-enhanced/reminder/overdue',method:'get'})}
|
||||
export function getCarePlanPage(p){return request({url:'/nursing-enhanced/care-plan/page',method:'get',params:p})}
|
||||
export function addCarePlan(d){return request({url:'/nursing-enhanced/care-plan/add',method:'post',data:d})}
|
||||
export function evaluateCarePlan(d){return request({url:'/nursing-enhanced/care-plan/evaluate',method:'post',data:d})}
|
||||
export function getQualityStats(p){return request({url:'/nursing-enhanced/quality/stats',method:'get',params:p})}
|
||||
110
healthlink-his-ui/src/views/nursingenhanced/index.vue
Normal file
110
healthlink-his-ui/src/views/nursingenhanced/index.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">护理评估增强</span></div>
|
||||
<el-tabs v-model="tab" type="border-card">
|
||||
<el-tab-pane label="评估提醒" name="reminder">
|
||||
<el-card shadow="never" style="margin-bottom:12px">
|
||||
<div style="display:flex;gap:40px;text-align:center">
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#e6a23c">{{ overdueCount }}</div><div>逾期未评估</div></div>
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#67c23a">{{ qualityStats.completedReminders||0 }}</div><div>已完成评估</div></div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-table :data="reminderData" border stripe>
|
||||
<el-table-column prop="patientName" label="患者" width="90"/>
|
||||
<el-table-column prop="assessmentType" label="评估类型" width="110">
|
||||
<template #default="{row}">
|
||||
{{ {fall_risk:'跌倒风险',pressure_ulcer:'压疮风险',nutrition:'营养筛查',pain:'疼痛评估',pipeline:'管道评估'}[row.assessmentType]||row.assessmentType }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="frequencyHours" label="频率(小时)" width="100" align="center"/>
|
||||
<el-table-column prop="nextDeadline" label="下次截止" width="170"/>
|
||||
<el-table-column prop="status" label="状态" width="90">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="['warning','success','danger'][row.status]" size="small">{{ ['待评估','已评估','已超时'][row.status] }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="{row}">
|
||||
<el-button v-if="row.status!==1" type="success" link size="small" @click="completeReminderAction(row)">完成评估</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="护理计划" name="carePlan">
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showCarePlan=true">新增护理计划</el-button></div>
|
||||
<el-table :data="carePlanData" border stripe>
|
||||
<el-table-column prop="patientName" label="患者" width="90"/>
|
||||
<el-table-column prop="nursingDiagnosis" label="护理诊断" width="150"/>
|
||||
<el-table-column prop="nursingGoal" label="护理目标" min-width="180" show-overflow-tooltip/>
|
||||
<el-table-column prop="nursingIntervention" label="护理措施" min-width="180" show-overflow-tooltip/>
|
||||
<el-table-column prop="status" label="状态" width="80">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="['info','','success',''][row.status]" size="small">{{ ['新建','执行中','已完成','已停止'][row.status] }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="{row}">
|
||||
<el-button v-if="row.status<2" type="primary" link size="small" @click="evaluatePlan(row)">评价</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="质量指标" name="quality">
|
||||
<el-card shadow="never">
|
||||
<div style="display:flex;gap:30px;text-align:center;flex-wrap:wrap">
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#409eff">{{ qualityStats.totalReminders||0 }}</div><div>总评估提醒</div></div>
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#67c23a">{{ qualityStats.completedReminders||0 }}</div><div>已完成</div></div>
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#f56c6c">{{ qualityStats.overdueReminders||0 }}</div><div>已超时</div></div>
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#409eff">{{ qualityStats.totalPlans||0 }}</div><div>总护理计划</div></div>
|
||||
<div><div style="font-size:24px;font-weight:bold;color:#67c23a">{{ qualityStats.completedPlans||0 }}</div><div>已完成计划</div></div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dialog v-model="showCarePlan" title="新增护理计划" width="600px">
|
||||
<el-form :model="carePlanForm" label-width="100px">
|
||||
<el-form-item label="患者"><el-input v-model="carePlanForm.patientName"/></el-form-item>
|
||||
<el-form-item label="就诊ID"><el-input-number v-model="carePlanForm.encounterId" :min="1"/></el-form-item>
|
||||
<el-form-item label="护理诊断"><el-input v-model="carePlanForm.nursingDiagnosis"/></el-form-item>
|
||||
<el-form-item label="护理目标"><el-input v-model="carePlanForm.nursingGoal" type="textarea"/></el-form-item>
|
||||
<el-form-item label="护理措施"><el-input v-model="carePlanForm.nursingIntervention" type="textarea"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showCarePlan=false">取消</el-button>
|
||||
<el-button type="primary" @click="submitCarePlan">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,reactive,onMounted} from 'vue'
|
||||
import {ElMessage,ElMessageBox} from 'element-plus'
|
||||
import {getReminderPage,completeReminder,getCarePlanPage,addCarePlan,evaluateCarePlan,getQualityStats} from './api'
|
||||
|
||||
const tab=ref('reminder')
|
||||
const reminderData=ref([]),carePlanData=ref([])
|
||||
const overdueCount=ref(0)
|
||||
const qualityStats=ref({})
|
||||
const showCarePlan=ref(false)
|
||||
const carePlanForm=reactive({patientName:'',encounterId:null,nursingDiagnosis:'',nursingGoal:'',nursingIntervention:''})
|
||||
|
||||
const loadData=async()=>{
|
||||
const [r,cp,q]=await Promise.all([
|
||||
getReminderPage({pageNo:1,pageSize:50}),getCarePlanPage({pageNo:1,pageSize:50}),getQualityStats({})
|
||||
])
|
||||
reminderData.value=r.data?.records||[];carePlanData.value=cp.data?.records||[]
|
||||
qualityStats.value=q.data||{}
|
||||
overdueCount.value=reminderData.value.filter(x=>x.status===2).length
|
||||
}
|
||||
const completeReminderAction=async(row)=>{await completeReminder(row.id);ElMessage.success('评估完成');loadData()}
|
||||
const evaluatePlan=async(row)=>{
|
||||
const{value}=await ElMessageBox.prompt('请输入评价结果','评价护理计划',{inputType:'textarea'})
|
||||
await evaluateCarePlan({id:row.id,evaluationResult:value});ElMessage.success('评价完成');loadData()
|
||||
}
|
||||
const submitCarePlan=async()=>{await addCarePlan(carePlanForm);ElMessage.success('新增成功');showCarePlan.value=false;loadData()}
|
||||
onMounted(()=>loadData())
|
||||
</script>
|
||||
Reference in New Issue
Block a user