Skip to content

API设计规范

🔌 接口标准:JPwise项目的API设计规范和最佳实践

📋 目录


HTTP方法规范

🚨 重要:只使用GET和POST方法

java
// ✅ 正确:JPwise项目标准
@GetMapping              // 查询操作
@PostMapping            // 创建、更新、删除操作

// ❌ 错误:不要使用其他HTTP方法
@PutMapping             // 不使用
@DeleteMapping          // 不使用
@PatchMapping          // 不使用

标准接口设计

java
@RestController
@RequestMapping("/api/extend/Business")
public class BusinessController {
    
    // 查询列表 - GET
    @BuildQuery
    @GetMapping
    public ActionResult<PageListVO<BusinessEntity>> getList(QueryBuilder pagination) { }
    
    // 获取详情 - GET
    @GetMapping("/{id}")
    public ActionResult<BusinessEntity> getInfo(@PathVariable String id) { }
    
    // 创建数据 - POST
    @PostMapping
    public ActionResult<String> create(@Valid @RequestBody BusinessEntity entity) { }
    
    // 更新数据 - POST (不是PUT)
    @PostMapping("/update/{id}")
    public ActionResult<String> update(@PathVariable String id, @Valid @RequestBody BusinessEntity entity) { }
    
    // 删除数据 - POST (不是DELETE)
    @PostMapping("/{id}")
    public ActionResult<String> delete(@PathVariable String id) { }
}

URL路径设计

基础路径规范

java
// 标准格式:/api/{模块}/{资源}
@RequestMapping("/api/extend/LeaveApplication")     // 业务扩展模块
@RequestMapping("/api/system/User")                 // 系统模块
@RequestMapping("/api/workflow/Task")               // 工作流模块
@RequestMapping("/api/file/Upload")                 // 文件模块

模块路径映射

