فهرست منبع

fix(api): 解决前端ID精度丢失问题

- 将PhysicalDataResponse中的id字段类型从Long改为String
- 修改服务层逻辑,将ID转换为字符串返回给前端
- 保持请求参数和数据库字段类型不变
- 添加删除体格数据接口及其实现
- 移除BMI计算相关代码和依赖
- 新增DeletePhysicalDataRequest用于删除操作参数校验
mcbaiyun 1 ماه پیش
والد
کامیت
9ec66959bd

+ 62 - 0
docs/前端ID精度丢失问题解决方案.md

@@ -0,0 +1,62 @@
+# 前端ID精度丢失问题解决方案
+
+## 问题描述
+在使用Snowflake算法生成的64位长整型ID时,前端JavaScript处理大整数时会出现精度丢失问题。这是因为JavaScript的Number类型只能精确表示到2^53-1(约9×10^15),超过这个范围的整数会丢失精度,导致前端显示或处理ID时出现错误。
+
+## 问题表现
+- API返回的ID在前端显示不正确
+- 删除或更新操作时ID参数不匹配
+- 前端日志显示ID值与后端不一致
+
+## 根本原因
+JavaScript的Number类型使用IEEE 754双精度浮点数标准,只能安全表示53位精度的整数。Snowflake ID通常是64位,超过了这个限制。
+
+## 解决方案
+将API响应中的ID字段类型从Long改为String,让前端以字符串形式处理ID,避免精度丢失。
+
+### 具体实现步骤
+1. **修改响应VO类**
+   - 将实体响应类中的id字段类型从`Long`改为`String`
+   - 更新Swagger注解描述
+
+2. **修改服务层转换逻辑**
+   - 在数据转换时,将Long类型的ID转换为字符串
+   - 使用`record.getId().toString()`进行转换
+
+3. **保持请求参数类型**
+   - 请求参数仍然可以使用Long类型(后端处理)
+   - 只有响应数据转换为字符串
+
+### 代码示例
+```java
+// 响应VO
+public class PhysicalDataResponse {
+    @Schema(description = "记录ID")
+    private String id;  // 改为String类型
+    // ...其他字段
+}
+
+// 服务层转换
+PhysicalDataResponse r = new PhysicalDataResponse();
+BeanUtils.copyProperties(record, r);
+r.setId(record.getId().toString());  // 关键转换
+```
+
+## 验证方法
+1. 调用API接口,检查返回的ID是否为字符串格式
+2. 在前端JavaScript中验证ID值是否正确
+3. 测试删除/更新操作是否正常工作
+
+## 适用场景
+- 使用大整数ID的系统(Snowflake、UUID等)
+- 前端需要精确处理ID的场景
+- 跨平台数据交互时
+
+## 注意事项
+- 数据库字段类型保持Long(或BigInt)
+- 后端业务逻辑继续使用Long类型
+- 只有API响应时转换为String
+- 前端接收后可根据需要转换为其他类型
+
+## 经验总结
+在前后端分离架构中,当遇到大整数精度问题时,优先考虑在API层面将数字类型转换为字符串类型返回,这样既保证了数据准确性,又避免了前端处理复杂性。字符串类型在JSON传输中是安全的,不会丢失精度。

+ 6 - 17
src/main/java/work/baiyun/chronicdiseaseapp/controller/PhysicalDataController.java

@@ -12,13 +12,10 @@ import work.baiyun.chronicdiseaseapp.common.R;
 import work.baiyun.chronicdiseaseapp.model.vo.AddPhysicalDataRequest;
 import work.baiyun.chronicdiseaseapp.model.vo.AddPhysicalDataRequest;
 import work.baiyun.chronicdiseaseapp.model.vo.PhysicalDataResponse;
 import work.baiyun.chronicdiseaseapp.model.vo.PhysicalDataResponse;
 import work.baiyun.chronicdiseaseapp.model.vo.BaseQueryRequest;
 import work.baiyun.chronicdiseaseapp.model.vo.BaseQueryRequest;
+import work.baiyun.chronicdiseaseapp.model.vo.DeletePhysicalDataRequest;
 import work.baiyun.chronicdiseaseapp.service.PhysicalDataService;
 import work.baiyun.chronicdiseaseapp.service.PhysicalDataService;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 
-import java.math.BigDecimal;
-import java.util.HashMap;
-import java.util.Map;
-
 @RestController
 @RestController
 @RequestMapping("/physical")
 @RequestMapping("/physical")
 @Tag(name = "体格 (Physical Data)", description = "体格数据相关接口")
 @Tag(name = "体格 (Physical Data)", description = "体格数据相关接口")
@@ -49,20 +46,12 @@ public class PhysicalDataController {
         }
         }
     }
     }
 
 
