Przeglądaj źródła

feat: 体检报告&指标模块

xdd 2 tygodni temu
rodzic
commit
ac5508f578
24 zmienionych plików z 2001 dodań i 0 usunięć
  1. 51 0
      fs-service/src/main/java/com/fs/medical/domain/MeasurementUnit.java
  2. 79 0
      fs-service/src/main/java/com/fs/medical/domain/MedicalIndicator.java
  3. 74 0
      fs-service/src/main/java/com/fs/medical/domain/PhysicalExamReport.java
  4. 69 0
      fs-service/src/main/java/com/fs/medical/domain/ReportIndicatorResult.java
  5. 93 0
      fs-service/src/main/java/com/fs/medical/mapper/MeasurementUnitMapper.java
  6. 108 0
      fs-service/src/main/java/com/fs/medical/mapper/MedicalIndicatorMapper.java
  7. 125 0
      fs-service/src/main/java/com/fs/medical/mapper/PhysicalExamReportMapper.java
  8. 104 0
      fs-service/src/main/java/com/fs/medical/mapper/ReportIndicatorResultMapper.java
  9. 40 0
      fs-service/src/main/java/com/fs/medical/param/MeasurementUnitQueryDto.java
  10. 50 0
      fs-service/src/main/java/com/fs/medical/param/MedicalIndicatorQueryDto.java
  11. 72 0
      fs-service/src/main/java/com/fs/medical/param/PhysicalExamReportQueryDto.java
  12. 50 0
      fs-service/src/main/java/com/fs/medical/param/ReportIndicatorResultQueryDto.java
  13. 46 0
      fs-service/src/main/java/com/fs/medical/service/MeasurementUnitService.java
  14. 46 0
      fs-service/src/main/java/com/fs/medical/service/MedicalIndicatorService.java
  15. 47 0
      fs-service/src/main/java/com/fs/medical/service/PhysicalExamReportService.java
  16. 46 0
      fs-service/src/main/java/com/fs/medical/service/ReportIndicatorResultService.java
  17. 55 0
      fs-service/src/main/java/com/fs/medical/service/impl/MeasurementUnitServiceImpl.java
  18. 55 0
      fs-service/src/main/java/com/fs/medical/service/impl/MedicalIndicatorServiceImpl.java
  19. 56 0
      fs-service/src/main/java/com/fs/medical/service/impl/PhysicalExamReportServiceImpl.java
  20. 55 0
      fs-service/src/main/java/com/fs/medical/service/impl/ReportIndicatorResultServiceImpl.java
  21. 160 0
      fs-user-app/src/main/java/com/fs/app/controller/medical/MeasurementUnitController.java
  22. 162 0
      fs-user-app/src/main/java/com/fs/app/controller/medical/MedicalIndicatorController.java
  23. 168 0
      fs-user-app/src/main/java/com/fs/app/controller/medical/PhysicalExamReportController.java
  24. 190 0
      fs-user-app/src/main/java/com/fs/app/controller/medical/ReportIndicatorResultController.java

+ 51 - 0
fs-service/src/main/java/com/fs/medical/domain/MeasurementUnit.java

