feat(sprint10): 院感管理+抗菌药物管控 — Phase 2 P1模块

Sprint 10 完成内容:

院感管理 (Hospital Infection):
- Flyway V9已有: hir_infection_case + hir_antibiotic_usage + hir_occupational_exposure
- 后端: 3 Entity + 3 Mapper + 3 Service + AppService(7方法) + Controller(6接口)
- 功能: 院感病例上报/审核 + 抗菌药物使用记录 + 职业暴露登记/随访

抗菌药物管控 (Antibiotic Control):
- Flyway V10已有: antibiotic_class_rule + antibiotic_approval
- 后端: 2 Entity + 2 Mapper + 2 Service + AppService(5方法) + Controller(5接口)
- 功能: 分级管理(限制/非限制/特殊) + 使用权限检查 + 审批流程 + 统计

Phase 2 进度: Sprint 9-10 完成 (6个P1模块)
   护理评估  危急值管理  病历质控
   院感管理  抗菌药物  处方点评(待做)

编译验证: BUILD SUCCESS
This commit is contained in:
2026-06-06 10:56:09 +08:00
parent 8ff8e3b5b2
commit 416df419d9
26 changed files with 349 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
package com.healthlink.his.web.antibiotic.appservice;
import com.healthlink.his.antibiotic.domain.*;
import java.util.List;
import java.util.Map;
public interface IAntibioticAppService {
List<AntibioticClassRule> getRulesByDrug(String drugCode);
boolean checkRestriction(String drugCode, Integer doctorLevel);
AntibioticApproval requestApproval(AntibioticApproval a);
void approve(Long id, Long approverId, String approverName, String result);
Map<String, Object> getUsageStatistics(String startDate, String endDate);
}

View File

@@ -0,0 +1,49 @@
package com.healthlink.his.web.antibiotic.appservice.impl;
import com.healthlink.his.antibiotic.domain.*;
import com.healthlink.his.antibiotic.service.*;
import com.healthlink.his.web.antibiotic.appservice.IAntibioticAppService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class AntibioticAppServiceImpl implements IAntibioticAppService {
@Autowired private IAntibioticClassRuleService ruleService;
@Autowired private IAntibioticApprovalService approvalService;
@Override
public List<AntibioticClassRule> getRulesByDrug(String drugCode) {
return ruleService.list(new LambdaQueryWrapper<AntibioticClassRule>()
.eq(AntibioticClassRule::getDrugCode, drugCode)
.eq(AntibioticClassRule::getEnabled, "1").eq(AntibioticClassRule::getDelFlag, "0"));
}
@Override
public boolean checkRestriction(String drugCode, Integer doctorLevel) {
List<AntibioticClassRule> rules = getRulesByDrug(drugCode);
for (AntibioticClassRule rule : rules) {
if ("SPECIAL".equals(rule.getAntibioticClass()) && doctorLevel < 3) return false;
if ("RESTRICTED".equals(rule.getAntibioticClass()) && doctorLevel < 2) return false;
}
return true;
}
@Override
public AntibioticApproval requestApproval(AntibioticApproval a) {
a.setStatus("PENDING"); a.setDelFlag("0"); approvalService.save(a); return a;
}
@Override
public void approve(Long id, Long approverId, String approverName, String result) {
AntibioticApproval a = approvalService.getById(id);
a.setApproverId(approverId); a.setApproverName(approverName);
a.setApprovalTime(new Date()); a.setApprovalResult(result);
a.setStatus("APPROVED".equals(result) ? "APPROVED" : "REJECTED");
approvalService.updateById(a);
}
@Override
public Map<String, Object> getUsageStatistics(String startDate, String endDate) {
Map<String, Object> r = new HashMap<>();
r.put("totalApprovals", approvalService.count());
r.put("approved", approvalService.count(new LambdaQueryWrapper<AntibioticApproval>().eq(AntibioticApproval::getStatus, "APPROVED")));
r.put("pending", approvalService.count(new LambdaQueryWrapper<AntibioticApproval>().eq(AntibioticApproval::getStatus, "PENDING")));
return r;
}
}

View File