-    @Operation(summary = "计算 BMI", description = "根据 height(cm) 和 weight(kg) 返回 BMI 值")
-    @PostMapping(path = "/bmi", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
-    public R<?> calculateBmi(@RequestBody Map<String, Object> body) {
+    @Operation(summary = "删除体格数据", description = "根据ID删除体格测量记录")
+    @PostMapping(path = "/delete", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    public R<?> deletePhysicalData(@RequestBody DeletePhysicalDataRequest req) {
         try {
         try {
-            if (body == null) return R.fail(400, "Missing body");
-            Object h = body.get("height");
-            Object w = body.get("weight");
-            if (h == null || w == null) return R.fail(400, "Missing height or weight");
-            BigDecimal height = new BigDecimal(h.toString());
-            BigDecimal weight = new BigDecimal(w.toString());
-            BigDecimal bmi = physicalDataService.calculateBMI(height, weight);
-            Map<String, Object> out = new HashMap<>();
-            out.put("bmi", bmi);
-            return R.success(200, "ok", out);
+            physicalDataService.deletePhysicalData(req.getId());
+            return R.success(200, "ok");
         } catch (Exception e) {
         } catch (Exception e) {
             return R.fail(500, e.getMessage());
             return R.fail(500, e.getMessage());
         }
         }

+ 14 - 0
src/main/java/work/baiyun/chronicdiseaseapp/model/vo/DeletePhysicalDataRequest.java

@@ -0,0 +1,14 @@
+package work.baiyun.chronicdiseaseapp.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "删除体格数据请求")
+@Data
+public class DeletePhysicalDataRequest {
+
+    @Schema(description = "记录ID")
+    @NotNull(message = "ID不能为空")
+    private Long id;
+}

+ 1 - 1
src/main/java/work/baiyun/chronicdiseaseapp/model/vo/PhysicalDataResponse.java

@@ -10,7 +10,7 @@ import java.time.LocalDateTime;
 @Data
 @Data
 public class PhysicalDataResponse {
 public class PhysicalDataResponse {
     @Schema(description = "记录ID")
     @Schema(description = "记录ID")
-    private Long id;
+    private String id;
 
 
     @Schema(description = "身高(cm)")
     @Schema(description = "身高(cm)")
     private BigDecimal height;
     private BigDecimal height;

+ 1 - 3
src/main/java/work/baiyun/chronicdiseaseapp/service/PhysicalDataService.java

@@ -5,10 +5,8 @@ import work.baiyun.chronicdiseaseapp.model.vo.BaseQueryRequest;
 import work.baiyun.chronicdiseaseapp.model.vo.PhysicalDataResponse;
 import work.baiyun.chronicdiseaseapp.model.vo.PhysicalDataResponse;
 import work.baiyun.chronicdiseaseapp.model.vo.AddPhysicalDataRequest;
 import work.baiyun.chronicdiseaseapp.model.vo.AddPhysicalDataRequest;
 
 
-import java.math.BigDecimal;
-
 public interface PhysicalDataService {
 public interface PhysicalDataService {
     void addPhysicalData(AddPhysicalDataRequest request);
     void addPhysicalData(AddPhysicalDataRequest request);
     Page<PhysicalDataResponse> listPhysicalData(BaseQueryRequest request);
     Page<PhysicalDataResponse> listPhysicalData(BaseQueryRequest request);
-    BigDecimal calculateBMI(BigDecimal height, BigDecimal weight);
+    void deletePhysicalData(Long id);
 }
 }

+ 7 - 8
src/main/java/work/baiyun/chronicdiseaseapp/service/impl/PhysicalDataServiceImpl.java

@@ -12,8 +12,6 @@ import work.baiyun.chronicdiseaseapp.model.vo.PhysicalDataResponse;
 import work.baiyun.chronicdiseaseapp.model.vo.AddPhysicalDataRequest;
 import work.baiyun.chronicdiseaseapp.model.vo.AddPhysicalDataRequest;
 import work.baiyun.chronicdiseaseapp.service.PhysicalDataService;
 import work.baiyun.chronicdiseaseapp.service.PhysicalDataService;
 
 
-import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.util.List;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
@@ -48,7 +46,7 @@ public class PhysicalDataServiceImpl implements PhysicalDataService {
                 .map(record -> {
                 .map(record -> {
                     PhysicalDataResponse r = new PhysicalDataResponse();
                     PhysicalDataResponse r = new PhysicalDataResponse();
                     BeanUtils.copyProperties(record, r);
                     BeanUtils.copyProperties(record, r);
-                    r.setBmi(calculateBMI(record.getHeight(), record.getWeight()));
+                    r.setId(record.getId().toString());
                     return r;
                     return r;
                 })
                 })
                 .collect(Collectors.toList());
                 .collect(Collectors.toList());
@@ -62,12 +60,13 @@ public class PhysicalDataServiceImpl implements PhysicalDataService {
     }
     }
 
 
     @Override
     @Override
-    public BigDecimal calculateBMI(BigDecimal height, BigDecimal weight) {
-        if (height == null || weight == null || height.compareTo(BigDecimal.ZERO) <= 0) {
-            return BigDecimal.ZERO;
+    public void deletePhysicalData(Long id) {
+        Long userId = work.baiyun.chronicdiseaseapp.util.SecurityUtils.getCurrentUserId();
+        PhysicalData record = physicalDataMapper.selectById(id);
+        if (record == null || !record.getUserId().equals(userId)) {
+            throw new RuntimeException("记录不存在或无权限删除");
         }
         }
-        BigDecimal heightInMeters = height.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
-        return weight.divide(heightInMeters.pow(2), 2, RoundingMode.HALF_UP);
+        physicalDataMapper.deleteById(id);
     }
     }