@@ -0,0 +1,51 @@
+package com.fs.medical.domain;
+
+import lombok.Data;
+import java.time.LocalDateTime;
+
+/**
+ * 计量单位表
+ */
+@Data
+public class MeasurementUnit {
+
+    /**
+     * 单位ID
+     */
+    private Long unitId;
+
+    /**
+     * 单位名称
+     */
+    private String unitName;
+
+    /**
+     * 单位符号
+     */
+    private String unitSymbol;
+
+    /**
+     * 单位类型
+     */
+    private String unitType;
+
+    /**
+     * 描述
+     */
+    private String description;
+
+    /**
+     * 状态(1:启用,0:禁用)
+     */
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 79 - 0
fs-service/src/main/java/com/fs/medical/domain/MedicalIndicator.java

@@ -0,0 +1,79 @@
+package com.fs.medical.domain;
+
+import lombok.Data;
+import lombok.AllArgsConstructor;
+import lombok.NoArgsConstructor;
+import lombok.Builder;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 指标表
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MedicalIndicator {
+
+    /**
+     * 指标ID
+     */
+    private Long indicatorId;
+
+    /**
+     * 指标名称
+     */
+    private String indicatorName;
+
+    /**
+     * 指标分类(用户自设,通用指标,查体,肝脏)
+     */
+    private String indicatorCategory;
+
+    /**
+     * 单位ID
+     */
+    private Long unitId;
+
+    /**
+     * 参考范围最小值
+     */
+    private BigDecimal referenceMin;
+
+    /**
+     * 参考范围最大值
+     */
+    private BigDecimal referenceMax;
+
+    /**
+     * 文本形式参考范围
+     */
+    private String referenceText;
+
+    /**
+     * 指标描述
+     */
+    private String description;
+
+    /**
+     * 排序字段
+     */
+    private Integer sortOrder;
+
+    /**
+     * 状态(1:启用,0:禁用)
+     */
+    private Byte status;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+}

+ 74 - 0
fs-service/src/main/java/com/fs/medical/domain/PhysicalExamReport.java

@@ -0,0 +1,74 @@
+package com.fs.medical.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 体检报告表
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PhysicalExamReport {
+
+    /**
+     * 报告ID
+     */
+    private Long reportId;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 体检日期
+     */
+    private LocalDate examDate;
+
+    /**
+     * 身高(cm)
+     */
+    private BigDecimal height;
+
+    /**
+     * 体重(kg)
+     */
+    private BigDecimal weight;
+
+    /**
+     * BMI指数
+     */
+    private BigDecimal bmi;
+
+    /**
+     * 腰围(cm)
+     */
+    private BigDecimal waistCircumference;
+
+    /**
+     * 胸围(cm)
+     */
+    private BigDecimal chestCircumference;
+
+    /**
+     * 报告状态(1:正常,0:删除)
+     */
+    private Integer reportStatus;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 69 - 0
fs-service/src/main/java/com/fs/medical/domain/ReportIndicatorResult.java

@@ -0,0 +1,69 @@
+package com.fs.medical.domain;
+
+import lombok.Data;
+import lombok.Builder;
+import lombok.NoArgsConstructor;
+import lombok.AllArgsConstructor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 报告指标检查表
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class ReportIndicatorResult {
+
+    /**
+     * 检查结果ID
+     */
+    private Long resultId;
+
+    /**
+     * 报告ID
+     */
+    private Long reportId;
+
+    /**
+     * 指标ID
+     */
+    private Long indicatorId;
+
+    /**
+     * 检查数值
+     */
+    private BigDecimal testValue;
+
+    /**
+     * 检查结果(文本)
+     */
+    private String testResult;
+
+    /**
+     * 是否异常(0:正常,1:异常)
+     */
+    private Integer isAbnormal;
+
+    /**
+     * 异常类型(偏高,偏低)
+     */
+    private String abnormalType;
+
+    /**
+     * 备注
+     */
+    private String remarks;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 更新时间
+     */
+    private LocalDateTime updateTime;
+}

+ 93 - 0
fs-service/src/main/java/com/fs/medical/mapper/MeasurementUnitMapper.java

@@ -0,0 +1,93 @@
+package com.fs.medical.mapper;
+
+import com.fs.medical.domain.MeasurementUnit;
+import com.fs.medical.param.MeasurementUnitQueryDto;
+import org.apache.ibatis.annotations.*;
+import java.util.List;
+
+/**
+ * 计量单位表Mapper
+ */
+@Mapper
+public interface MeasurementUnitMapper {
+
+    /**
+     * 根据ID查询计量单位
+     */
+    @Select("SELECT * FROM fs_measurement_unit WHERE unit_id = #{unitId}")
+    MeasurementUnit getById(@Param("unitId") Long unitId);
+
+    /**
+     * 查询所有计量单位
+     */
+    @Select("SELECT * FROM fs_measurement_unit")
+    List<MeasurementUnit> listAll();
+
+    /**
+     * 根据单位类型查询计量单位
+     */
+    @Select("SELECT * FROM fs_measurement_unit WHERE unit_type = #{unitType} AND status = 1")
+    List<MeasurementUnit> listByType(@Param("unitType") String unitType);
+
+    /**
+     * 新增计量单位
+     */
+    @Insert("INSERT INTO fs_measurement_unit(unit_name, unit_symbol, unit_type, description, status) " +
+            "VALUES(#{unitName}, #{unitSymbol}, #{unitType}, #{description}, #{status})")
+    @Options(useGeneratedKeys = true, keyProperty = "unitId")
+    int insert(MeasurementUnit unit);
+
+    /**
+     * 更新计量单位
+     */
+    @Update("<script>" +
+            "UPDATE fs_measurement_unit SET " +
+            "<if test='unitName != null'>unit_name = #{unitName}, </if>" +
+            "<if test='unitSymbol != null'>unit_symbol = #{unitSymbol}, </if>" +
+            "<if test='unitType != null'>unit_type = #{unitType}, </if>" +
+            "<if test='description != null'>description = #{description}, </if>" +
+            "<if test='status != null'>status = #{status}, </if>" +
+            "update_time = NOW() " +
+            "WHERE unit_id = #{unitId}" +
+            "</script>")
+    int update(MeasurementUnit unit);
+
+    /**
+     * 根据ID删除计量单位
+     */
+    @Update("UPDATE fs_measurement_unit SET status = 0 WHERE unit_id = #{unitId}")
+    int deleteById(@Param("unitId") Long unitId);
+
+    /**
+     * 分页查询计量单位列表
+     */
+    @Select("<script>" +
+            "SELECT * FROM fs_measurement_unit " +
+            "WHERE 1=1 " +
+            "<if test='unitName != null and unitName != \"\"'>" +
+            "AND unit_name LIKE CONCAT('%', #{unitName}, '%') " +
+            "</if>" +
+            "<if test='unitSymbol != null and unitSymbol != \"\"'>" +
+            "AND unit_symbol LIKE CONCAT('%', #{unitSymbol}, '%') " +
+            "</if>" +
+            "<if test='unitType != null and unitType != \"\"'>" +
+            "AND unit_type LIKE CONCAT('%', #{unitType}, '%') " +
+            "</if>" +
+            "<if test='description != null and description != \"\"'>" +
+            "AND description LIKE CONCAT('%', #{description}, '%') " +
+            "</if>" +
+            "<if test='status != null'>" +
+            "AND status = #{status} " +
+            "</if>" +
+            "ORDER BY " +
+            "<choose>" +
+            "<when test='orderBy != null and orderBy != \"\"'>" +
+            "${orderBy}" +
+            "</when>" +
+            "<otherwise>" +
+            "create_time DESC" +
+            "</otherwise>" +
+            "</choose>" +
+            "</script>")
+    List<MeasurementUnit> selectPageList(MeasurementUnitQueryDto queryDto);
+}

+ 108 - 0
fs-service/src/main/java/com/fs/medical/mapper/MedicalIndicatorMapper.java

@@ -0,0 +1,108 @@
+package com.fs.medical.mapper;
+
+import com.fs.medical.domain.MedicalIndicator;
+import com.fs.medical.param.MedicalIndicatorQueryDto;
+import org.apache.ibatis.annotations.*;
+import java.util.List;
+
+/**
+ * 指标表Mapper
+ */
+@Mapper
+public interface MedicalIndicatorMapper {
+
+    /**
+     * 根据ID查询指标
+     */
+    @Select("SELECT * FROM fs_medical_indicator WHERE indicator_id = #{indicatorId}")
+    MedicalIndicator getById(@Param("indicatorId") Long indicatorId);
+
+    /**
+     * 查询所有启用的指标
+     */
+    @Select("SELECT * FROM fs_medical_indicator WHERE status = 1 ORDER BY sort_order")
+    List<MedicalIndicator> listAllEnabled();
+
+    /**
+     * 根据分类查询指标
+     */
+    @Select("SELECT * FROM fs_medical_indicator WHERE indicator_category = #{category} AND status = 1 ORDER BY sort_order")
+    List<MedicalIndicator> listByCategory(@Param("category") String category);
+
+    /**
+     * 新增指标
+     */
+    @Insert("INSERT INTO fs_medical_indicator(indicator_name, indicator_category, unit_id, reference_min, " +
+            "reference_max, reference_text, description, sort_order, status) " +
+            "VALUES(#{indicatorName}, #{indicatorCategory}, #{unitId}, #{referenceMin}, " +
+            "#{referenceMax}, #{referenceText}, #{description}, #{sortOrder}, #{status})")
+    @Options(useGeneratedKeys = true, keyProperty = "indicatorId")
+    int insert(MedicalIndicator indicator);
+
+    /**
+     * 更新指标
+     */
+    @Update("<script>" +
+            "UPDATE fs_medical_indicator SET " +
+            "<if test='indicatorName != null'>indicator_name = #{indicatorName}, </if>" +
+            "<if test='indicatorCategory != null'>indicator_category = #{indicatorCategory}, </if>" +
+            "<if test='unitId != null'>unit_id = #{unitId}, </if>" +
+            "<if test='referenceMin != null'>reference_min = #{referenceMin}, </if>" +
+            "<if test='referenceMax != null'>reference_max = #{referenceMax}, </if>" +
+            "<if test='referenceText != null'>reference_text = #{referenceText}, </if>" +
+            "<if test='description != null'>description = #{description}, </if>" +
+            "<if test='sortOrder != null'>sort_order = #{sortOrder}, </if>" +
+            "<if test='status != null'>status = #{status}, </if>" +
+            "update_time = NOW() " +
+            "WHERE indicator_id = #{indicatorId}" +
+            "</script>")
+    int update(MedicalIndicator indicator);
+
+    /**
+     * 根据ID删除指标(逻辑删除)
+     */
+    @Update("UPDATE fs_medical_indicator SET status = 0, update_time = NOW() WHERE indicator_id = #{indicatorId}")
+    int deleteById(@Param("indicatorId") Long indicatorId);
+
+    /**
+     * 分页查询医疗指标列表
+     */
+    @Select("<script>" +
+            "SELECT * FROM fs_medical_indicator " +
+            "WHERE 1=1 " +
+            "<if test='indicatorName != null and indicatorName != \"\"'>" +
+            "AND indicator_name LIKE CONCAT('%', #{indicatorName}, '%') " +
+            "</if>" +
+            "<if test='indicatorCategory != null and indicatorCategory != \"\"'>" +
+            "AND indicator_category LIKE CONCAT('%', #{indicatorCategory}, '%') " +
+            "</if>" +
+            "<if test='unitId != null'>" +
+            "AND unit_id = #{unitId} " +
+            "</if>" +
+            "<if test='referenceMin != null'>" +
+            "AND reference_min >= #{referenceMin} " +
+            "</if>" +
+            "<if test='referenceMax != null'>" +
+            "AND reference_max <= #{referenceMax} " +
+            "</if>" +
+            "<if test='referenceText != null and referenceText != \"\"'>" +
+            "AND reference_text LIKE CONCAT('%', #{referenceText}, '%') " +
+            "</if>" +
+            "<if test='description != null and description != \"\"'>" +
+            "AND description LIKE CONCAT('%', #{description}, '%') " +
+            "</if>" +
+            "<if test='status != null'>" +
+            "AND status = #{status} " +
+            "</if>" +
+            "ORDER BY " +
+            "<choose>" +
+            "<when test='orderBy != null and orderBy != \"\"'>" +
+            "${orderBy}" +
+            "</when>" +
+            "<otherwise>" +
+            "sort_order ASC, create_time DESC" +
+            "</otherwise>" +
+            "</choose>" +
+            "</script>")
+    List<MedicalIndicator> selectPageList(MedicalIndicatorQueryDto queryDto);
+}

+ 125 - 0
fs-service/src/main/java/com/fs/medical/mapper/PhysicalExamReportMapper.java

@@ -0,0 +1,125 @@
+package com.fs.medical.mapper;
+
+import com.fs.medical.domain.PhysicalExamReport;
+import com.fs.medical.param.PhysicalExamReportQueryDto;
+import org.apache.ibatis.annotations.*;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 体检报告表Mapper
+ */
+@Mapper
+public interface PhysicalExamReportMapper {
+
+    /**
+     * 根据ID查询体检报告
+     */
+    @Select("SELECT * FROM fs_physical_exam_report WHERE report_id = #{reportId} AND report_status = 1")
+    PhysicalExamReport getById(@Param("reportId") Long reportId);
+
+    /**
+     * 根据用户ID查询体检报告列表
+     */
+    @Select("SELECT * FROM fs_physical_exam_report WHERE user_id = #{userId} AND report_status = 1 ORDER BY exam_date DESC")
+    List<PhysicalExamReport> listByUserId(@Param("userId") Long userId);
+
+    /**
+     * 根据用户ID和体检日期查询体检报告
+     */
+    @Select("SELECT * FROM fs_physical_exam_report WHERE user_id = #{userId} AND exam_date = #{examDate} AND report_status = 1")
+    PhysicalExamReport getByUserIdAndDate(@Param("userId") Long userId, @Param("examDate") Date examDate);
+
+    /**
+     * 新增体检报告
+     */
+    @Insert("INSERT INTO fs_physical_exam_report(user_id, exam_date, height, weight, bmi, waist_circumference, " +
+            "chest_circumference, report_status) VALUES(#{userId}, #{examDate}, #{height}, #{weight}, #{bmi}, " +
+            "#{waistCircumference}, #{chestCircumference}, #{reportStatus})")
+    @Options(useGeneratedKeys = true, keyProperty = "reportId")
+    int insert(PhysicalExamReport report);
+
+    /**
+     * 更新体检报告
+     */
+    @Update("<script>" +
+            "UPDATE fs_physical_exam_report SET " +
+            "<if test='userId != null'>user_id = #{userId}, </if>" +
+            "<if test='examDate != null'>exam_date = #{examDate}, </if>" +
+            "<if test='height != null'>height = #{height}, </if>" +
+            "<if test='weight != null'>weight = #{weight}, </if>" +
+            "<if test='bmi != null'>bmi = #{bmi}, </if>" +
+            "<if test='waistCircumference != null'>waist_circumference = #{waistCircumference}, </if>" +
+            "<if test='chestCircumference != null'>chest_circumference = #{chestCircumference}, </if>" +
+            "<if test='reportStatus != null'>report_status = #{reportStatus}, </if>" +
+            "update_time = NOW() " +
+            "WHERE report_id = #{reportId}" +
+            "</script>")
+    int update(PhysicalExamReport report);
+
+    /**
+     * 根据ID删除体检报告(逻辑删除)
+     */
+    @Update("UPDATE fs_physical_exam_report SET report_status = 0, update_time = NOW() WHERE report_id = #{reportId}")
+    int deleteById(@Param("reportId") Long reportId);
+
+    /**
+     * 分页查询体检报告列表
+     */
+    @Select("<script>" +
+            "SELECT * FROM fs_physical_exam_report " +
+            "WHERE 1=1 " +
+            "<if test='userId != null'>" +
+            "AND user_id = #{userId} " +
+            "</if>" +
+            "<if test='examDateStart != null'>" +
+            "AND exam_date >= #{examDateStart} " +
+            "</if>" +
+            "<if test='examDateEnd != null'>" +
+            "AND exam_date <= #{examDateEnd} " +
+            "</if>" +
+            "<if test='heightMin != null'>" +
+            "AND height >= #{heightMin} " +
+            "</if>" +
+            "<if test='heightMax != null'>" +
+            "AND height <= #{heightMax} " +
+            "</if>" +
+            "<if test='weightMin != null'>" +
+            "AND weight >= #{weightMin} " +
+            "</if>" +
+            "<if test='weightMax != null'>" +
+            "AND weight <= #{weightMax} " +
+            "</if>" +
+            "<if test='bmiMin != null'>" +
+            "AND bmi >= #{bmiMin} " +
+            "</if>" +
+            "<if test='bmiMax != null'>" +
+            "AND bmi <= #{bmiMax} " +
+            "</if>" +
+            "<if test='waistCircumferenceMin != null'>" +
+            "AND waist_circumference >= #{waistCircumferenceMin} " +
+            "</if>" +
+            "<if test='waistCircumferenceMax != null'>" +
+            "AND waist_circumference <= #{waistCircumferenceMax} " +
+            "</if>" +
+            "<if test='chestCircumferenceMin != null'>" +
+            "AND chest_circumference >= #{chestCircumferenceMin} " +
+            "</if>" +
+            "<if test='chestCircumferenceMax != null'>" +
+            "AND chest_circumference <= #{chestCircumferenceMax} " +
+            "</if>" +
+            "<if test='reportStatus != null'>" +
+            "AND report_status = #{reportStatus} " +
+            "</if>" +
+            "ORDER BY " +
+            "<choose>" +
+            "<when test='orderBy != null and orderBy != \"\"'>" +
+            "${orderBy}" +
+            "</when>" +
+            "<otherwise>" +
+            "exam_date DESC, create_time DESC" +
+            "</otherwise>" +
+            "</choose>" +
+            "</script>")
+    List<PhysicalExamReport> selectPageList(PhysicalExamReportQueryDto queryDto);
+}

+ 104 - 0
fs-service/src/main/java/com/fs/medical/mapper/ReportIndicatorResultMapper.java

@@ -0,0 +1,104 @@
+package com.fs.medical.mapper;
+
+import com.fs.medical.domain.ReportIndicatorResult;
+import com.fs.medical.param.ReportIndicatorResultQueryDto;
+import org.apache.ibatis.annotations.*;
+import java.util.List;
+
+/**
+ * 报告指标检查表Mapper
+ */
+@Mapper
+public interface ReportIndicatorResultMapper {
+
+    /**
+     * 根据ID查询检查结果
+     */
+    @Select("SELECT * FROM fs_report_indicator_result WHERE result_id = #{resultId}")
+    ReportIndicatorResult getById(@Param("resultId") Long resultId);
+
+    /**
+     * 根据报告ID查询所有指标结果
+     */
+    @Select("SELECT * FROM fs_report_indicator_result WHERE report_id = #{reportId}")
+    List<ReportIndicatorResult> listByReportId(@Param("reportId") Long reportId);
+
+    /**
+     * 根据指标ID查询所有结果
+     */
+    @Select("SELECT * FROM fs_report_indicator_result WHERE indicator_id = #{indicatorId}")
+    List<ReportIndicatorResult> listByIndicatorId(@Param("indicatorId") Long indicatorId);
+
+    /**
+     * 新增检查结果
+     */
+    @Insert("INSERT INTO fs_report_indicator_result(report_id, indicator_id, test_value, test_result, is_abnormal, abnormal_type, remarks) " +
+            "VALUES(#{reportId}, #{indicatorId}, #{testValue}, #{testResult}, #{isAbnormal}, #{abnormalType}, #{remarks})")
+    @Options(useGeneratedKeys = true, keyProperty = "resultId")
+    int insert(ReportIndicatorResult result);
+
+    /**
+     * 更新检查结果
+     */
+    @Update("<script>" +
+            "UPDATE fs_report_indicator_result SET " +
+            "<if test='reportId != null'>report_id = #{reportId}, </if>" +
+            "<if test='indicatorId != null'>indicator_id = #{indicatorId}, </if>" +
+            "<if test='testValue != null'>test_value = #{testValue}, </if>" +
+            "<if test='testResult != null'>test_result = #{testResult}, </if>" +
+            "<if test='isAbnormal != null'>is_abnormal = #{isAbnormal}, </if>" +
+            "<if test='abnormalType != null'>abnormal_type = #{abnormalType}, </if>" +
+            "<if test='remarks != null'>remarks = #{remarks}, </if>" +
+            "update_time = NOW() " +
+            "WHERE result_id = #{resultId}" +
+            "</script>")
+    int update(ReportIndicatorResult result);
+
+    /**
+     * 根据ID删除检查结果
+     */
+    @Delete("DELETE FROM fs_report_indicator_result WHERE result_id = #{resultId}")
+    int deleteById(@Param("resultId") Long resultId);
+
+    /**
+     * 分页查询报告指标检查结果列表
+     */
+    @Select("<script>" +
+            "SELECT * FROM fs_report_indicator_result " +
+            "WHERE 1=1 " +
+            "<if test='reportId != null'>" +
+            "AND report_id = #{reportId} " +
+            "</if>" +
+            "<if test='indicatorId != null'>" +
+            "AND indicator_id = #{indicatorId} " +
+            "</if>" +
+            "<if test='testValueMin != null'>" +
+            "AND test_value >= #{testValueMin} " +
+            "</if>" +
+            "<if test='testValueMax != null'>" +
+            "AND test_value <= #{testValueMax} " +
+            "</if>" +
+            "<if test='testResult != null and testResult != \"\"'>" +
+            "AND test_result LIKE CONCAT('%', #{testResult}, '%') " +
+            "</if>" +
+            "<if test='isAbnormal != null'>" +
+            "AND is_abnormal = #{isAbnormal} " +
+            "</if>" +
+            "<if test='abnormalType != null and abnormalType != \"\"'>" +
+            "AND abnormal_type LIKE CONCAT('%', #{abnormalType}, '%') " +
+            "</if>" +
+            "<if test='remarks != null and remarks != \"\"'>" +
+            "AND remarks LIKE CONCAT('%', #{remarks}, '%') " +
+            "</if>" +
+            "ORDER BY " +
+            "<choose>" +
+            "<when test='orderBy != null and orderBy != \"\"'>" +
+            "${orderBy}" +
+            "</when>" +
+            "<otherwise>" +
+            "create_time DESC" +
+            "</otherwise>" +
+            "</choose>" +
+            "</script>")
+    List<ReportIndicatorResult> selectPageList(ReportIndicatorResultQueryDto queryDto);
+}

+ 40 - 0
fs-service/src/main/java/com/fs/medical/param/MeasurementUnitQueryDto.java

@@ -0,0 +1,40 @@
+package com.fs.medical.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 计量单位查询参数
+ *
+ * @author fs
+ * @date 2024
+ */
+@Data
+public class MeasurementUnitQueryDto implements Serializable {
+
+    @ApiModelProperty(value = "单位名称")
+    private String unitName;
+
+    @ApiModelProperty(value = "单位符号")
+    private String unitSymbol;
+
+    @ApiModelProperty(value = "单位类型")
+    private String unitType;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "状态 1-启用 0-禁用")
+    private Integer status;
+
+    @ApiModelProperty(value = "页码,默认为1")
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10")
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "排序字段")
+    private String orderBy;
+}