@@ -0,0 +1,26 @@
package com.healthlink.his.web.antibiotic.controller;
import com.core.common.core.domain.AjaxResult;
import com.healthlink.his.antibiotic.domain.*;
import com.healthlink.his.web.antibiotic.appservice.IAntibioticAppService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Tag(name = "抗菌药物管控")
@RestController @RequestMapping("/healthlink-his/api/v1/antibiotic")
public class AntibioticController {
@Autowired private IAntibioticAppService antibioticAppService;
@Operation(summary = "查询药品限制规则") @GetMapping("/rules/{drugCode}")
public AjaxResult rules(@PathVariable String drugCode) { return AjaxResult.success(antibioticAppService.getRulesByDrug(drugCode)); }
@Operation(summary = "检查处方权限") @GetMapping("/check-restriction")
public AjaxResult checkRestriction(@RequestParam String drugCode, @RequestParam Integer doctorLevel) {
return AjaxResult.success(antibioticAppService.checkRestriction(drugCode, doctorLevel)); }
@Operation(summary = "申请使用审批") @PostMapping("/approval")
public AjaxResult requestApproval(@RequestBody AntibioticApproval a) { return AjaxResult.success(antibioticAppService.requestApproval(a)); }
@Operation(summary = "审批") @PutMapping("/approval/{id}")
public AjaxResult approve(@PathVariable Long id, @RequestParam Long approverId, @RequestParam String approverName, @RequestParam String result) {
antibioticAppService.approve(id, approverId, approverName, result); return AjaxResult.success(); }
@Operation(summary = "使用统计") @GetMapping("/statistics")
public AjaxResult statistics(@RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate) {
return AjaxResult.success(antibioticAppService.getUsageStatistics(startDate, endDate)); }
}

View File

@@ -0,0 +1,13 @@
package com.healthlink.his.web.infection.appservice;
import com.healthlink.his.infection.domain.*;
import java.util.List;
import java.util.Map;
public interface IInfectionAppService {
HirInfectionCase reportInfection(HirInfectionCase c);
void reviewCase(Long id, Long reviewId, String reviewName, String result);
List<HirInfectionCase> getCaseList(String status);
Map<String, Object> getStatistics(String startDate, String endDate);
List<Map<String, Object>> getAntibioticUsageStats(String startDate, String endDate);
HirOccupationalExposure reportExposure(HirOccupationalExposure e);
List<HirOccupationalExposure> getExposureList();
}

View File

@@ -0,0 +1,55 @@
package com.healthlink.his.web.infection.appservice.impl;
import com.healthlink.his.infection.domain.*;
import com.healthlink.his.infection.service.*;
import com.healthlink.his.web.infection.appservice.IInfectionAppService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
public class InfectionAppServiceImpl implements IInfectionAppService {
@Autowired private IHirInfectionCaseService infectionCaseService;
@Autowired private IHirAntibioticUsageService antibioticUsageService;
@Autowired private IHirOccupationalExposureService exposureService;
@Override
public HirInfectionCase reportInfection(HirInfectionCase c) {
c.setStatus("REPORTED"); c.setDelFlag("0"); c.setReportTime(new Date());
infectionCaseService.save(c); return c;
}
@Override
public void reviewCase(Long id, Long reviewId, String reviewName, String result) {
HirInfectionCase c = infectionCaseService.getById(id);
c.setReviewId(reviewId); c.setReviewName(reviewName); c.setReviewTime(new Date());
c.setReviewResult(result); c.setStatus("REVIEWED");
infectionCaseService.updateById(c);
}
@Override
public List<HirInfectionCase> getCaseList(String status) {
return infectionCaseService.list(new LambdaQueryWrapper<HirInfectionCase>()
.eq(status != null, HirInfectionCase::getStatus, status)
.eq(HirInfectionCase::getDelFlag, "0").orderByDesc(HirInfectionCase::getReportTime));
}
@Override
public Map<String, Object> getStatistics(String startDate, String endDate) {
Map<String, Object> r = new HashMap<>();
r.put("totalCases", infectionCaseService.count(new LambdaQueryWrapper<HirInfectionCase>().eq(HirInfectionCase::getDelFlag, "0")));
r.put("reportedCases", infectionCaseService.count(new LambdaQueryWrapper<HirInfectionCase>().eq(HirInfectionCase::getStatus, "REPORTED")));
r.put("reviewedCases", infectionCaseService.count(new LambdaQueryWrapper<HirInfectionCase>().eq(HirInfectionCase::getStatus, "REVIEWED")));
r.put("antibioticUsages", antibioticUsageService.count());
return r;
}
@Override
public List<Map<String, Object>> getAntibioticUsageStats(String startDate, String endDate) {
return Collections.emptyList();
}
@Override
public HirOccupationalExposure reportExposure(HirOccupationalExposure e) {
e.setStatus("REPORTED"); e.setDelFlag("0"); exposureService.save(e); return e;
}
@Override
public List<HirOccupationalExposure> getExposureList() {
return exposureService.list(new LambdaQueryWrapper<HirOccupationalExposure>()
.eq(HirOccupationalExposure::getDelFlag, "0").orderByDesc(HirOccupationalExposure::getExposureDate));
}
}

