前后端命名与接口规范
研发开发规范
- 给 Java 类、方法、变量起符合规范的名字
- 用 Swagger/OpenAPI3 注解把接口文档「写进代码里」
- 理解为什么前后端要共用同一套命名约定
- 驼峰命名 camelCase
- 首字母小写,后续单词首字母大写,如
driverInstance。Java 和 JavaScript 的标准命名风格。 - kebab-case
- 单词全小写,用连字符连接,如
driver-instance。URL 路径的标准风格。 - Swagger / OpenAPI
- 自动生成 API 文档的工具。通过在代码中加注解(如
@Tag、@Operation),Swagger 会自动生成可交互的接口文档页面。
下表示例取自 EasyPro 代码库(包名 com.sctech.opti.cloud、资源 DriverInstance 等),命名规范本身适用于所有产品。
| 对象 | 规范 | 示例 |
|---|---|---|
| 包名 | com.sctech.opti.cloud.<模块>.<子模块> | com.sctech.opti.cloud.modules.iot.driver |
| Controller | <资源>Controller,@RestController | DriverInstanceController |
| Service 接口 | <资源>Service | DriverInstanceService |
| Service 实现 | <资源>ServiceImpl | DriverInstanceServiceImpl |
| Mapper | <资源>Mapper + 同名 XML | DriverInstanceMapper |
| 实体 | <资源> 或 <资源>Entity | DriverInstance |
| DTO(入参) | <资源>DTO / <操作><资源>DTO | SaveDriverInstanceDTO |
| VO(出参) | <资源>VO | DriverInstanceVO |
| 枚举 | <语义>Enum | DriverTypeEnum |
| API 路径 | /api/<模块>/<资源>,小写连字符 | /api/iot/driver-instance |
| 方法名(Controller) | 驼峰,动词前缀 | list / getById / save / update / delete |
| 常量 | 全大写下划线 | MAX_RETRY_COUNT |
一眼能看懂的命名
DriverInstanceController— 管理什么+是什么SaveDriverInstanceDTO— 动作+资源+类型DriverTypeEnum— 语义+类型/api/iot/driver-instance— 模块/资源,小写连字符
让人猜的命名
Ctrl1/Manager— 编号/泛称,不知管啥Data/Info/Temp— 无意义后缀UserServiceImpl2— 靠编号区分实现/api/doSomething— 动词当资源,无模块归属
| 对象 | 规范 | 示例 |
|---|---|---|
| 组件文件 | PascalCase | DriverInstanceList.vue |
| 组件名(注册) | PascalCase | DriverInstanceList |
| 路由 path | kebab-case | /iot/driver-instance |
| 变量/方法 | camelCase | driverList / handleSave() |
| 常量 | UPPER_SNAKE_CASE | API_BASE_URL |
| API 请求文件 | 按模块组织,与后端模块对齐 | api/iot/driverInstance.js |
| API 函数名 | 与后端方法对齐 | getDriverInstanceList() |
| CSS class | kebab-case,BEM 可选 | driver-instance-card |
前后端路径必须一致:后端 /api/iot/driver-instance ↔ 前端 api/iot/driverInstance.js,避免命名漂移。
| 对象 | 规范 | 示例 |
|---|---|---|
| 数据库表名 | ibms-<模块>(业务库) / iot_<实体>(物联库),沿用现有 | ibms-system / iot_driver |
| 字段名 | snake_case | project_id / create_time |
| 文件名(文档) | <主题>.md,可中文 | 开发规范.md / 驱动开发指南.md |
| 文件名(代码/脚本) | 英文小写下划线/连字符 | driver_instance.sql |
| 目录名 | 小写英文,业务文档目录可中文 | doc/规则/ / sql/release/2.1.1/ |
本项目使用 springdoc-openapi(OpenAPI 3),接口文档由 @Tag/@Operation/@Schema 等注解描述。规范如下:
注解用法:
| 注解 | 作用位置 | 规范 |
|---|---|---|
@Tag | Controller 类 | 接口分组,统一格式 (<模块>)<中文描述> |
@Operation | Controller 方法 | 必填 summary(接口功能,中文祈使句) |
@Schema | DTO/VO/Entity 字段 | 必填 description;入参 DTO 补 example |
@Parameter | 方法参数(非实体字段) | 描述 path/query 参数 |
~~@Api~~ ~~@ApiOperation~~ ~~@ApiModel~~ | — | 已废弃(Swagger2 注解),全量替换为 OpenAPI3 注解 |
为什么把文档写进注解,而不是单独维护 Word?因为代码和文档一旦分离,就一定会脱节——改了接口忘了改文档,前端按旧文档联调就会踩坑。注解和代码放在一起,改接口时顺手就能改注解,Swagger 自动生成最新文档页面,前端随时可查。这是「单一事实来源」原则:代码即文档。
@Tag 命名格式(统一为现有主流风格,修正不一致项):
// [✓] 统一格式:<模块>用小写英文括号,后接中文描述 @Tag(name = "(system)用户管理") @Tag(name = "(iot)驱动实例") @Tag(name = "(energy)能耗报表") // [✗] 不规范(缺模块前缀 / 中英混排 / 大小写不一) @Tag(name = "收支对账管理") // 缺 (bill) 前缀 @Tag(name = "B 端支付订单查询") // 中英混排 @Tag(name = "(bill) 用户资金密码管理") // 前缀后多空格
模块前缀参考:(system) (iot) (energy) (eoms) (bill) (driver) (zw),与 §2.5 scope 对齐。
为什么 @Tag 要带 (模块) 前缀?Swagger 文档按 Tag 分组展示全部接口,一百多个 Controller 若只用中文描述命名,分组就是一堆无法检索的散乱标题;带模块前缀后,接口在文档里按模块聚合、可折叠可搜索。前缀还与提交 scope、模块包名共用同一套词汇——从提交记录到接口文档,一个模块名走遍全链路。
@Operation 与方法:
@Operation(summary = "新增驱动实例", description = "可选的详细说明")
@PostMapping
public Result<Void> save(@Validated @RequestBody SaveDriverDTO dto) { ... }SwaggerConfig 标准写法:
@Configuration
public class SwaggerConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
// 统一鉴权:让文档支持带 Authorization 请求调试
.components(new Components().addSecuritySchemes("Authorization",
new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER)
.name("Authorization")))
.addSecurityItem(new SecurityRequirement().addList("Authorization"))
.info(new Info()
.title("源创云平台")
.version("2.0.0")
.description("源创云平台在线 API"));
}
}统一返回 Result<T>:所有 Controller 返回 com.sctech.opti.cloud.common.core.api.data.Result<T>,结构为 { success, code, msg, data, errorMsg },禁止裸返回实体或 Map。
为什么必须统一返回 Result<T>,而不是直接返回实体或 Map?统一结构后,前端只需要写一套响应解析:先看 success/code,再取 data。裸返回意味着每个接口的出错形态都不一样——有的抛 500、有的返回 null、有的塞在 Map 里,前端没法统一处理,全局异常处理器也无从兜底。返回结构是前后端的「接口契约」,契约只有一种,协作才不会扯皮。
我给变量起了 UserServiceImpl2,因为已经有个 Impl 了,这样对吗?
UserService,实现类固定叫 UserServiceImpl(后缀 Impl)。如果需要第二个实现,说明它们代表不同策略,应该用语义命名,比如 UserRdbServiceImpl(关系库实现)和 UserRedisServiceImpl(缓存实现),而不是加数字编号。我给 Controller 方法加了 @Operation 但没写 summary,会怎样?
getUserById),前端同事看不出它是干嘛的。summary 是必填的,要用中文祈使句写清楚功能,比如 @Operation(summary = "根据ID查询用户详情")。提交前检查清单第 7 项会检查这个。我用的是 Swagger2 的 @Api / @ApiOperation,有什么问题?
@Api→@Tag、@ApiOperation→@Operation、@ApiModel→@Schema。Swagger2 注解已废弃,新代码必须用 OpenAPI3,存量代码遇到时顺手替换。前端组件名我用了驼峰 myComponent,review 时被要求改?
user-profile-card,避免和 HTML 原生元素冲突(单词名可能撞 <myComponent> 被当成未知标签)。变量、方法用驼峰(getUserInfo),但组件模板里用短横线。