+ 50 - 0
fs-service/src/main/java/com/fs/medical/param/MedicalIndicatorQueryDto.java

@@ -0,0 +1,50 @@
+package com.fs.medical.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 医疗指标查询参数
+ *
+ * @author fs
+ * @date 2024
+ */
+@Data
+public class MedicalIndicatorQueryDto implements Serializable {
+
+    @ApiModelProperty(value = "指标名称")
+    private String indicatorName;
+
+    @ApiModelProperty(value = "指标分类")
+    private String indicatorCategory;
+
+    @ApiModelProperty(value = "单位ID")
+    private Long unitId;
+
+    @ApiModelProperty(value = "参考值最小值")
+    private BigDecimal referenceMin;
+
+    @ApiModelProperty(value = "参考值最大值")
+    private BigDecimal referenceMax;
+
+    @ApiModelProperty(value = "参考值文本")
+    private String referenceText;
+
+    @ApiModelProperty(value = "描述")
+    private String description;
+
+    @ApiModelProperty(value = "状态 1-启用 0-禁用")
+    private Integer status;
+
+    @ApiModelProperty(value = "页码,默认为1")
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10")
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "排序字段")
+    private String orderBy;
+}

+ 72 - 0
fs-service/src/main/java/com/fs/medical/param/PhysicalExamReportQueryDto.java