View File

@@ -0,0 +1,27 @@
package com.healthlink.his.web.infection.controller;
import com.core.common.core.domain.AjaxResult;
import com.healthlink.his.infection.domain.*;
import com.healthlink.his.web.infection.appservice.IInfectionAppService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Tag(name = "院感管理")
@RestController @RequestMapping("/healthlink-his/api/v1/infection")
public class InfectionController {
@Autowired private IInfectionAppService infectionAppService;
@Operation(summary = "上报院感病例") @PostMapping("/case")
public AjaxResult reportCase(@RequestBody HirInfectionCase c) { return AjaxResult.success(infectionAppService.reportInfection(c)); }
@Operation(summary = "审核病例") @PutMapping("/case/review/{id}")
public AjaxResult reviewCase(@PathVariable Long id, @RequestParam Long reviewId, @RequestParam String reviewName, @RequestParam String result) {
infectionAppService.reviewCase(id, reviewId, reviewName, result); return AjaxResult.success(); }
@Operation(summary = "病例列表") @GetMapping("/case")
public AjaxResult caseList(@RequestParam(required = false) String status) { return AjaxResult.success(infectionAppService.getCaseList(status)); }
@Operation(summary = "统计") @GetMapping("/statistics")
public AjaxResult statistics(@RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate) {
return AjaxResult.success(infectionAppService.getStatistics(startDate, endDate)); }
@Operation(summary = "上报职业暴露") @PostMapping("/exposure")
public AjaxResult reportExposure(@RequestBody HirOccupationalExposure e) { return AjaxResult.success(infectionAppService.reportExposure(e)); }
@Operation(summary = "职业暴露列表") @GetMapping("/exposure")
public AjaxResult exposureList() { return AjaxResult.success(infectionAppService.getExposureList()); }
}

View File

@@ -0,0 +1,16 @@
package com.healthlink.his.antibiotic.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
import java.util.Date;
@Data @TableName("antibiotic_approval") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
public class AntibioticApproval extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private Long encounterId; private Long patientId;
private String drugCode; private String drugName; private String antibioticClass;
private Long requesterId; private String requesterName;
private Long approverId; private String approverName; private Date approvalTime;
private String approvalResult; private String reason; private String status; private String delFlag;
}

View File

@@ -0,0 +1,15 @@
package com.healthlink.his.antibiotic.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
@Data @TableName("antibiotic_class_rule") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
public class AntibioticClassRule extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private String drugCode; private String drugName;
private String antibioticClass; private String restrictionLevel;
private String indication; private String contraindication;
private Integer maxDurationDays; private Boolean requireApproval;
private String enabled; private String delFlag;
}

View File

@@ -0,0 +1,5 @@
package com.healthlink.his.antibiotic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.antibiotic.domain.AntibioticApproval;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface AntibioticApprovalMapper extends BaseMapper<AntibioticApproval> {}

View File

@@ -0,0 +1,5 @@
package com.healthlink.his.antibiotic.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.antibiotic.domain.AntibioticClassRule;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface AntibioticClassRuleMapper extends BaseMapper<AntibioticClassRule> {}

View File

@@ -0,0 +1,4 @@
package com.healthlink.his.antibiotic.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.antibiotic.domain.AntibioticApproval;
public interface IAntibioticApprovalService extends IService<AntibioticApproval> {}

View File

@@ -0,0 +1,4 @@
package com.healthlink.his.antibiotic.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.antibiotic.domain.AntibioticClassRule;
public interface IAntibioticClassRuleService extends IService<AntibioticClassRule> {}

View File

@@ -0,0 +1,8 @@
package com.healthlink.his.antibiotic.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.antibiotic.domain.AntibioticApproval;
import com.healthlink.his.antibiotic.mapper.AntibioticApprovalMapper;
import com.healthlink.his.antibiotic.service.IAntibioticApprovalService;
import org.springframework.stereotype.Service;
@Service
public class AntibioticApprovalServiceImpl extends ServiceImpl<AntibioticApprovalMapper, AntibioticApproval> implements IAntibioticApprovalService {}

View File

@@ -0,0 +1,8 @@
package com.healthlink.his.antibiotic.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.antibiotic.domain.AntibioticClassRule;
import com.healthlink.his.antibiotic.mapper.AntibioticClassRuleMapper;
import com.healthlink.his.antibiotic.service.IAntibioticClassRuleService;
import org.springframework.stereotype.Service;
@Service
public class AntibioticClassRuleServiceImpl extends ServiceImpl<AntibioticClassRuleMapper, AntibioticClassRule> implements IAntibioticClassRuleService {}

