From 2cff3135392d98e4049c9e0dcd9f78d2d59a84f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8E=E4=BD=97?= Date: Sat, 6 Jun 2026 20:55:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(P3):=20=E7=97=85=E6=A1=88=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=AE=8C=E5=96=84=20=E2=80=94=20DRG/DIP=E5=88=86?= =?UTF-8?q?=E7=BB=84+=E7=BB=9F=E8=AE=A1=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MrDrgController: DRG/DIP分组/无效标记/统计/排名 - MrDrgGrouping: 分组结果实体+V28 Flyway迁移 - 前端drg: 分组列表+DRG排名+统计卡片 - 后端编译通过,前端构建通过 --- .../controller/MrDrgController.java | 157 ++++++++++++++++++ .../db/migration/V28__drg_dip_grouping.sql | 52 ++++++ .../his/mrhomepage/domain/MrDrgGrouping.java | 42 +++++ .../mapper/MrDrgGroupingMapper.java | 9 + .../service/IMrDrgGroupingService.java | 7 + .../impl/MrDrgGroupingServiceImpl.java | 13 ++ .../src/views/mrhomepage/drg/api.js | 6 + .../src/views/mrhomepage/drg/index.vue | 91 ++++++++++ 8 files changed, 377 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/mrhomepage/controller/MrDrgController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V28__drg_dip_grouping.sql create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/domain/MrDrgGrouping.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/mapper/MrDrgGroupingMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/IMrDrgGroupingService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/impl/MrDrgGroupingServiceImpl.java create mode 100644 healthlink-his-ui/src/views/mrhomepage/drg/api.js create mode 100644 healthlink-his-ui/src/views/mrhomepage/drg/index.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/mrhomepage/controller/MrDrgController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/mrhomepage/controller/MrDrgController.java new file mode 100644 index 000000000..905ca7d94 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/mrhomepage/controller/MrDrgController.java @@ -0,0 +1,157 @@ +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.MrDrgGrouping; +import com.healthlink.his.mrhomepage.service.IMrDrgGroupingService; +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.math.BigDecimal; +import java.util.*; + +/** + * DRG/DIP分组 + 病案归档统计 Controller + */ +@RestController +@RequestMapping("/mr-drg") +@Slf4j +@AllArgsConstructor +public class MrDrgController { + + private final IMrDrgGroupingService drgGroupingService; + + // ==================== DRG/DIP分组 ==================== + + @GetMapping("/page") + public R getPage( + @RequestParam(value = "groupingType", required = false) String groupingType, + @RequestParam(value = "drgCode", required = false) String drgCode, + @RequestParam(value = "patientName", required = false) String patientName, + @RequestParam(value = "isValid", required = false) Boolean isValid, + @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) { + LambdaQueryWrapper w = new LambdaQueryWrapper<>(); + w.eq(StringUtils.hasText(groupingType), MrDrgGrouping::getGroupingType, groupingType) + .like(StringUtils.hasText(drgCode), MrDrgGrouping::getDrgCode, drgCode) + .like(StringUtils.hasText(patientName), MrDrgGrouping::getPatientName, patientName) + .eq(isValid != null, MrDrgGrouping::getIsValid, isValid) + .orderByDesc(MrDrgGrouping::getDischargeDate); + return R.ok(drgGroupingService.page(new Page<>(pageNo, pageSize), w)); + } + + @PostMapping("/group") + @Transactional(rollbackFor = Exception.class) + public R groupDrg(@RequestBody MrDrgGrouping grouping) { + // 简化DRG分组逻辑 — 根据主诊断+主手术推算DRG组 + String drgCode = calculateDrgCode(grouping); + grouping.setDrgCode(drgCode); + grouping.setDrgName(getDrgName(drgCode)); + grouping.setDrgWeight(getDrgWeight(drgCode)); + grouping.setIsValid(true); + grouping.setCreateTime(new Date()); + drgGroupingService.save(grouping); + return R.ok(grouping); + } + + @PutMapping("/invalidate/{id}") + @Transactional(rollbackFor = Exception.class) + public R invalidate(@PathVariable Long id, @RequestParam("reason") String reason) { + MrDrgGrouping g = drgGroupingService.getById(id); + if (g == null) return R.fail("分组记录不存在"); + g.setIsValid(false); + g.setInvalidReason(reason); + drgGroupingService.updateById(g); + return R.ok(); + } + + // ==================== 统计分析 ==================== + + @GetMapping("/stats/overview") + public R getOverview( + @RequestParam(value = "startDate", required = false) String startDate, + @RequestParam(value = "endDate", required = false) String endDate) { + Map stats = new HashMap<>(); + + // DRG分组统计 + LambdaQueryWrapper drgW = new LambdaQueryWrapper<>(); + drgW.eq(MrDrgGrouping::getGroupingType, "DRG"); + stats.put("drgTotal", drgGroupingService.count(drgW)); + + // DIP分组统计 + LambdaQueryWrapper dipW = new LambdaQueryWrapper<>(); + dipW.eq(MrDrgGrouping::getGroupingType, "DIP"); + stats.put("dipTotal", drgGroupingService.count(dipW)); + + // 无效分组数 + LambdaQueryWrapper invW = new LambdaQueryWrapper<>(); + invW.eq(MrDrgGrouping::getIsValid, false); + stats.put("invalidCount", drgGroupingService.count(invW)); + + // 费用统计 + List all = drgGroupingService.list(); + BigDecimal totalCost = BigDecimal.ZERO; + BigDecimal totalInsurance = BigDecimal.ZERO; + for (MrDrgGrouping g : all) { + if (g.getTotalCost() != null) totalCost = totalCost.add(g.getTotalCost()); + if (g.getInsurancePayment() != null) totalInsurance = totalInsurance.add(g.getInsurancePayment()); + } + stats.put("totalCost", totalCost); + stats.put("totalInsurance", totalInsurance); + + return R.ok(stats); + } + + @GetMapping("/stats/top-drg") + public R getTopDrg(@RequestParam(value = "limit", defaultValue = "10") Integer limit) { + List all = drgGroupingService.list(); + Map drgCount = new LinkedHashMap<>(); + Map drgCost = new LinkedHashMap<>(); + for (MrDrgGrouping g : all) { + String code = g.getDrgCode(); + if (code != null) { + drgCount.merge(code, 1, Integer::sum); + drgCost.merge(code, g.getTotalCost() != null ? g.getTotalCost() : BigDecimal.ZERO, BigDecimal::add); + } + } + List> topList = new ArrayList<>(); + drgCount.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(limit) + .forEach(e -> { + Map item = new HashMap<>(); + item.put("drgCode", e.getKey()); + item.put("count", e.getValue()); + item.put("totalCost", drgCost.getOrDefault(e.getKey(), BigDecimal.ZERO)); + topList.add(item); + }); + return R.ok(topList); + } + + // ==================== DRG分组算法(简化版) ==================== + + private String calculateDrgCode(MrDrgGrouping g) { + // 简化DRG分组: 根据主诊断前3位+主手术确定DRG组 + String diagCode = g.getPrimaryDiagnosisCode(); + String procCode = g.getPrimaryProcedureCode(); + if (diagCode == null || diagCode.isEmpty()) return "ZZ99"; + String mdc = diagCode.substring(0, Math.min(3, diagCode.length())); + if (procCode != null && !procCode.isEmpty()) { + return mdc + "-" + procCode.substring(0, Math.min(2, procCode.length())); + } + return mdc + "-MED"; + } + + private String getDrgName(String code) { + return "DRG组(" + code + ")"; + } + + private BigDecimal getDrgWeight(String code) { + // 简化权重计算 + return new BigDecimal("1.0000"); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V28__drg_dip_grouping.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V28__drg_dip_grouping.sql new file mode 100644 index 000000000..29f7e328b --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V28__drg_dip_grouping.sql @@ -0,0 +1,52 @@ +-- V28: 病案管理增强 — DRG/DIP分组+归档统计 + +-- DRG/DIP分组结果表 +CREATE TABLE IF NOT EXISTS mr_drg_grouping ( + id BIGSERIAL PRIMARY KEY, + encounter_id BIGINT NOT NULL, + patient_id BIGINT NOT NULL, + patient_name VARCHAR(50), + discharge_date DATE, + primary_diagnosis VARCHAR(200), + primary_diagnosis_code VARCHAR(20), + secondary_diagnosis_code VARCHAR(20), + primary_procedure VARCHAR(200), + primary_procedure_code VARCHAR(20), + drg_code VARCHAR(20), + drg_name VARCHAR(200), + drg_weight DECIMAL(8,4), + dip_code VARCHAR(20), + dip_name VARCHAR(200), + grouping_type VARCHAR(10) NOT NULL, + total_cost DECIMAL(12,2), + insurance_payment DECIMAL(12,2), + patient_payment DECIMAL(12,2), + los_days INT, + grouping_result TEXT, + is_valid BOOLEAN DEFAULT TRUE, + invalid_reason VARCHAR(200), + tenant_id BIGINT DEFAULT 0, + is_deleted INT NOT NULL DEFAULT 0, + create_time TIMESTAMP DEFAULT CURRENT CURRENT_TIMESTAMP +); +COMMENT ON TABLE mr_drg_grouping IS 'DRG/DIP分组结果'; +COMMENT ON COLUMN mr_drg_grouping.grouping_type IS '分组类型(DRG/DIP)'; +CREATE INDEX idx_drg_encounter ON mr_drg_grouping(encounter_id); +CREATE INDEX idx_drg_code ON mr_drg_grouping(drg_code); +CREATE INDEX idx_dip_code ON mr_drg_grouping(dip_code); + +-- 病案归档统计表 +CREATE TABLE IF NOT EXISTS mr_archive_stats ( + id BIGSERIAL PRIMARY KEY, + stat_date DATE NOT NULL, + department_id BIGINT, + department_name VARCHAR(100), + total_discharged INT DEFAULT 0, + archived_count INT DEFAULT 0, + archive_rate DECIMAL(5,2), + avg_archive_hours DECIMAL(8,2), + tenant_id BIGINT DEFAULT 0, + create_time TIMESTAMP DEFAULT CURRENT CURRENT_TIMESTAMP +); +COMMENT ON TABLE mr_archive_stats IS '病案归档统计(每日)'; +CREATE UNIQUE INDEX idx_mras_date_dept ON mr_archive_stats(stat_date, department_id); diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/domain/MrDrgGrouping.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/domain/MrDrgGrouping.java new file mode 100644 index 000000000..493b97ad5 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/domain/MrDrgGrouping.java @@ -0,0 +1,42 @@ +package com.healthlink.his.mrhomepage.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.core.common.core.domain.HisBaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * DRG/DIP分组结果 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("mr_drg_grouping") +public class MrDrgGrouping extends HisBaseEntity { + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + private Long encounterId; + private Long patientId; + private String patientName; + private LocalDate dischargeDate; + private String primaryDiagnosis; + private String primaryDiagnosisCode; + private String secondaryDiagnosisCode; + private String primaryProcedure; + private String primaryProcedureCode; + private String drgCode; + private String drgName; + private BigDecimal drgWeight; + private String dipCode; + private String dipName; + private String groupingType; + private BigDecimal totalCost; + private BigDecimal insurancePayment; + private BigDecimal patientPayment; + private Integer losDays; + private String groupingResult; + private Boolean isValid; + private String invalidReason; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/mapper/MrDrgGroupingMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/mapper/MrDrgGroupingMapper.java new file mode 100644 index 000000000..5633b06ff --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/mapper/MrDrgGroupingMapper.java @@ -0,0 +1,9 @@ +package com.healthlink.his.mrhomepage.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.mrhomepage.domain.MrDrgGrouping; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MrDrgGroupingMapper extends BaseMapper { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/IMrDrgGroupingService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/IMrDrgGroupingService.java new file mode 100644 index 000000000..35c3e2e82 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/IMrDrgGroupingService.java @@ -0,0 +1,7 @@ +package com.healthlink.his.mrhomepage.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.mrhomepage.domain.MrDrgGrouping; + +public interface IMrDrgGroupingService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/impl/MrDrgGroupingServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/impl/MrDrgGroupingServiceImpl.java new file mode 100644 index 000000000..edfdc2f4b --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/mrhomepage/service/impl/MrDrgGroupingServiceImpl.java @@ -0,0 +1,13 @@ +package com.healthlink.his.mrhomepage.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.mrhomepage.domain.MrDrgGrouping; +import com.healthlink.his.mrhomepage.mapper.MrDrgGroupingMapper; +import com.healthlink.his.mrhomepage.service.IMrDrgGroupingService; +import org.springframework.stereotype.Service; + +@Service +public class MrDrgGroupingServiceImpl + extends ServiceImpl + implements IMrDrgGroupingService { +} diff --git a/healthlink-his-ui/src/views/mrhomepage/drg/api.js b/healthlink-his-ui/src/views/mrhomepage/drg/api.js new file mode 100644 index 000000000..aba754420 --- /dev/null +++ b/healthlink-his-ui/src/views/mrhomepage/drg/api.js @@ -0,0 +1,6 @@ +import request from '@/utils/request' +export function getPage(p){return request({url:'/mr-drg/page',method:'get',params:p})} +export function groupDrg(d){return request({url:'/mr-drg/group',method:'post',data:d})} +export function invalidate(id,reason){return request({url:'/mr-drg/invalidate/'+id,method:'put',params:{reason}})} +export function getOverview(p){return request({url:'/mr-drg/stats/overview',method:'get',params:p})} +export function getTopDrg(p){return request({url:'/mr-drg/stats/top-drg',method:'get',params:p})} diff --git a/healthlink-his-ui/src/views/mrhomepage/drg/index.vue b/healthlink-his-ui/src/views/mrhomepage/drg/index.vue new file mode 100644 index 000000000..b7002d2c1 --- /dev/null +++ b/healthlink-his-ui/src/views/mrhomepage/drg/index.vue @@ -0,0 +1,91 @@ + +