Просмотр исходного кода

docs(guidelines): 补充长整型 ID 处理规范与映射建议

- 在各开发规范文档中新增关于雪花算法 ID 精度问题的说明
- 明确要求对外 API 响应中的 Long 类型 ID 应转为 String 类型
- 提供 VO/DTO 层手动转换及 MapStruct 映射配置示例
- 增加对测试、日志、序列化等场景下 ID 格式一致性的要求
- 补充 Jackson 序列化控制方式与注意事项
- 强调该变更为接口契约调整,需通知前端并做好兼容处理
mcbaiyun 1 месяц назад
Родитель
Сommit
22b2017e3e

+ 20 - 1
docs/DevRule/01-代码风格指南.md

@@ -195,4 +195,23 @@ public class ExampleController {
     return R.success(200, "ok", currentUserId);
   }
 }
-```
+```
+
+## VO/DTO 映射与 ID 类型处理(补充)
+
+为避免把数据库实体类型直接暴露给前端,建议在映射层(Service -> VO/DTO)统一处理 ID 字段类型转换。常见做法:
+
+- 在实体(PO)中使用 `Long`/`Long` 类型表示 ID,在响应 VO 中使用 `String` 类型;映射时显式将 `id` 转为字符串。
+- 推荐示例(简洁版):
+  ```java
+  PhysicalDataResponse r = new PhysicalDataResponse();
+  BeanUtils.copyProperties(record, r);
+  if (record.getId() != null) {
+      r.setId(record.getId().toString());
+  }
+  ```
+- 若团队引入 MapStruct 等映射框架,可在 mapper 配置中添加转换规则(例如 `@Mapping(target = "id", expression = "java(String.valueOf(record.getId()))")`),并在 `05-依赖管理规范.md` 中记录所用版本作为依赖约定。
+
+实现约定:
+- 映射逻辑放在 Service 的转换方法或单独的 Mapper 类中,避免 Controller/Repository 直接操作类型转换。 
+- 映射示例应写入代码风格指南,作为团队的示例片段,便于审查与复用。

+ 30 - 1
docs/DevRule/03-API设计规范.md

@@ -151,4 +151,33 @@
 ## 测试规范
 - API接口应编写单元测试和集成测试
 - 测试应覆盖正常流程和异常情况
-- 使用Mock框架模拟依赖服务
+- 使用Mock框架模拟依赖服务
+
+## 关于长整型 ID 的传输策略(补充)
+
+说明:项目使用雪花算法(Snowflake)生成的主键/业务 ID 为 64 位长整型(BIGINT / Long),在前端 JavaScript 中直接使用 Number 类型可能会发生精度丢失。为避免前端显示与处理错误,API 层对外响应时应把此类 ID 以字符串形式返回。
+
+要点:
+- 在对外响应的 DTO/VO 中,将 id 字段定义为 `String`(响应侧)。请求参数可继续使用 Long/BigInt(后端解析/校验)。
+- 在时序/批量返回场景(List/分页)中,同样应保证 id 字段为字符串类型。
+- 在 OpenAPI/Swagger 注释中显式标注字段类型为 `string`,并在说明中写明其实际在数据库/业务中的原始类型为 Long/BIGINT。
+
+示例(响应 VO 与转换):
+```java
+public class PhysicalDataResponse {
+   @Schema(description = "记录ID,数据库类型为BIGINT,JSON返回为字符串")
+   private String id; // 对外返回 String
+   // ... 其他字段
+}
+
+// 服务层转换示例
+PhysicalDataResponse r = new PhysicalDataResponse();
+BeanUtils.copyProperties(record, r);
+if (record.getId() != null) {
+   r.setId(record.getId().toString());
+}
+```
+
+兼容性与说明:
+- 该约定属于接口契约变更(响应字段类型从 number -> string),在发布到生产前请在 API 文档、变更日志与前端团队进行告知并做好契约验证(可用契约测试)。
+- 若需要向下兼容旧客户端,可考虑在文档中说明旧客户端如何兼容(例如前端接收到字符串后显式转换为 BigInt 或按字符串处理)。

+ 16 - 0
docs/DevRule/04-测试规范.md

@@ -74,3 +74,19 @@
 - 测试代码应与生产代码一同维护。
 - 避免在测试中使用硬编码的外部依赖。
 - 定期review测试代码,确保其有效性。
