critical-values-backend-design.md 13 KB

危急值模块 — 后端设计与实现(Processing)

目的

  • 为前端 uniapp-ts 的危急值管理页面提供后端实现设计与参考代码结构。
  • 保持与前端约定一致:只使用 GET(只读)与 POST(写)两种请求方法。

范围

  • 包含数据库表设计(建表 SQL)、REST 接口定义(路径与方法)、后端分层职责(Controller/Service/Mapper/Entity/DTO)、常见校验与错误处理、以及部署/测试建议。

说明

  • 本设计基于 docs/新增危急值模块.md 的字段与接口约定:表 t_critical_value_module、字段 value_typecritical_above_valuecritical_below_value 等;接口仅使用 GETPOST

一、数据库设计

表名:t_critical_value_module

示例建表 SQL:

CREATE TABLE `t_critical_value_module` (
  `id` BIGINT NOT NULL,
  `value_type` VARCHAR(50) NOT NULL COMMENT '危急值类型,如 height, weight, systolic, fasting 等',
  `critical_above_value` DECIMAL(10,2) DEFAULT NULL COMMENT '危急值上限',
  `critical_below_value` DECIMAL(10,2) DEFAULT NULL COMMENT '危急值下限',
  `record_time` DATETIME DEFAULT NULL COMMENT '记录时间',
  `create_user` BIGINT DEFAULT NULL,
  `create_time` DATETIME DEFAULT NULL,
  `update_user` BIGINT DEFAULT NULL,
  `update_time` DATETIME DEFAULT NULL,
  `remark` VARCHAR(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  INDEX `idx_value_type` (`value_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='危急值配置表';

备注:

  • value_type 使用枚举值(参见前端 critical-values.vue 提取的枚举),后端可以在 Service 层做校验。
  • 上下限字段允许为空(NULL),表示只配置其中一端。
    • 建议与项目现有 BaseEntity/CustomMetaObjectHandler 约定保持一致:
    • 应用侧使用 MyBatis-Plus 的 IdType.ASSIGN_ID(雪花算法)或项目通用 ID 策略来生成 id,因此建表时可不使用 AUTO_INCREMENT,以与现有表保持一致。
    • create_time / update_time 通常由项目的 CustomMetaObjectHandler 自动填充(见 model/po/BaseEntity.java),建议数据库列不依赖 DEFAULT CURRENT_TIMESTAMP,而由应用层填充以保持一致性和时区控制。
    • create_user / update_user 在项目中为 BIGINT(用户 id),文档上方示例已做相应调整。

二、后端包/类结构(建议,基于 Spring Boot + MyBatis-Plus)

建议使用项目现有包路径风格:work.baiyun.chronicdiseaseapp.processing.critical(模块根包),目录结构示例:

  • controller
    • CriticalValueController.java — REST 接口入口
  • service
    • CriticalValueService.java (接口)
    • CriticalValueServiceImpl.java (实现)
  • mapper
    • CriticalValueMapper.java (MyBatis-Plus 接口,继承 BaseMapper<CriticalValue>,使用注解或 Mapper 扫描,无 XML)
  • model.po
    • CriticalValue.java(实体,对应数据库表 t_critical_value_module
  • model.vo
    • CreateOrUpdateCriticalValueRequest.java(请求)
    • CriticalValueResponse.java(响应)
  • enum
    • ValueType.java(后端枚举,值与前端一致)
  • exception
    • ProcessingException.java(模块内统一异常)

说明:

  • 本项目使用 MyBatis-Plus(见现有 mapper 文件如 MedicineMapper),推荐 CriticalValueMapper 直接继承 com.baomidou.mybatisplus.core.mapper.BaseMapper<CriticalValue> 并添加 @Mapper 注解或通过包扫描注册,不需要 *.xml 映射文件。
  • 在 Service 中优先使用 MyBatis-Plus 的 QueryWrapper / LambdaQueryWrapper / UpdateWrapper 进行查询与更新;仅在特殊场景(复杂 SQL 或性能优化)下使用 @Select 注解编写原生 SQL。

三、主要接口(与文档一致)

1) 获取危急值列表

  • 方法:GET
  • 路径:/api/critical-values
  • 参数:value_type(可选)
  • 行为:根据 value_type 过滤并返回对应记录集合;若不传 value_type 返回所有。
  • 返回示例(HTTP 200):

    [{
    "id": 1,
    "value_type": "fasting",
    "critical_above_value": 7.0,
    "critical_below_value": 3.9,
    "remark": "空腹血糖危急值"
    }]
    

2) 获取危急值详情

  • 方法:GET
  • 路径:/api/critical-values/{id}
  • 行为:返回单条记录

3) 新增或更新危急值

  • 方法:POST
  • 路径:/api/critical-values
  • 行为:统一写接口;当请求体包含 id 字段时视为更新(先校验存在),否则为新增。
  • 建议校验:
    • value_type 非空并为合法枚举
    • critical_above_valuecritical_below_value 至少有一个非空
    • 数值范围合理(如不为负数,或根据具体指标做额外校验)

4) 删除危急值

  • 方法:POST
  • 路径:/api/critical-values/delete
  • 行为:接收 id(或 ids 列表)作为写操作进行删除。

5) 获取危急值类型枚举

  • 方法:GET
  • 路径:/api/critical-values/types
  • 行为:返回后端枚举列表(value+label),便于前端构建下拉:

返回示例:

[
  {"value":"height","label":"身高"},
  {"value":"weight","label":"体重"},
  {"value":"fasting","label":"血糖-空腹"}
]

四、示例 DTO / Entity(简化)

实体(model.po)示例:CriticalValue.java

package work.baiyun.chronicdiseaseapp.model.po;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import work.baiyun.chronicdiseaseapp.model.po.BaseEntity;

@TableName("t_critical_value_module")
public class CriticalValue extends BaseEntity {
  private String valueType;

  @TableField("critical_above_value")
  private BigDecimal criticalAboveValue;

  @TableField("critical_below_value")
  private BigDecimal criticalBelowValue;

  @TableField("record_time")
  private LocalDateTime recordTime;

  // remark / create/update fields inherited from BaseEntity
  // getter/setter
}

请求/响应(model.vo)示例命名:

1) 创建/更新请求:CreateOrUpdateCriticalValueRequest.java

public class CreateOrUpdateCriticalValueRequest {
  private Long id; // 可选,存在则为更新
  private String valueType;
  private BigDecimal criticalAboveValue;
  private BigDecimal criticalBelowValue;
  private String remark;
  // getter/setter
}

2) 对外响应:CriticalValueResponse.java

public class CriticalValueResponse {
  private String id; // 返回给前端为 String,避免 JS 精度问题
  private String valueType;
  private BigDecimal criticalAboveValue;
  private BigDecimal criticalBelowValue;
  private String remark;
  private LocalDateTime recordTime;
  // getter/setter
}

五、Controller 示例方法签名(伪码)

@RestController
@RequestMapping("/api/critical-values")
public class CriticalValueController {

  @GetMapping("")
  public R<List<CriticalValueResponse>> list(@RequestParam(required=false) String valueType) { ... }

  @GetMapping("/{id}")
  public R<CriticalValueResponse> get(@PathVariable String id) { ... }

  @PostMapping("")
  public R<?> saveOrUpdate(@RequestBody CreateOrUpdateCriticalValueRequest req) { ... }

  @PostMapping("/delete")
  public R<?> delete(@RequestBody DeleteRequest req) { ... }

  @GetMapping("/types")
  public R<List<EnumVO>> types() { ... }
}

六、Mapper (MyBatis-Plus) 示例(QueryWrapper / 注解 SQL)

说明:本项目优先使用 MyBatis-Plus 的 QueryWrapper / LambdaQueryWrapper 进行查询与更新;仅在复杂或性能敏感场景使用 @Select 注解编写原生 SQL。下面给出 QueryWrapper 的伪代码与经修正的示例 SQL(供参考),SQL 中参数请使用 MyBatis 占位符 #{...}

QueryWrapper 伪代码示例:

LambdaQueryWrapper<CriticalValue> wrapper = new LambdaQueryWrapper<>();
if (valueType != null) {
  wrapper.eq(CriticalValue::getValueType, valueType);
}
wrapper.orderByAsc(CriticalValue::getId);
List<CriticalValue> list = criticalValueMapper.selectList(wrapper);

原生 SQL(示意,若使用 @Select 请使用类似写法并注意字段映射):

  • 查询(按 type)

    SELECT id, value_type, critical_above_value, critical_below_value, remark
    FROM t_critical_value_module
    WHERE (#{valueType} IS NULL OR value_type = #{valueType})
    ORDER BY id;
    
  • 插入

    INSERT INTO t_critical_value_module (value_type, critical_above_value, critical_below_value, remark, create_user)
    VALUES (#{valueType}, #{criticalAboveValue}, #{criticalBelowValue}, #{remark}, #{createUser});
    
  • 更新

    UPDATE t_critical_value_module
    SET value_type = #{valueType}, critical_above_value = #{criticalAboveValue},
    critical_below_value = #{criticalBelowValue}, remark = #{remark}, update_user = #{updateUser}
    WHERE id = #{id};
    
  • 删除

    DELETE FROM t_critical_value_module WHERE id = #{id};
    

备注:推荐在文档中把以上 SQL 视为参考;实际实现优先用 QueryWrapper。如果使用 @Select 注解,请确保 SQL 与 VO 字段别名一致并使用 #{} 占位。

七、校验与异常处理

  • 使用统一的参数校验(JSR-303 注解)在 DTO 层校验 @NotBlank@DecimalMin 等。
  • Service 层检查业务一致性(例如:更新时若 id 不存在返回 404/自定义错误)。
  • 返回格式建议统一:{ code: int, message: string, data: object }

文档 / Swagger 注意:

  • 若项目使用泛型包装类(如 R<T>)返回值,Swagger 可能无法正确展示泛型内部结构。建议在 Controller 方法上使用 @ApiResponse / @Content / @Schema(implementation = ...) 明确指定实际返回类型;对于分页等复杂泛型,创建专用的文档 VO 并在注解中引用。示例:

    @Operation(summary = "获取危急值列表")
    @ApiResponse(responseCode = "200", description = "OK",
    content = @Content(mediaType = "application/json",
        schema = @Schema(implementation = CriticalValueListResponse.class)))
    public R<?> list(...) { ... }
    
    • 为常见的错误状态码也添加 @ApiResponse 注解(400/401/403/404/500),提高文档准确性和前端对接效率。

八、安全与权限

  • 仅医生或管理员有写入权限;GET 可根据权限决定是否所有用户可见。
  • 建议在 Controller 或 Service 层使用已有的认证/鉴权组件(如 Spring Security 或项目现有拦截器)。

九、测试建议

  • 单元测试:Service 层模拟 Mapper,测试新增/更新/删除/查询逻辑。
  • 集成测试:使用内存数据库(H2)或测试库执行 Mapper SQL、Controller 接口联调。

十、迁移与上线说明

  • 提交建表 SQL 到数据库迁移脚本(如 Flyway/Liquibase)或交付 DB 管理员执行。
  • 文档路径:api-springboot/docs/Dev/modules/Processing/critical-values-backend-design.md

十一、注意事项与前端对接要点

  • 前端使用的枚举值必须与后端 ValueType 一致;推荐在后端 /types 接口返回 label 与 value,前端直接消费。
  • 删除使用 POST /api/critical-values/delete,前端发送 {id: 123};避免跨域或预检问题需与前端同事确认请求头与 Content-Type(建议 application/json)。
  • 前端页面未提供导入/导出功能,后端无需实现该功能。

补充要点(从历史问题与经验教训中提取,强烈建议遵循):

  • ID 精度(前端显示/处理):

    • 问题背景:使用 Snowflake 等 64 位整型 ID 时,前端 JavaScript 的 Number 会出现精度丢失。
    • 建议:在 API 响应的 VO 中将 id 定义为 String 并在 Service 层转换:

      // VO / Response
      public class CriticalValueResponse {
      private String id; // 注意:String 类型以避免前端精度丢失
      // ...其他字段
      }
      
      // Service 层转换示例
      CriticalValueResponse vo = new CriticalValueResponse();
      BeanUtils.copyProperties(entity, vo);
      vo.setId(entity.getId() == null ? null : entity.getId().toString());
      
    • 说明:请求入参仍可按现有习惯使用 Long(或前端传 String 再后端解析),但响应务必返回 String,避免前端误差。

  • 类型映射兼容性(TINYINT(1) 与 Boolean):

    • 问题背景:历史问题中发现 MySQL 的 TINYINT(1) 在某些 JDBC 驱动下不能稳定映射为 Java Boolean,导致 VO 中布尔字段为 null。
    • 建议:如果模块涉及 TINYINT(1) 布尔字段,PO 使用 ByteInteger 更稳妥;由 Service 在返回 VO 时明确转换为 Boolean

示例:

// PO
private Byte notificationEnabled; // 对应 TINYINT(1)

// Service -> VO 转换
vo.setNotificationEnabled(entity.getNotificationEnabled() != null && entity.getNotificationEnabled() == 1);
  • 说明:本“危急值”模块当前字段为 DECIMAL 类型,不直接受此问题影响,但建议在文档中保留此经验以供团队复用。

—— 结束 ——