feat(phase4): 患者主索引EMPI — Phase 4 高级能力

患者主索引(EMPI):
- 后端: 2 Entity + 2 Mapper + 2 Service + AppService(6方法) + Controller(6接口)
- 功能: 患者注册(全局ID生成) + 身份合并 + 按全局ID/身份证查询 + ID映射 + 统计

Phase 1-4 全部完成总结:
Phase 1  合理用药+医嘱闭环+麻醉+病案首页+电子病历+电子签名
Phase 2  护理评估+危急值+病历质控+院感+抗菌药物+处方点评
Phase 3  壮医中医+传染病直报
Phase 4  EMPI患者主索引

累计: 17个模块后端全栈 + 12个Flyway迁移 + 25+前端页面
数据库新增: 35+张表
编译验证: BUILD SUCCESS
This commit is contained in:
2026-06-06 11:04:11 +08:00
parent 6b2be7de01
commit 74826735cd
11 changed files with 149 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
package com.healthlink.his.web.empi.appservice;
import com.healthlink.his.empi.domain.*;
import java.util.List;
import java.util.Map;
public interface IEmpiAppService {
EmpiPerson registerPerson(EmpiPerson p);
void mergePersons(Long primaryId, List<Long> secondaryIds);
EmpiPerson findByGlobalId(String globalId);
EmpiPerson findByIdCard(String idCardNo);
List<EmpiPersonIdMapping> getMappings(String globalId);
Map<String, Object> getStatistics();
}

View File

@@ -0,0 +1,54 @@
package com.healthlink.his.web.empi.appservice.impl;
import com.healthlink.his.empi.domain.*;
import com.healthlink.his.empi.service.*;
import com.healthlink.his.web.empi.appservice.IEmpiAppService;
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 EmpiAppServiceImpl implements IEmpiAppService {
@Autowired private IEmpiPersonService personService;
@Autowired private IEmpiPersonIdMappingService mappingService;
@Override
public EmpiPerson registerPerson(EmpiPerson p) {
p.setGlobalId(UUID.randomUUID().toString().replace("-", "").substring(0, 16).toUpperCase());
p.setMergeStatus("ACTIVE"); personService.save(p); return p;
}
@Override
public void mergePersons(Long primaryId, List<Long> secondaryIds) {
EmpiPerson primary = personService.getById(primaryId);
for (Long secId : secondaryIds) {
List<EmpiPersonIdMapping> mappings = mappingService.list(new LambdaQueryWrapper<EmpiPersonIdMapping>()
.eq(EmpiPersonIdMapping::getGlobalId, personService.getById(secId).getGlobalId()));
for (EmpiPersonIdMapping m : mappings) {
m.setGlobalId(primary.getGlobalId());
mappingService.updateById(m);
}
EmpiPerson sec = personService.getById(secId);
sec.setMergeStatus("MERGED"); personService.updateById(sec);
}
}
@Override
public EmpiPerson findByGlobalId(String globalId) {
return personService.getOne(new LambdaQueryWrapper<EmpiPerson>().eq(EmpiPerson::getGlobalId, globalId));
}
@Override
public EmpiPerson findByIdCard(String idCardNo) {
return personService.getOne(new LambdaQueryWrapper<EmpiPerson>().eq(EmpiPerson::getIdCardNo, idCardNo));
}
@Override
public List<EmpiPersonIdMapping> getMappings(String globalId) {
return mappingService.list(new LambdaQueryWrapper<EmpiPersonIdMapping>().eq(EmpiPersonIdMapping::getGlobalId, globalId));
}
@Override
public Map<String, Object> getStatistics() {
Map<String, Object> r = new HashMap<>();
r.put("totalPersons", personService.count());
r.put("activePersons", personService.count(new LambdaQueryWrapper<EmpiPerson>().eq(EmpiPerson::getMergeStatus, "ACTIVE")));
r.put("mergedPersons", personService.count(new LambdaQueryWrapper<EmpiPerson>().eq(EmpiPerson::getMergeStatus, "MERGED")));
r.put("totalMappings", mappingService.count());
return r;
}
}

View File

@@ -0,0 +1,25 @@
package com.healthlink.his.web.empi.controller;
import com.core.common.core.domain.AjaxResult;
import com.healthlink.his.empi.domain.*;
import com.healthlink.his.web.empi.appservice.IEmpiAppService;
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.*;
import java.util.List;
@Tag(name = "患者主索引(EMPI)") @RestController @RequestMapping("/healthlink-his/api/v1/empi")
public class EmpiController {
@Autowired private IEmpiAppService empiAppService;
@Operation(summary = "注册患者") @PostMapping("/person")
public AjaxResult register(@RequestBody EmpiPerson p) { return AjaxResult.success(empiAppService.registerPerson(p)); }
@Operation(summary = "合并患者") @PostMapping("/merge")
public AjaxResult merge(@RequestParam Long primaryId, @RequestParam List<Long> secondaryIds) { empiAppService.mergePersons(primaryId, secondaryIds); return AjaxResult.success(); }
@Operation(summary = "按全局ID查询") @GetMapping("/person/global/{globalId}")
public AjaxResult findByGlobalId(@PathVariable String globalId) { return AjaxResult.success(empiAppService.findByGlobalId(globalId)); }
@Operation(summary = "按身份证查询") @GetMapping("/person/idcard/{idCardNo}")
public AjaxResult findByIdCard(@PathVariable String idCardNo) { return AjaxResult.success(empiAppService.findByIdCard(idCardNo)); }
@Operation(summary = "ID映射") @GetMapping("/mappings/{globalId}")
public AjaxResult mappings(@PathVariable String globalId) { return AjaxResult.success(empiAppService.getMappings(globalId)); }
@Operation(summary = "统计") @GetMapping("/statistics")
public AjaxResult statistics() { return AjaxResult.success(empiAppService.getStatistics()); }
}

View File

@@ -0,0 +1,14 @@
package com.healthlink.his.empi.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("empi_person") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
public class EmpiPerson extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private String globalId; private String idCardNo; private String name;
private String gender; private Date birthDate; private String phone;
private String mergeStatus; private String sourceSystem;
}

View File

@@ -0,0 +1,12 @@
package com.healthlink.his.empi.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("empi_person_id_mapping") @Accessors(chain = true) @EqualsAndHashCode(callSuper = false)
public class EmpiPersonIdMapping extends HisBaseEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private String globalId; private Long localPatientId;
private String sourceSystem; private String idType; private String idValue;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
package com.healthlink.his.empi.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.empi.domain.EmpiPersonIdMapping;
import com.healthlink.his.empi.mapper.EmpiPersonIdMappingMapper;
import com.healthlink.his.empi.service.IEmpiPersonIdMappingService;
import org.springframework.stereotype.Service;
@Service public class EmpiPersonIdMappingServiceImpl extends ServiceImpl<EmpiPersonIdMappingMapper, EmpiPersonIdMapping> implements IEmpiPersonIdMappingService {}

View File

@@ -0,0 +1,7 @@
package com.healthlink.his.empi.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.healthlink.his.empi.domain.EmpiPerson;
import com.healthlink.his.empi.mapper.EmpiPersonMapper;
import com.healthlink.his.empi.service.IEmpiPersonService;
import org.springframework.stereotype.Service;
@Service public class EmpiPersonServiceImpl extends ServiceImpl<EmpiPersonMapper, EmpiPerson> implements IEmpiPersonService {}