View File

@@ -0,0 +1,17 @@
package com.healthlink.his.infection.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.util.Date;
@Data @TableName("hir_antibiotic_usage") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
public class HirAntibioticUsage extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private Long encounterId; private Long patientId;
private String drugCode; private String drugName; private BigDecimal dddValue;
private Integer usageDays; private String usageType;
private Date startDate; private Date endDate; private String indication;
private Long doctorId;
}

View File

@@ -0,0 +1,17 @@
package com.healthlink.his.infection.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
import java.util.Date;
@Data @TableName("hir_infection_case") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
public class HirInfectionCase extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private Long encounterId; private Long patientId; private String patientName;
private String infectionType; private String infectionSite; private String pathogen;
private Date diagnosisDate; private Long reporterId; private String reporterName;
private Date reportTime; private String status;
private Long reviewId; private String reviewName; private Date reviewTime; private String reviewResult;
private String delFlag;
}

View File

@@ -0,0 +1,18 @@
package com.healthlink.his.infection.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.core.common.core.domain.HisBaseEntity;
import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors;
import java.util.Date;
@Data @TableName("hir_occupational_exposure") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
public class HirOccupationalExposure extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private Long staffId; private String staffName; private String department;
private String exposureType; private String exposureSource;
private Date exposureDate; private String exposureLocation;
private String immediateProcessing; private String reportedTo;
private String followUpPlan;
private Boolean hivTestBaseline; private Boolean hivTest3month; private Boolean hivTest6month;
private String status; private String delFlag;
}

View File

@@ -0,0 +1,5 @@
package com.healthlink.his.infection.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.infection.domain.HirAntibioticUsage;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface HirAntibioticUsageMapper extends BaseMapper<HirAntibioticUsage> {}

View File

@@ -0,0 +1,5 @@
package com.healthlink.his.infection.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.infection.domain.HirInfectionCase;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface HirInfectionCaseMapper extends BaseMapper<HirInfectionCase> {}

View File

@@ -0,0 +1,5 @@
package com.healthlink.his.infection.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.healthlink.his.infection.domain.HirOccupationalExposure;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface HirOccupationalExposureMapper extends BaseMapper<HirOccupationalExposure> {}

View File

@@ -0,0 +1,4 @@
package com.healthlink.his.infection.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.infection.domain.HirAntibioticUsage;
public interface IHirAntibioticUsageService extends IService<HirAntibioticUsage> {}

View File

@@ -0,0 +1,4 @@
package com.healthlink.his.infection.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.infection.domain.HirInfectionCase;
public interface IHirInfectionCaseService extends IService<HirInfectionCase> {}

View File

@@ -0,0 +1,4 @@
package com.healthlink.his.infection.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.healthlink.his.infection.domain.HirOccupationalExposure;
public interface IHirOccupationalExposureService extends IService<HirOccupationalExposure> {}

View File

@@ -0,0 +1,8 @@
package com.healthlink.his.infection.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.infection.domain.HirAntibioticUsage;
import com.healthlink.his.infection.mapper.HirAntibioticUsageMapper;
import com.healthlink.his.infection.service.IHirAntibioticUsageService;
import org.springframework.stereotype.Service;
@Service
public class HirAntibioticUsageServiceImpl extends ServiceImpl<HirAntibioticUsageMapper, HirAntibioticUsage> implements IHirAntibioticUsageService {}

View File

@@ -0,0 +1,8 @@
package com.healthlink.his.infection.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.infection.domain.HirInfectionCase;
import com.healthlink.his.infection.mapper.HirInfectionCaseMapper;
import com.healthlink.his.infection.service.IHirInfectionCaseService;
import org.springframework.stereotype.Service;
@Service
public class HirInfectionCaseServiceImpl extends ServiceImpl<HirInfectionCaseMapper, HirInfectionCase> implements IHirInfectionCaseService {}

View File

@@ -0,0 +1,8 @@
package com.healthlink.his.infection.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.infection.domain.HirOccupationalExposure;
import com.healthlink.his.infection.mapper.HirOccupationalExposureMapper;
import com.healthlink.his.infection.service.IHirOccupationalExposureService;
import org.springframework.stereotype.Service;
@Service
public class HirOccupationalExposureServiceImpl extends ServiceImpl<HirOccupationalExposureMapper, HirOccupationalExposure> implements IHirOccupationalExposureService {}