@@ -0,0 +1,72 @@
+package com.fs.medical.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 体检报告查询参数
+ *
+ * @author fs
+ * @date 2024
+ */
+@Data
+public class PhysicalExamReportQueryDto implements Serializable {
+
+    @ApiModelProperty(value = "用户ID")
+    private Long userId;
+
+    @ApiModelProperty(value = "体检日期开始")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date examDateStart;
+
+    @ApiModelProperty(value = "体检日期结束")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date examDateEnd;
+
+    @ApiModelProperty(value = "身高最小值")
+    private BigDecimal heightMin;
+
+    @ApiModelProperty(value = "身高最大值")
+    private BigDecimal heightMax;
+
+    @ApiModelProperty(value = "体重最小值")
+    private BigDecimal weightMin;
+
+    @ApiModelProperty(value = "体重最大值")
+    private BigDecimal weightMax;
+
+    @ApiModelProperty(value = "BMI最小值")
+    private BigDecimal bmiMin;
+
+    @ApiModelProperty(value = "BMI最大值")
+    private BigDecimal bmiMax;
+
+    @ApiModelProperty(value = "腰围最小值")
+    private BigDecimal waistCircumferenceMin;
+
+    @ApiModelProperty(value = "腰围最大值")
+    private BigDecimal waistCircumferenceMax;
+
+    @ApiModelProperty(value = "胸围最小值")
+    private BigDecimal chestCircumferenceMin;
+
+    @ApiModelProperty(value = "胸围最大值")
+    private BigDecimal chestCircumferenceMax;
+
+    @ApiModelProperty(value = "报告状态 1-正常 0-已删除")
+    private Integer reportStatus;
+
+    @ApiModelProperty(value = "页码,默认为1")
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10")
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "排序字段")
+    private String orderBy;
+}

+ 50 - 0
fs-service/src/main/java/com/fs/medical/param/ReportIndicatorResultQueryDto.java

