diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/appservice/IFhirConversionAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/appservice/IFhirConversionAppService.java new file mode 100644 index 000000000..516ebfc29 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/appservice/IFhirConversionAppService.java @@ -0,0 +1,10 @@ +package com.healthlink.his.web.esbmanage.appservice; + +import com.healthlink.his.esb.domain.FhirResource; + +import java.util.Map; + +public interface IFhirConversionAppService { + FhirResource convertToFhir(Map internalData, String resourceType); + Map convertFromFhir(String resourceJson); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/appservice/impl/FhirConversionAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/appservice/impl/FhirConversionAppServiceImpl.java new file mode 100644 index 000000000..4797325fc --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/appservice/impl/FhirConversionAppServiceImpl.java @@ -0,0 +1,151 @@ +package com.healthlink.his.web.esbmanage.appservice.impl; + +import com.healthlink.his.esb.domain.FhirResource; +import com.healthlink.his.esb.service.IFhirResourceService; +import com.healthlink.his.web.esbmanage.appservice.IFhirConversionAppService; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +@Slf4j +@RequiredArgsConstructor +public class FhirConversionAppServiceImpl implements IFhirConversionAppService { + + private final IFhirResourceService fhirResourceService; + private final ObjectMapper objectMapper; + + @Override + public FhirResource convertToFhir(Map internalData, String resourceType) { + try { + Map fhirBundle = new LinkedHashMap<>(); + fhirBundle.put("resourceType", resourceType); + fhirBundle.put("id", UUID.randomUUID().toString()); + fhirBundle.put("meta", Map.of("lastUpdated", new Date().toString())); + + Map identifier = new LinkedHashMap<>(); + identifier.put("system", "urn:oid:2.16.156.10011"); + identifier.put("value", String.valueOf(internalData.getOrDefault("patientId", ""))); + fhirBundle.put("identifier", List.of(identifier)); + + if ("Patient".equals(resourceType)) { + Map name = new LinkedHashMap<>(); + name.put("use", "official"); + name.put("text", String.valueOf(internalData.getOrDefault("patientName", ""))); + fhirBundle.put("name", List.of(name)); + fhirBundle.put("gender", internalData.getOrDefault("gender", "unknown")); + fhirBundle.put("birthDate", String.valueOf(internalData.getOrDefault("birthDate", ""))); + } else if ("Encounter".equals(resourceType)) { + fhirBundle.put("status", "in-progress"); + Map classCode = new LinkedHashMap<>(); + classCode.put("system", "http://terminology.hl7.org/CodeSystem/v3-ActCode"); + classCode.put("code", "IMP"); + fhirBundle.put("class", classCode); + fhirBundle.put("period", Map.of("start", internalData.getOrDefault("admissionDate", ""))); + } else if ("Observation".equals(resourceType)) { + fhirBundle.put("status", "final"); + Map codeMap = new LinkedHashMap<>(); + codeMap.put("coding", List.of(Map.of("system", "http://loinc.org", "code", internalData.getOrDefault("obsCode", "")))); + fhirBundle.put("code", codeMap); + Map valueQuantity = new LinkedHashMap<>(); + valueQuantity.put("value", internalData.getOrDefault("obsValue", 0)); + valueQuantity.put("unit", internalData.getOrDefault("obsUnit", "")); + fhirBundle.put("valueQuantity", valueQuantity); + } else if ("Condition".equals(resourceType)) { + fhirBundle.put("clinicalStatus", Map.of("coding", List.of(Map.of("code", "active")))); + Map condCode = new LinkedHashMap<>(); + condCode.put("coding", List.of(Map.of("system", "http://snomed.info/sct", "code", internalData.getOrDefault("conditionCode", "")))); + fhirBundle.put("code", condCode); + } else if ("MedicationRequest".equals(resourceType)) { + fhirBundle.put("status", "active"); + fhirBundle.put("intent", "order"); + Map medCode = new LinkedHashMap<>(); + medCode.put("coding", List.of(Map.of("system", "http://www.nmpa.gov.cn", "code", internalData.getOrDefault("drugCode", "")))); + fhirBundle.put("medicationCodeableConcept", medCode); + fhirBundle.put("dosageInstruction", List.of(Map.of("text", internalData.getOrDefault("dosage", "")))); + } + + String resourceJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(fhirBundle); + + FhirResource resource = new FhirResource(); + resource.setResourceType(resourceType); + resource.setResourceId(fhirBundle.get("id").toString()); + resource.setPatientId(internalData.get("patientId") != null ? Long.valueOf(String.valueOf(internalData.get("patientId"))) : null); + resource.setEncounterId(internalData.get("encounterId") != null ? Long.valueOf(String.valueOf(internalData.get("encounterId"))) : null); + resource.setResourceJson(resourceJson); + resource.setStatus("ACTIVE"); + resource.setVersionId(1); + resource.setCreateTime(new Date()); + fhirResourceService.save(resource); + + return resource; + } catch (Exception e) { + log.error("FHIR转换失败: {}", e.getMessage(), e); + throw new RuntimeException("FHIR R4转换失败: " + e.getMessage()); + } + } + + @Override + public Map convertFromFhir(String resourceJson) { + try { + Map fhirResource = objectMapper.readValue(resourceJson, Map.class); + Map result = new LinkedHashMap<>(); + result.put("resourceType", fhirResource.get("resourceType")); + result.put("resourceId", fhirResource.get("id")); + + List> identifiers = (List>) fhirResource.get("identifier"); + if (identifiers != null && !identifiers.isEmpty()) { + result.put("patientId", identifiers.get(0).get("value")); + } + + if ("Patient".equals(fhirResource.get("resourceType"))) { + List> names = (List>) fhirResource.get("name"); + if (names != null && !names.isEmpty()) { + result.put("patientName", names.get(0).get("text")); + } + result.put("gender", fhirResource.get("gender")); + result.put("birthDate", fhirResource.get("birthDate")); + } else if ("Encounter".equals(fhirResource.get("resourceType"))) { + result.put("status", fhirResource.get("status")); + Map cls = (Map) fhirResource.get("class"); + if (cls != null) result.put("encounterClass", cls.get("code")); + Map period = (Map) fhirResource.get("period"); + if (period != null) result.put("admissionDate", period.get("start")); + } else if ("Observation".equals(fhirResource.get("resourceType"))) { + Map vq = (Map) fhirResource.get("valueQuantity"); + if (vq != null) { + result.put("obsValue", vq.get("value")); + result.put("obsUnit", vq.get("unit")); + } + } else if ("Condition".equals(fhirResource.get("resourceType"))) { + Map code = (Map) fhirResource.get("code"); + if (code != null) { + List> codings = (List>) code.get("coding"); + if (codings != null && !codings.isEmpty()) { + result.put("conditionCode", codings.get(0).get("code")); + } + } + } else if ("MedicationRequest".equals(fhirResource.get("resourceType"))) { + Map med = (Map) fhirResource.get("medicationCodeableConcept"); + if (med != null) { + List> codings = (List>) med.get("coding"); + if (codings != null && !codings.isEmpty()) { + result.put("drugCode", codings.get(0).get("code")); + } + } + List> dosage = (List>) fhirResource.get("dosageInstruction"); + if (dosage != null && !dosage.isEmpty()) { + result.put("dosage", dosage.get(0).get("text")); + } + } + + return result; + } catch (Exception e) { + log.error("FHIR反向转换失败: {}", e.getMessage(), e); + throw new RuntimeException("FHIR R4反向转换失败: " + e.getMessage()); + } + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/controller/FhirConversionController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/controller/FhirConversionController.java new file mode 100644 index 000000000..0f4b264c1 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/esbmanage/controller/FhirConversionController.java @@ -0,0 +1,47 @@ +package com.healthlink.his.web.esbmanage.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.esb.domain.FhirResource; +import com.healthlink.his.web.esbmanage.appservice.IFhirConversionAppService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * FHIR R4消息转换 Controller — 内部数据 ↔ FHIR R4标准格式 + */ +@RestController +@RequestMapping("/esb/fhir") +@Slf4j +@RequiredArgsConstructor +public class FhirConversionController { + + private final IFhirConversionAppService fhirConversionAppService; + + @PostMapping("/convert-to") + @PreAuthorize("hasAuthority('infection:esb:edit')") + public R convertToFhir(@RequestBody Map params) { + String resourceType = (String) params.get("resourceType"); + @SuppressWarnings("unchecked") + Map internalData = (Map) params.get("data"); + if (resourceType == null || internalData == null) { + return R.fail("resourceType和data不能为空"); + } + FhirResource result = fhirConversionAppService.convertToFhir(internalData, resourceType); + return R.ok(result); + } + + @PostMapping("/convert-from") + @PreAuthorize("hasAuthority('infection:esb:edit')") + public R convertFromFhir(@RequestBody Map params) { + String resourceJson = params.get("resourceJson"); + if (resourceJson == null || resourceJson.isBlank()) { + return R.fail("resourceJson不能为空"); + } + Map result = fhirConversionAppService.convertFromFhir(resourceJson); + return R.ok(result); + } +} diff --git a/healthlink-his-ui/src/views/esbmanage/fhirconversion/api.js b/healthlink-his-ui/src/views/esbmanage/fhirconversion/api.js new file mode 100644 index 000000000..9baa905b1 --- /dev/null +++ b/healthlink-his-ui/src/views/esbmanage/fhirconversion/api.js @@ -0,0 +1,5 @@ +import request from '@/utils/request' +export function convertToFhir(data) { return request({ url: '/esb/fhir/convert-to', method: 'post', data }) } +export function convertFromFhir(data) { return request({ url: '/esb/fhir/convert-from', method: 'post', data }) } +export function getFhirPage(params) { return request({ url: '/fhir-cda/fhir/page', method: 'get', params }) } +export function getFhirTypeStats() { return request({ url: '/fhir-cda/fhir/type-stats', method: 'get' }) } diff --git a/healthlink-his-ui/src/views/esbmanage/fhirconversion/index.vue b/healthlink-his-ui/src/views/esbmanage/fhirconversion/index.vue new file mode 100644 index 000000000..a45836b65 --- /dev/null +++ b/healthlink-his-ui/src/views/esbmanage/fhirconversion/index.vue @@ -0,0 +1,153 @@ + +