java
jpwise-extend    → /api/extend/*      // 业务扩展
jpwise-system    → /api/system/*      // 系统功能
jpwise-workflow  → /api/workflow/*    // 工作流
jpwise-file      → /api/file/*        // 文件管理
jpwise-hr        → /api/hr/*          // 人力资源

资源操作路径

java
// 基础CRUD操作
GET    /api/extend/Business              // 查询列表(分页)
GET    /api/extend/Business/{id}         // 查询详情
POST   /api/extend/Business              // 创建
POST   /api/extend/Business/update/{id}  // 更新
POST   /api/extend/Business/{id}         // 删除

// 批量操作
POST   /api/extend/Business/Actions/Import    // 批量导入
POST   /api/extend/Business/Actions/Export    // 批量导出
POST   /api/extend/Business/Actions/BatchDelete  // 批量删除

// 业务操作
POST   /api/extend/Business/{id}/submit       // 提交
POST   /api/extend/Business/{id}/approve      // 审批
POST   /api/extend/Business/{id}/reject       // 拒绝

URL命名规范

  • 使用名词,不使用动词
  • 资源名称使用驼峰命名
  • 路径参数用小写
  • 批量操作放在 /Actions/

请求参数规范

查询参数 - QueryBuilder

java
// ✅ 使用@BuildQuery注解自动构建查询参数
@BuildQuery
@GetMapping
public ActionResult<PageListVO<Entity>> getList(QueryBuilder pagination) {
    // pagination自动包含:
    // - currentPage: 当前页码
    // - pageSize: 页大小
    // - keyword: 搜索关键字
    // - condition: 其他查询条件
}

// 前端传参示例
// GET /api/extend/Business?currentPage=1&pageSize=10&keyword=测试&status=APPROVED

请求体参数

java
// 创建和更新直接使用实体类
@PostMapping
public ActionResult<String> create(@Valid @RequestBody BusinessEntity entity) {
    // @Valid 自动验证实体类的约束注解
}

// 复杂操作使用DTO
@PostMapping("/batch")
public ActionResult<String> batchOperation(@RequestBody BatchOperationDTO dto) {
    // DTO包含操作类型和数据列表
}

参数验证

java
// 实体类验证注解
public class BusinessEntity extends BaseEntity {
    
    @NotBlank(message = "标题不能为空")
    @Size(max = 200, message = "标题长度不能超过200字符")
    private String TITLE;
    
    @NotNull(message = "金额不能为空")
    @DecimalMin(value = "0.01", message = "金额必须大于0")
    private BigDecimal AMOUNT;
    
    @Pattern(regexp = "^(DRAFT|PENDING|APPROVED)$", message = "状态值不正确")
    private String STATUS;
}

响应格式规范

统一响应结构 - ActionResult

java
// 成功响应结构
{
    "code": 200,
    "msg": "操作成功",
    "data": {
        // 具体业务数据
    },
    "time": 1692000000000
}

// 分页响应结构
{
    "code": 200,
    "msg": "查询成功",
    "data": [
        // 数据列表
    ],
    "pagination": {
        "currentPage": 1,
        "pageSize": 10,
        "totalRow": 100,
        "totalPage": 10
    },
    "time": 1692000000000
}

// 错误响应结构
{
    "code": 500,
    "msg": "操作失败",
    "data": null,
    "time": 1692000000000
}

响应方法使用

java
// 成功返回数据
return ActionResult.success(entity);

// 成功返回消息
return ActionResult.success(MsgCode.SU001.get());

// 失败返回
return ActionResult.fail("操作失败");
return ActionResult.fail(MsgCode.FA001.get());

// 分页返回
PaginationVO paginationVO = JsonUtil.getJsonToBean(pagination, PaginationVO.class);
return ActionResult.page(dataList, paginationVO);

状态码规范

java
// 使用项目定义的消息码
public class MsgCode {
    public static final MsgCode SU001 = new MsgCode(200, "操作成功");
    public static final MsgCode FA001 = new MsgCode(500, "操作失败");
    public static final MsgCode FA002 = new MsgCode(500, "数据不存在");
    public static final MsgCode FA003 = new MsgCode(500, "数据已存在");
}

分页查询规范

分页参数

java
// 标准分页参数
public class QueryBuilder {
    private Integer currentPage = 1;    // 当前页,从1开始
    private Integer pageSize = 10;      // 页大小,默认10
    private String keyword;             // 搜索关键字
    private Map<String, Object> condition; // 其他查询条件
}

分页实现

java
@Service
public class BusinessServiceImpl {
    
    @Override
    public List<BusinessEntity> getList(QueryBuilder pagination) {
        QueryWrapper queryWrapper = new QueryWrapper();
        
        // 基础过滤
        queryWrapper.eq(BusinessEntity::getENABLEDMARK, "1")
                   .isNull(BusinessEntity::getDELETEUSER);
        
        // 关键字搜索
        if (StringUtil.isNotEmpty(pagination.getKeyword())) {
            queryWrapper.and((Consumer<QueryWrapper>) w -> {
                w.like(BusinessEntity::getTITLE, pagination.getKeyword())
                 .or((Consumer<QueryWrapper>) ww -> ww.like(BusinessEntity::getCODE, pagination.getKeyword()));
            });
        }
        
        // 其他条件过滤
        Map<String, Object> condition = pagination.getCondition();
        if (condition != null) {
            if (condition.get("status") != null) {
                queryWrapper.eq(BusinessEntity::getSTATUS, condition.get("status"));
            }
        }
        
        // 分页查询
        Page<BusinessEntity> page = new Page<>(pagination.getCurrentPage(), pagination.getPageSize());
        Page<BusinessEntity> result = this.page(page, queryWrapper);
        
        // 设置分页数据并返回
        return pagination.setData(result.getRecords(), result.getTotalRow());
    }
}

前端调用示例

javascript
// GET请求示例
/api/extend/Business?currentPage=1&pageSize=10&keyword=测试&status=APPROVED

// 响应数据
{
    "code": 200,
    "data": [
        // 业务数据列表
    ],
    "pagination": {
        "currentPage": 1,
        "pageSize": 10,
        "totalRow": 25,
        "totalPage": 3
    }
}

错误处理规范

全局异常处理

java
@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(BusinessException.class)
    public ActionResult handleBusinessException(BusinessException e) {
        log.error("业务异常:{}", e.getMessage());
        return ActionResult.fail(e.getMessage());
    }
    
    @ExceptionHandler(DataException.class)
    public ActionResult handleDataException(DataException e) {
        log.error("数据异常:{}", e.getMessage());
        return ActionResult.fail("数据异常:" + e.getMessage());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ActionResult handleValidationException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getFieldErrors().stream()
            .map(DefaultMessageSourceResolvable::getDefaultMessage)
            .collect(Collectors.joining(", "));
        return ActionResult.fail("参数验证失败:" + message);
    }
}

业务异常抛出

java
// Controller层
@PostMapping
public ActionResult<String> create(@Valid @RequestBody BusinessEntity entity) {
    try {
        businessService.create(entity);
        return ActionResult.success(MsgCode.SU001.get());
    } catch (BusinessException e) {
        return ActionResult.fail(e.getMessage());
    }
}

// Service层
@Override
public void create(BusinessEntity entity) {
    if (StringUtil.isEmpty(entity.getTITLE())) {
        throw new BusinessException("标题不能为空");
    }
    
    if (entity.getAMOUNT().compareTo(BigDecimal.ZERO) <= 0) {
        throw new BusinessException("金额必须大于0");
    }
    
    // 业务逻辑处理...
}

接口文档规范

Swagger注解

java
@RestController
@RequestMapping("/api/extend/Business")
@Api(tags = "业务管理")
public class BusinessController {
    
    @ApiOperation(value = "获取业务列表", notes = "支持分页和关键字搜索")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "currentPage", value = "当前页", defaultValue = "1"),
        @ApiImplicitParam(name = "pageSize", value = "页大小", defaultValue = "10"),
        @ApiImplicitParam(name = "keyword", value = "搜索关键字")
    })
    @BuildQuery
    @GetMapping
    public ActionResult<PageListVO<BusinessEntity>> getList(QueryBuilder pagination) {
        // 实现
    }
    
    @ApiOperation(value = "创建业务", notes = "创建新的业务记录")
    @PostMapping
    public ActionResult<String> create(@ApiParam("业务实体") @Valid @RequestBody BusinessEntity entity) {
        // 实现
    }
}

实体类文档注解

java
@ApiModel("业务实体")
public class BusinessEntity extends BaseEntity {
    
    @ApiModelProperty(value = "业务标题", required = true, example = "测试业务")
    private String TITLE;
    
    @ApiModelProperty(value = "业务金额", required = true, example = "1000.00")
    private BigDecimal AMOUNT;
    
    @ApiModelProperty(value = "业务状态", allowableValues = "DRAFT,PENDING,APPROVED", example = "DRAFT")
    private String STATUS;
}

接口设计最佳实践

1. RESTful风格适配

虽然只使用GET/POST,但URL设计仍遵循RESTful原则:

  • 资源用名词,不用动词
  • 层级关系清晰
  • 语义明确

2. 统一性原则

  • 所有接口返回格式统一
  • 参数命名风格统一
  • 错误处理方式统一

3. 可扩展性

  • 预留扩展字段
  • 版本控制考虑
  • 向后兼容保证

4. 安全性

  • 参数验证完整
  • 权限控制到位
  • 敏感信息过滤

5. 性能考虑

  • 分页查询必须
  • 大数据量导出异步处理
  • 缓存策略合理

接口测试规范

单元测试

java
@SpringBootTest
@AutoConfigureMockMvc
class BusinessControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void testGetList() throws Exception {
        mockMvc.perform(get("/api/extend/Business")
                .param("currentPage", "1")
                .param("pageSize", "10"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }
    
    @Test
    void testCreate() throws Exception {
        BusinessEntity entity = new BusinessEntity();
        entity.setTITLE("测试标题");
        entity.setAMOUNT(new BigDecimal("1000"));
        
        mockMvc.perform(post("/api/extend/Business")
                .contentType(MediaType.APPLICATION_JSON)
                .content(JsonUtil.getObjectToString(entity)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200));
    }
}

💡 提示:良好的API设计是系统稳定性和可维护性的基础。遵循这些规范,可以确保接口的一致性和可预测性,提高开发效率和系统质量。