@@ -0,0 +1,50 @@
+package com.fs.medical.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 报告指标检查结果查询参数
+ *
+ * @author fs
+ * @date 2024
+ */
+@Data
+public class ReportIndicatorResultQueryDto implements Serializable {
+
+    @ApiModelProperty(value = "报告ID")
+    private Long reportId;
+
+    @ApiModelProperty(value = "指标ID")
+    private Long indicatorId;
+
+    @ApiModelProperty(value = "检测值最小值")
+    private BigDecimal testValueMin;
+
+    @ApiModelProperty(value = "检测值最大值")
+    private BigDecimal testValueMax;
+
+    @ApiModelProperty(value = "检测结果")
+    private String testResult;
+
+    @ApiModelProperty(value = "是否异常 1-异常 0-正常")
+    private Integer isAbnormal;
+
+    @ApiModelProperty(value = "异常类型")
+    private String abnormalType;
+
+    @ApiModelProperty(value = "备注")
+    private String remarks;
+
+    @ApiModelProperty(value = "页码,默认为1")
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10")
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "排序字段")
+    private String orderBy;
+}

+ 46 - 0
fs-service/src/main/java/com/fs/medical/service/MeasurementUnitService.java

@@ -0,0 +1,46 @@
+package com.fs.medical.service;
+
+import com.fs.medical.domain.MeasurementUnit;
+import com.fs.medical.param.MeasurementUnitQueryDto;
+import java.util.List;
+
+/**
+ * 计量单位服务接口
+ */
+public interface MeasurementUnitService {
+
+    /**
+     * 根据ID查询计量单位
+     */
+    MeasurementUnit getById(Long unitId);
+
+    /**
+     * 查询所有计量单位
+     */
+    List<MeasurementUnit> listAll();
+
+    /**
+     * 根据单位类型查询计量单位
+     */
+    List<MeasurementUnit> listByType(String unitType);
+
+    /**
+     * 新增计量单位
+     */
+    boolean save(MeasurementUnit unit);
+
+    /**
+     * 更新计量单位
+     */
+    boolean update(MeasurementUnit unit);
+
+    /**
+     * 根据ID删除计量单位
+     */
+    boolean deleteById(Long unitId);
+
+    /**
+     * 分页查询计量单位列表
+     */
+    List<MeasurementUnit> selectPageList(MeasurementUnitQueryDto queryDto);
+}

+ 46 - 0
fs-service/src/main/java/com/fs/medical/service/MedicalIndicatorService.java

@@ -0,0 +1,46 @@
+package com.fs.medical.service;
+
+import com.fs.medical.domain.MedicalIndicator;
+import com.fs.medical.param.MedicalIndicatorQueryDto;
+import java.util.List;
+
+/**
+ * 医疗指标服务接口
+ */
+public interface MedicalIndicatorService {
+
+    /**
+     * 根据ID查询指标
+     */
+    MedicalIndicator getById(Long indicatorId);
+
+    /**
+     * 查询所有启用的指标
+     */
+    List<MedicalIndicator> listAllEnabled();
+
+    /**
+     * 根据分类查询指标
+     */
+    List<MedicalIndicator> listByCategory(String category);
+
+    /**
+     * 新增指标
+     */
+    boolean save(MedicalIndicator indicator);
+
+    /**
+     * 更新指标
+     */
+    boolean update(MedicalIndicator indicator);
+
+    /**
+     * 根据ID删除指标(逻辑删除)
+     */
+    boolean deleteById(Long indicatorId);
+
+    /**
+     * 分页查询医疗指标列表
+     */
+    List<MedicalIndicator> selectPageList(MedicalIndicatorQueryDto queryDto);
+}

+ 47 - 0
fs-service/src/main/java/com/fs/medical/service/PhysicalExamReportService.java

@@ -0,0 +1,47 @@
+package com.fs.medical.service;
+
+import com.fs.medical.domain.PhysicalExamReport;
+import com.fs.medical.param.PhysicalExamReportQueryDto;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 体检报告服务接口
+ */
+public interface PhysicalExamReportService {
+
+    /**
+     * 根据ID查询体检报告
+     */
+    PhysicalExamReport getById(Long reportId);
+
+    /**
+     * 根据用户ID查询体检报告列表
+     */
+    List<PhysicalExamReport> listByUserId(Long userId);
+
+    /**
+     * 根据用户ID和体检日期查询体检报告
+     */
+    PhysicalExamReport getByUserIdAndDate(Long userId, Date examDate);
+
+    /**
+     * 新增体检报告
+     */
+    boolean save(PhysicalExamReport report);
+
+    /**
+     * 更新体检报告
+     */
+    boolean update(PhysicalExamReport report);
+
+    /**
+     * 根据ID删除体检报告(逻辑删除)
+     */
+    boolean deleteById(Long reportId);
+
+    /**
+     * 分页查询体检报告列表
+     */
+    List<PhysicalExamReport> selectPageList(PhysicalExamReportQueryDto queryDto);
+}

+ 46 - 0
fs-service/src/main/java/com/fs/medical/service/ReportIndicatorResultService.java

@@ -0,0 +1,46 @@
+package com.fs.medical.service;
+
+import com.fs.medical.domain.ReportIndicatorResult;
+import com.fs.medical.param.ReportIndicatorResultQueryDto;
+import java.util.List;
+
+/**
+ * 报告指标检查服务接口
+ */
+public interface ReportIndicatorResultService {
+
+    /**
+     * 根据ID查询检查结果
+     */
+    ReportIndicatorResult getById(Long resultId);
+
+    /**
+     * 根据报告ID查询所有指标结果
+     */
+    List<ReportIndicatorResult> listByReportId(Long reportId);
+
+    /**
+     * 根据指标ID查询所有结果
+     */
+    List<ReportIndicatorResult> listByIndicatorId(Long indicatorId);
+
+    /**
+     * 新增检查结果
+     */
+    boolean save(ReportIndicatorResult result);
+
+    /**
+     * 更新检查结果
+     */
+    boolean update(ReportIndicatorResult result);
+
+    /**
+     * 根据ID删除检查结果
+     */
+    boolean deleteById(Long resultId);
+
+    /**
+     * 分页查询报告指标检查结果列表
+     */
+    List<ReportIndicatorResult> selectPageList(ReportIndicatorResultQueryDto queryDto);
+}

+ 55 - 0
fs-service/src/main/java/com/fs/medical/service/impl/MeasurementUnitServiceImpl.java

@@ -0,0 +1,55 @@
+package com.fs.medical.service.impl;
+
+import com.fs.medical.domain.MeasurementUnit;
+import com.fs.medical.mapper.MeasurementUnitMapper;
+import com.fs.medical.param.MeasurementUnitQueryDto;
+import com.fs.medical.service.MeasurementUnitService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 计量单位服务实现类
+ */
+@Service
+public class MeasurementUnitServiceImpl implements MeasurementUnitService {
+
+    @Autowired
+    private MeasurementUnitMapper measurementUnitMapper;
+
+    @Override
+    public MeasurementUnit getById(Long unitId) {
+        return measurementUnitMapper.getById(unitId);
+    }
+
+    @Override
+    public List<MeasurementUnit> listAll() {
+        return measurementUnitMapper.listAll();
+    }
+
+    @Override
+    public List<MeasurementUnit> listByType(String unitType) {
+        return measurementUnitMapper.listByType(unitType);
+    }
+
+    @Override
+    public boolean save(MeasurementUnit unit) {
+        return measurementUnitMapper.insert(unit) > 0;
+    }
+
+    @Override
+    public boolean update(MeasurementUnit unit) {
+        return measurementUnitMapper.update(unit) > 0;
+    }
+
+    @Override
+    public boolean deleteById(Long unitId) {
+        return measurementUnitMapper.deleteById(unitId) > 0;
+    }
+
+    @Override
+    public List<MeasurementUnit> selectPageList(MeasurementUnitQueryDto queryDto) {
+        return measurementUnitMapper.selectPageList(queryDto);
+    }
+}

