From d9a1b188b57026b950ed462b413be59d42ddac75 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 10:18:32 +0800 Subject: [PATCH 01/19] =?UTF-8?q?feat(kg):=20=E5=8C=BB=E7=96=97=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=9B=BE=E8=B0=B1=E5=85=A8=E6=A0=88=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=20-=20=E8=A1=A5=E5=85=85=E7=BC=BA=E5=A4=B1=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/knowledgegraph/dto/KgDiseaseDto.java | 4 ++ .../his/web/knowledgegraph/dto/KgDrugDto.java | 2 + .../knowledgegraph/dto/KgExaminationDto.java | 2 + .../web/knowledgegraph/dto/KgSymptomDto.java | 2 + .../db/migration/V89__knowledge_graph.sql | 62 +++++++++++++++++++ .../his/knowledgegraph/domain/KgDisease.java | 4 ++ .../his/knowledgegraph/domain/KgDrug.java | 2 + .../knowledgegraph/domain/KgExamination.java | 2 + .../his/knowledgegraph/domain/KgSymptom.java | 2 + 9 files changed, 82 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V89__knowledge_graph.sql diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java index 86c728731..f5b1c02bb 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDiseaseDto.java @@ -24,4 +24,8 @@ public class KgDiseaseDto implements Serializable { private String department; private String severityLevel; + + private String description; + + private String keywords; } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java index dc2de755d..9d393c5ec 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgDrugDto.java @@ -26,4 +26,6 @@ public class KgDrugDto implements Serializable { private String dosageForm; private String contraindications; + + private String sideEffects; } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java index c79663d5d..ef82c1343 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgExaminationDto.java @@ -24,4 +24,6 @@ public class KgExaminationDto implements Serializable { private String department; private String referenceRange; + + private String clinicalSignificance; } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java index 3cbbf5440..55486c403 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/KgSymptomDto.java @@ -22,4 +22,6 @@ public class KgSymptomDto implements Serializable { private String bodyPart; private String symptomType; + + private String severityIndicator; } diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V89__knowledge_graph.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V89__knowledge_graph.sql new file mode 100644 index 000000000..df1002d9b --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V89__knowledge_graph.sql @@ -0,0 +1,62 @@ +-- V89: 知识图谱 - 添加缺失字段 (description, keywords, severityIndicator, sideEffects, clinicalSignificance, evidenceSource, department, required) + +-- kg_disease: 添加 description, keywords +DO $$ BEGIN + ALTER TABLE kg_disease ADD COLUMN IF NOT EXISTS description TEXT; +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; +DO $$ BEGIN + ALTER TABLE kg_disease ADD COLUMN IF NOT EXISTS keywords VARCHAR(512); +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; + +COMMENT ON COLUMN kg_disease.description IS '疾病描述'; +COMMENT ON COLUMN kg_disease.keywords IS '关键词'; + +-- kg_symptom: 添加 severity_indicator +DO $$ BEGIN + ALTER TABLE kg_symptom ADD COLUMN IF NOT EXISTS severity_indicator VARCHAR(32); +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; + +COMMENT ON COLUMN kg_symptom.severity_indicator IS '严重程度指标'; + +-- kg_drug: 添加 side_effects +DO $$ BEGIN + ALTER TABLE kg_drug ADD COLUMN IF NOT EXISTS side_effects TEXT; +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; + +COMMENT ON COLUMN kg_drug.side_effects IS '不良反应'; + +-- kg_examination: 添加 clinical_significance +DO $$ BEGIN + ALTER TABLE kg_examination ADD COLUMN IF NOT EXISTS clinical_significance TEXT; +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; + +COMMENT ON COLUMN kg_examination.clinical_significance IS '临床意义'; + +-- kg_entity_relation: 添加 evidence_source +DO $$ BEGIN + ALTER TABLE kg_entity_relation ADD COLUMN IF NOT EXISTS evidence_source VARCHAR(512); +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; + +COMMENT ON COLUMN kg_entity_relation.evidence_source IS '证据来源'; + +-- kg_clinical_pathway: 添加 department +DO $$ BEGIN + ALTER TABLE kg_clinical_pathway ADD COLUMN IF NOT EXISTS department VARCHAR(128); +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; + +COMMENT ON COLUMN kg_clinical_pathway.department IS '所属科室'; + +-- kg_pathway_step: 添加 required +DO $$ BEGIN + ALTER TABLE kg_pathway_step ADD COLUMN IF NOT EXISTS required CHAR(1) DEFAULT '1'; +EXCEPTION WHEN duplicate_column THEN NULL; +END $$; + +COMMENT ON COLUMN kg_pathway_step.required IS '是否必选(1-是 0-否)'; diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java index 118de459f..61bbc5eb6 100644 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDisease.java @@ -29,4 +29,8 @@ public class KgDisease extends HisBaseEntity { private String department; private String severityLevel; + + private String description; + + private String keywords; } diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java index c1a7ae21a..5b692a185 100644 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgDrug.java @@ -31,4 +31,6 @@ public class KgDrug extends HisBaseEntity { private String dosageForm; private String contraindications; + + private String sideEffects; } diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java index 8aba928a9..e0048fd18 100644 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgExamination.java @@ -29,4 +29,6 @@ public class KgExamination extends HisBaseEntity { private String department; private String referenceRange; + + private String clinicalSignificance; } diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java index 24bc07bf3..5fa2e5bcf 100644 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/knowledgegraph/domain/KgSymptom.java @@ -27,4 +27,6 @@ public class KgSymptom extends HisBaseEntity { private String bodyPart; private String symptomType; + + private String severityIndicator; } From 523a64daf09cbc9e75ab31856f75a2ba7c7c8ae5 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 10:29:47 +0800 Subject: [PATCH 02/19] =?UTF-8?q?feat(miniprogram):=20=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E6=8A=A4=E7=90=86=E5=B0=8F=E7=A8=8B=E5=BA=8F=E5=90=8E=E7=AB=AF?= =?UTF-8?q?API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 MpNursingTask 实体 + Mapper + Service - 新增 MpVitalSignRecord 实体 + Mapper + Service - 新增 MpAssessmentRecord 实体 + Mapper + Service - 新增 IMpNursingAppService 7个API接口 - 新增 MpNursingController 7个REST端点 - 新增 V90 Flyway迁移(3张表) - 所有接口加 @PreAuthorize 权限控制 --- .../appservice/IMpNursingAppService.java | 57 ++++++ .../impl/MpNursingAppServiceImpl.java | 171 ++++++++++++++++++ .../controller/MpNursingController.java | 80 ++++++++ .../miniprogram/dto/AssessmentSubmitDto.java | 31 ++++ .../web/miniprogram/dto/PatientInfoDto.java | 32 ++++ .../web/miniprogram/dto/TaskCompleteDto.java | 14 ++ .../miniprogram/dto/VitalSignSubmitDto.java | 37 ++++ .../db/migration/V90__miniprogram_nursing.sql | 93 ++++++++++ .../miniprogram/MpAssessmentRecordMapper.xml | 15 ++ .../miniprogram/MpNursingTaskMapper.xml | 24 +++ .../miniprogram/MpVitalSignRecordMapper.xml | 18 ++ .../domain/MpAssessmentRecord.java | 43 +++++ .../his/miniprogram/domain/MpNursingTask.java | 41 +++++ .../miniprogram/domain/MpVitalSignRecord.java | 49 +++++ .../mapper/MpAssessmentRecordMapper.java | 17 ++ .../mapper/MpNursingTaskMapper.java | 18 ++ .../mapper/MpVitalSignRecordMapper.java | 18 ++ .../service/IMpAssessmentRecordService.java | 10 + .../service/IMpNursingTaskService.java | 10 + .../service/IMpVitalSignRecordService.java | 10 + .../impl/MpAssessmentRecordServiceImpl.java | 15 ++ .../impl/MpNursingTaskServiceImpl.java | 15 ++ .../impl/MpVitalSignRecordServiceImpl.java | 15 ++ 23 files changed, 833 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/IMpNursingAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/impl/MpNursingAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/controller/MpNursingController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/AssessmentSubmitDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/PatientInfoDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/TaskCompleteDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/VitalSignSubmitDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V90__miniprogram_nursing.sql create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpAssessmentRecordMapper.xml create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpNursingTaskMapper.xml create mode 100644 healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpVitalSignRecordMapper.xml create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpAssessmentRecord.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpNursingTask.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpVitalSignRecord.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpAssessmentRecordMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpNursingTaskMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpVitalSignRecordMapper.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpAssessmentRecordService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpNursingTaskService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpVitalSignRecordService.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpAssessmentRecordServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpNursingTaskServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpVitalSignRecordServiceImpl.java diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/IMpNursingAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/IMpNursingAppService.java new file mode 100644 index 000000000..c35571c72 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/IMpNursingAppService.java @@ -0,0 +1,57 @@ +package com.healthlink.his.web.miniprogram.appservice; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.miniprogram.dto.AssessmentSubmitDto; +import com.healthlink.his.web.miniprogram.dto.TaskCompleteDto; +import com.healthlink.his.web.miniprogram.dto.VitalSignSubmitDto; + +/** + * 移动护理小程序 AppService + */ +public interface IMpNursingAppService { + + /** + * 获取护士任务列表 + * @param nurseId 护士ID + * @param status 任务状态(可选) + */ + R getTaskList(Long nurseId, String status); + + /** + * 完成任务 + * @param taskId 任务ID + * @param dto 完成结果 + */ + R completeTask(Long taskId, TaskCompleteDto dto); + + /** + * 获取患者信息(精简版) + * @param patientId 患者ID + */ + R getPatientInfo(Long patientId); + + /** + * 获取生命体征趋势 + * @param patientId 患者ID + * @param days 查询天数(默认7天) + */ + R getVitalSigns(Long patientId, Integer days); + + /** + * 录入生命体征 + * @param dto 体征数据 + */ + R submitVitalSign(VitalSignSubmitDto dto); + + /** + * 获取评估记录列表 + * @param patientId 患者ID + */ + R getAssessmentList(Long patientId); + + /** + * 提交护理评估 + * @param dto 评估数据 + */ + R submitAssessment(AssessmentSubmitDto dto); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/impl/MpNursingAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/impl/MpNursingAppServiceImpl.java new file mode 100644 index 000000000..402e2621b --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/appservice/impl/MpNursingAppServiceImpl.java @@ -0,0 +1,171 @@ +package com.healthlink.his.web.miniprogram.appservice.impl; + +import com.core.common.core.domain.R; +import com.healthlink.his.miniprogram.domain.MpAssessmentRecord; +import com.healthlink.his.miniprogram.domain.MpNursingTask; +import com.healthlink.his.miniprogram.domain.MpVitalSignRecord; +import com.healthlink.his.miniprogram.mapper.MpAssessmentRecordMapper; +import com.healthlink.his.miniprogram.mapper.MpNursingTaskMapper; +import com.healthlink.his.miniprogram.mapper.MpVitalSignRecordMapper; +import com.healthlink.his.miniprogram.service.IMpAssessmentRecordService; +import com.healthlink.his.miniprogram.service.IMpNursingTaskService; +import com.healthlink.his.miniprogram.service.IMpVitalSignRecordService; +import com.healthlink.his.web.miniprogram.appservice.IMpNursingAppService; +import com.healthlink.his.web.miniprogram.dto.AssessmentSubmitDto; +import com.healthlink.his.web.miniprogram.dto.TaskCompleteDto; +import com.healthlink.his.web.miniprogram.dto.VitalSignSubmitDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * 移动护理小程序 AppService实现 + */ +@Slf4j +@Service +public class MpNursingAppServiceImpl implements IMpNursingAppService { + + @Resource + private IMpNursingTaskService nursingTaskService; + + @Resource + private MpNursingTaskMapper nursingTaskMapper; + + @Resource + private IMpVitalSignRecordService vitalSignRecordService; + + @Resource + private MpVitalSignRecordMapper vitalSignRecordMapper; + + @Resource + private IMpAssessmentRecordService assessmentRecordService; + + @Resource + private MpAssessmentRecordMapper assessmentRecordMapper; + + @Override + @Transactional(readOnly = true) + public R getTaskList(Long nurseId, String status) { + List tasks = nursingTaskMapper.selectTaskListByNurse(nurseId, status); + long pendingCount = tasks.stream().filter(t -> "PENDING".equals(t.getTaskStatus())).count(); + long inProgressCount = tasks.stream().filter(t -> "IN_PROGRESS".equals(t.getTaskStatus())).count(); + long completedCount = tasks.stream().filter(t -> "COMPLETED".equals(t.getTaskStatus())).count(); + + return R.ok(Map.of( + "tasks", tasks, + "summary", Map.of( + "pending", pendingCount, + "inProgress", inProgressCount, + "completed", completedCount, + "total", tasks.size() + ) + )); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public R completeTask(Long taskId, TaskCompleteDto dto) { + MpNursingTask task = nursingTaskService.getById(taskId); + if (task == null) { + return R.fail("任务不存在"); + } + if ("COMPLETED".equals(task.getTaskStatus())) { + return R.fail("任务已完成"); + } + + task.setTaskStatus("COMPLETED"); + task.setCompleteTime(LocalDateTime.now()); + nursingTaskService.updateById(task); + + log.info("任务完成: taskId={}, nurseId={}, result={}", taskId, task.getNurseId(), dto.getResult()); + return R.ok("任务已完成"); + } + + @Override + @Transactional(readOnly = true) + public R getPatientInfo(Long patientId) { + return R.ok(Map.of( + "patientId", patientId, + "message", "患者信息查询待接入基础数据模块" + )); + } + + @Override + @Transactional(readOnly = true) + public R getVitalSigns(Long patientId, Integer days) { + if (days == null || days <= 0) { + days = 7; + } + List records = vitalSignRecordMapper.selectByPatientId(patientId, days); + return R.ok(records); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public R submitVitalSign(VitalSignSubmitDto dto) { + if (dto.getPatientId() == null || dto.getNurseId() == null) { + return R.fail("患者ID和护士ID不能为空"); + } + if (dto.getRecordTime() == null) { + dto.setRecordTime(LocalDateTime.now()); + } + + MpVitalSignRecord record = new MpVitalSignRecord(); + record.setPatientId(dto.getPatientId()); + record.setEncounterId(dto.getEncounterId()); + record.setNurseId(dto.getNurseId()); + record.setRecordTime(dto.getRecordTime()); + record.setTemperature(dto.getTemperature()); + record.setPulse(dto.getPulse()); + record.setRespiration(dto.getRespiration()); + record.setSystolicBp(dto.getSystolicBp()); + record.setDiastolicBp(dto.getDiastolicBp()); + record.setBloodOxygen(dto.getBloodOxygen()); + record.setHeight(dto.getHeight()); + record.setWeight(dto.getWeight()); + + vitalSignRecordService.save(record); + log.info("生命体征录入: patientId={}, nurseId={}, recordId={}", + dto.getPatientId(), dto.getNurseId(), record.getId()); + return R.ok(Map.of("recordId", record.getId())); + } + + @Override + @Transactional(readOnly = true) + public R getAssessmentList(Long patientId) { + List records = assessmentRecordMapper.selectByPatientId(patientId); + return R.ok(records); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public R submitAssessment(AssessmentSubmitDto dto) { + if (dto.getPatientId() == null || dto.getNurseId() == null || dto.getAssessmentType() == null) { + return R.fail("患者ID、护士ID和评估类型不能为空"); + } + if (dto.getRecordTime() == null) { + dto.setRecordTime(LocalDateTime.now()); + } + + MpAssessmentRecord record = new MpAssessmentRecord(); + record.setPatientId(dto.getPatientId()); + record.setEncounterId(dto.getEncounterId()); + record.setNurseId(dto.getNurseId()); + record.setAssessmentType(dto.getAssessmentType()); + record.setAssessmentContent(dto.getAssessmentContent()); + record.setAssessmentResult(dto.getAssessmentResult()); + record.setScore(dto.getScore()); + record.setRiskLevel(dto.getRiskLevel()); + record.setRecordTime(dto.getRecordTime()); + + assessmentRecordService.save(record); + log.info("护理评估提交: patientId={}, type={}, recordId={}", + dto.getPatientId(), dto.getAssessmentType(), record.getId()); + return R.ok(Map.of("recordId", record.getId())); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/controller/MpNursingController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/controller/MpNursingController.java new file mode 100644 index 000000000..3c59dcdaa --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/controller/MpNursingController.java @@ -0,0 +1,80 @@ +package com.healthlink.his.web.miniprogram.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.miniprogram.appservice.IMpNursingAppService; +import com.healthlink.his.web.miniprogram.dto.AssessmentSubmitDto; +import com.healthlink.his.web.miniprogram.dto.TaskCompleteDto; +import com.healthlink.his.web.miniprogram.dto.VitalSignSubmitDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +/** + * 移动护理小程序 Controller + */ +@Slf4j +@Tag(name = "移动护理小程序") +@RestController +@RequestMapping("/mp/nursing") +public class MpNursingController { + + private final IMpNursingAppService mpNursingAppService; + + public MpNursingController(IMpNursingAppService mpNursingAppService) { + this.mpNursingAppService = mpNursingAppService; + } + + @Operation(summary = "获取护士任务列表") + @PreAuthorize("@ss.hasPermi('nursing:nursing:list')") + @GetMapping("/tasks") + public R getTaskList(@RequestParam Long nurseId, + @RequestParam(required = false) String status) { + return mpNursingAppService.getTaskList(nurseId, status); + } + + @Operation(summary = "完成任务") + @PreAuthorize("@ss.hasPermi('nursing:nursing:edit')") + @PostMapping("/tasks/{id}/complete") + public R completeTask(@PathVariable Long id, + @RequestBody TaskCompleteDto dto) { + return mpNursingAppService.completeTask(id, dto); + } + + @Operation(summary = "获取患者信息") + @PreAuthorize("@ss.hasPermi('nursing:nursing:list')") + @GetMapping("/patient/{id}") + public R getPatientInfo(@PathVariable Long id) { + return mpNursingAppService.getPatientInfo(id); + } + + @Operation(summary = "获取生命体征趋势") + @PreAuthorize("@ss.hasPermi('nursing:nursing:list')") + @GetMapping("/vital-signs/{patientId}") + public R getVitalSigns(@PathVariable Long patientId, + @RequestParam(required = false, defaultValue = "7") Integer days) { + return mpNursingAppService.getVitalSigns(patientId, days); + } + + @Operation(summary = "录入生命体征") + @PreAuthorize("@ss.hasPermi('nursing:nursing:edit')") + @PostMapping("/vital-sign") + public R submitVitalSign(@RequestBody VitalSignSubmitDto dto) { + return mpNursingAppService.submitVitalSign(dto); + } + + @Operation(summary = "获取评估记录列表") + @PreAuthorize("@ss.hasPermi('nursing:nursing:list')") + @GetMapping("/assessments/{patientId}") + public R getAssessmentList(@PathVariable Long patientId) { + return mpNursingAppService.getAssessmentList(patientId); + } + + @Operation(summary = "提交护理评估") + @PreAuthorize("@ss.hasPermi('nursing:nursing:edit')") + @PostMapping("/assessment") + public R submitAssessment(@RequestBody AssessmentSubmitDto dto) { + return mpNursingAppService.submitAssessment(dto); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/AssessmentSubmitDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/AssessmentSubmitDto.java new file mode 100644 index 000000000..dedcf03cc --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/AssessmentSubmitDto.java @@ -0,0 +1,31 @@ +package com.healthlink.his.web.miniprogram.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 护理评估提交请求DTO + */ +@Data +public class AssessmentSubmitDto { + + private Long patientId; + + private Long encounterId; + + private Long nurseId; + + private String assessmentType; + + private String assessmentContent; + + private String assessmentResult; + + private BigDecimal score; + + private String riskLevel; + + private LocalDateTime recordTime; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/PatientInfoDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/PatientInfoDto.java new file mode 100644 index 000000000..659a3fc0e --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/PatientInfoDto.java @@ -0,0 +1,32 @@ +package com.healthlink.his.web.miniprogram.dto; + +import lombok.Data; + +import java.time.LocalDate; + +/** + * 患者信息精简版DTO + */ +@Data +public class PatientInfoDto { + + private Long id; + + private String patientName; + + private String gender; + + private LocalDate birthDate; + + private String medicalNo; + + private String phone; + + private String departmentName; + + private String bedNo; + + private String diagnosis; + + private String nurseLevel; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/TaskCompleteDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/TaskCompleteDto.java new file mode 100644 index 000000000..441b45464 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/TaskCompleteDto.java @@ -0,0 +1,14 @@ +package com.healthlink.his.web.miniprogram.dto; + +import lombok.Data; + +/** + * 任务完成请求DTO + */ +@Data +public class TaskCompleteDto { + + private String result; + + private String remark; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/VitalSignSubmitDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/VitalSignSubmitDto.java new file mode 100644 index 000000000..d984e6d7f --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/miniprogram/dto/VitalSignSubmitDto.java @@ -0,0 +1,37 @@ +package com.healthlink.his.web.miniprogram.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 生命体征录入请求DTO + */ +@Data +public class VitalSignSubmitDto { + + private Long patientId; + + private Long encounterId; + + private Long nurseId; + + private LocalDateTime recordTime; + + private BigDecimal temperature; + + private Integer pulse; + + private Integer respiration; + + private Integer systolicBp; + + private Integer diastolicBp; + + private BigDecimal bloodOxygen; + + private BigDecimal height; + + private BigDecimal weight; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V90__miniprogram_nursing.sql b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V90__miniprogram_nursing.sql new file mode 100644 index 000000000..d9c9b2a33 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/db/migration/V90__miniprogram_nursing.sql @@ -0,0 +1,93 @@ +-- 移动护理小程序 - 护理任务表 +CREATE TABLE IF NOT EXISTS mp_nursing_task ( + id BIGSERIAL PRIMARY KEY, + patient_id BIGINT NOT NULL, + encounter_id BIGINT NOT NULL, + nurse_id BIGINT NOT NULL, + task_type VARCHAR(32) NOT NULL, + task_content TEXT, + task_status VARCHAR(20) DEFAULT 'PENDING', + due_time TIMESTAMP, + complete_time TIMESTAMP, + tenant_id BIGINT DEFAULT 0, + delete_flag CHAR(1) DEFAULT '0', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + create_by VARCHAR(64) +); + +COMMENT ON TABLE mp_nursing_task IS '移动护理-护理任务'; +COMMENT ON COLUMN mp_nursing_task.id IS '主键ID'; +COMMENT ON COLUMN mp_nursing_task.patient_id IS '患者ID'; +COMMENT ON COLUMN mp_nursing_task.encounter_id IS '就诊ID'; +COMMENT ON COLUMN mp_nursing_task.nurse_id IS '护士ID'; +COMMENT ON COLUMN mp_nursing_task.task_type IS '任务类型'; +COMMENT ON COLUMN mp_nursing_task.task_content IS '任务内容'; +COMMENT ON COLUMN mp_nursing_task.task_status IS '任务状态: PENDING/IN_PROGRESS/COMPLETED/CANCELLED'; +COMMENT ON COLUMN mp_nursing_task.due_time IS '截止时间'; +COMMENT ON COLUMN mp_nursing_task.complete_time IS '完成时间'; + +-- 移动护理小程序 - 生命体征记录表 +CREATE TABLE IF NOT EXISTS mp_vital_sign_record ( + id BIGSERIAL PRIMARY KEY, + patient_id BIGINT NOT NULL, + encounter_id BIGINT NOT NULL, + nurse_id BIGINT NOT NULL, + record_time TIMESTAMP NOT NULL, + temperature DECIMAL(4,1), + pulse INTEGER, + respiration INTEGER, + systolic_bp INTEGER, + diastolic_bp INTEGER, + blood_oxygen DECIMAL(5,2), + height DECIMAL(5,1), + weight DECIMAL(5,1), + tenant_id BIGINT DEFAULT 0, + delete_flag CHAR(1) DEFAULT '0', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + create_by VARCHAR(64) +); + +COMMENT ON TABLE mp_vital_sign_record IS '移动护理-生命体征记录'; +COMMENT ON COLUMN mp_vital_sign_record.id IS '主键ID'; +COMMENT ON COLUMN mp_vital_sign_record.patient_id IS '患者ID'; +COMMENT ON COLUMN mp_vital_sign_record.encounter_id IS '就诊ID'; +COMMENT ON COLUMN mp_vital_sign_record.nurse_id IS '记录护士ID'; +COMMENT ON COLUMN mp_vital_sign_record.record_time IS '记录时间'; +COMMENT ON COLUMN mp_vital_sign_record.temperature IS '体温(℃)'; +COMMENT ON COLUMN mp_vital_sign_record.pulse IS '脉搏(次/分)'; +COMMENT ON COLUMN mp_vital_sign_record.respiration IS '呼吸(次/分)'; +COMMENT ON COLUMN mp_vital_sign_record.systolic_bp IS '收缩压(mmHg)'; +COMMENT ON COLUMN mp_vital_sign_record.diastolic_bp IS '舒张压(mmHg)'; +COMMENT ON COLUMN mp_vital_sign_record.blood_oxygen IS '血氧饱和度(%)'; +COMMENT ON COLUMN mp_vital_sign_record.height IS '身高(cm)'; +COMMENT ON COLUMN mp_vital_sign_record.weight IS '体重(kg)'; + +-- 移动护理小程序 - 护理评估记录表 +CREATE TABLE IF NOT EXISTS mp_assessment_record ( + id BIGSERIAL PRIMARY KEY, + patient_id BIGINT NOT NULL, + encounter_id BIGINT NOT NULL, + nurse_id BIGINT NOT NULL, + assessment_type VARCHAR(32) NOT NULL, + assessment_content TEXT, + assessment_result TEXT, + score DECIMAL(5,1), + risk_level VARCHAR(20), + record_time TIMESTAMP NOT NULL, + tenant_id BIGINT DEFAULT 0, + delete_flag CHAR(1) DEFAULT '0', + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + create_by VARCHAR(64) +); + +COMMENT ON TABLE mp_assessment_record IS '移动护理-护理评估记录'; +COMMENT ON COLUMN mp_assessment_record.id IS '主键ID'; +COMMENT ON COLUMN mp_assessment_record.patient_id IS '患者ID'; +COMMENT ON COLUMN mp_assessment_record.encounter_id IS '就诊ID'; +COMMENT ON COLUMN mp_assessment_record.nurse_id IS '评估护士ID'; +COMMENT ON COLUMN mp_assessment_record.assessment_type IS '评估类型'; +COMMENT ON COLUMN mp_assessment_record.assessment_content IS '评估内容'; +COMMENT ON COLUMN mp_assessment_record.assessment_result IS '评估结果'; +COMMENT ON COLUMN mp_assessment_record.score IS '评分'; +COMMENT ON COLUMN mp_assessment_record.risk_level IS '风险等级'; +COMMENT ON COLUMN mp_assessment_record.record_time IS '评估时间'; diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpAssessmentRecordMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpAssessmentRecordMapper.xml new file mode 100644 index 000000000..c0bab5310 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpAssessmentRecordMapper.xml @@ -0,0 +1,15 @@ + + + + + + + diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpNursingTaskMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpNursingTaskMapper.xml new file mode 100644 index 000000000..c7e24c377 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpNursingTaskMapper.xml @@ -0,0 +1,24 @@ + + + + + + + diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpVitalSignRecordMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpVitalSignRecordMapper.xml new file mode 100644 index 000000000..0b4d52368 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/miniprogram/MpVitalSignRecordMapper.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpAssessmentRecord.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpAssessmentRecord.java new file mode 100644 index 000000000..d9ce07f51 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpAssessmentRecord.java @@ -0,0 +1,43 @@ +package com.healthlink.his.miniprogram.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.time.LocalDateTime; + +/** + * 移动护理-护理评估记录 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@TableName("mp_assessment_record") +public class MpAssessmentRecord extends HisBaseEntity { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long patientId; + + private Long encounterId; + + private Long nurseId; + + private String assessmentType; + + private String assessmentContent; + + private String assessmentResult; + + private BigDecimal score; + + private String riskLevel; + + private LocalDateTime recordTime; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpNursingTask.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpNursingTask.java new file mode 100644 index 000000000..b1ade59fa --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpNursingTask.java @@ -0,0 +1,41 @@ +package com.healthlink.his.miniprogram.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +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.time.LocalDateTime; + +/** + * 移动护理-护理任务 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@TableName("mp_nursing_task") +public class MpNursingTask extends HisBaseEntity { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long patientId; + + private Long encounterId; + + private Long nurseId; + + private String taskType; + + private String taskContent; + + private String taskStatus; + + private LocalDateTime dueTime; + + private LocalDateTime completeTime; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpVitalSignRecord.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpVitalSignRecord.java new file mode 100644 index 000000000..34e945c7a --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/domain/MpVitalSignRecord.java @@ -0,0 +1,49 @@ +package com.healthlink.his.miniprogram.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.time.LocalDateTime; + +/** + * 移动护理-生命体征记录 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@TableName("mp_vital_sign_record") +public class MpVitalSignRecord extends HisBaseEntity { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long patientId; + + private Long encounterId; + + private Long nurseId; + + private LocalDateTime recordTime; + + private BigDecimal temperature; + + private Integer pulse; + + private Integer respiration; + + private Integer systolicBp; + + private Integer diastolicBp; + + private BigDecimal bloodOxygen; + + private BigDecimal height; + + private BigDecimal weight; +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpAssessmentRecordMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpAssessmentRecordMapper.java new file mode 100644 index 000000000..07ddb16ce --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpAssessmentRecordMapper.java @@ -0,0 +1,17 @@ +package com.healthlink.his.miniprogram.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.miniprogram.domain.MpAssessmentRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 移动护理-护理评估记录 Mapper + */ +@Mapper +public interface MpAssessmentRecordMapper extends BaseMapper { + + List selectByPatientId(@Param("patientId") Long patientId); +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpNursingTaskMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpNursingTaskMapper.java new file mode 100644 index 000000000..eb1b855b0 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpNursingTaskMapper.java @@ -0,0 +1,18 @@ +package com.healthlink.his.miniprogram.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.miniprogram.domain.MpNursingTask; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 移动护理-护理任务 Mapper + */ +@Mapper +public interface MpNursingTaskMapper extends BaseMapper { + + List selectTaskListByNurse(@Param("nurseId") Long nurseId, + @Param("taskStatus") String taskStatus); +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpVitalSignRecordMapper.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpVitalSignRecordMapper.java new file mode 100644 index 000000000..8310afa98 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/mapper/MpVitalSignRecordMapper.java @@ -0,0 +1,18 @@ +package com.healthlink.his.miniprogram.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.healthlink.his.miniprogram.domain.MpVitalSignRecord; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 移动护理-生命体征记录 Mapper + */ +@Mapper +public interface MpVitalSignRecordMapper extends BaseMapper { + + List selectByPatientId(@Param("patientId") Long patientId, + @Param("days") Integer days); +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpAssessmentRecordService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpAssessmentRecordService.java new file mode 100644 index 000000000..2b6cb8a65 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpAssessmentRecordService.java @@ -0,0 +1,10 @@ +package com.healthlink.his.miniprogram.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.miniprogram.domain.MpAssessmentRecord; + +/** + * 移动护理-护理评估记录 Service + */ +public interface IMpAssessmentRecordService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpNursingTaskService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpNursingTaskService.java new file mode 100644 index 000000000..600945bee --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpNursingTaskService.java @@ -0,0 +1,10 @@ +package com.healthlink.his.miniprogram.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.miniprogram.domain.MpNursingTask; + +/** + * 移动护理-护理任务 Service + */ +public interface IMpNursingTaskService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpVitalSignRecordService.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpVitalSignRecordService.java new file mode 100644 index 000000000..1189e8146 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/IMpVitalSignRecordService.java @@ -0,0 +1,10 @@ +package com.healthlink.his.miniprogram.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.healthlink.his.miniprogram.domain.MpVitalSignRecord; + +/** + * 移动护理-生命体征记录 Service + */ +public interface IMpVitalSignRecordService extends IService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpAssessmentRecordServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpAssessmentRecordServiceImpl.java new file mode 100644 index 000000000..c17ac0b5e --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpAssessmentRecordServiceImpl.java @@ -0,0 +1,15 @@ +package com.healthlink.his.miniprogram.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.miniprogram.domain.MpAssessmentRecord; +import com.healthlink.his.miniprogram.mapper.MpAssessmentRecordMapper; +import com.healthlink.his.miniprogram.service.IMpAssessmentRecordService; +import org.springframework.stereotype.Service; + +/** + * 移动护理-护理评估记录 Service实现 + */ +@Service +public class MpAssessmentRecordServiceImpl extends ServiceImpl + implements IMpAssessmentRecordService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpNursingTaskServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpNursingTaskServiceImpl.java new file mode 100644 index 000000000..ccadf4b33 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpNursingTaskServiceImpl.java @@ -0,0 +1,15 @@ +package com.healthlink.his.miniprogram.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.miniprogram.domain.MpNursingTask; +import com.healthlink.his.miniprogram.mapper.MpNursingTaskMapper; +import com.healthlink.his.miniprogram.service.IMpNursingTaskService; +import org.springframework.stereotype.Service; + +/** + * 移动护理-护理任务 Service实现 + */ +@Service +public class MpNursingTaskServiceImpl extends ServiceImpl + implements IMpNursingTaskService { +} diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpVitalSignRecordServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpVitalSignRecordServiceImpl.java new file mode 100644 index 000000000..238cfa932 --- /dev/null +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/miniprogram/service/impl/MpVitalSignRecordServiceImpl.java @@ -0,0 +1,15 @@ +package com.healthlink.his.miniprogram.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.healthlink.his.miniprogram.domain.MpVitalSignRecord; +import com.healthlink.his.miniprogram.mapper.MpVitalSignRecordMapper; +import com.healthlink.his.miniprogram.service.IMpVitalSignRecordService; +import org.springframework.stereotype.Service; + +/** + * 移动护理-生命体征记录 Service实现 + */ +@Service +public class MpVitalSignRecordServiceImpl extends ServiceImpl + implements IMpVitalSignRecordService { +} From ed1dd56ad45d2770949613e561a715f5c2035ba0 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 10:33:41 +0800 Subject: [PATCH 03/19] =?UTF-8?q?feat(kg):=20=E6=8E=A8=E7=90=86=E5=BC=95?= =?UTF-8?q?=E6=93=8E+=E6=95=B0=E6=8D=AE=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../appservice/IKgDataImportAppService.java | 13 + .../appservice/IKgReasoningAppService.java | 17 ++ .../impl/KgDataImportAppServiceImpl.java | 273 ++++++++++++++++++ .../impl/KgReasoningAppServiceImpl.java | 243 ++++++++++++++++ .../controller/KgDataImportController.java | 103 +++++++ .../controller/KgReasoningController.java | 76 +++++ 6 files changed, 725 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgDataImportAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgReasoningAppService.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgDataImportAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgReasoningAppServiceImpl.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgDataImportController.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgReasoningController.java diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgDataImportAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgDataImportAppService.java new file mode 100644 index 000000000..13d15e98e --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgDataImportAppService.java @@ -0,0 +1,13 @@ +package com.healthlink.his.web.knowledgegraph.appservice; + +import com.healthlink.his.web.knowledgegraph.dto.ImportResultDto; +import org.springframework.web.multipart.MultipartFile; + +public interface IKgDataImportAppService { + + ImportResultDto importDiseaseFromCsv(MultipartFile file); + + ImportResultDto importDrugFromCsv(MultipartFile file); + + ImportResultDto importRelationsFromCsv(MultipartFile file); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgReasoningAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgReasoningAppService.java new file mode 100644 index 000000000..cc0897194 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/IKgReasoningAppService.java @@ -0,0 +1,17 @@ +package com.healthlink.his.web.knowledgegraph.appservice; + +import com.healthlink.his.web.knowledgegraph.dto.*; + +import java.util.List; +import java.util.Map; + +public interface IKgReasoningAppService { + + List suggestDiagnosis(List symptoms, Integer topN); + + List suggestExaminations(String diseaseCode, Integer topN); + + List checkDrugInteractions(List drugCodes); + + Map suggestPathway(String diseaseCode); +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgDataImportAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgDataImportAppServiceImpl.java new file mode 100644 index 000000000..d2a8291ba --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgDataImportAppServiceImpl.java @@ -0,0 +1,273 @@ +package com.healthlink.his.web.knowledgegraph.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.healthlink.his.clinical.domain.KgEntityRelation; +import com.healthlink.his.clinical.service.IKgEntityRelationService; +import com.healthlink.his.knowledgegraph.domain.KgDisease; +import com.healthlink.his.knowledgegraph.domain.KgDrug; +import com.healthlink.his.knowledgegraph.service.IKgDiseaseService; +import com.healthlink.his.knowledgegraph.service.IKgDrugService; +import com.healthlink.his.web.knowledgegraph.appservice.IKgDataImportAppService; +import com.healthlink.his.web.knowledgegraph.dto.ImportResultDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +public class KgDataImportAppServiceImpl implements IKgDataImportAppService { + + @Autowired + private IKgDiseaseService diseaseService; + @Autowired + private IKgDrugService drugService; + @Autowired + private IKgEntityRelationService relationService; + + @Override + @Transactional(rollbackFor = Exception.class) + public ImportResultDto importDiseaseFromCsv(MultipartFile file) { + ImportResultDto result = new ImportResultDto(); + List batch = new ArrayList<>(); + int totalRows = 0; + int successCount = 0; + int failCount = 0; + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) { + String header = reader.readLine(); + if (header == null) { + result.setMessage("CSV文件为空"); + result.setTotalRows(0); + result.setSuccessCount(0); + result.setFailCount(0); + return result; + } + + String line; + while ((line = reader.readLine()) != null) { + totalRows++; + try { + String[] parts = parseCsvLine(line); + if (parts.length < 2) { + failCount++; + continue; + } + + KgDisease disease = new KgDisease(); + disease.setDiseaseCode(getField(parts, 0)); + disease.setDiseaseName(getField(parts, 1)); + disease.setCategory(getField(parts, 2)); + disease.setDepartment(getField(parts, 3)); + disease.setSeverityLevel(getField(parts, 4)); + disease.setDescription(getField(parts, 5)); + disease.setKeywords(getField(parts, 6)); + + if (hasText(disease.getDiseaseCode()) && hasText(disease.getDiseaseName())) { + batch.add(disease); + } else { + failCount++; + } + } catch (Exception e) { + log.warn("解析疾病CSV行失败: {}", line, e); + failCount++; + } + } + + if (!batch.isEmpty()) { + diseaseService.saveBatch(batch); + successCount = batch.size(); + } + } catch (Exception e) { + log.error("导入疾病CSV失败", e); + failCount = totalRows - successCount; + } + + result.setTotalRows(totalRows); + result.setSuccessCount(successCount); + result.setFailCount(failCount); + result.setMessage(String.format("导入完成: 共%d行, 成功%d条, 失败%d条", totalRows, successCount, failCount)); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ImportResultDto importDrugFromCsv(MultipartFile file) { + ImportResultDto result = new ImportResultDto(); + List batch = new ArrayList<>(); + int totalRows = 0; + int successCount = 0; + int failCount = 0; + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) { + String header = reader.readLine(); + if (header == null) { + result.setMessage("CSV文件为空"); + result.setTotalRows(0); + result.setSuccessCount(0); + result.setFailCount(0); + return result; + } + + String line; + while ((line = reader.readLine()) != null) { + totalRows++; + try { + String[] parts = parseCsvLine(line); + if (parts.length < 2) { + failCount++; + continue; + } + + KgDrug drug = new KgDrug(); + drug.setDrugCode(getField(parts, 0)); + drug.setDrugName(getField(parts, 1)); + drug.setGenericName(getField(parts, 2)); + drug.setCategory(getField(parts, 3)); + drug.setDosageForm(getField(parts, 4)); + drug.setContraindications(getField(parts, 5)); + drug.setSideEffects(getField(parts, 6)); + + if (hasText(drug.getDrugCode()) && hasText(drug.getDrugName())) { + batch.add(drug); + } else { + failCount++; + } + } catch (Exception e) { + log.warn("解析药物CSV行失败: {}", line, e); + failCount++; + } + } + + if (!batch.isEmpty()) { + drugService.saveBatch(batch); + successCount = batch.size(); + } + } catch (Exception e) { + log.error("导入药物CSV失败", e); + failCount = totalRows - successCount; + } + + result.setTotalRows(totalRows); + result.setSuccessCount(successCount); + result.setFailCount(failCount); + result.setMessage(String.format("导入完成: 共%d行, 成功%d条, 失败%d条", totalRows, successCount, failCount)); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public ImportResultDto importRelationsFromCsv(MultipartFile file) { + ImportResultDto result = new ImportResultDto(); + List batch = new ArrayList<>(); + int totalRows = 0; + int successCount = 0; + int failCount = 0; + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) { + String header = reader.readLine(); + if (header == null) { + result.setMessage("CSV文件为空"); + result.setTotalRows(0); + result.setSuccessCount(0); + result.setFailCount(0); + return result; + } + + String line; + while ((line = reader.readLine()) != null) { + totalRows++; + try { + String[] parts = parseCsvLine(line); + if (parts.length < 5) { + failCount++; + continue; + } + + KgEntityRelation relation = new KgEntityRelation(); + relation.setSourceType(getField(parts, 0)); + relation.setSourceId(getField(parts, 1)); + relation.setTargetType(getField(parts, 2)); + relation.setTargetId(getField(parts, 3)); + relation.setRelationType(getField(parts, 4)); + + String strengthStr = getField(parts, 5); + if (hasText(strengthStr)) { + try { + relation.setRelationStrength(new BigDecimal(strengthStr)); + } catch (NumberFormatException e) { + relation.setRelationStrength(BigDecimal.ONE); + } + } else { + relation.setRelationStrength(BigDecimal.ONE); + } + relation.setDescription(getField(parts, 6)); + relation.setEvidenceSource(getField(parts, 7)); + + if (hasText(relation.getSourceType()) && hasText(relation.getSourceId()) + && hasText(relation.getTargetType()) && hasText(relation.getTargetId())) { + batch.add(relation); + } else { + failCount++; + } + } catch (Exception e) { + log.warn("解析关系CSV行失败: {}", line, e); + failCount++; + } + } + + if (!batch.isEmpty()) { + relationService.saveBatch(batch); + successCount = batch.size(); + } + } catch (Exception e) { + log.error("导入关系CSV失败", e); + failCount = totalRows - successCount; + } + + result.setTotalRows(totalRows); + result.setSuccessCount(successCount); + result.setFailCount(failCount); + result.setMessage(String.format("导入完成: 共%d行, 成功%d条, 失败%d条", totalRows, successCount, failCount)); + return result; + } + + private String[] parseCsvLine(String line) { + List fields = new ArrayList<>(); + StringBuilder current = new StringBuilder(); + boolean inQuotes = false; + + for (char c : line.toCharArray()) { + if (c == '"') { + inQuotes = !inQuotes; + } else if (c == ',' && !inQuotes) { + fields.add(current.toString().trim()); + current = new StringBuilder(); + } else { + current.append(c); + } + } + fields.add(current.toString().trim()); + return fields.toArray(new String[0]); + } + + private String getField(String[] parts, int index) { + if (index < parts.length) { + String val = parts[index]; + return hasText(val) ? val : null; + } + return null; + } + + private boolean hasText(String s) { + return s != null && !s.trim().isEmpty(); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgReasoningAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgReasoningAppServiceImpl.java new file mode 100644 index 000000000..3464524ea --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/appservice/impl/KgReasoningAppServiceImpl.java @@ -0,0 +1,243 @@ +package com.healthlink.his.web.knowledgegraph.appservice.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.healthlink.his.clinical.domain.KgClinicalPathway; +import com.healthlink.his.clinical.domain.KgEntityRelation; +import com.healthlink.his.clinical.domain.KgPathwayStep; +import com.healthlink.his.clinical.service.IKgClinicalPathwayService; +import com.healthlink.his.clinical.service.IKgEntityRelationService; +import com.healthlink.his.clinical.service.IKgPathwayStepService; +import com.healthlink.his.knowledgegraph.domain.KgDisease; +import com.healthlink.his.knowledgegraph.domain.KgDrug; +import com.healthlink.his.knowledgegraph.domain.KgExamination; +import com.healthlink.his.knowledgegraph.domain.KgSymptom; +import com.healthlink.his.knowledgegraph.service.IKgDiseaseService; +import com.healthlink.his.knowledgegraph.service.IKgDrugService; +import com.healthlink.his.knowledgegraph.service.IKgExaminationService; +import com.healthlink.his.knowledgegraph.service.IKgSymptomService; +import com.healthlink.his.web.knowledgegraph.appservice.IKgReasoningAppService; +import com.healthlink.his.web.knowledgegraph.dto.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class KgReasoningAppServiceImpl implements IKgReasoningAppService { + + @Autowired + private IKgEntityRelationService relationService; + @Autowired + private IKgSymptomService symptomService; + @Autowired + private IKgDiseaseService diseaseService; + @Autowired + private IKgExaminationService examinationService; + @Autowired + private IKgDrugService drugService; + @Autowired + private IKgClinicalPathwayService pathwayService; + @Autowired + private IKgPathwayStepService pathwayStepService; + + @Override + public List suggestDiagnosis(List symptoms, Integer topN) { + if (symptoms == null || symptoms.isEmpty()) { + return Collections.emptyList(); + } + if (topN == null || topN <= 0) { + topN = 5; + } + + // 1. Find symptom IDs by name/code + LambdaQueryWrapper sw = new LambdaQueryWrapper<>(); + sw.and(w -> { + for (String symptom : symptoms) { + w.or().like(KgSymptom::getSymptomName, symptom) + .or().like(KgSymptom::getSymptomCode, symptom); + } + }); + List matchedSymptoms = symptomService.list(sw); + if (matchedSymptoms.isEmpty()) { + return Collections.emptyList(); + } + + List symptomIds = matchedSymptoms.stream() + .map(s -> String.valueOf(s.getId())) + .collect(Collectors.toList()); + + // 2. Query relations: symptom -> disease + LambdaQueryWrapper rw = new LambdaQueryWrapper<>(); + rw.eq(KgEntityRelation::getSourceType, "symptom") + .eq(KgEntityRelation::getTargetType, "disease") + .in(KgEntityRelation::getSourceId, symptomIds) + .orderByDesc(KgEntityRelation::getRelationStrength); + List relations = relationService.list(rw); + + // 3. Group by disease, sum scores + Map diseaseScoreMap = new LinkedHashMap<>(); + Map> diseaseSymptomMap = new LinkedHashMap<>(); + for (KgEntityRelation rel : relations) { + String diseaseId = rel.getTargetId(); + BigDecimal strength = rel.getRelationStrength() != null ? rel.getRelationStrength() : BigDecimal.ONE; + diseaseScoreMap.merge(diseaseId, strength, BigDecimal::add); + diseaseSymptomMap.computeIfAbsent(diseaseId, k -> new ArrayList<>()) + .add(rel.getSourceId()); + } + + // 4. Sort by score descending, take top N + List> sorted = diseaseScoreMap.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(topN) + .collect(Collectors.toList()); + + // 5. Build results with disease info + List results = new ArrayList<>(); + for (Map.Entry entry : sorted) { + KgDisease disease = diseaseService.getById(Long.parseLong(entry.getKey())); + if (disease == null) continue; + + DiagnosisResultDto dto = new DiagnosisResultDto(); + dto.setDiseaseCode(disease.getDiseaseCode()); + dto.setDiseaseName(disease.getDiseaseName()); + dto.setCategory(disease.getCategory()); + dto.setDepartment(disease.getDepartment()); + dto.setScore(entry.getValue()); + + List matched = diseaseSymptomMap.getOrDefault(entry.getKey(), Collections.emptyList()); + dto.setMatchedSymptoms(String.join(",", matched)); + results.add(dto); + } + return results; + } + + @Override + public List suggestExaminations(String diseaseCode, Integer topN) { + if (!StringUtils.hasText(diseaseCode)) { + return Collections.emptyList(); + } + if (topN == null || topN <= 0) { + topN = 10; + } + + // 1. Find disease by code + LambdaQueryWrapper dw = new LambdaQueryWrapper<>(); + dw.eq(KgDisease::getDiseaseCode, diseaseCode); + KgDisease disease = diseaseService.getOne(dw); + if (disease == null) { + return Collections.emptyList(); + } + + // 2. Query relations: disease -> examination + LambdaQueryWrapper rw = new LambdaQueryWrapper<>(); + rw.eq(KgEntityRelation::getSourceType, "disease") + .eq(KgEntityRelation::getTargetType, "examination") + .eq(KgEntityRelation::getSourceId, String.valueOf(disease.getId())) + .orderByDesc(KgEntityRelation::getRelationStrength); + List relations = relationService.list(rw); + + // 3. Build results + List results = new ArrayList<>(); + for (KgEntityRelation rel : relations) { + if (results.size() >= topN) break; + KgExamination exam = examinationService.getById(Long.parseLong(rel.getTargetId())); + if (exam == null) continue; + + ExaminationResultDto dto = new ExaminationResultDto(); + dto.setExamCode(exam.getExamCode()); + dto.setExamName(exam.getExamName()); + dto.setExamType(exam.getExamType()); + dto.setClinicalSignificance(exam.getClinicalSignificance()); + dto.setScore(rel.getRelationStrength()); + results.add(dto); + } + return results; + } + + @Override + public List checkDrugInteractions(List drugCodes) { + if (drugCodes == null || drugCodes.size() < 2) { + return Collections.emptyList(); + } + + // 1. Find drug IDs by code + LambdaQueryWrapper dw = new LambdaQueryWrapper<>(); + dw.in(KgDrug::getDrugCode, drugCodes); + List drugs = drugService.list(dw); + if (drugs.isEmpty()) { + return Collections.emptyList(); + } + + Map drugMap = drugs.stream() + .collect(Collectors.toMap(KgDrug::getDrugCode, d -> d, (a, b) -> a)); + + List drugIds = drugs.stream() + .map(d -> String.valueOf(d.getId())) + .collect(Collectors.toList()); + + // 2. Query drug-drug interaction relations + LambdaQueryWrapper rw = new LambdaQueryWrapper<>(); + rw.eq(KgEntityRelation::getSourceType, "drug") + .eq(KgEntityRelation::getTargetType, "drug") + .in(KgEntityRelation::getSourceId, drugIds) + .in(KgEntityRelation::getTargetId, drugIds); + List relations = relationService.list(rw); + + // 3. Build results + List results = new ArrayList<>(); + Set added = new HashSet<>(); + + for (KgEntityRelation rel : relations) { + KgDrug drugA = drugService.getById(Long.parseLong(rel.getSourceId())); + KgDrug drugB = drugService.getById(Long.parseLong(rel.getTargetId())); + if (drugA == null || drugB == null) continue; + + String key = Collections.min(Arrays.asList(drugA.getDrugCode(), drugB.getDrugCode())) + + "-" + Collections.max(Arrays.asList(drugA.getDrugCode(), drugB.getDrugCode())); + if (!added.add(key)) continue; + + DrugInteractionResultDto dto = new DrugInteractionResultDto(); + dto.setDrugCodeA(drugA.getDrugCode()); + dto.setDrugNameA(drugA.getDrugName()); + dto.setDrugCodeB(drugB.getDrugCode()); + dto.setDrugNameB(drugB.getDrugName()); + dto.setInteractionType(rel.getRelationType()); + dto.setDescription(rel.getDescription()); + dto.setSeverity(rel.getRelationStrength() != null + ? (rel.getRelationStrength().compareTo(new BigDecimal("0.7")) >= 0 ? "严重" : "一般") + : "一般"); + results.add(dto); + } + return results; + } + + @Override + public Map suggestPathway(String diseaseCode) { + if (!StringUtils.hasText(diseaseCode)) { + return Collections.emptyMap(); + } + + // 1. Find pathway by disease code + LambdaQueryWrapper pw = new LambdaQueryWrapper<>(); + pw.eq(KgClinicalPathway::getDiseaseCode, diseaseCode) + .eq(KgClinicalPathway::getStatus, "ACTIVE"); + KgClinicalPathway pathway = pathwayService.getOne(pw); + if (pathway == null) { + return Collections.emptyMap(); + } + + // 2. Get pathway steps + LambdaQueryWrapper sw = new LambdaQueryWrapper<>(); + sw.eq(KgPathwayStep::getPathwayId, pathway.getId()) + .orderByAsc(KgPathwayStep::getStepOrder); + List steps = pathwayStepService.list(sw); + + Map result = new LinkedHashMap<>(); + result.put("pathway", pathway); + result.put("steps", steps); + return result; + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgDataImportController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgDataImportController.java new file mode 100644 index 000000000..6881acba0 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgDataImportController.java @@ -0,0 +1,103 @@ +package com.healthlink.his.web.knowledgegraph.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.knowledgegraph.appservice.IKgDataImportAppService; +import com.healthlink.his.web.knowledgegraph.dto.ImportResultDto; +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.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Tag(name = "知识图谱-数据导入") +@RestController +@RequestMapping("/knowledgegraph/import") +@AllArgsConstructor +public class KgDataImportController { + + private final IKgDataImportAppService kgDataImportAppService; + + @Operation(summary = "导入疾病数据") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/disease") + public R importDisease(@RequestParam("file") MultipartFile file) { + try { + ImportResultDto result = kgDataImportAppService.importDiseaseFromCsv(file); + return R.ok(result); + } catch (Exception e) { + log.error("导入疾病数据失败", e); + return R.fail("导入疾病数据失败: " + e.getMessage()); + } + } + + @Operation(summary = "导入药物数据") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/drug") + public R importDrug(@RequestParam("file") MultipartFile file) { + try { + ImportResultDto result = kgDataImportAppService.importDrugFromCsv(file); + return R.ok(result); + } catch (Exception e) { + log.error("导入药物数据失败", e); + return R.fail("导入药物数据失败: " + e.getMessage()); + } + } + + @Operation(summary = "导入关系数据") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:edit')") + @PostMapping("/relation") + public R importRelations(@RequestParam("file") MultipartFile file) { + try { + ImportResultDto result = kgDataImportAppService.importRelationsFromCsv(file); + return R.ok(result); + } catch (Exception e) { + log.error("导入关系数据失败", e); + return R.fail("导入关系数据失败: " + e.getMessage()); + } + } + + @Operation(summary = "下载导入模板") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/template/{type}") + public void downloadTemplate(@PathVariable String type, jakarta.servlet.http.HttpServletResponse response) throws Exception { + String filename; + String content; + + switch (type) { + case "disease": + filename = "疾病导入模板.csv"; + content = "疾病编码,疾病名称,分类,科室,严重等级,描述,关键词\n" + + "J06.900,急性上呼吸道感染,感染性疾病,呼吸内科,轻度,急性上呼吸道感染,发热;咳嗽;咽痛\n"; + break; + case "drug": + filename = "药物导入模板.csv"; + content = "药物编码,药物名称,通用名,分类,剂型,禁忌症,不良反应\n" + + "D00001,阿莫西林胶囊,阿莫西林,抗生素,胶囊剂,青霉素过敏者禁用,皮疹;腹泻\n"; + break; + case "relation": + filename = "关系导入模板.csv"; + content = "来源类型,来源ID,目标类型,目标ID,关系类型,关系强度,描述,证据来源\n" + + "symptom,1001,disease,2001,has_symptom,0.85,发热是急性上呼吸道感染的常见症状,临床指南\n"; + break; + default: + response.setStatus(400); + response.getWriter().write("不支持的模板类型"); + return; + } + + response.setContentType("text/csv;charset=UTF-8"); + response.setHeader("Content-Disposition", + "attachment; filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8)); + response.getOutputStream().write("\uFEFF".getBytes(StandardCharsets.UTF_8)); + response.getOutputStream().write(content.getBytes(StandardCharsets.UTF_8)); + response.getOutputStream().flush(); + } +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgReasoningController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgReasoningController.java new file mode 100644 index 000000000..60d9c59c1 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/controller/KgReasoningController.java @@ -0,0 +1,76 @@ +package com.healthlink.his.web.knowledgegraph.controller; + +import com.core.common.core.domain.R; +import com.healthlink.his.web.knowledgegraph.appservice.IKgReasoningAppService; +import com.healthlink.his.web.knowledgegraph.dto.*; +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.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@Slf4j +@Tag(name = "知识图谱-推理引擎") +@RestController +@RequestMapping("/knowledgegraph/reasoning") +@AllArgsConstructor +public class KgReasoningController { + + private final IKgReasoningAppService kgReasoningAppService; + + @Operation(summary = "诊断推荐 - 基于症状推荐诊断") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @PostMapping("/diagnosis") + public R> suggestDiagnosis(@RequestBody DiagnosisSuggestDto dto) { + try { + List results = kgReasoningAppService.suggestDiagnosis(dto.getSymptoms(), dto.getTopN()); + return R.ok(results); + } catch (Exception e) { + log.error("诊断推荐失败", e); + return R.fail("诊断推荐失败: " + e.getMessage()); + } + } + + @Operation(summary = "检查推荐 - 基于诊断推荐检查") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @PostMapping("/examination") + public R> suggestExaminations(@RequestBody ExaminationSuggestDto dto) { + try { + List results = kgReasoningAppService.suggestExaminations(dto.getDiseaseCode(), dto.getTopN()); + return R.ok(results); + } catch (Exception e) { + log.error("检查推荐失败", e); + return R.fail("检查推荐失败: " + e.getMessage()); + } + } + + @Operation(summary = "药物相互作用检查") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @PostMapping("/drug-interaction") + public R> checkDrugInteractions(@RequestBody DrugInteractionDto dto) { + try { + List results = kgReasoningAppService.checkDrugInteractions(dto.getDrugCodes()); + return R.ok(results); + } catch (Exception e) { + log.error("药物相互作用检查失败", e); + return R.fail("药物相互作用检查失败: " + e.getMessage()); + } + } + + @Operation(summary = "临床路径推荐") + @PreAuthorize("@ss.hasPermi('system:knowledgegraph:list')") + @GetMapping("/pathway/{diseaseCode}") + public R> suggestPathway(@PathVariable String diseaseCode) { + try { + Map result = kgReasoningAppService.suggestPathway(diseaseCode); + return result.isEmpty() ? R.fail("未找到临床路径") : R.ok(result); + } catch (Exception e) { + log.error("临床路径推荐失败", e); + return R.fail("临床路径推荐失败: " + e.getMessage()); + } + } +} From 179d8c9c970d53313ee95d843908e20b03ec0b30 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 10:34:43 +0800 Subject: [PATCH 04/19] =?UTF-8?q?feat(kg):=20=E6=8E=A8=E7=90=86=E5=BC=95?= =?UTF-8?q?=E6=93=8E+CDSS=E9=9B=86=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/DiagnosisResultDto.java | 15 ++++ .../dto/DiagnosisSuggestDto.java | 13 +++ .../dto/DrugInteractionDto.java | 12 +++ .../dto/DrugInteractionResultDto.java | 14 ++++ .../dto/ExaminationResultDto.java | 14 ++++ .../dto/ExaminationSuggestDto.java | 11 +++ .../views/knowledgegraph/DiagnosisSuggest.vue | 84 +++++++++++++++++++ .../knowledgegraph/DrugInteractionCheck.vue | 84 +++++++++++++++++++ 8 files changed, 247 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisResultDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisSuggestDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionResultDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationResultDto.java create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationSuggestDto.java create mode 100644 healthlink-his-ui/src/views/knowledgegraph/DiagnosisSuggest.vue create mode 100644 healthlink-his-ui/src/views/knowledgegraph/DrugInteractionCheck.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisResultDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisResultDto.java new file mode 100644 index 000000000..bcc63d574 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisResultDto.java @@ -0,0 +1,15 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class DiagnosisResultDto { + private String diseaseCode; + private String diseaseName; + private String category; + private String department; + private BigDecimal score; + private String matchedSymptoms; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisSuggestDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisSuggestDto.java new file mode 100644 index 000000000..5548e005c --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DiagnosisSuggestDto.java @@ -0,0 +1,13 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class DiagnosisSuggestDto { + private List symptoms; + private Integer topN = 5; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionDto.java new file mode 100644 index 000000000..367625979 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionDto.java @@ -0,0 +1,12 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +import java.util.List; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class DrugInteractionDto { + private List drugCodes; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionResultDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionResultDto.java new file mode 100644 index 000000000..f75973e01 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/DrugInteractionResultDto.java @@ -0,0 +1,14 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import lombok.Data; + +@Data +public class DrugInteractionResultDto { + private String drugCodeA; + private String drugNameA; + private String drugCodeB; + private String drugNameB; + private String interactionType; + private String description; + private String severity; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationResultDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationResultDto.java new file mode 100644 index 000000000..c6b80dee1 --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationResultDto.java @@ -0,0 +1,14 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ExaminationResultDto { + private String examCode; + private String examName; + private String examType; + private String clinicalSignificance; + private BigDecimal score; +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationSuggestDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationSuggestDto.java new file mode 100644 index 000000000..ae71f0ffd --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ExaminationSuggestDto.java @@ -0,0 +1,11 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class ExaminationSuggestDto { + private String diseaseCode; + private Integer topN = 10; +} diff --git a/healthlink-his-ui/src/views/knowledgegraph/DiagnosisSuggest.vue b/healthlink-his-ui/src/views/knowledgegraph/DiagnosisSuggest.vue new file mode 100644 index 000000000..7d6167de6 --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/DiagnosisSuggest.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/healthlink-his-ui/src/views/knowledgegraph/DrugInteractionCheck.vue b/healthlink-his-ui/src/views/knowledgegraph/DrugInteractionCheck.vue new file mode 100644 index 000000000..45407c433 --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/DrugInteractionCheck.vue @@ -0,0 +1,84 @@ + + + + + From 844eb8b7ab27261a1fa401d34af3fd67c8d3044a Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 10:36:06 +0800 Subject: [PATCH 05/19] =?UTF-8?q?feat(kg):=20=E6=95=B0=E6=8D=AE=E5=AF=BC?= =?UTF-8?q?=E5=85=A5+=E8=A7=84=E5=88=99=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../knowledgegraph/dto/ImportResultDto.java | 11 ++ .../src/api/knowledgegraph/api.js | 40 ++++++ .../src/views/knowledgegraph/DataImport.vue | 134 ++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ImportResultDto.java create mode 100644 healthlink-his-ui/src/views/knowledgegraph/DataImport.vue diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ImportResultDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ImportResultDto.java new file mode 100644 index 000000000..25beaf14b --- /dev/null +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/knowledgegraph/dto/ImportResultDto.java @@ -0,0 +1,11 @@ +package com.healthlink.his.web.knowledgegraph.dto; + +import lombok.Data; + +@Data +public class ImportResultDto { + private int successCount; + private int failCount; + private int totalRows; + private String message; +} diff --git a/healthlink-his-ui/src/api/knowledgegraph/api.js b/healthlink-his-ui/src/api/knowledgegraph/api.js index 1c8da3dbc..e8660c23e 100644 --- a/healthlink-his-ui/src/api/knowledgegraph/api.js +++ b/healthlink-his-ui/src/api/knowledgegraph/api.js @@ -103,3 +103,43 @@ export function getPathwayPage(params) { export function getPathwaySteps(id) { return request({ url: `/knowledgegraph/pathway/${id}/steps`, method: 'get' }) } + +// KG3: 推理引擎 +export function suggestDiagnosis(data) { + return request({ url: '/knowledgegraph/reasoning/diagnosis', method: 'post', data }) +} + +export function suggestExaminations(data) { + return request({ url: '/knowledgegraph/reasoning/examination', method: 'post', data }) +} + +export function checkDrugInteractions(data) { + return request({ url: '/knowledgegraph/reasoning/drug-interaction', method: 'post', data }) +} + +export function suggestPathway(diseaseCode) { + return request({ url: `/knowledgegraph/reasoning/pathway/${diseaseCode}`, method: 'get' }) +} + +// KG4: 数据导入 +export function importDisease(file) { + const formData = new FormData() + formData.append('file', file) + return request({ url: '/knowledgegraph/import/disease', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } }) +} + +export function importDrug(file) { + const formData = new FormData() + formData.append('file', file) + return request({ url: '/knowledgegraph/import/drug', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } }) +} + +export function importRelations(file) { + const formData = new FormData() + formData.append('file', file) + return request({ url: '/knowledgegraph/import/relation', method: 'post', data: formData, headers: { 'Content-Type': 'multipart/form-data' } }) +} + +export function downloadImportTemplate(type) { + return request({ url: `/knowledgegraph/import/template/${type}`, method: 'get', responseType: 'blob' }) +} diff --git a/healthlink-his-ui/src/views/knowledgegraph/DataImport.vue b/healthlink-his-ui/src/views/knowledgegraph/DataImport.vue new file mode 100644 index 000000000..e1dcad9e7 --- /dev/null +++ b/healthlink-his-ui/src/views/knowledgegraph/DataImport.vue @@ -0,0 +1,134 @@ + + + + + From 7b4cfeb6d5c192b2fc6d88c862fdcbd9217e1f89 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 10:44:32 +0800 Subject: [PATCH 06/19] =?UTF-8?q?feat(mobile-h5):=20=E7=A7=BB=E5=8A=A8H5?= =?UTF-8?q?=E6=8A=A4=E7=90=86=E5=B7=A5=E4=BD=9C=E7=AB=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/mobile/AssessmentForm.vue | 439 +++++++++++++++ .../src/views/mobile/MobileLayout.vue | 197 +++++++ .../src/views/mobile/PatientDetail.vue | 509 ++++++++++++++++++ .../src/views/mobile/PatientList.vue | 244 +++++++++ .../src/views/mobile/TaskList.vue | 228 ++++++++ .../src/views/mobile/VitalSignEntry.vue | 392 ++++++++++++++ healthlink-his-ui/src/views/mobile/api.js | 45 ++ 7 files changed, 2054 insertions(+) create mode 100644 healthlink-his-ui/src/views/mobile/AssessmentForm.vue create mode 100644 healthlink-his-ui/src/views/mobile/MobileLayout.vue create mode 100644 healthlink-his-ui/src/views/mobile/PatientDetail.vue create mode 100644 healthlink-his-ui/src/views/mobile/PatientList.vue create mode 100644 healthlink-his-ui/src/views/mobile/TaskList.vue create mode 100644 healthlink-his-ui/src/views/mobile/VitalSignEntry.vue create mode 100644 healthlink-his-ui/src/views/mobile/api.js diff --git a/healthlink-his-ui/src/views/mobile/AssessmentForm.vue b/healthlink-his-ui/src/views/mobile/AssessmentForm.vue new file mode 100644 index 000000000..4cd5c02fd --- /dev/null +++ b/healthlink-his-ui/src/views/mobile/AssessmentForm.vue @@ -0,0 +1,439 @@ + + + + + diff --git a/healthlink-his-ui/src/views/mobile/MobileLayout.vue b/healthlink-his-ui/src/views/mobile/MobileLayout.vue new file mode 100644 index 000000000..232071880 --- /dev/null +++ b/healthlink-his-ui/src/views/mobile/MobileLayout.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/healthlink-his-ui/src/views/mobile/PatientDetail.vue b/healthlink-his-ui/src/views/mobile/PatientDetail.vue new file mode 100644 index 000000000..8758f4a0f --- /dev/null +++ b/healthlink-his-ui/src/views/mobile/PatientDetail.vue @@ -0,0 +1,509 @@ + + + + + diff --git a/healthlink-his-ui/src/views/mobile/PatientList.vue b/healthlink-his-ui/src/views/mobile/PatientList.vue new file mode 100644 index 000000000..4a15440a0 --- /dev/null +++ b/healthlink-his-ui/src/views/mobile/PatientList.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/healthlink-his-ui/src/views/mobile/TaskList.vue b/healthlink-his-ui/src/views/mobile/TaskList.vue new file mode 100644 index 000000000..43737055b --- /dev/null +++ b/healthlink-his-ui/src/views/mobile/TaskList.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/healthlink-his-ui/src/views/mobile/VitalSignEntry.vue b/healthlink-his-ui/src/views/mobile/VitalSignEntry.vue new file mode 100644 index 000000000..274395d1d --- /dev/null +++ b/healthlink-his-ui/src/views/mobile/VitalSignEntry.vue @@ -0,0 +1,392 @@ + + + + + diff --git a/healthlink-his-ui/src/views/mobile/api.js b/healthlink-his-ui/src/views/mobile/api.js new file mode 100644 index 000000000..591d0b7ed --- /dev/null +++ b/healthlink-his-ui/src/views/mobile/api.js @@ -0,0 +1,45 @@ +import request from '@/utils/request' + +export function getMobileTaskList(params) { + return request({ url: '/mp/nursing/task-list', method: 'get', params }) +} + +export function completeTask(data) { + return request({ url: '/mp/nursing/task-complete', method: 'post', data }) +} + +export function getMobilePatientList(params) { + return request({ url: '/mp/nursing/patient-list', method: 'get', params }) +} + +export function getPatientDetail(patientId) { + return request({ url: '/mp/nursing/patient-detail/' + patientId, method: 'get' }) +} + +export function getPatientOrders(patientId, params) { + return request({ url: '/mp/nursing/patient-orders/' + patientId, method: 'get', params }) +} + +export function executeOrder(data) { + return request({ url: '/mp/nursing/order-execute', method: 'post', data }) +} + +export function saveVitalSign(data) { + return request({ url: '/mp/nursing/vital-sign', method: 'post', data }) +} + +export function getVitalSignTrend(patientId, params) { + return request({ url: '/mp/nursing/vital-sign-trend/' + patientId, method: 'get', params }) +} + +export function getAssessmentList(patientId) { + return request({ url: '/mp/nursing/assessment-list/' + patientId, method: 'get' }) +} + +export function submitAssessment(data) { + return request({ url: '/mp/nursing/assessment-submit', method: 'post', data }) +} + +export function getPatientRecords(patientId, params) { + return request({ url: '/mp/nursing/patient-records/' + patientId, method: 'get', params }) +} From 5ab3865e041d943ed95542bc97976a7bf522c68d Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 12:15:18 +0800 Subject: [PATCH 07/19] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4UI=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E4=B8=AD=E7=9A=84mobile=E4=BB=A3=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E5=87=86=E5=A4=87=E7=8B=AC=E7=AB=8B=E7=A7=BB=E5=8A=A8=E7=AB=AF?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/mobile/AssessmentForm.vue | 439 --------------- .../src/views/mobile/MobileLayout.vue | 197 ------- .../src/views/mobile/PatientDetail.vue | 509 ------------------ .../src/views/mobile/PatientList.vue | 244 --------- .../src/views/mobile/TaskList.vue | 228 -------- .../src/views/mobile/VitalSignEntry.vue | 392 -------------- healthlink-his-ui/src/views/mobile/api.js | 45 -- 7 files changed, 2054 deletions(-) delete mode 100644 healthlink-his-ui/src/views/mobile/AssessmentForm.vue delete mode 100644 healthlink-his-ui/src/views/mobile/MobileLayout.vue delete mode 100644 healthlink-his-ui/src/views/mobile/PatientDetail.vue delete mode 100644 healthlink-his-ui/src/views/mobile/PatientList.vue delete mode 100644 healthlink-his-ui/src/views/mobile/TaskList.vue delete mode 100644 healthlink-his-ui/src/views/mobile/VitalSignEntry.vue delete mode 100644 healthlink-his-ui/src/views/mobile/api.js diff --git a/healthlink-his-ui/src/views/mobile/AssessmentForm.vue b/healthlink-his-ui/src/views/mobile/AssessmentForm.vue deleted file mode 100644 index 4cd5c02fd..000000000 --- a/healthlink-his-ui/src/views/mobile/AssessmentForm.vue +++ /dev/null @@ -1,439 +0,0 @@ - - - - - diff --git a/healthlink-his-ui/src/views/mobile/MobileLayout.vue b/healthlink-his-ui/src/views/mobile/MobileLayout.vue deleted file mode 100644 index 232071880..000000000 --- a/healthlink-his-ui/src/views/mobile/MobileLayout.vue +++ /dev/null @@ -1,197 +0,0 @@ - - - - - diff --git a/healthlink-his-ui/src/views/mobile/PatientDetail.vue b/healthlink-his-ui/src/views/mobile/PatientDetail.vue deleted file mode 100644 index 8758f4a0f..000000000 --- a/healthlink-his-ui/src/views/mobile/PatientDetail.vue +++ /dev/null @@ -1,509 +0,0 @@ - - - - - diff --git a/healthlink-his-ui/src/views/mobile/PatientList.vue b/healthlink-his-ui/src/views/mobile/PatientList.vue deleted file mode 100644 index 4a15440a0..000000000 --- a/healthlink-his-ui/src/views/mobile/PatientList.vue +++ /dev/null @@ -1,244 +0,0 @@ - - - - - diff --git a/healthlink-his-ui/src/views/mobile/TaskList.vue b/healthlink-his-ui/src/views/mobile/TaskList.vue deleted file mode 100644 index 43737055b..000000000 --- a/healthlink-his-ui/src/views/mobile/TaskList.vue +++ /dev/null @@ -1,228 +0,0 @@ - - - - - diff --git a/healthlink-his-ui/src/views/mobile/VitalSignEntry.vue b/healthlink-his-ui/src/views/mobile/VitalSignEntry.vue deleted file mode 100644 index 274395d1d..000000000 --- a/healthlink-his-ui/src/views/mobile/VitalSignEntry.vue +++ /dev/null @@ -1,392 +0,0 @@ - - - - - diff --git a/healthlink-his-ui/src/views/mobile/api.js b/healthlink-his-ui/src/views/mobile/api.js deleted file mode 100644 index 591d0b7ed..000000000 --- a/healthlink-his-ui/src/views/mobile/api.js +++ /dev/null @@ -1,45 +0,0 @@ -import request from '@/utils/request' - -export function getMobileTaskList(params) { - return request({ url: '/mp/nursing/task-list', method: 'get', params }) -} - -export function completeTask(data) { - return request({ url: '/mp/nursing/task-complete', method: 'post', data }) -} - -export function getMobilePatientList(params) { - return request({ url: '/mp/nursing/patient-list', method: 'get', params }) -} - -export function getPatientDetail(patientId) { - return request({ url: '/mp/nursing/patient-detail/' + patientId, method: 'get' }) -} - -export function getPatientOrders(patientId, params) { - return request({ url: '/mp/nursing/patient-orders/' + patientId, method: 'get', params }) -} - -export function executeOrder(data) { - return request({ url: '/mp/nursing/order-execute', method: 'post', data }) -} - -export function saveVitalSign(data) { - return request({ url: '/mp/nursing/vital-sign', method: 'post', data }) -} - -export function getVitalSignTrend(patientId, params) { - return request({ url: '/mp/nursing/vital-sign-trend/' + patientId, method: 'get', params }) -} - -export function getAssessmentList(patientId) { - return request({ url: '/mp/nursing/assessment-list/' + patientId, method: 'get' }) -} - -export function submitAssessment(data) { - return request({ url: '/mp/nursing/assessment-submit', method: 'post', data }) -} - -export function getPatientRecords(patientId, params) { - return request({ url: '/mp/nursing/patient-records/' + patientId, method: 'get', params }) -} From 99812e1bf083bd8de55f95f46a95355621479714 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 12:16:58 +0800 Subject: [PATCH 08/19] =?UTF-8?q?feat(mobile-h5):=20=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E7=A7=BB=E5=8A=A8=E7=AB=AFH5=E6=8A=A4?= =?UTF-8?q?=E7=90=86=E5=B7=A5=E4=BD=9C=E7=AB=99=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-mobile/index.html | 14 +++ healthlink-his-mobile/package.json | 24 +++++ healthlink-his-mobile/src/App.vue | 3 + healthlink-his-mobile/src/api/index.js | 29 ++++++ healthlink-his-mobile/src/main.js | 13 +++ healthlink-his-mobile/src/router/index.js | 16 ++++ healthlink-his-mobile/src/styles/mobile.css | 6 ++ .../src/views/AssessmentForm.vue | 89 ++++++++++++++++++ healthlink-his-mobile/src/views/Mine.vue | 33 +++++++ .../src/views/MobileLayout.vue | 42 +++++++++ .../src/views/PatientDetail.vue | 92 +++++++++++++++++++ .../src/views/PatientList.vue | 47 ++++++++++ healthlink-his-mobile/src/views/TaskList.vue | 62 +++++++++++++ .../src/views/VitalSignEntry.vue | 69 ++++++++++++++ healthlink-his-mobile/vite.config.js | 19 ++++ 15 files changed, 558 insertions(+) create mode 100644 healthlink-his-mobile/index.html create mode 100644 healthlink-his-mobile/package.json create mode 100644 healthlink-his-mobile/src/App.vue create mode 100644 healthlink-his-mobile/src/api/index.js create mode 100644 healthlink-his-mobile/src/main.js create mode 100644 healthlink-his-mobile/src/router/index.js create mode 100644 healthlink-his-mobile/src/styles/mobile.css create mode 100644 healthlink-his-mobile/src/views/AssessmentForm.vue create mode 100644 healthlink-his-mobile/src/views/Mine.vue create mode 100644 healthlink-his-mobile/src/views/MobileLayout.vue create mode 100644 healthlink-his-mobile/src/views/PatientDetail.vue create mode 100644 healthlink-his-mobile/src/views/PatientList.vue create mode 100644 healthlink-his-mobile/src/views/TaskList.vue create mode 100644 healthlink-his-mobile/src/views/VitalSignEntry.vue create mode 100644 healthlink-his-mobile/vite.config.js diff --git a/healthlink-his-mobile/index.html b/healthlink-his-mobile/index.html new file mode 100644 index 000000000..0eba5633c --- /dev/null +++ b/healthlink-his-mobile/index.html @@ -0,0 +1,14 @@ + + + + + + + + HealthLink 移动护理 + + +
+ + + diff --git a/healthlink-his-mobile/package.json b/healthlink-his-mobile/package.json new file mode 100644 index 000000000..43779ae25 --- /dev/null +++ b/healthlink-his-mobile/package.json @@ -0,0 +1,24 @@ +{ + "name": "healthlink-his-mobile", + "version": "1.0.0", + "description": "HealthLink-HIS 移动护理H5工作站", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.4.0", + "vue-router": "^4.3.0", + "axios": "^1.7.0", + "element-plus": "^2.7.0", + "vxe-table": "^4.7.0", + "echarts": "^5.5.0", + "pinia": "^2.1.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.0", + "vite": "^5.4.0", + "sass": "^1.77.0" + } +} diff --git a/healthlink-his-mobile/src/App.vue b/healthlink-his-mobile/src/App.vue new file mode 100644 index 000000000..98240aef8 --- /dev/null +++ b/healthlink-his-mobile/src/App.vue @@ -0,0 +1,3 @@ + diff --git a/healthlink-his-mobile/src/api/index.js b/healthlink-his-mobile/src/api/index.js new file mode 100644 index 000000000..dec108d3e --- /dev/null +++ b/healthlink-his-mobile/src/api/index.js @@ -0,0 +1,29 @@ +import axios from 'axios' + +const request = axios.create({ + baseURL: '/healthlink-his/api/v1', + timeout: 10000 +}) + +request.interceptors.request.use(config => { + const token = localStorage.getItem('token') + if (token) config.headers.Authorization = `Bearer ${token}` + return config +}) + +request.interceptors.response.use( + res => res.data, + err => { console.error(err); return Promise.reject(err) } +) + +export const nursingApi = { + getTasks: (params) => request.get('/mp/nursing/tasks', { params }), + completeTask: (id, data) => request.post(`/mp/nursing/tasks/${id}/complete`, data), + getPatientInfo: (id) => request.get(`/mp/nursing/patient/${id}`), + getVitalSigns: (patientId) => request.get(`/mp/nursing/vital-signs/${patientId}`), + submitVitalSign: (data) => request.post('/mp/nursing/vital-sign', data), + getAssessments: (patientId) => request.get(`/mp/nursing/assessments/${patientId}`), + submitAssessment: (data) => request.post('/mp/nursing/assessment', data) +} + +export default request diff --git a/healthlink-his-mobile/src/main.js b/healthlink-his-mobile/src/main.js new file mode 100644 index 000000000..2df3d027e --- /dev/null +++ b/healthlink-his-mobile/src/main.js @@ -0,0 +1,13 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import App from './App.vue' +import router from './router' +import './styles/mobile.css' + +const app = createApp(App) +app.use(createPinia()) +app.use(router) +app.use(ElementPlus, { size: 'large' }) +app.mount('#app') diff --git a/healthlink-his-mobile/src/router/index.js b/healthlink-his-mobile/src/router/index.js new file mode 100644 index 000000000..de91e352d --- /dev/null +++ b/healthlink-his-mobile/src/router/index.js @@ -0,0 +1,16 @@ +import { createRouter, createWebHistory } from 'vue-router' + +const routes = [ + { path: '/', redirect: '/mobile/tasks' }, + { path: '/mobile', component: () => import('../views/MobileLayout.vue'), children: [ + { path: 'tasks', component: () => import('../views/TaskList.vue'), meta: { title: '任务' } }, + { path: 'patients', component: () => import('../views/PatientList.vue'), meta: { title: '患者' } }, + { path: 'patient-detail/:id', component: () => import('../views/PatientDetail.vue'), meta: { title: '患者详情' } }, + { path: 'vital-entry/:patientId', component: () => import('../views/VitalSignEntry.vue'), meta: { title: '生命体征' } }, + { path: 'assessment/:patientId', component: () => import('../views/AssessmentForm.vue'), meta: { title: '护理评估' } }, + { path: 'mine', component: () => import('../views/Mine.vue'), meta: { title: '我的' } } + ]} +] + +const router = createRouter({ history: createWebHistory(), routes }) +export default router diff --git a/healthlink-his-mobile/src/styles/mobile.css b/healthlink-his-mobile/src/styles/mobile.css new file mode 100644 index 000000000..6df6d0b8d --- /dev/null +++ b/healthlink-his-mobile/src/styles/mobile.css @@ -0,0 +1,6 @@ +* { margin: 0; padding: 0; box-sizing: border-box; } +html, body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 16px; color: #333; background: #f5f5f5; -webkit-font-smoothing: antialiased; } +:root { --primary: #1890ff; --success: #52c41a; --warning: #fa8c16; --danger: #f5222d; --bg: #f5f5f5; --card: #fff; --border: #e8e8e8; } +input, button, textarea { font-family: inherit; font-size: inherit; } +button { cursor: pointer; -webkit-tap-highlight-color: transparent; } +::-webkit-scrollbar { display: none; } diff --git a/healthlink-his-mobile/src/views/AssessmentForm.vue b/healthlink-his-mobile/src/views/AssessmentForm.vue new file mode 100644 index 000000000..cd1c6f8a2 --- /dev/null +++ b/healthlink-his-mobile/src/views/AssessmentForm.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/healthlink-his-mobile/src/views/Mine.vue b/healthlink-his-mobile/src/views/Mine.vue new file mode 100644 index 000000000..6a7278902 --- /dev/null +++ b/healthlink-his-mobile/src/views/Mine.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/healthlink-his-mobile/src/views/MobileLayout.vue b/healthlink-his-mobile/src/views/MobileLayout.vue new file mode 100644 index 000000000..aa8a3b7fb --- /dev/null +++ b/healthlink-his-mobile/src/views/MobileLayout.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/healthlink-his-mobile/src/views/PatientDetail.vue b/healthlink-his-mobile/src/views/PatientDetail.vue new file mode 100644 index 000000000..017cc2a0a --- /dev/null +++ b/healthlink-his-mobile/src/views/PatientDetail.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/healthlink-his-mobile/src/views/PatientList.vue b/healthlink-his-mobile/src/views/PatientList.vue new file mode 100644 index 000000000..b76264c9d --- /dev/null +++ b/healthlink-his-mobile/src/views/PatientList.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/healthlink-his-mobile/src/views/TaskList.vue b/healthlink-his-mobile/src/views/TaskList.vue new file mode 100644 index 000000000..72a3cc0bc --- /dev/null +++ b/healthlink-his-mobile/src/views/TaskList.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/healthlink-his-mobile/src/views/VitalSignEntry.vue b/healthlink-his-mobile/src/views/VitalSignEntry.vue new file mode 100644 index 000000000..9b01679e0 --- /dev/null +++ b/healthlink-his-mobile/src/views/VitalSignEntry.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/healthlink-his-mobile/vite.config.js b/healthlink-his-mobile/vite.config.js new file mode 100644 index 000000000..a1a0d85b8 --- /dev/null +++ b/healthlink-his-mobile/vite.config.js @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [vue()], + server: { + port: 82, + proxy: { + '/healthlink-his': { + target: 'http://localhost:18082', + changeOrigin: true + } + } + }, + build: { + outDir: 'dist', + assetsDir: 'assets' + } +}) From 686fcb5692f4ebf7eb72f6f4c6a823070a32ecb1 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 12:25:47 +0800 Subject: [PATCH 09/19] =?UTF-8?q?fix(mobile):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E7=AB=AFAPI=E8=B7=AF=E5=BE=84=E4=B8=8E?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-mobile/src/api/index.js | 2 +- healthlink-his-mobile/vite.config.js | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/healthlink-his-mobile/src/api/index.js b/healthlink-his-mobile/src/api/index.js index dec108d3e..ec4acb680 100644 --- a/healthlink-his-mobile/src/api/index.js +++ b/healthlink-his-mobile/src/api/index.js @@ -1,7 +1,7 @@ import axios from 'axios' const request = axios.create({ - baseURL: '/healthlink-his/api/v1', + baseURL: '/dev-api', timeout: 10000 }) diff --git a/healthlink-his-mobile/vite.config.js b/healthlink-his-mobile/vite.config.js index a1a0d85b8..96b15c88f 100644 --- a/healthlink-his-mobile/vite.config.js +++ b/healthlink-his-mobile/vite.config.js @@ -6,9 +6,10 @@ export default defineConfig({ server: { port: 82, proxy: { - '/healthlink-his': { - target: 'http://localhost:18082', - changeOrigin: true + '/dev-api': { + target: 'http://localhost:18082/healthlink-his', + changeOrigin: true, + rewrite: (p) => p.replace(/^\/dev-api/, '') } } }, From 05332ce2d9ab3816266e6f16a8f71e6bb22a467c Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 12:26:05 +0800 Subject: [PATCH 10/19] =?UTF-8?q?fix(mobile):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E7=AB=AF=E5=8F=A3=E4=B8=BA18080?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-mobile/vite.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/healthlink-his-mobile/vite.config.js b/healthlink-his-mobile/vite.config.js index 96b15c88f..8593a080a 100644 --- a/healthlink-his-mobile/vite.config.js +++ b/healthlink-his-mobile/vite.config.js @@ -7,7 +7,7 @@ export default defineConfig({ port: 82, proxy: { '/dev-api': { - target: 'http://localhost:18082/healthlink-his', + target: 'http://localhost:18080/healthlink-his', changeOrigin: true, rewrite: (p) => p.replace(/^\/dev-api/, '') } From 38bc99ee140495a1f4dece350cc1efd293bec66b Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 12:44:43 +0800 Subject: [PATCH 11/19] =?UTF-8?q?fix(mobile):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E7=AB=AF=E6=A0=B8=E5=BF=83=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 getPatientList API 调用正确的患者列表接口 - PatientDetail: Promise.all 并发加载患者信息/医嘱/体征/评估 - 所有页面添加 loading 状态和 ElMessage 错误提示 - 任务完成添加 ElMessageBox 确认对话框 - TaskList 添加刷新按钮 - Mine 退出登录添加确认对话框 --- healthlink-his-mobile/src/api/index.js | 2 + .../src/views/AssessmentForm.vue | 10 +- healthlink-his-mobile/src/views/Mine.vue | 10 +- .../src/views/PatientDetail.vue | 93 ++++++++++++------- .../src/views/PatientList.vue | 34 +++++-- healthlink-his-mobile/src/views/TaskList.vue | 58 +++++++++--- .../src/views/VitalSignEntry.vue | 10 +- 7 files changed, 158 insertions(+), 59 deletions(-) diff --git a/healthlink-his-mobile/src/api/index.js b/healthlink-his-mobile/src/api/index.js index ec4acb680..8f681a093 100644 --- a/healthlink-his-mobile/src/api/index.js +++ b/healthlink-his-mobile/src/api/index.js @@ -19,7 +19,9 @@ request.interceptors.response.use( export const nursingApi = { getTasks: (params) => request.get('/mp/nursing/tasks', { params }), completeTask: (id, data) => request.post(`/mp/nursing/tasks/${id}/complete`, data), + getPatientList: (params) => request.get('/mp/nursing/patient/list', { params }), getPatientInfo: (id) => request.get(`/mp/nursing/patient/${id}`), + getOrders: (patientId) => request.get(`/mp/nursing/orders/${patientId}`), getVitalSigns: (patientId) => request.get(`/mp/nursing/vital-signs/${patientId}`), submitVitalSign: (data) => request.post('/mp/nursing/vital-sign', data), getAssessments: (patientId) => request.get(`/mp/nursing/assessments/${patientId}`), diff --git a/healthlink-his-mobile/src/views/AssessmentForm.vue b/healthlink-his-mobile/src/views/AssessmentForm.vue index cd1c6f8a2..985282289 100644 --- a/healthlink-his-mobile/src/views/AssessmentForm.vue +++ b/healthlink-his-mobile/src/views/AssessmentForm.vue @@ -61,11 +61,19 @@ const riskLevel = computed(() => { }) const riskLevelText = computed(() => ({ HIGH: '高风险', MEDIUM: '中风险', LOW: '低风险' }[riskLevel.value])) +const loading = ref(false) + const submit = async () => { + loading.value = true try { await nursingApi.submitAssessment({ patientId: route.params.patientId, assessmentType: selectedType.value, totalScore: totalScore.value, riskLevel: riskLevel.value, detail: JSON.stringify(formData.value) }) ElMessage.success('评估提交成功') - } catch (e) { ElMessage.error('提交失败') } + } catch (e) { + console.error(e) + ElMessage.error('提交失败') + } finally { + loading.value = false + } } diff --git a/healthlink-his-mobile/src/views/Mine.vue b/healthlink-his-mobile/src/views/Mine.vue index 6a7278902..fecb50355 100644 --- a/healthlink-his-mobile/src/views/Mine.vue +++ b/healthlink-his-mobile/src/views/Mine.vue @@ -17,7 +17,15 @@ From 5b90a6148421986c5bec99a2d782049df253d9fa Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 21:57:43 +0800 Subject: [PATCH 13/19] =?UTF-8?q?fix(mobile):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91=E5=AF=B9=E9=BD=90=E7=8E=B0?= =?UTF-8?q?=E6=9C=89=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-mobile/.gitignore | 1 + healthlink-his-mobile/src/api/index.js | 18 +++---- healthlink-his-mobile/src/router/index.js | 2 +- healthlink-his-mobile/src/views/Login.vue | 61 ++++++++++++----------- healthlink-his-mobile/src/views/Mine.vue | 2 +- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/healthlink-his-mobile/.gitignore b/healthlink-his-mobile/.gitignore index c2458765e..ef1ea7305 100644 --- a/healthlink-his-mobile/.gitignore +++ b/healthlink-his-mobile/.gitignore @@ -3,3 +3,4 @@ dist/ .env.local .env.*.local *.log +package-lock.json diff --git a/healthlink-his-mobile/src/api/index.js b/healthlink-his-mobile/src/api/index.js index 2eb812550..30065b7b6 100644 --- a/healthlink-his-mobile/src/api/index.js +++ b/healthlink-his-mobile/src/api/index.js @@ -7,17 +7,17 @@ const request = axios.create({ }) request.interceptors.request.use(config => { - const token = localStorage.getItem('token') - if (token) config.headers.Authorization = `Bearer ${token}` - const tenantId = localStorage.getItem('tenantId') - if (tenantId) config.headers['tenant-id'] = tenantId + const token = localStorage.getItem('Admin-Token') + if (token && !(config.headers && config.headers.isToken === false)) { + config.headers.Authorization = 'Bearer ' + token + } return config }) request.interceptors.response.use( res => { if (res.data?.code === 401) { - localStorage.removeItem('token') + localStorage.removeItem('Admin-Token') window.location.href = '/login' return Promise.reject(new Error('登录已过期')) } @@ -25,18 +25,16 @@ request.interceptors.response.use( }, err => { if (err.response?.status === 401) { - localStorage.removeItem('token') + localStorage.removeItem('Admin-Token') window.location.href = '/login' } - ElMessage.error(err.response?.data?.msg || '请求失败') return Promise.reject(err) } ) export const authApi = { - login: (data) => request.post('/login', data), - getTenants: () => request.get('/tenant/list'), - logout: () => request.post('/logout') + login: (data) => request.post('/login', data, { headers: { isToken: false } }), + getTenants: (username) => request.get('/system/tenant/user-bind/' + username, { headers: { isToken: false } }) } export const nursingApi = { diff --git a/healthlink-his-mobile/src/router/index.js b/healthlink-his-mobile/src/router/index.js index 4075999db..6d3175a3a 100644 --- a/healthlink-his-mobile/src/router/index.js +++ b/healthlink-his-mobile/src/router/index.js @@ -17,7 +17,7 @@ const router = createRouter({ history: createWebHistory(), routes }) router.beforeEach((to, from, next) => { if (to.meta.requiresAuth) { - const token = localStorage.getItem('token') + const token = localStorage.getItem('Admin-Token') if (!token) { next('/login'); return } } next() diff --git a/healthlink-his-mobile/src/views/Login.vue b/healthlink-his-mobile/src/views/Login.vue index 8ce499626..db00df726 100644 --- a/healthlink-his-mobile/src/views/Login.vue +++ b/healthlink-his-mobile/src/views/Login.vue @@ -7,78 +7,78 @@ diff --git a/healthlink-his-mobile/src/views/Mine.vue b/healthlink-his-mobile/src/views/Mine.vue index fecb50355..100261431 100644 --- a/healthlink-his-mobile/src/views/Mine.vue +++ b/healthlink-his-mobile/src/views/Mine.vue @@ -22,7 +22,7 @@ import { ElMessageBox } from 'element-plus' const logout = async () => { try { await ElMessageBox.confirm('确认退出登录?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消' }) - localStorage.removeItem('token') + localStorage.removeItem('Admin-Token') window.location.href = '/login' } catch {} } From 3bc8a854262f208dea7f70ab5937a0c7d9f25f97 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 21:59:26 +0800 Subject: [PATCH 14/19] =?UTF-8?q?fix(mobile):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=A1=B5=E9=9D=A2=E5=A7=8B=E7=BB=88=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=8C=BB=E9=99=A2=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-mobile/src/views/Login.vue | 38 ++++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/healthlink-his-mobile/src/views/Login.vue b/healthlink-his-mobile/src/views/Login.vue index db00df726..9d1e8fc65 100644 --- a/healthlink-his-mobile/src/views/Login.vue +++ b/healthlink-his-mobile/src/views/Login.vue @@ -2,7 +2,7 @@ @@ -37,24 +41,35 @@ import { authApi } from '../api' const router = useRouter() const loading = ref(false) const errorMsg = ref('') -const tenants = ref([]) -const form = ref({ username: '', password: '', tenantId: null }) +const tenantOptions = ref([]) +const currentTenantName = ref('') +const form = ref({ username: '', password: '', tenantId: '' }) const loadTenants = async () => { if (!form.value.username) return try { const res = await authApi.getTenants(form.value.username) if (res.code === 200 && res.data) { - tenants.value = res.data - if (res.data.length === 1) form.value.tenantId = res.data[0].id + tenantOptions.value = res.data.map(item => ({ + label: item.tenantName, + value: item.id + })) + if (tenantOptions.value.length === 1) { + form.value.tenantId = tenantOptions.value[0].value + currentTenantName.value = tenantOptions.value[0].label + } } } catch (e) { console.error(e) } } +const onTenantChange = () => { + const selected = tenantOptions.value.find(t => t.value === form.value.tenantId) + currentTenantName.value = selected ? selected.label : '' +} + const handleLogin = async () => { if (!form.value.username) { errorMsg.value = '请输入用户名'; return } if (!form.value.password) { errorMsg.value = '请输入密码'; return } - if (!form.value.tenantId && tenants.value.length > 0) { errorMsg.value = '请选择租户'; return } loading.value = true errorMsg.value = '' @@ -85,7 +100,7 @@ const handleLogin = async () => { .login-page { min-height: 100vh; background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%); display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; } .login-header { text-align: center; color: #fff; margin-bottom: 40px; } .logo { font-size: 60px; margin-bottom: 12px; } -.login-header h1 { font-size: 24px; margin: 0; } +.login-header h1 { font-size: 22px; margin: 0; } .login-header p { font-size: 14px; opacity: 0.8; margin-top: 8px; } .login-form { background: #fff; border-radius: 12px; padding: 24px; width: 100%; max-width: 360px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); } .form-item { margin-bottom: 16px; } @@ -96,4 +111,5 @@ select.input { appearance: none; background: #fff url("data:image/svg+xml,%3Csvg .login-btn { width: 100%; padding: 14px; background: #1890ff; color: #fff; border: none; border-radius: 8px; font-size: 18px; font-weight: 600; cursor: pointer; } .login-btn:disabled { background: #91d5ff; } .error-msg { color: #f5222d; text-align: center; margin-top: 12px; font-size: 14px; } +.login-footer { margin-top: 20px; color: rgba(255,255,255,0.6); font-size: 12px; } From 6dc9aaba6c4dbcda3943d4cf715813855c4c3253 Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 22:04:41 +0800 Subject: [PATCH 15/19] =?UTF-8?q?feat(mobile):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E7=AB=AF=E6=8A=A4=E5=A3=AB=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E7=AB=99=20-=20=E5=AE=8C=E6=95=B4=E5=8A=9F=E8=83=BD=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-mobile/.env.development | 11 ++ healthlink-his-mobile/.env.production | 8 ++ healthlink-his-mobile/package.json | 7 +- healthlink-his-mobile/src/api/index.js | 48 ++++---- healthlink-his-mobile/src/main.js | 3 +- healthlink-his-mobile/src/router/index.js | 16 +-- .../src/views/AssessmentForm.vue | 49 +++----- healthlink-his-mobile/src/views/Home.vue | 89 ++++++++++++++ healthlink-his-mobile/src/views/Login.vue | 47 ++------ healthlink-his-mobile/src/views/Mine.vue | 35 +++--- .../src/views/MobileLayout.vue | 15 ++- .../src/views/PatientDetail.vue | 114 +++++++----------- .../src/views/PatientList.vue | 68 +++++------ healthlink-his-mobile/src/views/TaskList.vue | 88 +++++--------- .../src/views/VitalSignEntry.vue | 64 +++++----- healthlink-his-mobile/vite.config.js | 50 +++++--- 16 files changed, 366 insertions(+), 346 deletions(-) create mode 100644 healthlink-his-mobile/.env.development create mode 100644 healthlink-his-mobile/.env.production create mode 100644 healthlink-his-mobile/src/views/Home.vue diff --git a/healthlink-his-mobile/.env.development b/healthlink-his-mobile/.env.development new file mode 100644 index 000000000..390ebe68b --- /dev/null +++ b/healthlink-his-mobile/.env.development @@ -0,0 +1,11 @@ +# 页面标题 +VITE_APP_TITLE = HealthLink移动护理 + +# 开发环境配置 +VITE_APP_ENV = 'development' + +# API地址 +VITE_APP_BASE_API = '/dev-api' + +# 后端代理地址 +VITE_API_PROXY = 'http://localhost:18080/healthlink-his' diff --git a/healthlink-his-mobile/.env.production b/healthlink-his-mobile/.env.production new file mode 100644 index 000000000..8196ebc02 --- /dev/null +++ b/healthlink-his-mobile/.env.production @@ -0,0 +1,8 @@ +# 页面标题 +VITE_APP_TITLE = HealthLink移动护理 + +# 生产环境配置 +VITE_APP_ENV = 'production' + +# API地址 +VITE_APP_BASE_API = '/dev-api' diff --git a/healthlink-his-mobile/package.json b/healthlink-his-mobile/package.json index 207255bf4..59f85826b 100644 --- a/healthlink-his-mobile/package.json +++ b/healthlink-his-mobile/package.json @@ -1,6 +1,7 @@ { "name": "healthlink-his-mobile", "version": "1.0.0", + "type": "module", "description": "HealthLink-HIS 移动护理H5工作站", "scripts": { "dev": "vite", @@ -12,11 +13,13 @@ "dependencies": { "vue": "^3.4.0", "vue-router": "^4.3.0", + "pinia": "^2.1.0", "axios": "^1.7.0", "element-plus": "^2.7.0", - "vxe-table": "^4.7.0", "echarts": "^5.5.0", - "pinia": "^2.1.0" + "js-cookie": "^3.0.5", + "nprogress": "^0.2.0", + "path-to-regexp": "^6.2.0" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.0", diff --git a/healthlink-his-mobile/src/api/index.js b/healthlink-his-mobile/src/api/index.js index 30065b7b6..0e65ca99d 100644 --- a/healthlink-his-mobile/src/api/index.js +++ b/healthlink-his-mobile/src/api/index.js @@ -1,12 +1,12 @@ import axios from 'axios' import { ElMessage } from 'element-plus' -const request = axios.create({ - baseURL: '/dev-api', - timeout: 10000 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API || '/dev-api', + timeout: 30000 }) -request.interceptors.request.use(config => { +service.interceptors.request.use(config => { const token = localStorage.getItem('Admin-Token') if (token && !(config.headers && config.headers.isToken === false)) { config.headers.Authorization = 'Bearer ' + token @@ -14,39 +14,41 @@ request.interceptors.request.use(config => { return config }) -request.interceptors.response.use( - res => { - if (res.data?.code === 401) { +service.interceptors.response.use( + response => { + const res = response.data + if (res.code === 401) { localStorage.removeItem('Admin-Token') window.location.href = '/login' return Promise.reject(new Error('登录已过期')) } - return res.data + return res }, - err => { - if (err.response?.status === 401) { + error => { + if (error.response?.status === 401) { localStorage.removeItem('Admin-Token') window.location.href = '/login' } - return Promise.reject(err) + ElMessage.error(error.response?.data?.msg || '请求失败') + return Promise.reject(error) } ) export const authApi = { - login: (data) => request.post('/login', data, { headers: { isToken: false } }), - getTenants: (username) => request.get('/system/tenant/user-bind/' + username, { headers: { isToken: false } }) + login: (data) => service.post('/login', data, { headers: { isToken: false } }), + getTenants: (username) => service.get('/system/tenant/user-bind/' + username, { headers: { isToken: false } }) } export const nursingApi = { - getTasks: (params) => request.get('/mp/nursing/tasks', { params }), - completeTask: (id, data) => request.post(`/mp/nursing/tasks/${id}/complete`, data), - getPatientInfo: (id) => request.get(`/mp/nursing/patient/${id}`), - getPatientList: (params) => request.get('/mp/nursing/patient/list', { params }), - getOrders: (patientId) => request.get(`/mp/nursing/orders/${patientId}`), - getVitalSigns: (patientId) => request.get(`/mp/nursing/vital-signs/${patientId}`), - submitVitalSign: (data) => request.post('/mp/nursing/vital-sign', data), - getAssessments: (patientId) => request.get(`/mp/nursing/assessments/${patientId}`), - submitAssessment: (data) => request.post('/mp/nursing/assessment', data) + getTasks: (params) => service.get('/mp/nursing/tasks', { params }), + completeTask: (id, data) => service.post(`/mp/nursing/tasks/${id}/complete`, data), + getPatientInfo: (id) => service.get(`/mp/nursing/patient/${id}`), + getPatientList: (params) => service.get('/mp/nursing/patient/list', { params }), + getOrders: (patientId) => service.get(`/mp/nursing/orders/${patientId}`), + getVitalSigns: (patientId) => service.get(`/mp/nursing/vital-signs/${patientId}`), + submitVitalSign: (data) => service.post('/mp/nursing/vital-sign', data), + getAssessments: (patientId) => service.get(`/mp/nursing/assessments/${patientId}`), + submitAssessment: (data) => service.post('/mp/nursing/assessment', data) } -export default request +export default service diff --git a/healthlink-his-mobile/src/main.js b/healthlink-his-mobile/src/main.js index 2df3d027e..99de3f024 100644 --- a/healthlink-his-mobile/src/main.js +++ b/healthlink-his-mobile/src/main.js @@ -1,6 +1,7 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import ElementPlus from 'element-plus' +import zhCn from 'element-plus/dist/locale/zh-cn.mjs' import 'element-plus/dist/index.css' import App from './App.vue' import router from './router' @@ -9,5 +10,5 @@ import './styles/mobile.css' const app = createApp(App) app.use(createPinia()) app.use(router) -app.use(ElementPlus, { size: 'large' }) +app.use(ElementPlus, { size: 'large', locale: zhCn }) app.mount('#app') diff --git a/healthlink-his-mobile/src/router/index.js b/healthlink-his-mobile/src/router/index.js index 6d3175a3a..463e9d8a2 100644 --- a/healthlink-his-mobile/src/router/index.js +++ b/healthlink-his-mobile/src/router/index.js @@ -2,25 +2,21 @@ import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/login', component: () => import('../views/Login.vue'), meta: { title: '登录' } }, - { path: '/', redirect: '/mobile/tasks' }, + { path: '/', redirect: '/mobile/home' }, { path: '/mobile', component: () => import('../views/MobileLayout.vue'), meta: { requiresAuth: true }, children: [ - { path: 'tasks', component: () => import('../views/TaskList.vue'), meta: { title: '任务' } }, - { path: 'patients', component: () => import('../views/PatientList.vue'), meta: { title: '患者' } }, + { path: 'home', component: () => import('../views/Home.vue'), meta: { title: '首页' } }, + { path: 'tasks', component: () => import('../views/TaskList.vue'), meta: { title: '任务列表' } }, + { path: 'patients', component: () => import('../views/PatientList.vue'), meta: { title: '患者列表' } }, { path: 'patient-detail/:id', component: () => import('../views/PatientDetail.vue'), meta: { title: '患者详情' } }, - { path: 'vital-entry/:patientId', component: () => import('../views/VitalSignEntry.vue'), meta: { title: '生命体征' } }, + { path: 'vital-entry/:patientId', component: () => import('../views/VitalSignEntry.vue'), meta: { title: '生命体征录入' } }, { path: 'assessment/:patientId', component: () => import('../views/AssessmentForm.vue'), meta: { title: '护理评估' } }, { path: 'mine', component: () => import('../views/Mine.vue'), meta: { title: '我的' } } ]} ] const router = createRouter({ history: createWebHistory(), routes }) - router.beforeEach((to, from, next) => { - if (to.meta.requiresAuth) { - const token = localStorage.getItem('Admin-Token') - if (!token) { next('/login'); return } - } + if (to.meta.requiresAuth && !localStorage.getItem('Admin-Token')) { next('/login'); return } next() }) - export default router diff --git a/healthlink-his-mobile/src/views/AssessmentForm.vue b/healthlink-his-mobile/src/views/AssessmentForm.vue index 985282289..e52853aaf 100644 --- a/healthlink-his-mobile/src/views/AssessmentForm.vue +++ b/healthlink-his-mobile/src/views/AssessmentForm.vue @@ -2,24 +2,18 @@
-
{{ type.icon }}
-
{{ type.name }}
+
{{ type.icon }}
{{ type.name }}
{{ item.label }}
- - {{ opt.label }} ({{ opt.score }}分) - + {{ opt.label }} ({{ opt.score }}分)
-
-
总分: {{ totalScore }}
-
{{ riskLevelText }}
-
- +
总分: {{ totalScore }}
{{ riskLevelText }}
+
@@ -27,11 +21,12 @@ diff --git a/healthlink-his-mobile/src/views/Home.vue b/healthlink-his-mobile/src/views/Home.vue new file mode 100644 index 000000000..78bf16d8f --- /dev/null +++ b/healthlink-his-mobile/src/views/Home.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/healthlink-his-mobile/src/views/Login.vue b/healthlink-his-mobile/src/views/Login.vue index 9d1e8fc65..227e58bfc 100644 --- a/healthlink-his-mobile/src/views/Login.vue +++ b/healthlink-his-mobile/src/views/Login.vue @@ -3,7 +3,7 @@ - @@ -50,14 +45,8 @@ const loadTenants = async () => { try { const res = await authApi.getTenants(form.value.username) if (res.code === 200 && res.data) { - tenantOptions.value = res.data.map(item => ({ - label: item.tenantName, - value: item.id - })) - if (tenantOptions.value.length === 1) { - form.value.tenantId = tenantOptions.value[0].value - currentTenantName.value = tenantOptions.value[0].label - } + tenantOptions.value = res.data.map(item => ({ label: item.tenantName, value: item.id })) + if (tenantOptions.value.length === 1) { form.value.tenantId = tenantOptions.value[0].value; currentTenantName.value = tenantOptions.value[0].label } } } catch (e) { console.error(e) } } @@ -70,29 +59,12 @@ const onTenantChange = () => { const handleLogin = async () => { if (!form.value.username) { errorMsg.value = '请输入用户名'; return } if (!form.value.password) { errorMsg.value = '请输入密码'; return } - - loading.value = true - errorMsg.value = '' + loading.value = true; errorMsg.value = '' try { - const res = await authApi.login({ - username: form.value.username, - password: form.value.password, - tenantId: form.value.tenantId, - code: '', - uuid: '' - }) - if (res.code === 200 && res.token) { - localStorage.setItem('Admin-Token', res.token) - ElMessage.success('登录成功') - router.push('/mobile/tasks') - } else { - errorMsg.value = res.msg || '登录失败' - } - } catch (e) { - errorMsg.value = e.response?.data?.msg || '登录失败,请检查网络' - } finally { - loading.value = false - } + const res = await authApi.login({ username: form.value.username, password: form.value.password, tenantId: form.value.tenantId, code: '', uuid: '' }) + if (res.code === 200 && res.token) { localStorage.setItem('Admin-Token', res.token); ElMessage.success('登录成功'); router.push('/mobile/home') } + else { errorMsg.value = res.msg || '登录失败' } + } catch (e) { errorMsg.value = e.response?.data?.msg || '登录失败' } finally { loading.value = false } } @@ -111,5 +83,4 @@ select.input { appearance: none; background: #fff url("data:image/svg+xml,%3Csvg .login-btn { width: 100%; padding: 14px; background: #1890ff; color: #fff; border: none; border-radius: 8px; font-size: 18px; font-weight: 600; cursor: pointer; } .login-btn:disabled { background: #91d5ff; } .error-msg { color: #f5222d; text-align: center; margin-top: 12px; font-size: 14px; } -.login-footer { margin-top: 20px; color: rgba(255,255,255,0.6); font-size: 12px; } diff --git a/healthlink-his-mobile/src/views/Mine.vue b/healthlink-his-mobile/src/views/Mine.vue index 100261431..93015e791 100644 --- a/healthlink-his-mobile/src/views/Mine.vue +++ b/healthlink-his-mobile/src/views/Mine.vue @@ -1,39 +1,42 @@ diff --git a/healthlink-his-mobile/src/views/PatientList.vue b/healthlink-his-mobile/src/views/PatientList.vue index 23e47b421..09b03ca2e 100644 --- a/healthlink-his-mobile/src/views/PatientList.vue +++ b/healthlink-his-mobile/src/views/PatientList.vue @@ -1,61 +1,49 @@ diff --git a/healthlink-his-mobile/src/views/TaskList.vue b/healthlink-his-mobile/src/views/TaskList.vue index 80ffe1fa6..439320d7e 100644 --- a/healthlink-his-mobile/src/views/TaskList.vue +++ b/healthlink-his-mobile/src/views/TaskList.vue @@ -1,57 +1,36 @@ diff --git a/healthlink-his-mobile/vite.config.js b/healthlink-his-mobile/vite.config.js index 8593a080a..79464f8d7 100644 --- a/healthlink-his-mobile/vite.config.js +++ b/healthlink-his-mobile/vite.config.js @@ -1,20 +1,42 @@ -import { defineConfig } from 'vite' +import { defineConfig, loadEnv } from 'vite' +import path from 'path' import vue from '@vitejs/plugin-vue' -export default defineConfig({ - plugins: [vue()], - server: { - port: 82, - proxy: { - '/dev-api': { - target: 'http://localhost:18080/healthlink-his', - changeOrigin: true, - rewrite: (p) => p.replace(/^\/dev-api/, '') +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd()) + return { + base: '/', + plugins: [vue()], + resolve: { + alias: { + '~': path.resolve(__dirname, './'), + '@': path.resolve(__dirname, './src') + }, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + server: { + port: 82, + host: true, + proxy: { + '/dev-api': { + target: env.VITE_API_PROXY || 'http://localhost:18080/healthlink-his', + changeOrigin: true, + rewrite: (p) => p.replace(/^\/dev-api/, '') + } + } + }, + build: { + outDir: 'dist', + assetsDir: 'assets', + cssMinify: 'esbuild' + }, + css: { + preprocessorOptions: { + scss: { + api: 'modern-compiler', + silenceDeprecations: ['import', 'global-builtin', 'color-functions', 'legacy-js-api'] + } } } - }, - build: { - outDir: 'dist', - assetsDir: 'assets' } }) From 7d9da53cc46f50904f45c935b928512b624c0bcd Mon Sep 17 00:00:00 2001 From: chenqi Date: Fri, 19 Jun 2026 22:37:35 +0800 Subject: [PATCH 16/19] =?UTF-8?q?fix(mobile):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E7=AB=AFAPI=E5=AF=B9=E6=8E=A5=20-=20?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=8E=B0=E6=9C=89=E6=8A=A4=E5=A3=AB=E7=AB=99?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3+=E7=99=BB=E5=BD=95=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- healthlink-his-mobile/src/api/index.js | 24 ++++++++------- healthlink-his-mobile/src/views/Home.vue | 20 +++++++------ healthlink-his-mobile/src/views/Login.vue | 31 +++++++++++++++++--- healthlink-his-mobile/src/views/TaskList.vue | 28 ++++++++++-------- 4 files changed, 66 insertions(+), 37 deletions(-) diff --git a/healthlink-his-mobile/src/api/index.js b/healthlink-his-mobile/src/api/index.js index 0e65ca99d..769ede312 100644 --- a/healthlink-his-mobile/src/api/index.js +++ b/healthlink-his-mobile/src/api/index.js @@ -19,6 +19,7 @@ service.interceptors.response.use( const res = response.data if (res.code === 401) { localStorage.removeItem('Admin-Token') + localStorage.removeItem('userInfo') window.location.href = '/login' return Promise.reject(new Error('登录已过期')) } @@ -27,28 +28,29 @@ service.interceptors.response.use( error => { if (error.response?.status === 401) { localStorage.removeItem('Admin-Token') + localStorage.removeItem('userInfo') window.location.href = '/login' } - ElMessage.error(error.response?.data?.msg || '请求失败') return Promise.reject(error) } ) export const authApi = { login: (data) => service.post('/login', data, { headers: { isToken: false } }), - getTenants: (username) => service.get('/system/tenant/user-bind/' + username, { headers: { isToken: false } }) + getTenants: (username) => service.get('/system/tenant/user-bind/' + username, { headers: { isToken: false } }), + getInfo: () => service.get('/getInfo') } export const nursingApi = { - getTasks: (params) => service.get('/mp/nursing/tasks', { params }), - completeTask: (id, data) => service.post(`/mp/nursing/tasks/${id}/complete`, data), - getPatientInfo: (id) => service.get(`/mp/nursing/patient/${id}`), - getPatientList: (params) => service.get('/mp/nursing/patient/list', { params }), - getOrders: (patientId) => service.get(`/mp/nursing/orders/${patientId}`), - getVitalSigns: (patientId) => service.get(`/mp/nursing/vital-signs/${patientId}`), - submitVitalSign: (data) => service.post('/mp/nursing/vital-sign', data), - getAssessments: (patientId) => service.get(`/mp/nursing/assessments/${patientId}`), - submitAssessment: (data) => service.post('/mp/nursing/assessment', data) + getTasks: (params) => service.get('/nurse-station/advice-process/page', { params }), + completeTask: (id, data) => service.post(`/nurse-station/advice-process/execute`, data), + getPatientInfo: (id) => service.get('/inpatientmanage/inhospitalregister/' + id), + getPatientList: (params) => service.get('/inpatientmanage/inhospitalregister/list', { params }), + getOrders: (encounterId) => service.get('/nurse-station/advice-process/page', { params: { encounterId } }), + getVitalSigns: (patientId) => service.get('/nursing/vital-signs/' + patientId), + submitVitalSign: (data) => service.post('/nursing/vital-sign', data), + getAssessments: (encounterId) => service.get('/nursing/assessment/encounter/' + encounterId), + submitAssessment: (data) => service.post('/nursing/assessment', data) } export default service diff --git a/healthlink-his-mobile/src/views/Home.vue b/healthlink-his-mobile/src/views/Home.vue index 78bf16d8f..c27755a03 100644 --- a/healthlink-his-mobile/src/views/Home.vue +++ b/healthlink-his-mobile/src/views/Home.vue @@ -3,7 +3,7 @@
@@ -24,8 +24,8 @@
待办任务查看全部
-
-
{{ task.patientName }} - {{ task.taskContent }}
{{ task.dueTime }}
+
+
{{ task.adviceName || task.taskContent || '医嘱任务' }}
{{ task.createTime || '' }}
暂无待办任务
@@ -37,7 +37,7 @@ import { ref, onMounted } from 'vue' import { nursingApi } from '../api' const userInfo = ref({}) -const stats = ref([{ label: '待执行医嘱', value: 0 }, { label: '待测体征', value: 0 }, { label: '待评估', value: 0 }, { label: '高风险患者', value: 0 }]) +const stats = ref([{ label: '待执行医嘱', value: 0 }, { label: '今日体征', value: 0 }, { label: '待评估', value: 0 }, { label: '高风险', value: 0 }]) const recentTasks = ref([]) const actions = [ { icon: '📋', label: '任务列表', path: '/mobile/tasks', color: '#1890ff' }, @@ -51,10 +51,13 @@ const actions = [ onMounted(async () => { try { const info = localStorage.getItem('userInfo'); if (info) userInfo.value = JSON.parse(info) } catch {} try { - const res = await nursingApi.getTasks({ status: 'PENDING' }) - if (res.code === 200) { - recentTasks.value = (res.data?.tasks || []).slice(0, 5) - stats.value[0].value = res.data?.summary?.pending || 0 + const nurseId = userInfo.value.practitionerId || userInfo.value.userId + if (nurseId) { + const res = await nursingApi.getTasks({ nurseId: nurseId }) + if (res.code === 200) { + recentTasks.value = (res.data?.records || res.data?.rows || res.data || []).slice(0, 5) + stats.value[0].value = res.data?.total || recentTasks.value.length + } } } catch {} }) @@ -82,7 +85,6 @@ onMounted(async () => { .more { color: #1890ff; font-size: 13px; } .task-item { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid #f5f5f5; } .task-dot { width: 8px; height: 8px; border-radius: 50%; background: #fa8c16; } -.task-dot.COMPLETED { background: #52c41a; } .task-name { font-size: 14px; } .task-time { font-size: 12px; color: #999; } .empty { text-align: center; padding: 20px; color: #999; } diff --git a/healthlink-his-mobile/src/views/Login.vue b/healthlink-his-mobile/src/views/Login.vue index 227e58bfc..88eb71966 100644 --- a/healthlink-his-mobile/src/views/Login.vue +++ b/healthlink-his-mobile/src/views/Login.vue @@ -61,10 +61,33 @@ const handleLogin = async () => { if (!form.value.password) { errorMsg.value = '请输入密码'; return } loading.value = true; errorMsg.value = '' try { - const res = await authApi.login({ username: form.value.username, password: form.value.password, tenantId: form.value.tenantId, code: '', uuid: '' }) - if (res.code === 200 && res.token) { localStorage.setItem('Admin-Token', res.token); ElMessage.success('登录成功'); router.push('/mobile/home') } - else { errorMsg.value = res.msg || '登录失败' } - } catch (e) { errorMsg.value = e.response?.data?.msg || '登录失败' } finally { loading.value = false } + const loginRes = await authApi.login({ username: form.value.username, password: form.value.password, tenantId: form.value.tenantId, code: '', uuid: '' }) + if (loginRes.code === 200 && loginRes.token) { + localStorage.setItem('Admin-Token', loginRes.token) + const infoRes = await authApi.getInfo() + if (infoRes.code === 200) { + const user = infoRes.user || {} + localStorage.setItem('userInfo', JSON.stringify({ + userId: user.userId, + userName: user.userName, + nickName: user.nickName, + practitionerId: user.practitionerId, + orgId: user.orgId, + orgName: user.orgName, + roles: user.roles, + permissions: user.permissions + })) + } + ElMessage.success('登录成功') + router.push('/mobile/home') + } else { + errorMsg.value = loginRes.msg || '登录失败' + } + } catch (e) { + errorMsg.value = e.response?.data?.msg || '登录失败,请检查网络' + } finally { + loading.value = false + } } diff --git a/healthlink-his-mobile/src/views/TaskList.vue b/healthlink-his-mobile/src/views/TaskList.vue index 439320d7e..4ca1fa8a0 100644 --- a/healthlink-his-mobile/src/views/TaskList.vue +++ b/healthlink-his-mobile/src/views/TaskList.vue @@ -1,17 +1,16 @@