From 815b80437e4031a65fefe0ac05f61881d280d842 Mon Sep 17 00:00:00 2001 From: chenqi Date: Wed, 17 Jun 2026 12:12:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(clinical-pathway):=20=E4=B8=B4=E5=BA=8A?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=89=A7=E8=A1=8C=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IClinicalPathwayAppService.java | 17 + .../impl/ClinicalPathwayAppServiceImpl.java | 178 +++++++ .../controller/ClinicalPathwayController.java | 49 +- .../V58__clinical_pathway_variance.sql | 21 + .../domain/ClinicalPathwayVariance.java | 12 + .../mapper/ClinicalPathwayVarianceMapper.java | 6 + .../IClinicalPathwayVarianceService.java | 4 + .../ClinicalPathwayVarianceServiceImpl.java | 8 + healthlink-his-ui/src/api/clinicalPathway.js | 37 ++ .../views/inpatientDoctor/ClinicalPathway.vue | 472 ++++++++++++++++++ 10 files changed, 803 insertions(+), 1 deletion(-) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IClinicalPathwayAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/ClinicalPathwayAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V58__clinical_pathway_variance.sql create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/ClinicalPathwayVariance.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/ClinicalPathwayVarianceMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IClinicalPathwayVarianceService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/ClinicalPathwayVarianceServiceImpl.java create mode 100644 healthlink-his-ui/src/api/clinicalPathway.js create mode 100644 healthlink-his-ui/src/views/inpatientDoctor/ClinicalPathway.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IClinicalPathwayAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IClinicalPathwayAppService.java new file mode 100644 index 000000000..07d5ee99d --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/IClinicalPathwayAppService.java @@ -0,0 +1,17 @@ +package com.healthlink.his.web.clinical.appservice; +import com.healthlink.his.clinical.domain.ClinicalPathway; +import com.healthlink.his.clinical.domain.ClinicalPathwayExecution; +import com.healthlink.his.clinical.domain.ClinicalPathwayVariance; +import com.baomidou.mybatisplus.core.metadata.IPage; +import java.util.Map; +public interface IClinicalPathwayAppService { + Map evaluate(Long encounterId); + void admit(ClinicalPathwayExecution execution); + void recordExecution(ClinicalPathwayExecution execution); + void recordVariance(ClinicalPathwayVariance variance); + void discharge(Long executionId); + Map getStatistics(String startDate, String endDate); + IPage page(String diseaseCode, Integer pageNo, Integer pageSize); + void add(ClinicalPathway pathway); + IPage> getExecutionPage(Long pathwayId, Integer pageNo, Integer pageSize); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/ClinicalPathwayAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/ClinicalPathwayAppServiceImpl.java new file mode 100644 index 000000000..e91a81625 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/appservice/impl/ClinicalPathwayAppServiceImpl.java @@ -0,0 +1,178 @@ +package com.healthlink.his.web.clinical.appservice.impl; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.healthlink.his.clinical.domain.ClinicalPathway; +import com.healthlink.his.clinical.domain.ClinicalPathwayExecution; +import com.healthlink.his.clinical.domain.ClinicalPathwayVariance; +import com.healthlink.his.clinical.service.IClinicalPathwayService; +import com.healthlink.his.clinical.service.IClinicalPathwayExecutionService; +import com.healthlink.his.clinical.service.IClinicalPathwayVarianceService; +import com.healthlink.his.web.clinical.appservice.IClinicalPathwayAppService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.*; +@Service +public class ClinicalPathwayAppServiceImpl implements IClinicalPathwayAppService { + @Autowired + private IClinicalPathwayService pathwayService; + @Autowired + private IClinicalPathwayExecutionService executionService; + @Autowired + private IClinicalPathwayVarianceService varianceService; + @Override + public Map evaluate(Long encounterId) { + Map result = new LinkedHashMap<>(); + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(ClinicalPathwayExecution::getEncounterId, encounterId) + .eq(ClinicalPathwayExecution::getStatus, "IN_PATH"); + ClinicalPathwayExecution active = executionService.getOne(qw); + result.put("hasActivePathway", active != null); + if (active != null) { + result.put("activeExecution", active); + LambdaQueryWrapper pw = new LambdaQueryWrapper<>(); + pw.eq(ClinicalPathway::getId, active.getPathwayId()); + result.put("pathway", pathwayService.getOne(pw)); + } + LambdaQueryWrapper allQw = new LambdaQueryWrapper<>(); + allQw.eq(ClinicalPathwayExecution::getEncounterId, encounterId); + result.put("totalExecutions", executionService.count(allQw)); + return result; + } + @Override + @Transactional(rollbackFor = Exception.class) + public void admit(ClinicalPathwayExecution execution) { + execution.setStatus("IN_PATH"); + execution.setEnterDate(LocalDate.now()); + execution.setCreateTime(new Date()); + executionService.save(execution); + } + @Override + @Transactional(rollbackFor = Exception.class) + public void recordExecution(ClinicalPathwayExecution execution) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(ClinicalPathwayExecution::getEncounterId, execution.getEncounterId()) + .eq(ClinicalPathwayExecution::getStatus, "IN_PATH"); + ClinicalPathwayExecution existing = executionService.getOne(qw); + if (existing == null) { + throw new RuntimeException("当前无在径执行记录"); + } + if (execution.getActualDays() != null) { + existing.setActualDays(execution.getActualDays()); + } + if (execution.getActualCost() != null) { + existing.setActualCost(execution.getActualCost()); + } + existing.setUpdateTime(new Date()); + executionService.updateById(existing); + } + @Override + @Transactional(rollbackFor = Exception.class) + public void recordVariance(ClinicalPathwayVariance variance) { + variance.setCreateTime(new Date()); + varianceService.save(variance); + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(ClinicalPathwayExecution::getId, variance.getExecutionId()); + ClinicalPathwayExecution exec = executionService.getOne(qw); + if (exec != null) { + exec.setStatus("VARIATION"); + exec.setVariationReason(variance.getVarianceReason()); + exec.setUpdateTime(new Date()); + executionService.updateById(exec); + } + } + @Override + @Transactional(rollbackFor = Exception.class) + public void discharge(Long executionId) { + ClinicalPathwayExecution exec = executionService.getById(executionId); + if (exec == null) { + throw new RuntimeException("执行记录不存在"); + } + exec.setStatus("COMPLETED"); + exec.setCompleteDate(LocalDate.now()); + if (exec.getEnterDate() != null) { + long days = ChronoUnit.DAYS.between(exec.getEnterDate(), LocalDate.now()); + exec.setActualDays((int) days); + } + exec.setUpdateTime(new Date()); + executionService.updateById(exec); + } + @Override + public Map getStatistics(String startDate, String endDate) { + Map stats = new LinkedHashMap<>(); + stats.put("totalPathways", pathwayService.count()); + stats.put("totalExecutions", executionService.count()); + LambdaQueryWrapper inPathQw = new LambdaQueryWrapper<>(); + inPathQw.eq(ClinicalPathwayExecution::getStatus, "IN_PATH"); + stats.put("inPathCount", executionService.count(inPathQw)); + LambdaQueryWrapper completedQw = new LambdaQueryWrapper<>(); + completedQw.eq(ClinicalPathwayExecution::getStatus, "COMPLETED"); + stats.put("completedCount", executionService.count(completedQw)); + LambdaQueryWrapper variationQw = new LambdaQueryWrapper<>(); + variationQw.eq(ClinicalPathwayExecution::getStatus, "VARIATION"); + stats.put("variationCount", executionService.count(variationQw)); + LambdaQueryWrapper exitQw = new LambdaQueryWrapper<>(); + exitQw.eq(ClinicalPathwayExecution::getStatus, "EXIT"); + stats.put("exitCount", executionService.count(exitQw)); + long total = executionService.count(); + long completed = (long) stats.get("completedCount"); + stats.put("completionRate", total > 0 ? Math.round(completed * 100.0 / total) : 0); + stats.put("totalVariances", varianceService.count()); + return stats; + } + @Override + public IPage page(String diseaseCode, Integer pageNo, Integer pageSize) { + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + w.eq(StringUtils.hasText(diseaseCode), ClinicalPathway::getDiseaseCode, diseaseCode) + .eq(ClinicalPathway::getStatus, "ACTIVE"); + return pathwayService.page(new Page<>(pageNo, pageSize), w); + } + @Override + @Transactional(rollbackFor = Exception.class) + public void add(ClinicalPathway pathway) { + pathway.setVersion(1); + pathway.setStatus("ACTIVE"); + pathway.setCreateTime(new Date()); + pathwayService.save(pathway); + } + @Override + public IPage> getExecutionPage(Long pathwayId, Integer pageNo, Integer pageSize) { + LambdaQueryWrapper qw = new LambdaQueryWrapper<>(); + qw.eq(pathwayId != null, ClinicalPathwayExecution::getPathwayId, pathwayId) + .orderByDesc(ClinicalPathwayExecution::getCreateTime); + Page execPage = executionService.page(new Page<>(pageNo, pageSize), qw); + Page> result = new Page<>(execPage.getCurrent(), execPage.getSize(), execPage.getTotal()); + List> records = new ArrayList<>(); + for (ClinicalPathwayExecution exec : execPage.getRecords()) { + Map row = new LinkedHashMap<>(); + row.put("id", exec.getId()); + row.put("pathwayId", exec.getPathwayId()); + row.put("encounterId", exec.getEncounterId()); + row.put("patientId", exec.getPatientId()); + row.put("patientName", exec.getPatientName()); + row.put("enterDate", exec.getEnterDate()); + row.put("expectedDays", exec.getExpectedDays()); + row.put("actualDays", exec.getActualDays()); + row.put("expectedCost", exec.getExpectedCost()); + row.put("actualCost", exec.getActualCost()); + row.put("status", exec.getStatus()); + row.put("completeDate", exec.getCompleteDate()); + row.put("createTime", exec.getCreateTime()); + LambdaQueryWrapper pw = new LambdaQueryWrapper<>(); + pw.eq(ClinicalPathway::getId, exec.getPathwayId()); + ClinicalPathway pwObj = pathwayService.getOne(pw); + if (pwObj != null) { + row.put("pathwayName", pwObj.getPathwayName()); + row.put("diseaseCode", pwObj.getDiseaseCode()); + row.put("diseaseName", pwObj.getDiseaseName()); + } + records.add(row); + } + result.setRecords(records); + return result; + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/ClinicalPathwayController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/ClinicalPathwayController.java index ddacec21d..67c8a19d0 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/ClinicalPathwayController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/clinical/controller/ClinicalPathwayController.java @@ -1,17 +1,23 @@ package com.healthlink.his.web.clinical.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.core.common.core.domain.AjaxResult; import com.core.common.core.domain.R; import com.healthlink.his.clinical.domain.*; import com.healthlink.his.clinical.service.*; +import com.healthlink.his.web.clinical.appservice.IClinicalPathwayAppService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor;import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils;import org.springframework.web.bind.annotation.*; import java.util.*; -@RestController @RequestMapping("/clinical-pathway") @Slf4j @AllArgsConstructor +@Tag(name = "临床路径管理") @RestController @RequestMapping("/clinical-pathway") @Slf4j @AllArgsConstructor public class ClinicalPathwayController { private final IClinicalPathwayService pathwayService; private final IClinicalPathwayExecutionService executionService; + private final IClinicalPathwayAppService clinicalPathwayAppService; @GetMapping("/page") public R getPage(@RequestParam(value="diseaseCode",required=false) String diseaseCode, @RequestParam(value="pageNo",defaultValue="1") Integer pageNo, @@ -59,4 +65,45 @@ public class ClinicalPathwayController { stats.put("completionRate", total > 0 ? Math.round(completed * 100.0 / total) : 0); return R.ok(stats); } + @Operation(summary = "入径评估") + @PreAuthorize("@ss.hasPermi('inpatient:clinical:list')") + @GetMapping("/evaluate/{encounterId}") + public AjaxResult evaluate(@PathVariable Long encounterId) { + return AjaxResult.success(clinicalPathwayAppService.evaluate(encounterId)); + } + @Operation(summary = "入径") + @PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')") + @PostMapping("/admission") + public AjaxResult admission(@RequestBody ClinicalPathwayExecution execution) { + clinicalPathwayAppService.admit(execution); + return AjaxResult.success(); + } + @Operation(summary = "执行记录") + @PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')") + @PostMapping("/execution/record") + public AjaxResult executionRecord(@RequestBody ClinicalPathwayExecution execution) { + clinicalPathwayAppService.recordExecution(execution); + return AjaxResult.success(); + } + @Operation(summary = "变异记录") + @PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')") + @PostMapping("/variance/record") + public AjaxResult varianceRecord(@RequestBody ClinicalPathwayVariance variance) { + clinicalPathwayAppService.recordVariance(variance); + return AjaxResult.success(); + } + @Operation(summary = "出径") + @PreAuthorize("@ss.hasPermi('inpatient:clinical:edit')") + @PostMapping("/discharge") + public AjaxResult discharge(@RequestParam Long executionId) { + clinicalPathwayAppService.discharge(executionId); + return AjaxResult.success(); + } + @Operation(summary = "路径统计") + @PreAuthorize("@ss.hasPermi('inpatient:clinical:list')") + @GetMapping("/statistics") + public AjaxResult statistics(@RequestParam(required = false) String startDate, + @RequestParam(required = false) String endDate) { + return AjaxResult.success(clinicalPathwayAppService.getStatistics(startDate, endDate)); + } } diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V58__clinical_pathway_variance.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V58__clinical_pathway_variance.sql new file mode 100644 index 000000000..3c8ff9d16 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V58__clinical_pathway_variance.sql @@ -0,0 +1,21 @@ +CREATE TABLE IF NOT EXISTS clinical_pathway_variance ( + id BIGSERIAL PRIMARY KEY, + execution_id BIGINT NOT NULL, + pathway_id BIGINT NOT NULL, + variance_date DATE NOT NULL, + variance_type VARCHAR(20) NOT NULL, + expected_item VARCHAR(200), + actual_item VARCHAR(200), + variance_reason TEXT, + adjustment_action TEXT, + record_by BIGINT, + record_by_name VARCHAR(50), + tenant_id INTEGER DEFAULT 0, + del_flag CHAR(1) DEFAULT '0', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + create_by VARCHAR(64), + update_time TIMESTAMP, + update_by VARCHAR(64) +); +CREATE INDEX IF NOT EXISTS idx_cpv_execution ON clinical_pathway_variance(execution_id); +CREATE INDEX IF NOT EXISTS idx_cpv_pathway ON clinical_pathway_variance(pathway_id); diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/ClinicalPathwayVariance.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/ClinicalPathwayVariance.java new file mode 100644 index 000000000..8d0126fa2 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/domain/ClinicalPathwayVariance.java @@ -0,0 +1,12 @@ +package com.healthlink.his.clinical.domain; +import com.baomidou.mybatisplus.annotation.*;import com.core.common.core.domain.HisBaseEntity; +import lombok.Data;import lombok.EqualsAndHashCode; +import java.time.LocalDate; +@Data @EqualsAndHashCode(callSuper=true) @TableName("clinical_pathway_variance") +public class ClinicalPathwayVariance extends HisBaseEntity { + @TableId(value="id",type=IdType.ASSIGN_ID) private Long id; + private Long executionId; private Long pathwayId; private LocalDate varianceDate; + private String varianceType; private String expectedItem; private String actualItem; + private String varianceReason; private String adjustmentAction; + private Long recordBy; private String recordByName; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/ClinicalPathwayVarianceMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/ClinicalPathwayVarianceMapper.java new file mode 100644 index 000000000..2ffe26351 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/mapper/ClinicalPathwayVarianceMapper.java @@ -0,0 +1,6 @@ +package com.healthlink.his.clinical.mapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.clinical.domain.ClinicalPathwayVariance; +import org.apache.ibatis.annotations.Mapper; +@Mapper +public interface ClinicalPathwayVarianceMapper extends BaseMapper {} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IClinicalPathwayVarianceService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IClinicalPathwayVarianceService.java new file mode 100644 index 000000000..88d303770 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/IClinicalPathwayVarianceService.java @@ -0,0 +1,4 @@ +package com.healthlink.his.clinical.service; +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.clinical.domain.ClinicalPathwayVariance; +public interface IClinicalPathwayVarianceService extends IService {} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/ClinicalPathwayVarianceServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/ClinicalPathwayVarianceServiceImpl.java new file mode 100644 index 000000000..7b4501015 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/clinical/service/impl/ClinicalPathwayVarianceServiceImpl.java @@ -0,0 +1,8 @@ +package com.healthlink.his.clinical.service.impl; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.clinical.domain.ClinicalPathwayVariance; +import com.healthlink.his.clinical.mapper.ClinicalPathwayVarianceMapper; +import com.healthlink.his.clinical.service.IClinicalPathwayVarianceService; +import org.springframework.stereotype.Service; +@Service +public class ClinicalPathwayVarianceServiceImpl extends ServiceImpl implements IClinicalPathwayVarianceService {} diff --git a/healthlink-his-ui/src/api/clinicalPathway.js b/healthlink-his-ui/src/api/clinicalPathway.js new file mode 100644 index 000000000..de101f662 --- /dev/null +++ b/healthlink-his-ui/src/api/clinicalPathway.js @@ -0,0 +1,37 @@ +import request from '@/utils/request' + +export function evaluatePathway(encounterId) { + return request({ url: '/clinical-pathway/evaluate/' + encounterId, method: 'get' }) +} + +export function admitPathway(data) { + return request({ url: '/clinical-pathway/admission', method: 'post', data: data }) +} + +export function recordExecution(data) { + return request({ url: '/clinical-pathway/execution/record', method: 'post', data: data }) +} + +export function recordVariance(data) { + return request({ url: '/clinical-pathway/variance/record', method: 'post', data: data }) +} + +export function dischargePathway(executionId) { + return request({ url: '/clinical-pathway/discharge', method: 'post', params: { executionId: executionId } }) +} + +export function getPathwayStatistics(params) { + return request({ url: '/clinical-pathway/statistics', method: 'get', params: params }) +} + +export function getPathwayPage(params) { + return request({ url: '/clinical-pathway/page', method: 'get', params: params }) +} + +export function addPathway(data) { + return request({ url: '/clinical-pathway/add', method: 'post', data: data }) +} + +export function getExecutionPage(params) { + return request({ url: '/clinical-pathway/execution/page', method: 'get', params: params }) +} diff --git a/healthlink-his-ui/src/views/inpatientDoctor/ClinicalPathway.vue b/healthlink-his-ui/src/views/inpatientDoctor/ClinicalPathway.vue new file mode 100644 index 000000000..d25406a83 --- /dev/null +++ b/healthlink-his-ui/src/views/inpatientDoctor/ClinicalPathway.vue @@ -0,0 +1,472 @@ + + + + +