+ 55 - 0
fs-service/src/main/java/com/fs/medical/service/impl/MedicalIndicatorServiceImpl.java

@@ -0,0 +1,55 @@
+package com.fs.medical.service.impl;
+
+import com.fs.medical.domain.MedicalIndicator;
+import com.fs.medical.mapper.MedicalIndicatorMapper;
+import com.fs.medical.param.MedicalIndicatorQueryDto;
+import com.fs.medical.service.MedicalIndicatorService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 医疗指标服务实现类
+ */
+@Service
+public class MedicalIndicatorServiceImpl implements MedicalIndicatorService {
+
+    @Autowired
+    private MedicalIndicatorMapper medicalIndicatorMapper;
+
+    @Override
+    public MedicalIndicator getById(Long indicatorId) {
+        return medicalIndicatorMapper.getById(indicatorId);
+    }
+
+    @Override
+    public List<MedicalIndicator> listAllEnabled() {
+        return medicalIndicatorMapper.listAllEnabled();
+    }
+
+    @Override
+    public List<MedicalIndicator> listByCategory(String category) {
+        return medicalIndicatorMapper.listByCategory(category);
+    }
+
+    @Override
+    public boolean save(MedicalIndicator indicator) {
+        return medicalIndicatorMapper.insert(indicator) > 0;
+    }
+
+    @Override
+    public boolean update(MedicalIndicator indicator) {
+        return medicalIndicatorMapper.update(indicator) > 0;
+    }
+
+    @Override
+    public boolean deleteById(Long indicatorId) {
+        return medicalIndicatorMapper.deleteById(indicatorId) > 0;
+    }
+
+    @Override
+    public List<MedicalIndicator> selectPageList(MedicalIndicatorQueryDto queryDto) {
+        return medicalIndicatorMapper.selectPageList(queryDto);
+    }
+}

+ 56 - 0
fs-service/src/main/java/com/fs/medical/service/impl/PhysicalExamReportServiceImpl.java

@@ -0,0 +1,56 @@
+package com.fs.medical.service.impl;
+
+import com.fs.medical.domain.PhysicalExamReport;
+import com.fs.medical.mapper.PhysicalExamReportMapper;
+import com.fs.medical.param.PhysicalExamReportQueryDto;
+import com.fs.medical.service.PhysicalExamReportService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 体检报告服务实现类
+ */
+@Service
+public class PhysicalExamReportServiceImpl implements PhysicalExamReportService {
+
+    @Autowired
+    private PhysicalExamReportMapper physicalExamReportMapper;
+
+    @Override
+    public PhysicalExamReport getById(Long reportId) {
+        return physicalExamReportMapper.getById(reportId);
+    }
+
+    @Override
+    public List<PhysicalExamReport> listByUserId(Long userId) {
+        return physicalExamReportMapper.listByUserId(userId);
+    }
+
+    @Override
+    public PhysicalExamReport getByUserIdAndDate(Long userId, Date examDate) {
+        return physicalExamReportMapper.getByUserIdAndDate(userId, examDate);
+    }
+
+    @Override
+    public boolean save(PhysicalExamReport report) {
+        return physicalExamReportMapper.insert(report) > 0;
+    }
+
+    @Override
+    public boolean update(PhysicalExamReport report) {
+        return physicalExamReportMapper.update(report) > 0;
+    }
+
+    @Override
+    public boolean deleteById(Long reportId) {
+        return physicalExamReportMapper.deleteById(reportId) > 0;
+    }
+
+    @Override
+    public List<PhysicalExamReport> selectPageList(PhysicalExamReportQueryDto queryDto) {
+        return physicalExamReportMapper.selectPageList(queryDto);
+    }
+}

+ 55 - 0
fs-service/src/main/java/com/fs/medical/service/impl/ReportIndicatorResultServiceImpl.java

@@ -0,0 +1,55 @@
+package com.fs.medical.service.impl;
+
+import com.fs.medical.domain.ReportIndicatorResult;
+import com.fs.medical.mapper.ReportIndicatorResultMapper;
+import com.fs.medical.param.ReportIndicatorResultQueryDto;
+import com.fs.medical.service.ReportIndicatorResultService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 报告指标检查服务实现类
+ */
+@Service
+public class ReportIndicatorResultServiceImpl implements ReportIndicatorResultService {
+
+    @Autowired
+    private ReportIndicatorResultMapper reportIndicatorResultMapper;
+
+    @Override
+    public ReportIndicatorResult getById(Long resultId) {
+        return reportIndicatorResultMapper.getById(resultId);
+    }
+
+    @Override
+    public List<ReportIndicatorResult> listByReportId(Long reportId) {
+        return reportIndicatorResultMapper.listByReportId(reportId);
+    }
+
+    @Override
+    public List<ReportIndicatorResult> listByIndicatorId(Long indicatorId) {
+        return reportIndicatorResultMapper.listByIndicatorId(indicatorId);
+    }
+
+    @Override
+    public boolean save(ReportIndicatorResult result) {
+        return reportIndicatorResultMapper.insert(result) > 0;
+    }
+
+    @Override
+    public boolean update(ReportIndicatorResult result) {
+        return reportIndicatorResultMapper.update(result) > 0;
+    }
+
+    @Override
+    public boolean deleteById(Long resultId) {
+        return reportIndicatorResultMapper.deleteById(resultId) > 0;
+    }
+
+    @Override
+    public List<ReportIndicatorResult> selectPageList(ReportIndicatorResultQueryDto queryDto) {
+        return reportIndicatorResultMapper.selectPageList(queryDto);
+    }
+}

+ 160 - 0
fs-user-app/src/main/java/com/fs/app/controller/medical/MeasurementUnitController.java