+
+### 关于长整型 ID 的测试
+
+目的:验证 API 在面对超过 JavaScript 安全整数范围(> 2^53-1)的 ID 时,前端与后端之间不会因为精度丢失而导致 CRUD 操作失败或数据不一致。
+
+建议测试项:
+- 接口契约测试:断言返回的 `id` 字段在 JSON 中为字符串类型(例如使用 MockMvc / RestAssured 检查 JSON schema)。
+- 集成测试(Controller -> Service -> Mapper):创建一条使用雪花算法生成的 ID(或模拟一个大于 2^53-1 的 ID),确保返回的 DTO 中 `id` 为字符串且值为 `record.getId().toString()`。
+- 更新/删除端到端测试:前端(或测试脚本)以字符串形式传递 ID 时,后端能正确解析并执行对应操作(请求参数仍可用 Long,或后端先尝试解析字符串为 Long)。
+- 边界测试:使用最大/最小 Long 值、以及接收非法字符串(非数字)时的错误处理与返回码。
+
+示例(伪测试步骤):
+1. 插入记录并获取数据库生成的 Long ID(例如:9223372036854775807 或任意大于 JS 安全整数的值)。
+2. 调用查询接口,断言响应 JSON 中 `id` 为字符串且等于数据库 ID 的字符串表示。
+3. 调用删除接口,传入字符串形式的 ID,断言操作成功并且数据库记录被删除。
+

+ 9 - 0
docs/DevRule/05-依赖管理规范.md

@@ -127,3 +127,12 @@
 ### 2. 缓存管理
 - 配置 Maven 仓库缓存,提高构建速度。
 - 使用本地仓库镜像加速依赖下载。
+
+## 可选映射库建议(补充)
+
+说明:为降低手动复制属性导致的错误并统一映射规则(例如 Long -> String 的 id 映射),团队可以选择引入映射库(如 MapStruct)以生成高效且可维护的转换代码。此处为可选建议:
+
+- 若采纳 MapStruct,推荐在 `pom.xml` 中通过 `<dependencyManagement>` 或 `<properties>` 约定 MapStruct 及其注解处理器的版本,并在 `05-依赖管理规范.md` 中记录推荐版本。示例版本:MapStruct 1.5.x(请根据项目的 Spring Boot / Java 版本选择兼容版本)。
+- 引入映射库后,应在 Code Review 与 CI 中加入对生成代码的检查,确保映射规则(如 id 字段转为字符串)在 mapper 接口中有明确配置。
+- 若不引入映射库,请在 `01-代码风格指南.md` 中保留映射示例(手动 copy + 显式 id.toString())作为团队约定。
+

+ 13 - 1
docs/DevRule/06-日志和错误处理规范.md

@@ -173,4 +173,16 @@ try {
 - 业务逻辑中的异常应抛出 `CustomException` 或记录日志后处理。
 - 全局异常处理器确保所有未捕获异常被统一处理。
 - 异常信息不应暴露敏感数据给客户端。
-- 所有控制器应统一使用R类返回错误响应,避免直接抛出异常,确保API响应格式的一致性。
+- 所有控制器应统一使用R类返回错误响应,避免直接抛出异常,确保API响应格式的一致性。
+
+### ID 在日志与错误信息中的格式(补充)
+
+建议:
+- 在记录日志或返回错误信息时,建议以字符串形式记录业务 ID,确保与对外 API 返回格式一致,便于排查与跨端比对。
+- 避免在日志格式化中对大整数进行科学计数或截断(例如使用占位符 `{}` 直接记录字符串值)。
+- 示例:
+  ```java
+  logger.info("处理记录完成,recordId={}", String.valueOf(record.getId()));
+  ```
+
+注意:日志中以字符串记录 ID 并不改变数据库或实体类型,仅是记录/展示层的一致性约定。

+ 21 - 0
docs/DevRule/07-数据库规范.md

@@ -93,3 +93,24 @@
 - 所有规范基于现有代码实现,不得引入未实现的特性。
 - 数据库设计应遵循第三范式(3NF),但可根据性能需求适当冗余。
 - 定期备份数据库,确保数据安全。
+
+## 对外接口 ID 传输策略(补充)
+
+说明:数据库中 `id` 及其它业务 ID 字段保持 `BIGINT`(或 Long)类型以保证唯一性与性能。但在对外 JSON 响应中,建议将此类长整型 ID 以字符串形式返回,以避免 JavaScript 的 Number 类型在表示超过 2^53-1 的整数时发生精度丢失。
+
+实施建议:
+- 数据库层与实体(Entity/PO)保持 `Long`/`BigInt` 类型。
+- 在序列化到客户端时,统一使用字符串序列化:可以在响应 VO/DTO 层手动转换(`record.getId().toString()`),或在 Jackson 中通过 `ToStringSerializer` 对 Long 类型字段做统一序列化(但注意这会影响所有 Long 字段的序列化表现)。
+- 在 ORM/Mapper 层不做改变,仅在 DTO/VO 层或全局序列化配置中做转换。
+
+示例(使用 Jackson 注解局部控制):
+```java
+public class PhysicalDataResponse {
+  @Schema(description = "记录ID,返回为字符串以避免前端精度丢失")
+  @JsonSerialize(using = ToStringSerializer.class)
+  private String id;
+}
+```
+
+注意:统一序列化所有 Long 为字符串会影响日志/调试输出和部分客户端契约,如需细粒度控制建议在 VO 层显式转换并在 API 文档中说明。
+