# 危急值模块 — 后端设计与实现(Processing) 目的 - 为前端 `uniapp-ts` 的危急值管理页面提供后端实现设计与参考代码结构。 - 保持与前端约定一致:只使用 `GET`(只读)与 `POST`(写)两种请求方法。 范围 - 包含数据库表设计(建表 SQL)、REST 接口定义(路径与方法)、后端分层职责(Controller/Service/Mapper/Entity/DTO)、常见校验与错误处理、以及部署/测试建议。 说明 - 本设计基于 `docs/新增危急值模块.md` 的字段与接口约定:表 `t_critical_value_module`、字段 `value_type`、`critical_above_value`、`critical_below_value` 等;接口仅使用 `GET` 与 `POST`。 一、数据库设计 表名:`t_critical_value_module` 示例建表 SQL: ```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`,使用注解或 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` 并添加 `@Mapper` 注解或通过包扫描注册,不需要 `*.xml` 映射文件。 - 在 Service 中优先使用 MyBatis-Plus 的 `QueryWrapper` / `LambdaQueryWrapper` / `UpdateWrapper` 进行查询与更新;仅在特殊场景(复杂 SQL 或性能优化)下使用 `@Select` 注解编写原生 SQL。 三、主要接口(与文档一致) 1) 获取危急值列表 - 方法:GET - 路径:`/api/critical-values` - 参数:`value_type`(可选) - 行为:根据 `value_type` 过滤并返回对应记录集合;若不传 `value_type` 返回所有。 - 返回示例(HTTP 200): ```json [{ "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_value` 与 `critical_below_value` 至少有一个非空 - 数值范围合理(如不为负数,或根据具体指标做额外校验) 4) 删除危急值 - 方法:POST - 路径:`/api/critical-values/delete` - 行为:接收 `id`(或 `ids` 列表)作为写操作进行删除。 5) 获取危急值类型枚举 - 方法:GET - 路径:`/api/critical-values/types` - 行为:返回后端枚举列表(value+label),便于前端构建下拉: 返回示例: ```json [ {"value":"height","label":"身高"}, {"value":"weight","label":"体重"}, {"value":"fasting","label":"血糖-空腹"} ] ``` 四、示例 DTO / Entity(简化) 实体(model.po)示例:`CriticalValue.java` ```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` ```java public class CreateOrUpdateCriticalValueRequest { private Long id; // 可选,存在则为更新 private String valueType; private BigDecimal criticalAboveValue; private BigDecimal criticalBelowValue; private String remark; // getter/setter } ``` 2) 对外响应:`CriticalValueResponse.java` ```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 示例方法签名(伪码) ```java @RestController @RequestMapping("/api/critical-values") public class CriticalValueController { @GetMapping("") public R> list(@RequestParam(required=false) String valueType) { ... } @GetMapping("/{id}") public R get(@PathVariable String id) { ... } @PostMapping("") public R saveOrUpdate(@RequestBody CreateOrUpdateCriticalValueRequest req) { ... } @PostMapping("/delete") public R delete(@RequestBody DeleteRequest req) { ... } @GetMapping("/types") public R> types() { ... } } ``` 六、Mapper (MyBatis-Plus) 示例(QueryWrapper / 注解 SQL) 说明:本项目优先使用 MyBatis-Plus 的 `QueryWrapper` / `LambdaQueryWrapper` 进行查询与更新;仅在复杂或性能敏感场景使用 `@Select` 注解编写原生 SQL。下面给出 QueryWrapper 的伪代码与经修正的示例 SQL(供参考),SQL 中参数请使用 MyBatis 占位符 `#{...}`。 QueryWrapper 伪代码示例: ```java LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); if (valueType != null) { wrapper.eq(CriticalValue::getValueType, valueType); } wrapper.orderByAsc(CriticalValue::getId); List list = criticalValueMapper.selectList(wrapper); ``` 原生 SQL(示意,若使用 `@Select` 请使用类似写法并注意字段映射): - 查询(按 type) ```sql 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; ``` - 插入 ```sql INSERT INTO t_critical_value_module (value_type, critical_above_value, critical_below_value, remark, create_user) VALUES (#{valueType}, #{criticalAboveValue}, #{criticalBelowValue}, #{remark}, #{createUser}); ``` - 更新 ```sql 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}; ``` - 删除 ```sql 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`)返回值,Swagger 可能无法正确展示泛型内部结构。建议在 Controller 方法上使用 `@ApiResponse` / `@Content` / `@Schema(implementation = ...)` 明确指定实际返回类型;对于分页等复杂泛型,创建专用的文档 VO 并在注解中引用。示例: ```java @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 层转换: ```java // 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 使用 `Byte` 或 `Integer` 更稳妥;由 Service 在返回 VO 时明确转换为 `Boolean`。 示例: ```java // PO private Byte notificationEnabled; // 对应 TINYINT(1) // Service -> VO 转换 vo.setNotificationEnabled(entity.getNotificationEnabled() != null && entity.getNotificationEnabled() == 1); ``` - 说明:本“危急值”模块当前字段为 DECIMAL 类型,不直接受此问题影响,但建议在文档中保留此经验以供团队复用。 —— 结束 ——