@@ -0,0 +1,160 @@
+package com.fs.app.controller.medical;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.controller.AppBaseController;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.medical.domain.MeasurementUnit;
+import com.fs.medical.param.MeasurementUnitQueryDto;
+import com.fs.medical.service.MeasurementUnitService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 计量单位Controller
+ *
+ * @author fs
+ * @date 2024
+ */
+@Api("计量单位")
+@RestController
+@RequestMapping("/app/medical/unit")
+public class MeasurementUnitController extends AppBaseController {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private MeasurementUnitService measurementUnitService;
+
+    /**
+     * 查询所有计量单位
+     */
+    @ApiOperation("查询所有计量单位")
+    @GetMapping("/listAll")
+    public R listAll() {
+        try {
+            List<MeasurementUnit> list = measurementUnitService.listAll();
+            return R.ok().put("data",list);
+        } catch (Exception e) {
+            logger.error("查询计量单位失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 分页查询计量单位列表(带筛选)
+     */
+    @ApiOperation("分页查询计量单位列表")
+    @GetMapping("/page")
+    public R page(MeasurementUnitQueryDto queryDto) {
+        try {
+            PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
+            List<MeasurementUnit> list = measurementUnitService.selectPageList(queryDto);
+            PageInfo<MeasurementUnit> pageInfo = new PageInfo<>(list);
+            return R.ok().put("data", pageInfo);
+        } catch (Exception e) {
+            logger.error("分页查询计量单位失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据单位类型查询计量单位
+     */
+    @ApiOperation("根据单位类型查询计量单位")
+    @GetMapping("/listByType")
+    public R listByType(@RequestParam String unitType) {
+        try {
+            List<MeasurementUnit> list = measurementUnitService.listByType(unitType);
+            return R.ok().put("data",list);
+        } catch (Exception e) {
+            logger.error("根据类型查询计量单位失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据ID查询计量单位详情
+     */
+    @ApiOperation("根据ID查询计量单位详情")
+    @GetMapping("/{unitId}")
+    public R getById(@PathVariable("unitId") Long unitId) {
+        try {
+            MeasurementUnit unit = measurementUnitService.getById(unitId);
+            return R.ok().put("data",unit);
+        } catch (Exception e) {
+            logger.error("查询计量单位详情失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 新增计量单位
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("新增计量单位")
+    @PostMapping("/add")
+    public R add(@RequestBody MeasurementUnit unit) {
+        try {
+            boolean result = measurementUnitService.save(unit);
+            if (result) {
+                return R.ok("新增成功");
+            } else {
+                return R.error("新增失败");
+            }
+        } catch (Exception e) {
+            logger.error("新增计量单位失败", e);
+            return R.error("新增失败");
+        }
+    }
+
+    /**
+     * 更新计量单位
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("更新计量单位")
+    @PutMapping("/update")
+    public R update(@RequestBody MeasurementUnit unit) {
+        try {
+            boolean result = measurementUnitService.update(unit);
+            if (result) {
+                return R.ok("更新成功");
+            } else {
+                return R.error("更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("更新计量单位失败", e);
+            return R.error("更新失败");
+        }
+    }
+
+    /**
+     * 删除计量单位
+     */
+    @Login
+    @ApiOperation("删除计量单位")
+    @DeleteMapping("/{unitId}")
+    public R delete(@PathVariable("unitId") Long unitId) {
+        try {
+            boolean result = measurementUnitService.deleteById(unitId);
+            if (result) {
+                return R.ok("删除成功");
+            } else {
+                return R.error("删除失败");
+            }
+        } catch (Exception e) {
+            logger.error("删除计量单位失败", e);
+            return R.error("删除失败");
+        }
+    }
+}

+ 162 - 0
fs-user-app/src/main/java/com/fs/app/controller/medical/MedicalIndicatorController.java

@@ -0,0 +1,162 @@
+package com.fs.app.controller.medical;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.controller.AppBaseController;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.medical.domain.MedicalIndicator;
+import com.fs.medical.param.MedicalIndicatorQueryDto;
+import com.fs.medical.service.MedicalIndicatorService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 医疗指标Controller
+ *
+ * @author fs
+ * @date 2024
+ */
+@Slf4j
+@Api("医疗指标")
+@RestController
+@RequestMapping("/app/medical/indicator")
+public class MedicalIndicatorController extends AppBaseController {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private MedicalIndicatorService medicalIndicatorService;
+
+    /**
+     * 查询所有启用的指标
+     */
+    @ApiOperation("查询所有启用的指标")
+    @GetMapping("/listEnabled")
+    public R listAllEnabled() {
+        try {
+            List<MedicalIndicator> list = medicalIndicatorService.listAllEnabled();
+            return R.ok().put("data",list);
+        } catch (Exception e) {
+            logger.error("查询启用指标失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 分页查询医疗指标列表(带筛选)
+     */
+    @ApiOperation("分页查询医疗指标列表")
+    @GetMapping("/page")
+    public R page(MedicalIndicatorQueryDto queryDto) {
+        try {
+            PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
+            List<MedicalIndicator> list = medicalIndicatorService.selectPageList(queryDto);
+            PageInfo<MedicalIndicator> pageInfo = new PageInfo<>(list);
+            return R.ok().put("data", pageInfo);
+        } catch (Exception e) {
+            logger.error("分页查询医疗指标失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据分类查询指标
+     */
+    @ApiOperation("根据分类查询指标")
+    @GetMapping("/listByCategory")
+    public R listByCategory(@RequestParam String category) {
+        try {
+            List<MedicalIndicator> list = medicalIndicatorService.listByCategory(category);
+            return R.ok().put("data",list);
+        } catch (Exception e) {
+            logger.error("根据分类查询指标失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据ID查询指标详情
+     */
+    @ApiOperation("根据ID查询指标详情")
+    @GetMapping("/{indicatorId}")
+    public R getById(@PathVariable("indicatorId") Long indicatorId) {
+        try {
+            MedicalIndicator indicator = medicalIndicatorService.getById(indicatorId);
+            return R.ok().put("data",indicator);
+        } catch (Exception e) {
+            logger.error("查询指标详情失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 新增指标
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("新增指标")
+    @PostMapping("/add")
+    public R add(@RequestBody MedicalIndicator indicator) {
+        try {
+            boolean result = medicalIndicatorService.save(indicator);
+            if (result) {
+                return R.ok("新增成功");
+            } else {
+                return R.error("新增失败");
+            }
+        } catch (Exception e) {
+            logger.error("新增指标失败", e);
+            return R.error("新增失败");
+        }
+    }
+
+    /**
+     * 更新指标
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("更新指标")
+    @PutMapping("/update")
+    public R update(@RequestBody MedicalIndicator indicator) {
+        try {
+            boolean result = medicalIndicatorService.update(indicator);
+            if (result) {
+                return R.ok("更新成功");
+            } else {
+                return R.error("更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("更新指标失败", e);
+            return R.error("更新失败");
+        }
+    }
+
+    /**
+     * 删除指标
+     */
+    @Login
+    @ApiOperation("删除指标")
+    @DeleteMapping("/{indicatorId}")
+    public R delete(@PathVariable("indicatorId") Long indicatorId) {
+        try {
+            boolean result = medicalIndicatorService.deleteById(indicatorId);
+            if (result) {
+                return R.ok("删除成功");
+            } else {
+                return R.error("删除失败");
+            }
+        } catch (Exception e) {
+            logger.error("删除指标失败", e);
+            return R.error("删除失败");
+        }
+    }
+}

+ 168 - 0
fs-user-app/src/main/java/com/fs/app/controller/medical/PhysicalExamReportController.java

@@ -0,0 +1,168 @@
+package com.fs.app.controller.medical;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.controller.AppBaseController;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.medical.domain.PhysicalExamReport;
+import com.fs.medical.param.PhysicalExamReportQueryDto;
+import com.fs.medical.service.PhysicalExamReportService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 体检报告Controller
+ *
+ * @author fs
+ * @date 2024
+ */
+@Api("体检报告")
+@RestController
+@RequestMapping("/app/medical/report")
+public class PhysicalExamReportController extends AppBaseController {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private PhysicalExamReportService physicalExamReportService;
+
+    /**
+     * 根据用户ID查询体检报告列表
+     */
+    @Login
+    @ApiOperation("查询用户体检报告列表")
+    @GetMapping("/listByUser/{userId}")
+    public R listByUserId(@PathVariable("userId") Long userId) {
+        try {
+            List<PhysicalExamReport> list = physicalExamReportService.listByUserId(userId);
+            return R.ok().put("data",list);
+        } catch (Exception e) {
+            logger.error("查询用户体检报告失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 分页查询体检报告列表(带筛选)
+     */
+    @Login
+    @ApiOperation("分页查询体检报告列表")
+    @GetMapping("/page")
+    public R page(PhysicalExamReportQueryDto queryDto) {
+        try {
+            PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
+            List<PhysicalExamReport> list = physicalExamReportService.selectPageList(queryDto);
+            PageInfo<PhysicalExamReport> pageInfo = new PageInfo<>(list);
+            return R.ok().put("data", pageInfo);
+        } catch (Exception e) {
+            logger.error("分页查询体检报告失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据用户ID和体检日期查询体检报告
+     */
+    @Login
+    @ApiOperation("根据用户ID和体检日期查询体检报告")
+    @GetMapping("/getByUserAndDate")
+    public R getByUserIdAndDate(
+            @RequestParam Long userId,
+            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date examDate) {
+        try {
+            PhysicalExamReport report = physicalExamReportService.getByUserIdAndDate(userId, examDate);
+            return R.ok().put("data",report);
+        } catch (Exception e) {
+            logger.error("根据用户ID和日期查询体检报告失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据ID查询体检报告详情
+     */
+    @Login
+    @ApiOperation("查询体检报告详情")
+    @GetMapping("/{reportId}")
+    public R getById(@PathVariable("reportId") Long reportId) {
+        try {
+            PhysicalExamReport report = physicalExamReportService.getById(reportId);
+            return R.ok().put("data",report);
+        } catch (Exception e) {
+            logger.error("查询体检报告详情失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 新增体检报告
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("新增体检报告")
+    @PostMapping("/add")
+    public R add(@RequestBody PhysicalExamReport report) {
+        try {
+            boolean result = physicalExamReportService.save(report);
+            if (result) {
+                return R.ok("新增成功");
+            } else {
+                return R.error("新增失败");
+            }
+        } catch (Exception e) {
+            logger.error("新增体检报告失败", e);
+            return R.error("新增失败");
+        }
+    }
+
+    /**
+     * 更新体检报告
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("更新体检报告")
+    @PutMapping("/update")
+    public R update(@RequestBody PhysicalExamReport report) {
+        try {
+            boolean result = physicalExamReportService.update(report);
+            if (result) {
+                return R.ok("更新成功");
+            } else {
+                return R.error("更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("更新体检报告失败", e);
+            return R.error("更新失败");
+        }
+    }
+
+    /**
+     * 删除体检报告
+     */
+    @Login
+    @ApiOperation("删除体检报告")
+    @DeleteMapping("/{reportId}")
+    public R delete(@PathVariable("reportId") Long reportId) {
+        try {
+            boolean result = physicalExamReportService.deleteById(reportId);
+            if (result) {
+                return R.ok("删除成功");
+            } else {
+                return R.error("删除失败");
+            }
+        } catch (Exception e) {
+            logger.error("删除体检报告失败", e);
+            return R.error("删除失败");
+        }
+    }
+}

+ 190 - 0
fs-user-app/src/main/java/com/fs/app/controller/medical/ReportIndicatorResultController.java

@@ -0,0 +1,190 @@
+package com.fs.app.controller.medical;
+
+import com.fs.app.annotation.Login;
+import com.fs.app.controller.AppBaseController;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.medical.domain.ReportIndicatorResult;
+import com.fs.medical.param.ReportIndicatorResultQueryDto;
+import com.fs.medical.service.ReportIndicatorResultService;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 报告指标检查Controller
+ *
+ * @author fs
+ * @date 2024
+ */
+@Api("报告指标检查")
+@RestController
+@RequestMapping("/app/medical/result")
+public class ReportIndicatorResultController extends AppBaseController {
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private ReportIndicatorResultService reportIndicatorResultService;
+
+    /**
+     * 根据报告ID查询所有指标结果
+     */
+    @Login
+    @ApiOperation("根据报告ID查询所有指标结果")
+    @GetMapping("/listByReport/{reportId}")
+    public R listByReportId(@PathVariable("reportId") Long reportId) {
+        try {
+            List<ReportIndicatorResult> list = reportIndicatorResultService.listByReportId(reportId);
+            return R.ok().put("data",list);
+        } catch (Exception e) {
+            logger.error("根据报告ID查询指标结果失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 分页查询报告指标检查结果列表(带筛选)
+     */
+    @Login
+    @ApiOperation("分页查询报告指标检查结果列表")
+    @GetMapping("/page")
+    public R page(ReportIndicatorResultQueryDto queryDto) {
+        try {
+            PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
+            List<ReportIndicatorResult> list = reportIndicatorResultService.selectPageList(queryDto);
+            PageInfo<ReportIndicatorResult> pageInfo = new PageInfo<>(list);
+            return R.ok().put("data", pageInfo);
+        } catch (Exception e) {
+            logger.error("分页查询报告指标检查结果失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据指标ID查询所有结果
+     */
+    @Login
+    @ApiOperation("根据指标ID查询所有结果")
+    @GetMapping("/listByIndicator/{indicatorId}")
+    public R listByIndicatorId(@PathVariable("indicatorId") Long indicatorId) {
+        try {
+            List<ReportIndicatorResult> list = reportIndicatorResultService.listByIndicatorId(indicatorId);
+            return R.ok().put("data",list);
+        } catch (Exception e) {
+            logger.error("根据指标ID查询结果失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 根据ID查询检查结果详情
+     */
+    @Login
+    @ApiOperation("查询检查结果详情")
+    @GetMapping("/{resultId}")
+    public R getById(@PathVariable("resultId") Long resultId) {
+        try {
+            ReportIndicatorResult result = reportIndicatorResultService.getById(resultId);
+            return R.ok().put("data",result);
+        } catch (Exception e) {
+            logger.error("查询检查结果详情失败", e);
+            return R.error("查询失败");
+        }
+    }
+
+    /**
+     * 新增检查结果
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("新增检查结果")
+    @PostMapping("/add")
+    public R add(@RequestBody ReportIndicatorResult result) {
+        try {
+            boolean success = reportIndicatorResultService.save(result);
+            if (success) {
+                return R.ok("新增成功");
+            } else {
+                return R.error("新增失败");
+            }
+        } catch (Exception e) {
+            logger.error("新增检查结果失败", e);
+            return R.error("新增失败");
+        }
+    }
+
+    /**
+     * 批量新增检查结果
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("批量新增检查结果")
+    @PostMapping("/batchAdd")
+    public R batchAdd(@RequestBody List<ReportIndicatorResult> results) {
+        try {
+            int successCount = 0;
+            for (ReportIndicatorResult result : results) {
+                if (reportIndicatorResultService.save(result)) {
+                    successCount++;
+                }
+            }
+            if (successCount == results.size()) {
+                return R.ok("批量新增成功");
+            } else {
+                return R.ok("部分新增成功,成功" + successCount + "条");
+            }
+        } catch (Exception e) {
+            logger.error("批量新增检查结果失败", e);
+            return R.error("批量新增失败");
+        }
+    }
+
+    /**
+     * 更新检查结果
+     */
+    @Login
+    @RepeatSubmit
+    @ApiOperation("更新检查结果")
+    @PutMapping("/update")
+    public R update(@RequestBody ReportIndicatorResult result) {
+        try {
+            boolean success = reportIndicatorResultService.update(result);
+            if (success) {
+                return R.ok("更新成功");
+            } else {
+                return R.error("更新失败");
+            }
+        } catch (Exception e) {
+            logger.error("更新检查结果失败", e);
+            return R.error("更新失败");
+        }
+    }
+
+    /**
+     * 删除检查结果
+     */
+    @Login
+    @ApiOperation("删除检查结果")
+    @DeleteMapping("/{resultId}")
+    public R delete(@PathVariable("resultId") Long resultId) {
+        try {
+            boolean success = reportIndicatorResultService.deleteById(resultId);
+            if (success) {
+                return R.ok("删除成功");
+            } else {
+                return R.error("删除失败");
+            }
+        } catch (Exception e) {
+            logger.error("删除检查结果失败", e);
+            return R.error("删除失败");
+        }
+    }
+}